{
  "version": "act 2.1.1",
  "slug": "boundary-analysis",
  "name": "boundary-analysis",
  "description": "Use when looking for extraction candidates, planning a module split, analyzing module boundaries, or before decomposing a large component. Also use when asked \"where should I split this?\" or \"find natural module boundaries\". Depth 2 \u2014 investigate. Produces extraction candidates with cut costs, layer violations, interface width assessment, orphan types, and a recommended decomposition sequence.",
  "url": "https://act101.ai/docs/skills/boundary-analysis",
  "body_md": "# Boundary Analysis\n\n**Depth:** Level 2 (Investigate).\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 1: Parallel Tool Dispatch\n\nDispatch all available tools in a **single parallel batch**.\nEach subagent runs one tool, saves raw JSON to `raw/<tool-name>.json`, returns a\nstructured summary.\n\n**Must-have tools:**\n\n| Tool | Purpose | MCP call |\n|------|---------|----------|\n| `analyze_clusters` | Current module groupings | `analyze_clusters` |\n| `analyze_seams` | Natural boundaries | `analyze_seams` |\n\nIf both must-have tools are unavailable, report that and stop.\n\nInterpret `analyze_seams` through `analyze_clusters`. Empty seams are actionable only\nwhen clustering produced at least two non-hub clusters. If clustering is hub-collapsed\nor collapsed into one cluster, treat seam output as uninformative and investigate with\ndampened clusters, `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_extraction` | Extraction candidates (M2) | `analyze_extraction` |\n| `analyze_interfaces` | Cross-module contracts (M4) | `analyze_interfaces` |\n| `analyze_cohesion` | Are current modules coherent? (H2) | `analyze_cohesion` |\n| `analyze_interface_bloat` | Are APIs too wide? (H3) | `analyze_interface_bloat` |\n| `analyze_orphan_types` | Misplaced type definitions (H4) | `analyze_orphan_types` |\n\n## Phase 2: Investigation\n\nFor each significant finding, form a hypothesis before dispatching a subagent:\n\n**Extraction candidate hypothesis example:**\n> **Hypothesis N:** `<candidate>` can be extracted as a clean module.\n> **Evidence:** `analyze_seams` identified a seam at this boundary; `analyze_extraction`\n> scored it high.\n> **Confirming query:** `analyze_surface` on the candidate files to measure\n> cut cost (edges to sever vs. edges retained internally).\n> **Confirms if:** Internal edges >> external edges (low cut cost relative to cohesion).\n> **Refutes if:** External edges \u2265 internal edges (high entanglement \u2014 extraction would\n> be expensive).\n\n**Layer violation hypothesis example:**\n> **Hypothesis N:** The violation from `<source>` to `<target>` is structural (not\n> accidental), indicating architectural erosion.\n> **Evidence:** `analyze_layers` flagged an inversion from layer X to layer Y.\n> **Confirming query:** `graph` on `<source>` to trace the full import path.\n> **Confirms if:** Multiple files in layer X import from layer Y (pattern, not one-off).\n> **Refutes if:** Single file, single import \u2014 likely a quick fix rather than erosion.\n\n**Low-cohesion split hypothesis example:**\n> **Hypothesis N:** `<file>` contains two distinct concept clusters that should be\n> separate modules.\n> **Evidence:** `analyze_cohesion` scored this module low; it is large.\n> **Confirming query:** Read the `analyze_cohesion` LCOM4 fields. For any class with\n> `lcom4` \u2265 2, its `components` are the disjoint method/field clusters \u2014 the NAMED split\n> boundary, preferred over eyeballing the `skeleton`. Cross-check the `lcom4_summary`\n> `modeled_kinds` for coverage honesty: a grammar absent from `modeled_kinds` is unjudged,\n> not cohesive.\n> **Confirms if:** A class reports `lcom4` \u2265 2 and its `components` partition the methods\n> into groups with no shared state \u2014 split along those clusters.\n> **Refutes if:** `lcom4` is 1 (methods share common state/fields), or the file's grammar\n> is absent from `modeled_kinds` so cohesion was never judged.\n\n**Interface bloat hypothesis example:**\n> **Hypothesis N:** `<module>`'s public API is wider than necessary \u2014 some exports are\n> never used externally.\n> **Evidence:** `analyze_interface_bloat` flagged this module as over-exposed.\n> **Confirming query:** `references` on each exported symbol.\n> **Confirms if:** Several exports have 0 external callers.\n> **Refutes if:** All exports have at least one external caller.\n\nSave investigation notes to `investigation/hypothesis-N.md`.\n\n## Report Structure\n\n```markdown\n# Boundary Analysis: <project name>\n\n## Boundary Map\nCurrent module structure with cluster sizes and labels.\nWhich clusters are cohesive vs. artificially grouped.\n\n## Layer Architecture\nDetected or user-specified layers. Direction consistency score.\n(Present only if analyze_layers ran successfully.)\n\n## Extraction Candidates\nRanked by extraction score. Per-candidate:\n- Files included\n- Internal vs. external edges\n- API surface width\n- Cut cost (edges to sever)\n- Recommended extraction steps (specific act MCP operations)\n\n## Layer Violations\n(Present only if analyze_layers ran successfully.)\nPer-violation: source layer, target layer, specific files involved, imported symbols,\nfix recommendation. Distinguish: inversions (higher layer imports from lower) vs.\nskips (layer bypasses an intermediate layer).\n\n## Interface Width Assessment\n(Present only if analyze_interface_bloat ran successfully.)\nModules with disproportionately wide public APIs.\nPer-module: which exports are used externally, which are never called externally,\nrecommended visibility reduction.\n\n## Orphan Types\n(Present only if analyze_orphan_types ran successfully.)\nType definitions living in the wrong module.\nPer-orphan: where defined, where used, recommended move destination.\n\n## Cohesion Assessment\n(Present only if analyze_cohesion ran successfully.)\nLow-cohesion modules with natural split points. Per-module: cohesion score, identified\nsplit boundary, recommended next step.\nFor each class with `lcom4` \u2265 2, report its `components` (the disjoint method/field\nclusters) as the named split boundary \u2014 this is the LCOM4 evidence, preferred over an\neyeballed skeleton read. Quote the `lcom4_summary` `modeled_kinds` so coverage is honest:\na grammar absent from `modeled_kinds` was never judged and must not be reported as cohesive.\n\n## Recommended Decomposition Steps\nPrioritized, ordered steps to improve module boundaries.\nEach step links to a specific act MCP tool call or skill.\nOrder: cheapest/highest-value extractions first, invasive restructuring last.\n\nBefore committing to a cut, simulate it. Express the proposed cut as `simulate` ops \u2014\n`split_file{file,groups}` for a class/module split, `move_file{from,to}` for a relocation \u2014\nand record the predicted deltas: `cycles.resolved`/`cycles.introduced`, the `coupling`\nchanges, `chokepoints`, and conformance `violations.cleared`/`violations.introduced` (the\nlast only when an `[architecture]` contract exists in `.act/config.toml`). `simulate` never\nwrites disk. Revise the cut if it introduces violations or fails to resolve the target cycle.\n(Delta-field names match the architectural-refactoring skill's simulate step.)\n\nTo decide whether a suspected pass-through module is a real boundary at all \u2014 the\ndeletion test \u2014 simulate `delete_module{file}`. It drops the module and re-wires\ntransitive bridges (`A\u2192M\u2192B` \u21d2 `A\u2192B`), then reports a `deletions` delta. Read it in\nthis order:\n\n1. `surface_consumers` \u2014 external symbols that **call or extend the module's own\n   symbols** (the load-bearing core; `top_consumers` names them). A non-zero count\n   means the module is load-bearing and earns its keep \u2014 those dependencies can\n   never be re-homed because the callee/superclass body is deleted. Cite the count\n   and a name or two instead of arguing the deletion test in prose.\n2. `surface_modeled` \u2014 **honesty gate.** `surface_consumers: 0` is a genuine\n   conduit signal ONLY when this is `true`. When `false` the call channel was not\n   modeled for the module's grammar; the verdict is UNKNOWN, never \"clean conduit\"\n   (field-access consumption is not modeled at all \u2014 treat 0 with care).\n3. `rewired_edges` / `severed_edges` \u2014 the file-import routing side: high\n   `rewired_edges` with `severed_edges: 0` and `surface_consumers: 0` (modeled) is\n   a clean pass-through whose callers only routed through it.\n```\n\n## Project Map Updates\n\nUpdates **\"Module Map\"**, **\"Layer Architecture\"**, and **\"Key Boundaries\"** sections.\nAppends to the Analysis History table."
}