{
  "version": "act 2.1.1",
  "slug": "architecture-audit",
  "name": "architecture-audit",
  "description": "Use when asked to audit architecture, produce a comprehensive architectural overview, get the full structural picture of a codebase, assess overall health and porting readiness, find module boundaries, circular dependencies, coupling hotspots, dead code, or code patterns. Depth 3 \u2014 full audit with hypothesis-driven investigation. Optionally enriches the structural audit with runtime and git evidence (coverage, churn, co-change coupling, ownership) when those overlays are available. Replaces the architectural-analysis and architecture-audit-plus skills.",
  "url": "https://act101.ai/docs/skills/architecture-audit",
  "body_md": "# Architecture Audit\n\n**Depth:** Level 3 (Full Audit).\n\nSee `../analysis-protocol/references/protocol.md` for: artifact directory structure,\nthe investigation loop, depth levels, summary format, token budget rules, and project\nmap structure. Read that document before proceeding.\n\n## Phase 0: Load the Prior Project Map (before any tool dispatch)\n\nBefore dispatching anything, check for an existing `project-map.md` at the workspace root.\n**If it exists, read it first** and extract:\n\n- **Refuted & Re-characterized Findings** ledger \u2014 these are hypotheses already investigated\n  and disproven. Do **not** re-investigate them from scratch in Phase 2. Re-open a refuted\n  finding **only** if Phase 1 surfaces new structural evidence that contradicts the prior\n  refutation; otherwise carry each entry forward verbatim and note \"previously refuted\n  (date), no new evidence\" in the report. This is the single most important reason to read\n  the map first \u2014 it stops every audit from re-flagging the same disproven smells.\n- **Analysis History** table \u2014 for the trend verdict (improving / stable / degrading) and to\n  append (not overwrite) the new row.\n- **Chokepoints, hotspots, cycles, pattern counts** \u2014 capture prior numbers so the new report\n  can show deltas (e.g. \"god_function 1,321 \u2192 1,651\"), and so a *newly appeared* cycle or\n  chokepoint is flagged as a regression.\n- **Scan-score trend:** if `.act/scan-history.jsonl` exists, run `act trends --root <repo>`\n  for the score series, per-class count deltas, and banded verdict \u2014 never hand-diff scan\n  scores across runs. Analyzer outputs not in the history records (chokepoints, clusters,\n  hotspot rankings) still come from the prior map's numbers as above.\n\n**Also read `remediation-log.md`** (workspace root, if present). This is the append-only ledger\nthe `architectural-refactoring` skill writes \u2014 one row per remediation, keyed to a finding ID. It\nis the other half of the audit \u2192 refactor \u2192 re-audit loop: it tells you which prior findings were\n*claimed* fixed since the last audit, and how. Treat each `RESOLVED`/`PARTIAL` row not as truth\nbut as a **hypothesis to re-verify** in Phase 2 against this run's fresh structural evidence:\n\n- If the structure confirms the fix (the cycle is gone, instability dropped, the god-object split),\n  reflect it in synthesis \u2014 mark the recommendation resolved, move the item out of Weaknesses, and\n  show it in the delta/trend.\n- If the structure *contradicts* the claim (the row says RESOLVED but the cycle is still present, or\n  a \"split\" reintroduced coupling elsewhere), that is a **regression / incomplete-remediation\n  finding** \u2014 surface it explicitly with the conflicting evidence.\n\nThe audit only **reads** this log; the refactoring skill owns the writes. Never edit\n`remediation-log.md` from the audit.\n\nIf no `project-map.md` exists, this is the first audit \u2014 note that, proceed to Phase 1, and\ncreate the map (including an empty Refuted ledger) during synthesis. A `remediation-log.md` with no\nprior map is an inconsistency \u2014 note it and treat its rows as unverified claims.\n\n## Phase 1: Parallel Tool Dispatch\n\nDispatch all available tools in a **single parallel batch** \u2014 never sequentially.\nEach subagent runs one tool, saves raw JSON to `raw/<tool-name>.json`, returns a\nstructured summary.\n\n**Must-have tools (report is not useful without at least some of these):**\n\n| Tool | Purpose | MCP call |\n|------|---------|----------|\n| `analyze_clusters` | Module groupings | `analyze_clusters` |\n| `analyze_coupling` | Instability rankings | `analyze_coupling` (sort: instability) |\n| `analyze_cycles` | Circular dependencies | `analyze_cycles` |\n| `analyze_seams` | Natural boundaries | `analyze_seams` |\n| `analyze_dead_code` | Unreachable symbols | `analyze_dead_code` |\n| `analyze_patterns` | Structural smells | `analyze_patterns` |\n| `analyze_export` | Codebase dimensions | `analyze_export` |\n\nInterpret `analyze_seams` through the cluster output that feeds it. `total_seams: 0`\nonly means there are no crossing edges between the detected clusters. If\n`analyze_clusters` reports fewer than two clusters, `hub_collapse: true`, or one cluster\ncontaining the whole scope, do not report \"no boundary\"; report the cluster artifact and\ninvestigate with `dampen_top_k`, `split_module`, `analyze_surface`, or `simulate`.\n\n**Extended tools (use if available, skip and note in manifest if not):**\n\n| Tool | Purpose | MCP call |\n|------|---------|----------|\n| `analyze_layers` | Layer detection + violations (S1+S2) | `analyze_layers` |\n| `analyze_hotspots` | Complexity ranking (H1) | `analyze_hotspots` |\n| `analyze_cohesion` | Internal relatedness (H2) | `analyze_cohesion` |\n| `analyze_chokepoints` | Betweenness centrality (R4) | `analyze_chokepoints` |\n| `analyze_stability` | Stability index + violations (R2+R3) | `analyze_stability` |\n| `analyze_roles` | Module role classification (S3) | `analyze_roles` |\n| `analyze_entry_points` | Application entry points (S5) | `analyze_entry_points` |\n| `analyze_clones` | Duplicated-code mass (clone classes) | `analyze_clones` \u2014 start with the default `min_tokens` (50); raise it only if the top classes are micro-boilerplate (e.g. single-expression patterns); use `include`/`exclude` to scope large monorepos. Caveats: type-2 clone classes require \u22651 function-context member (same-shape data-only matches are dropped by the engine); test files excluded by default and disclosed via `summary.test_files_excluded`; type-3 (gapped) clones are not detected in v1. |\n| `analyze_conformance` | Declared-vs-actual architecture (contract violations) | `analyze_conformance` \u2014 **promotes to must-have when the repo declares a contract** (`.act/config.toml` with an `[architecture]` section: layer ranks, deny rules, surface caps; probe for the file before dispatch). Violations cite the offending import's file:line. `contract_present: false` means no contract is declared \u2014 that is \"nothing to check,\" never \"conformant.\" Rank convention: 0 = outermost layer. |\n\n**Conformance worked example** (real run): a repo declaring\n\n```toml\n[architecture]\n[[architecture.deny]]\nfrom = \"src/core/**\"\nto = \"src/ui/**\"\nreason = \"core must not depend on presentation\"\n```\n\nwhere `src/core/logic.ts` imports `src/ui/render.ts` returns\n`contract_present: true` and one violation: `kind: \"deny\"`, `rule: \"src/core/** -> src/ui/**\"`,\n`file: \"src/core/logic.ts\"`, `line: 1`, evidence `src/core/logic.ts --imports--> src/ui/render.ts`,\nwith the rule's written `reason` echoed. Report violations with that file:line \u2014\nit is the offending import statement, not the rule definition.\n\n## Phase 2: Smell Taxonomy + Hypothesis Formation\n\nBefore dispatching any investigation subagents, form explicit hypotheses using this\ntaxonomy. One module appearing in multiple columns is a stronger signal.\n\n| Evidence pattern | Architectural smell | Confirming query |\n|-----------------|--------------------|--------------------|\n| High instability + appears in cycle | God object / hub | `skeleton` + `interface` \u2014 is the API surface wider than the abstraction warrants? |\n| High instability + wide seam API | Leaky abstraction | `analyze_surface` at boundary \u2014 count unrelated symbols crossing it |\n| Large cluster + low cohesion score | Accidental cluster / false boundary | `skeleton` on cluster members \u2014 do they share a concept? |\n| Cycle length > 3 | Tightly-coupled subsystem | `analyze_surface --files <members>` \u2014 find the minimum cut |\n| Single-file cluster | Orphan / orphaned extract | `references` \u2014 does anything depend on this module? |\n| Dead code in frequently-modified file | Zombie code / incomplete refactor | `symbols` + `references` \u2014 confirm no live callers |\n| Pattern hotspot + high instability | Design debt accumulation point | `skeleton` \u2014 does the file serve multiple unrelated concerns? |\n| Module instability = 1.0, many dependents | Unstable abstraction | `interface` \u2014 is this a leaf that should be stable? |\n| Seam API surface > 10 symbols | Wide interface / tight coupling | `analyze_surface` \u2014 which symbols cross; can the seam be narrowed? |\n| No cycles but very large clusters | Low cohesion, high coupling within cluster | `analyze_coupling` within cluster |\n| High centrality + low cohesion | Overloaded chokepoint | `skeleton` \u2014 is this file doing too many things? |\n| Layer violations + high coupling | Architectural erosion | `graph` \u2014 trace the violation path, assess if structural or incidental |\n| Top clone classes by mass (token_count \u00d7 members) | Structural duplication / abstraction debt | When a class carries a `remediation.operation: \"extract_function\"` pointer, use `extract_function` \u2014 all members already sit in function bodies. For classes without a remediation pointer (data literals, top-level declarations), consider consolidation at the module level rather than extraction. |\n| Contract violations (declared layers/deny rules breached) | Architectural erosion against an explicit contract \u2014 stronger than inferred-layer violations because the team wrote the rule down | `analyze_conformance` output cites the violating import's file:line; `graph` to trace whether the dependency is load-bearing or incidental before recommending the cut |\n\n**Anomaly flags \u2014 investigate regardless of smell match:**\n- Cluster containing 1 file (orphan or misclassified)\n- Cluster containing >30% of all files (god cluster) \u2014 `analyze_clusters` now self-discloses via `hub_collapse=true` + `top_hubs` (named hub files); consume these fields from the result and report the named hubs; the >30% rule remains as a cross-check (it also catches large clusters whose cohesion sits above the 0.05 disclosure floor)\n- All instability scores near 0 or near 1 (degenerate dependency structure)\n- Seam with 0 API symbols (disconnected component)\n- Dead code in a file that is also in a cycle (phantom dependency)\n- Zero cycles in a codebase >100 files (suspiciously clean)\n\n## Phase 3: Full Audit Synthesis\n\nAfter all investigation subagents return:\n\n1. **Cross-reference findings** across all categories \u2014 a module appearing in hotspots,\n   coupling, AND cycles is a compounding risk\n2. **Build evidence chains** \u2014 not \"instability = 0.94\" but \"module X has instability\n   0.94, is in 2 cycles, and its API surface exposes 23 symbols across a seam to\n   cluster B \u2014 skeleton shows it handles both auth and request routing, confirming\n   god-object smell\"\n3. **Note negative space** \u2014 absence of expected problems is a meaningful finding\n4. **Record every refuted or re-characterized hypothesis** \u2014 for each Phase 2 hypothesis that\n   came back refuted, partially refuted, or re-characterized (the real issue differs from the\n   hypothesized smell), and for each tool output that is an artifact rather than a finding\n   (e.g. degenerate dead-code/cohesion/layers results), capture it with its disproving\n   evidence. These feed the project map's **Refuted & Re-characterized Findings** ledger.\n5. **Reconcile claimed remediations** \u2014 for each `remediation-log.md` row read in Phase 0, state\n   the verdict from this run's evidence: *confirmed* (fold into the map, mark the recommendation\n   resolved, show the delta), or *contradicted* (surface as a regression / incomplete-remediation\n   finding). Do not silently drop a claimed fix that the structure no longer supports.\n6. Write `report.md`, then update `project-map.md` (full rewrite of all sections, carrying the\n   Refuted ledger forward and reflecting confirmed remediations \u2014 see Project Map Updates)\n\n## Report Structure\n\n```markdown\n# Architecture Audit: <project name>\n\n## Overview\nFiles, symbols, languages, analysis date.\n\n## Executive Summary\n2-3 sentence assessment. Explicit verdict: **Ready** / **Needs work** / **Not ready**.\nOne-sentence justification for the verdict.\n\n## Module Map\nClusters with sizes, labels, cohesion scores. Anomalies noted.\nModule roles (entry point, routing, business logic, data access, utility, etc.).\n\n## Entry Points\nApplication entry points by kind (application, HTTP route, CLI, event listener, test,\npublic API).\n\n## Layer Architecture\nDetected layers with directory mappings. Violations with evidence.\n\n## Dependency Structure\nInstability rankings with interpretation \u2014 what the scores mean structurally.\nStability violations.\n\n## Circular Dependencies\nAll cycles: length, members, confirmed break points from investigation.\n\n## Boundary Assessment\nSeams: API surface width, confirmed leaky vs. clean.\n\n## Complexity Hotspots\nRanked hotspots with skeleton context from investigation.\n\n## Chokepoints\nHigh-centrality files with blast radius and split recommendations.\n\n## Dead Code\nConfirmed unreachable symbols. Zombie code noted separately.\n\n## Code Patterns\nFindings grouped by pattern type and severity.\n\n## Strengths\nWhat the architecture does well. Notable absence of expected problems.\n\n## Weaknesses\nConfirmed structural issues with evidence chains.\n\n## Refuted & Re-characterized Findings\nHypotheses investigated and NOT confirmed (refuted / partially refuted / re-characterized),\nand tool outputs that are artifacts rather than findings. Each with its disproving evidence.\nInclude findings carried forward from the prior project map (mark \"previously refuted, no new\nevidence\"). This section prevents future audits from re-flagging disproven smells.\n\n## Risks\nCausal chains: what breaks first, what cascades, minimum intervention.\n\n## Recommendations\nPrioritized, actionable steps, each linked to a confirmed finding.\n\nWhen the audit confirms structural findings that warrant remediation (cycles, god\nobjects, coupling hotspots, dead code), the single recommended next action is to run the\n`architectural-refactoring` skill \u2014 **not** a cherry-picked inline fix. That skill executes\nthe prioritized set as a unit and records each remediation to `remediation-log.md`, which is\nwhat closes the audit \u2192 refactor \u2192 re-audit cycle and lets it go deeper each pass. Proposing a\none-off \"move X, fix two imports, re-run\" edit here short-circuits the cycle and leaves no\ndurable remediation record. The specific act MCP tool calls are the *mechanism* the refactoring\nskill uses, not a substitute for invoking it.\n\nBecause the handoff is artifact-based (this report + `project-map.md` + `remediation-log.md` on\ndisk), the refactoring skill needs no conversation context. Phrase the next action so it works\nfrom a clean slate, e.g.: \"Clear context, then run `/architectural-refactoring` \u2014 it rehydrates\nfrom this report and the project map.\" Use individual tool-call recommendations only for findings\nthat are genuinely outside a refactoring pass (e.g. \"add a coverage run before the next audit\").\n```\n\n## Project Map Updates\n\nUpdates **all sections** of `project-map.md` (workspace root, full rewrite). Appends to the\nAnalysis History table.\n\n**Confirmed remediations:** when this run's evidence confirms a `remediation-log.md` claim, reflect\nit in the rewritten map \u2014 drop or down-rank the resolved item in Chokepoints & Risks / Weaknesses,\nand let the resolution show in the delta and trend verdict. Do **not** copy the remediation log\ninto the map and do **not** edit the log itself \u2014 the map records *current state*, the log records\n*actions taken*, and the refactoring skill owns the log. A claim the structure contradicts stays an\nopen finding (and may warrant a Refuted-ledger entry if the \"fix\" was based on a misdiagnosis).\n\n**Refuted & Re-characterized Findings ledger (required):** the project map carries a persistent\n`## Refuted & Re-characterized Findings` section (see the protocol's Project Map Structure). On\nevery run:\n\n1. **Carry forward** every entry from the prior map's ledger \u2014 never drop a refutation just\n   because this run didn't re-test it. Keep its original \"Since\" date.\n2. **Add** each newly refuted / re-characterized hypothesis and each newly identified tool\n   artifact from this run's Phase 2, with the disproving evidence and this run's date.\n3. **Update** an entry's status only if this run found new evidence that overturns the prior\n   refutation (e.g. a once-clean boundary now genuinely leaks) \u2014 note the change explicitly.\n\nEach ledger row: `| Finding | Status (REFUTED / RE-CHARACTERIZED / ARTIFACT / UNRELIABLE) |\nSince (date first established) | Evidence / why |`. This ledger is what Phase 0 reads on the\nnext run to avoid re-investigating disproven smells.\n\n## Rules\n\n1. Dispatch Phase 1 in a single parallel batch \u2014 never sequentially\n2. Write all hypotheses before dispatching Phase 2 \u2014 no browsing without a hypothesis\n3. Evidence chains, not data points \u2014 explain what scores mean structurally\n4. Follow new signals \u2014 if a Phase 2 subagent finds something unexpected, dispatch a\n   follow-up (capped at one extra round)\n5. Note negative space \u2014 absence of expected problems is a finding\n6. Executive summary must include an explicit Ready / Needs work / Not ready verdict\n\n## Worked Example: Clone-Mass Dispatch\n\nCall (default min_tokens; scoped to source):\n\n```\nanalyze_clones(include=[\"crates\"])\n```\n\nTop of result (act repo, 2026-06-12, min_tokens 50):\n\n```json\n{\n  \"clone_classes\": [\n    {\n      \"id\": \"clone-3407fede907f0634\",\n      \"clone_type\": \"type-2\",\n      \"token_count\": 57,\n      \"members\": [\n        { \"file\": \"crates/act-refactor/src/operations/actionscript/operations/add_documentation_comment.rs\",\n          \"start_line\": 24, \"end_line\": 30 }\n        // \u2026 1506 more members\n      ],\n      \"remediation\": {\n        \"operation\": \"extract_function\",\n        \"note\": \"All members are function definitions or lie within function bodies.\"\n      }\n    }\n  ],\n  \"summary\": {\n    \"files_analyzed\": 5392,\n    \"files_with_clones\": 3970,\n    \"clone_class_count\": 8156,\n    \"duplicated_tokens\": 5067066,\n    \"min_tokens\": 50,\n    \"test_files_excluded\": 47488\n  }\n}\n```\n\nReading this: the top class by mass (57 tokens \u00d7 1507 members = 85,842 mass) has a `remediation.operation: \"extract_function\"` pointer \u2014 every member lies in a function body, so `extract_function` at the first member site is the right next step. The 5,067,066 `duplicated_tokens` total against 5,392 files analyzed signals systematic abstraction debt, not isolated copy-paste.\n\n## Optional Enrichment: Runtime & Git Evidence\n\nThe structural audit above is AST/graph-based. When the workspace has a coverage\nrun and git history, enrich each structural finding with evidence overlays so the\nreport reflects what actually runs and what actually changes \u2014 not just the static\ngraph. This is optional: skip any overlay whose evidence is absent and say so.\n\n**Tier:** the four overlays (`coverage_overlay`, `churn_hotspots` in workspace mode,\n`co_change_clusters`, `ownership_map`) are **Architecture** and enforce that tier\nthemselves; the base structural audit is unaffected. If an overlay is tier-blocked,\nits dimension is UNASSESSED \u2014 state it, never imply \"clean.\"\n\n| Tool | Enriches |\n|---|---|\n| `coverage_overlay` | Joins lcov coverage onto symbols \u2014 turns \"this hotspot is complex\" into \"complex AND uncovered.\" |\n| `churn_hotspots` | Ranks symbols by git change frequency \u00d7 recency \u2014 \"high coupling\" becomes \"high coupling AND frequently churned.\" |\n| `co_change_clusters` | Symbols that change together across history \u2014 confirms or contradicts the static clusters (hidden / shotgun-surgery coupling). |\n| `ownership_map` | Per-symbol author shares + bus factor \u2014 flags knowledge-concentration risk on critical modules. |\n\n**Enrichment workflow (after the structural audit):**\n\n1. `coverage_overlay` with the lcov `report` \u2014 a complexity hotspot or chokepoint\n   with `covered: false` is a high-risk node (complex, central, untested).\n2. `churn_hotspots` (workspace mode) \u2014 a high-coupling / high-instability module\n   that is also high-churn is an active design-debt accumulation point.\n3. `co_change_clusters` \u2014 where these disagree with the static `analyze_clusters`\n   boundaries, you have hidden coupling the structure does not show.\n4. `ownership_map` \u2014 a low bus factor on a chokepoint or god-module is a compounding\n   people-risk on top of the structural risk.\n\nWhen enrichment runs, every Risk and Recommendation should cite the structural\nfinding AND its overlay evidence \u2014 e.g. \"module X: instability 0.94, in 2 cycles\n(structure) + uncovered + 31 commits in 90 days + bus factor 1 (overlays) \u2192\ntop-priority refactor.\" Overlays are window-bounded: report `unmapped`/`modeled_kinds`\nand the coverage/git window as caveats. Absence of overlay evidence is \"no evidence,\"\nnot absolution."
}