architecture-drift-review
Skill0 starsChanged 3 months agoMITView on GitHub
Audit a codebase against its own declared architecture — invariants, boundaries, contracts — and drive out drift with concrete fixes and durable guards, not a report. The architecture doc stays current as a byproduct, never the goal. Run after major feature work or before a significant release.
In this repositoryOne other file it gives its agents.
- agent-context-auditSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. About 0 are common, 13 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈13
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Every concept has a single producer — one module owns writing each store/artifact/config.
- Distinctive: line 43
Before anything else — every later command depends on this:
- Distinctive: line 77
Search repo-wide and case-insensitively — architecture docs live in nested doc trees at least as often as at the root, and are not always …
- Distinctive: line 93
Read the architecture document in full if one exists.
- Distinctive: line 111
The N/A protocol: When a dimension's concept is absent from this shape (no database, no routes, no long-lived runtime), write one line — …
- Distinctive: line 137
Verify layering: do lower layers import upward?
- Distinctive: line 203
If this returns empty, prove the absence before recording it: open one or two central files, identify the project's *actual* …
- Distinctive: line 206
Only then is the emptiness verified.
- Distinctive: line 208
Every such duplication is a future inconsistency.
- Distinctive: line 216
Verify the doc's factual claims against code — triaged, not uniformly:
- Distinctive: line 244
Run the structural checks found in Step 2.
- Distinctive: line 285
Deterministic evidence first: Run the counting command before writing the opinion.
- Distinctive: line 292
The doc is a byproduct: If the review produces only an updated document and no fixes or guards, it described the system instead of making …
- Distinctive: line 295
Record the run: Leave a dated completion note wherever this project tracks review cadence (a checks file, a doc header), so a review cycle …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Architecture Drift Review
You are a senior software architect whose job is to ensure this architecture
is robust and stays robust — not to describe it. Be specific, critical, and
practical. Tie every finding to actual code (file:line); no generic advice.
"Robust" is concrete here:
- The project's declared invariants hold when checked against real code.
- Every concept has a single producer — one module owns writing each store/artifact/config; everyone else reads.
- Structural contracts (import rules, layering, lint budgets) pass and are draining — a violation budget that never shrinks is a dead letter.
- Existing guards still earn their place; guards for retired subsystems get retired with them.
- Any drift you find leaves with a fix or a new guard. A finding with no detector or fix attached will regress — that is a failure of this review.
Every example command in this skill is an illustration for one stack, not
the instruction itself. Detect the project's languages and layout in Step 1
FIRST, then regenerate each example's file extensions, source roots, and
idioms for what you actually found before running it. An unadapted example
run against the wrong stack returns empty or — worse — plausible wrong
numbers, and empty output from a wrong-stack command is not evidence of
anything (see "verified absence" below). Prefer git ls-files/find over
bare shell globs: an unmatched glob aborts the whole command in zsh, and
2>/dev/null cannot suppress it.
Two run modes. In your own repo, findings land as edits, guards, and tracked work items. Reviewing a repo you don't own (or any read-only run): produce the same findings as a report with proposed patches, and do not file upstream issues or write into the repo unasked. State the mode at the top of your output.
Step 1: Detect the stack, size, and shape
Before anything else — every later command depends on this:
# Language mix (extension histogram — no hardcoded list). Before picking the
# dominant language, discount fixture/generated/snapshot formats (e.g. a test
# framework's golden-file extension can rival real source counts).
git ls-files | sed -n 's/.*\.//p' | sort | uniq -c | sort -rn | head -10
# Dependency manifest(s), any ecosystem
git ls-files | grep -E '(^|/)(package\.json|pyproject\.toml|setup\.cfg|go\.mod|Cargo\.toml|Gemfile|pom\.xml|build\.gradle(\.kts)?|composer\.json|mix\.exs|Package\.swift|.*\.csproj)$'
# Directory shape; then size the DOMINANT language(s) from the histogram above
git ls-files | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -20
git ls-files '*.<dominant-ext>' | xargs wc -l 2>/dev/null | sort -rn | head -15
# CI and checks (find, not globs — catches .yml and .yaml)
find .github/workflows .gitlab-ci.yml .circleci -type f 2>/dev/null | head -10 | xargs -I{} sh -c 'echo "== {}"; head -30 "{}"'
# Test layout — exclude fixture/golden dirs from the count (check the repo's
# own naming too: test_data, __snapshots__, spec/fixtures, …)
git ls-files | grep -E '(test|spec)' | grep -viE '(test_?data|fixtures|golden|snapshots)/' | sed 's|/[^/]*$||' | sort -u | headThen classify the shape — it decides which review dimensions apply and how (Step 3's table): deployed service (owns a runtime, stores, inbound traffic) · library/framework (consumed by other code; its "API surface" is the architecture) · CLI tool (argv in, exit code out) · local server/daemon (LSP, agent — long-lived but not networked-public) · monorepo/workspace (apply per-package). State stack + size + shape in one paragraph at the top of your findings — misreading the shape invalidates the review.
Step 2: Find the declared architecture
Search repo-wide and case-insensitively — architecture docs live in nested doc trees at least as often as at the root, and are not always Markdown:
git ls-files | grep -iE '(architecture|design|adr[s]?/|decision|rfc|invariant)' | head -20
git ls-files | grep -iE '(contributing|hacking|development)\.(md|rst|txt)$' | headCollect the encoded contracts too — lint and structural-check configs are
declared architecture. Check the names for the stack you detected (e.g.
.importlinter, [tool.ruff] in pyproject.toml · eslint.config.* and
legacy .eslintrc.* · .golangci.yml · clippy.toml, rustfmt.toml,
deny.toml, [workspace.lints] in Cargo.toml · checkstyle/detekt), and
look where checks hide when they're custom: Makefile/justfile targets, CI
steps, xtask-style helper crates, bin/check-* scripts.
Read the architecture document in full if one exists. Note every factual claim: component boundaries, data ownership, layering rules, named invariants. You will verify these in Step 3.10.
If no architecture document exists, the review inverts: derive the actual
architecture from evidence and write the first ARCHITECTURE.md — scoped to
your run. A shallow or --focus run produces a verified skeleton (shape,
components, the 3–5 invariants the code visibly relies on), not a treatise;
note what a deeper pass should add. In read-only mode, the doc goes in your
report as a proposal instead of into the repo.
Step 3: Conduct the review
Work through each dimension (or only --focus). For each: adapt the example
commands to the detected stack, gather evidence first, then judge.
Deterministic counts beat impressions — "17 modules import the DB layer
directly, the contract says 1" is a finding; "coupling seems high" is not.
The N/A protocol. When a dimension's concept is absent from this shape
(no database, no routes, no long-lived runtime), write one line — "N/A: no
<concept> in this codebase (verified: <command you ran>)" — and move on.
Verified absence (you looked for the concept and proved it isn't there)
is a legitimate result; unverified emptiness (your grep matched nothing
because it was written for another stack) is not a result at all. Never let
the second masquerade as the first.
Shape reframings for the narrow dimensions:
| Dimension | Service | Library | CLI / local daemon |
| 3.4 Security | AuthN/Z on every route; SQL/shell/template injection; secrets | unsafe API defaults; input parsing of untrusted data; supply chain | argv/env/filesystem trust boundary; subprocess & shell construction (an --exec flag is an injection surface); parsing untrusted files |
| 3.6 Observability | logs/metrics answer "what happened at 3am"; health checks that check something | error types that carry context; debuggability of consumer failures | distinguishing exit codes; actionable stderr; --verbose/trace paths |
| 3.3 Reliability | dependency-down behavior; retries; idempotency | API stability contracts; error propagation discipline | partial-failure semantics; interrupted-run/resume behavior |
The table covers the three most service-biased dimensions; the others carry their shape adjustments inline (e.g. 3.1's library analogue) — reframe any dimension, not just these three, when the shape calls for it.
3.1 Boundaries & single-producer
- Map who writes each store (DB table, config file, generated artifact, cache). More than one writer per concept is drift — name the canonical producer. In a library, the analogue is public-API ownership: which module exports each concept, and does anything bypass it?
- Verify layering: do lower layers import upward? Do siblings that should be independent import each other?
# Example (adapt idioms + roots to the stack): who opens the datastore directly?
# Python/JS: "sqlite3.connect|createConnection|new PrismaClient"
# Go: "sql.Open|pgxpool.New" Rust: "Connection::open|Pool::"
# -E is required: these patterns use alternation, which plain grep treats as
# literal characters and silently matches nothing.
grep -rnE "<store-opening-idioms>" --include='*.<ext>' <source-roots> | grep -v test3.2 Scalability & performance
- What breaks first under 10× load (service) or 10× input size (CLI/library) — connection handling, N+1 queries, unbounded result sets, quadratic scans, synchronous I/O on hot paths?
3.3 Reliability & fault tolerance
See the shape table. For services: what happens when each external dependency is down? For batch/CLI: are writes idempotent and interruptions resumable?
3.4 Security
See the shape table. Always: secrets in code or config; dependency freshness; how untrusted input reaches dangerous sinks for this shape.
3.5 Maintainability & code organization
- Is there a service/domain layer, or does logic live in routes and CLI commands directly? Find logic duplicated between entry points.
- Dead code: files no entry point, registry, or import reaches.
3.6 Observability
See the shape table.
3.7 Testing architecture
- Shape of the pyramid (excluding fixture/golden data from the count); what the tests would not catch — the gaps are the finding. Do tests assert behavior or implementation? Flake sources.
3.8 Data architecture
- Schema quality: types, constraints, indexes matching query patterns (N/A protocol applies if there is no persistent store).
- Config/registry files: is each fact defined once? Cross-file duplication of the same list/mapping is a magic-value finding (3.9).
3.9 Magic values & config drift
The canonical drift pattern — hardcoded literals duplicating a config source. The pattern below is per-language; regenerate for the stack:
# Python: ^[A-Z_]+ = \[|^[A-Z_]+ = { JS/TS: ^(export )?const [A-Z_]+ = [\[{]
# Go: ^var [A-Z]\w* = \[\]|^const \( Rust: ^(pub )?(const|static) [A-Z_]+
# CommonJS/legacy JS: ^(var|let) [A-Z_]+ = [\[{]|^(module\.)?exports\.[A-Z_]+
# -E is required: alternation in plain grep silently matches nothing.
grep -rnE "<all-caps-collection-idiom>" --include='*.<ext>' <source-roots> | grep -v test | head -20
# Then: for each hit, does it duplicate a registry/config/schema that already owns these values?If this returns empty, prove the absence before recording it: open one or two central files, identify the project's actual constant-declaration idiom (older codebases predate the conventions above), and re-run with that idiom. Only then is the emptiness verified.
Every such duplication is a future inconsistency. The fix is single-sourcing, and where the class recurs, a detector.
3.10 Architecture document accuracy
(Vacuously N/A if Step 2 found no doc — skip to 3.11; the inversion branch already owns the doc deliverable.)
Verify the doc's factual claims against code — triaged, not uniformly:
- Mechanical claims (paths, module names, "only X does Y"): verify ALL,
but in batch — extract the path-shaped references (contains
/or a file extension; backticked type/function/env-var names are not paths and only produce noise) and match them as substrings of the file list, which also handles paths written relative to a subdirectory:
grep -oE '`[a-zA-Z0-9_/.-]+`' <doc> | tr -d '`' | grep -E '/|\.[a-z]+$' | sort -u |
while read -r p; do git ls-files | grep -qF "$p" || echo "STALE REF: $p"; done- Behavioral claims ("all writes go through the queue"): verify the 3–5 with the largest blast radius using targeted greps; list the rest as spot-checked or unverified. Normative claims with no cheap probe ("the server is stateless") don't get force-grepped or silently skipped — list them as needs-a-probe, which is itself a finding: they're candidates for new detectors.
- Aspirational prose ("strives to provide a perfect API"): not falsifiable — mark as intent, don't grep it, and don't count it toward coverage.
Claims that fail get corrected in the doc (Step 5) and assessed: was the doc stale, or did the code drift from a rule that should be enforced?
3.11 Contract & guard health
- Run the structural checks found in Step 2. If a tool isn't installed locally, don't skip silently: reproduce what CI runs (read the workflow / Makefile target and run its available sub-steps), or mark that contract "declared but unverified in this run" — never "passing." Reproduction is only worth it when it needs no new installs or network fetches; in a read-only or third-party run, never install dependencies — mark unverified instead.
- For each ratcheted budget: is it draining? A budget at 0 → tighten to a hard fail. A budget unchanged for months → either enforce progress or admit the rule is aspirational and remove it.
- For each guard: what incident class does it prevent? Guards whose subject was deleted get retired — dead guards teach people to ignore guards.
Step 4: Synthesize
Produce, in order:
- Robustness verdict — one paragraph, would you sign off a release?
- Ranked risks — likelihood × blast radius, each with
file:line. - Prioritized action plan — every item is a fix or a new guard, sized (S/M/L), with acceptance criteria a future session could verify.
- Structural vs. symptom split — for each finding class with ≥2 instances, the action targets the class: a detector (grep/AST/lint rule/CI check) that counts instances and ratchets down, not just fixes to the instances found today.
In your own repo, actions land in the project's work-tracking system (issues, task files — wherever this project tracks work), not only in the chat transcript. In read-only mode, the action plan is the deliverable — don't file issues on repos you don't maintain unasked.
Step 5: Update the architecture doc
Correct in passing everything Step 3.10 found stale — component moves, retired subsystems, new invariants worth declaring. Keep it a map, not a changelog: the doc describes the current system; git history remembers the rest. If Step 2 found no doc, write the first one now (scoped per Step 2's sizing rule; proposed-not-committed in read-only mode).
Principles
- Deterministic evidence first. Run the counting command before writing the opinion. If a claim can be counted, count it.
- Adapt, then run. An example command executed against the wrong stack produces convincing non-evidence. If a command returned empty, prove the emptiness is real (verified absence) before recording it.
- No finding without an exit. Fix attached, or detector attached, or explicitly accepted with a dated note. Nothing ends as "should be looked at."
- The doc is a byproduct. If the review produces only an updated document and no fixes or guards, it described the system instead of making it more robust — start over at Step 3.
- Record the run. Leave a dated completion note wherever this project tracks review cadence (a checks file, a doc header), so a review cycle that stops running goes visibly stale.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

