agentleFS
ken-talltree-ioken-talltree-io/agent-skills/architecture-drift-review/SKILL.md

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.

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
  1. Very specific:

    Every concept has a single producer — one module owns writing each store/artifact/config.

    line 16
  2. Distinctive:

    Before anything else — every later command depends on this:

    line 43
  3. Distinctive:

    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 …

    line 77
  4. Distinctive:

    Read the architecture document in full if one exists.

    line 93
  5. Distinctive:

    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 — …

    line 111
  6. Distinctive:

    Verify layering: do lower layers import upward?

    line 137
  7. Distinctive:

    If this returns empty, prove the absence before recording it: open one or two central files, identify the project's *actual* …

    line 203
  8. Distinctive:

    Only then is the emptiness verified.

    line 206
  9. Distinctive:

    Every such duplication is a future inconsistency.

    line 208
  10. Distinctive:

    Verify the doc's factual claims against code — triaged, not uniformly:

    line 216
  11. Distinctive:

    Run the structural checks found in Step 2.

    line 244
  12. Distinctive:

    Deterministic evidence first: Run the counting command before writing the opinion.

    line 285
  13. Distinctive:

    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 …

    line 292
  14. Distinctive:

    Record the run: Leave a dated completion note wherever this project tracks review cadence (a checks file, a doc header), so a review cycle …

    line 295

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 | head

Then 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)$' | head

Collect 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:

DimensionServiceLibraryCLI / local daemon
3.4 SecurityAuthN/Z on every route; SQL/shell/template injection; secretsunsafe API defaults; input parsing of untrusted data; supply chainargv/env/filesystem trust boundary; subprocess & shell construction (an --exec flag is an injection surface); parsing untrusted files
3.6 Observabilitylogs/metrics answer "what happened at 3am"; health checks that check somethingerror types that carry context; debuggability of consumer failuresdistinguishing exit codes; actionable stderr; --verbose/trace paths
3.3 Reliabilitydependency-down behavior; retries; idempotencyAPI stability contracts; error propagation disciplinepartial-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 test

3.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:

  1. Robustness verdict — one paragraph, would you sign off a release?
  2. Ranked risks — likelihood × blast radius, each with file:line.
  3. Prioritized action plan — every item is a fix or a new guard, sized (S/M/L), with acceptance criteria a future session could verify.
  4. 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.