@nklisch/pi-agile-workflow 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/.claude-plugin/plugin.json +10 -0
  2. package/.codex-plugin/plugin.json +28 -0
  3. package/CHANGELOG.md +591 -0
  4. package/README.md +107 -0
  5. package/docs/ARCHITECTURE.md +716 -0
  6. package/docs/MIGRATION.md +419 -0
  7. package/docs/PRINCIPLES.md +464 -0
  8. package/docs/ROADMAP.md +95 -0
  9. package/docs/SPEC.md +855 -0
  10. package/docs/VISION.md +106 -0
  11. package/extensions/agile-workflow.test.ts +419 -0
  12. package/extensions/agile-workflow.ts +467 -0
  13. package/hooks/hooks.json +52 -0
  14. package/hooks/scripts/prompt-context.py +628 -0
  15. package/hooks/scripts/substrate-maintainer.py +359 -0
  16. package/hooks/scripts/test_prompt_context.py +757 -0
  17. package/package.json +30 -0
  18. package/scripts/install-work-view.sh +165 -0
  19. package/scripts/tests/agent-metadata.test.sh +75 -0
  20. package/scripts/tests/bump-version.test.sh +552 -0
  21. package/scripts/tests/channel-parity.test.sh +117 -0
  22. package/scripts/tests/convert-content-integrity.test.sh +246 -0
  23. package/scripts/tests/convert-install-routing.test.sh +159 -0
  24. package/scripts/tests/install-work-view.test.sh +411 -0
  25. package/scripts/tests/pi-package-metadata.test.sh +119 -0
  26. package/scripts/tests/work-board-shim.test.sh +239 -0
  27. package/scripts/tests/work-view-dist-version.test.sh +105 -0
  28. package/scripts/work-board.sh +64 -0
  29. package/scripts/work-view.sh +423 -0
  30. package/skills/autopilot/SKILL.md +330 -0
  31. package/skills/autopilot/agents/openai.yaml +6 -0
  32. package/skills/board/SKILL.md +62 -0
  33. package/skills/board/agents/openai.yaml +6 -0
  34. package/skills/bold-refactor/SKILL.md +244 -0
  35. package/skills/bold-refactor/agents/openai.yaml +6 -0
  36. package/skills/bug-scan/SKILL.md +378 -0
  37. package/skills/bug-scan/agents/openai.yaml +6 -0
  38. package/skills/bug-scan/references/async-promises.md +119 -0
  39. package/skills/bug-scan/references/concurrency-races.md +148 -0
  40. package/skills/bug-scan/references/data-layer.md +167 -0
  41. package/skills/bug-scan/references/error-handling.md +197 -0
  42. package/skills/bug-scan/references/gate-item-template.md +94 -0
  43. package/skills/bug-scan/references/language-footguns.md +219 -0
  44. package/skills/bug-scan/references/parked-item-template.md +103 -0
  45. package/skills/bug-scan/references/report-template.md +115 -0
  46. package/skills/bug-scan/references/resource-leaks.md +199 -0
  47. package/skills/bug-scan/references/state-closures.md +148 -0
  48. package/skills/bug-scan/references/time-numbers.md +158 -0
  49. package/skills/convert/SKILL.md +1463 -0
  50. package/skills/convert/agents/openai.yaml +6 -0
  51. package/skills/convert/references/legacy-overlap-migration.md +179 -0
  52. package/skills/deep-code-scan/SKILL.md +378 -0
  53. package/skills/deep-code-scan/agents/openai.yaml +6 -0
  54. package/skills/deep-code-scan/references/artifact-ledger.md +148 -0
  55. package/skills/deep-code-scan/references/consolidation.md +91 -0
  56. package/skills/deep-code-scan/references/decomposition.md +127 -0
  57. package/skills/deep-code-scan/references/item-templates.md +263 -0
  58. package/skills/deep-code-scan/references/lane-catalog.md +129 -0
  59. package/skills/deep-code-scan/references/review-gauntlet.md +108 -0
  60. package/skills/deep-code-scan/references/scanner-brief.md +152 -0
  61. package/skills/e2e-test-design/SKILL.md +517 -0
  62. package/skills/e2e-test-design/references/anti-tautology.md +261 -0
  63. package/skills/e2e-test-design/references/service-mocks.md +234 -0
  64. package/skills/e2e-test-design/references/test-taxonomy.md +211 -0
  65. package/skills/epic-design/SKILL.md +476 -0
  66. package/skills/epicize/SKILL.md +210 -0
  67. package/skills/epicize/agents/openai.yaml +6 -0
  68. package/skills/feature-design/SKILL.md +472 -0
  69. package/skills/fix/SKILL.md +173 -0
  70. package/skills/gate-cruft/SKILL.md +258 -0
  71. package/skills/gate-docs/SKILL.md +278 -0
  72. package/skills/gate-patterns/SKILL.md +389 -0
  73. package/skills/gate-refactor/SKILL.md +408 -0
  74. package/skills/gate-security/SKILL.md +275 -0
  75. package/skills/gate-tests/SKILL.md +367 -0
  76. package/skills/groom/SKILL.md +162 -0
  77. package/skills/groom/agents/openai.yaml +6 -0
  78. package/skills/ideate/SKILL.md +185 -0
  79. package/skills/ideate/agents/openai.yaml +6 -0
  80. package/skills/implement/SKILL.md +288 -0
  81. package/skills/implement-orchestrator/SKILL.md +604 -0
  82. package/skills/park/SKILL.md +111 -0
  83. package/skills/perf-design/SKILL.md +427 -0
  84. package/skills/perf-scout/SKILL.md +404 -0
  85. package/skills/perf-scout/agents/openai.yaml +6 -0
  86. package/skills/perf-scout/references/algorithmic-and-data-structures.md +126 -0
  87. package/skills/perf-scout/references/approximation-and-precomputation.md +136 -0
  88. package/skills/perf-scout/references/caching-and-memoization.md +123 -0
  89. package/skills/perf-scout/references/compiler-runtime-and-language.md +136 -0
  90. package/skills/perf-scout/references/database-and-storage-internals.md +144 -0
  91. package/skills/perf-scout/references/distributed-systems.md +150 -0
  92. package/skills/perf-scout/references/game-engine-and-realtime.md +136 -0
  93. package/skills/perf-scout/references/gpu-and-accelerators.md +130 -0
  94. package/skills/perf-scout/references/idea-ranking.md +75 -0
  95. package/skills/perf-scout/references/io-and-batching.md +135 -0
  96. package/skills/perf-scout/references/memory-and-data-locality.md +137 -0
  97. package/skills/perf-scout/references/parallelism-and-vectorization.md +126 -0
  98. package/skills/perf-scout/references/parked-item-template.md +117 -0
  99. package/skills/perf-scout/references/peer-review-pass.md +150 -0
  100. package/skills/perf-scout/references/report-template.md +148 -0
  101. package/skills/principles/SKILL.md +678 -0
  102. package/skills/principles/references/models.md +191 -0
  103. package/skills/principles/references/subagents.md +158 -0
  104. package/skills/prose-author/SKILL.md +185 -0
  105. package/skills/refactor-conventions-creator/SKILL.md +359 -0
  106. package/skills/refactor-conventions-creator/agents/openai.yaml +6 -0
  107. package/skills/refactor-conventions-creator/references/common-structures.md +91 -0
  108. package/skills/refactor-conventions-creator/references/common-styles.md +90 -0
  109. package/skills/refactor-design/SKILL.md +384 -0
  110. package/skills/release-deploy/SKILL.md +667 -0
  111. package/skills/release-deploy/agents/openai.yaml +6 -0
  112. package/skills/research/SKILL.md +166 -0
  113. package/skills/research/agents/openai.yaml +6 -0
  114. package/skills/review/SKILL.md +267 -0
  115. package/skills/review/references/deep-review.md +98 -0
  116. package/skills/review/references/review-lenses.md +68 -0
  117. package/skills/review/references/substrate-side-effects.md +161 -0
  118. package/skills/review/references/target-resolution.md +52 -0
  119. package/skills/scope/SKILL.md +486 -0
  120. package/work-view/Cargo.lock +486 -0
  121. package/work-view/Cargo.toml +9 -0
  122. package/work-view/crates/cli/.work-view-version +1 -0
  123. package/work-view/crates/cli/Cargo.toml +18 -0
  124. package/work-view/crates/cli/src/actionable.rs +500 -0
  125. package/work-view/crates/cli/src/args.rs +792 -0
  126. package/work-view/crates/cli/src/board/assets/board.css +855 -0
  127. package/work-view/crates/cli/src/board/assets/board.js +259 -0
  128. package/work-view/crates/cli/src/board/assets/card.js +139 -0
  129. package/work-view/crates/cli/src/board/assets/components.css +290 -0
  130. package/work-view/crates/cli/src/board/assets/dependency.js +1193 -0
  131. package/work-view/crates/cli/src/board/assets/detail.js +256 -0
  132. package/work-view/crates/cli/src/board/assets/filters.js +389 -0
  133. package/work-view/crates/cli/src/board/assets/index.html +77 -0
  134. package/work-view/crates/cli/src/board/assets/kanban.js +231 -0
  135. package/work-view/crates/cli/src/board/assets/markdown.js +260 -0
  136. package/work-view/crates/cli/src/board/assets/motion.css +131 -0
  137. package/work-view/crates/cli/src/board/assets/state.js +245 -0
  138. package/work-view/crates/cli/src/board/assets/table.js +304 -0
  139. package/work-view/crates/cli/src/board/assets/tokens.css +602 -0
  140. package/work-view/crates/cli/src/board/assets/views.js +41 -0
  141. package/work-view/crates/cli/src/board/assets.rs +162 -0
  142. package/work-view/crates/cli/src/board/feed.rs +204 -0
  143. package/work-view/crates/cli/src/board/mod.rs +246 -0
  144. package/work-view/crates/cli/src/board/open.rs +144 -0
  145. package/work-view/crates/cli/src/board/server.rs +407 -0
  146. package/work-view/crates/cli/src/main.rs +195 -0
  147. package/work-view/crates/cli/src/render.rs +354 -0
  148. package/work-view/crates/cli/src/scope.rs +157 -0
  149. package/work-view/crates/cli/src/stale.rs +581 -0
  150. package/work-view/crates/cli/tests/board-js/dependency-table.test.mjs +64 -0
  151. package/work-view/crates/cli/tests/board-js/expanded-browsing.test.mjs +79 -0
  152. package/work-view/crates/cli/tests/board-js/harness-smoke.test.mjs +11 -0
  153. package/work-view/crates/cli/tests/board-js/harness.mjs +437 -0
  154. package/work-view/crates/cli/tests/board-js/kanban-detail.test.mjs +75 -0
  155. package/work-view/crates/cli/tests/board-js/markdown-filter.test.mjs +60 -0
  156. package/work-view/crates/cli/tests/fixtures/golden/.work/CONVENTIONS.md +3 -0
  157. package/work-view/crates/cli/tests/fixtures/golden/.work/active/epics/epic-alpha.md +16 -0
  158. package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-a.md +16 -0
  159. package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-b.md +16 -0
  160. package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-alpha-1.md +16 -0
  161. package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-research-1.md +19 -0
  162. package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-done.md +16 -0
  163. package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-shipped.md +18 -0
  164. package/work-view/crates/cli/tests/fixtures/golden/.work/backlog/idea-backlog.md +7 -0
  165. package/work-view/crates/cli/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
  166. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/CONVENTIONS.md +6 -0
  167. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-dep-done.md +17 -0
  168. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-design-ready.md +17 -0
  169. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-impl-ready.md +17 -0
  170. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/stories/story-review-ready.md +17 -0
  171. package/work-view/crates/cli/tests/fixtures/with-malformed/.work/CONVENTIONS.md +4 -0
  172. package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/epics/malformed-no-closing-fence.md +8 -0
  173. package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/features/good-item.md +16 -0
  174. package/work-view/crates/cli/tests/integration.rs +2833 -0
  175. package/work-view/crates/core/Cargo.toml +13 -0
  176. package/work-view/crates/core/src/error.rs +61 -0
  177. package/work-view/crates/core/src/filter.rs +804 -0
  178. package/work-view/crates/core/src/graph.rs +324 -0
  179. package/work-view/crates/core/src/index.rs +590 -0
  180. package/work-view/crates/core/src/lib.rs +54 -0
  181. package/work-view/crates/core/src/model.rs +202 -0
  182. package/work-view/crates/core/src/parse.rs +494 -0
  183. package/work-view/crates/core/tests/fixtures/golden/.work/CONVENTIONS.md +15 -0
  184. package/work-view/crates/core/tests/fixtures/golden/.work/active/epics/epic-core.md +16 -0
  185. package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-parser.md +16 -0
  186. package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-query.md +16 -0
  187. package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/malformed-no-id.md +15 -0
  188. package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-parse-unit.md +16 -0
  189. package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-security-gate.md +16 -0
  190. package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-old.md +16 -0
  191. package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-parser.md +17 -0
  192. package/work-view/crates/core/tests/fixtures/golden/.work/backlog/idea-future-board.md +7 -0
  193. package/work-view/crates/core/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
  194. package/work-view/crates/core/tests/fixtures/precedence/.work/CONVENTIONS.md +10 -0
  195. package/work-view/crates/core/tests/fixtures/precedence/.work/archive/dup-item.md +18 -0
  196. package/work-view/crates/core/tests/fixtures/precedence/.work/releases/v2.0/dup-item.md +18 -0
  197. package/work-view/crates/core/tests/integration.rs +541 -0
  198. package/work-view/dist/.gitattributes +1 -0
  199. package/work-view/dist/README.md +42 -0
  200. package/work-view/dist/aarch64-apple-darwin/work-view +0 -0
  201. package/work-view/dist/aarch64-unknown-linux-musl/work-view +0 -0
  202. package/work-view/dist/x86_64-apple-darwin/work-view +0 -0
  203. package/work-view/dist/x86_64-unknown-linux-musl/work-view +0 -0
@@ -0,0 +1,148 @@
1
+ # Artifact Ledger — temporary durable scanner output and rollups
2
+
3
+ The artifact ledger prevents context loss during large scan campaigns. Scanner output can exceed the
4
+ orchestrator's context budget; the filesystem is the source of truth while the campaign is running.
5
+ It is **not** a permanent deliverable. After the final scan summary, fix epic, resolved finding
6
+ index, and gauntlet ledgers are collated, remove the artifact root.
7
+
8
+ ## Root And Lifecycle
9
+
10
+ Create the root before Phase 4:
11
+
12
+ ```text
13
+ .work/scan-artifacts/scan-<goal>/
14
+ manifest.jsonl
15
+ raw/<lane>/<band>/<component-slug>.md
16
+ candidates/<lane>/<band>/<component-slug>.jsonl
17
+ status/<lane>/<band>/<component-slug>.json
18
+ accepted/<lane>/<band>.jsonl
19
+ rollups/<lane>/<band>.md
20
+ gate1-input.md
21
+ gate1-ledger.md
22
+ fix-draft.md
23
+ gate2-ledger.md
24
+ ```
25
+
26
+ Use stable lowercase slugs for components. If two components slug the same, prefix with the story's
27
+ component index: `003-parser.md`.
28
+
29
+ The repo should ignore `.work/scan-artifacts/`. Do not force-add it. These files are local recovery
30
+ state, not release artifacts.
31
+
32
+ Lifecycle:
33
+
34
+ 1. Create the root before scanner fan-out.
35
+ 2. Let scanners write their assigned raw/candidate/status files.
36
+ 3. Collate accepted findings, rollups, Gate 1/Gate 2 inputs, and ledgers from those files.
37
+ 4. Copy the durable summary into `.work/` scan/fix item bodies:
38
+ - scan epic campaign record
39
+ - altitude story findings and coverage gaps
40
+ - fix epic audit summary
41
+ - fix epic resolved finding index
42
+ - fix feature/story finding citations and gauntlet constraints
43
+ 5. Delete `.work/scan-artifacts/scan-<goal>/`.
44
+ 6. Commit only the collated `.work` item updates. The final git tree should not keep raw scanner
45
+ packets.
46
+
47
+ ## Write Order
48
+
49
+ 1. Before dispatch, the orchestrator creates the directories and assigns each scanner unique
50
+ `raw`, `candidates`, and `status` paths.
51
+ 2. Each scanner writes only those assigned files and returns one terse chat line naming them.
52
+ 3. No scanner writes `manifest.jsonl`, story bodies, rollups, gate packets, source files, or another
53
+ scanner's paths.
54
+ 4. At the band checkpoint, the orchestrator verifies every expected `status` file exists.
55
+ 5. The orchestrator rebuilds or appends `manifest.jsonl` from status files; no concurrent appends.
56
+ 6. The orchestrator spot-checks candidate findings from disk, not from chat.
57
+ 7. Accepted findings go to `accepted/<lane>/<band>.jsonl`; inheritance summaries go to
58
+ `rollups/<lane>/<band>.md`.
59
+ 8. Link raw packet directories, accepted findings, and rollups from the altitude story body.
60
+ 9. For long-running campaigns, commit after each completed altitude story or approved checkpoint,
61
+ but commit only collated `.work` item updates. Do not force-add `.work/scan-artifacts/`.
62
+
63
+ If context compacts, resume by reading `manifest.jsonl`, the current altitude story body, and the
64
+ needed `rollups/` files. Do not ask scanners to regenerate packets that already exist.
65
+
66
+ ## Status And Manifest Schema
67
+
68
+ Each scanner writes one status file:
69
+
70
+ ```json
71
+ {"scan":"scan-goal","lane":"correctness","band":"leaf","component":"parser","story":"scan-goal-correctness-leaf","status":"ok","raw_packet":".work/scan-artifacts/scan-goal/raw/correctness/leaf/parser.md","candidates":".work/scan-artifacts/scan-goal/candidates/correctness/leaf/parser.jsonl","counts":{"critical":0,"high":2,"medium":1,"low":0},"coverage_gap":false}
72
+ ```
73
+
74
+ Use `status: "error"` and `coverage_gap: true` for scanner failures; include a short `error` string
75
+ and still write the status file. The orchestrator writes `manifest.jsonl` from these status files at
76
+ the checkpoint. Missing status files are coverage gaps too; record them in the story body and
77
+ manifest with `status: "missing"`.
78
+
79
+ ## Raw Packets
80
+
81
+ Raw packets are scanner-authored markdown with a short metadata header:
82
+
83
+ ```markdown
84
+ ---
85
+ scan: scan-<goal>
86
+ lane: <lane>
87
+ band: <band>
88
+ component: <component-slug>
89
+ story: scan-<goal>-<lane>-<band>
90
+ status: ok
91
+ ---
92
+
93
+ # Raw scanner packet: <lane>/<band>/<component>
94
+
95
+ <scanner output verbatim>
96
+ ```
97
+
98
+ Do not edit scanner prose after writing it. Corrections belong in accepted findings and gauntlet
99
+ ledgers so the raw record remains auditable.
100
+
101
+ ## Candidate And Accepted Findings
102
+
103
+ Each scanner writes candidate findings to its assigned `candidates/.../<component>.jsonl`. One JSON
104
+ object per finding; write an empty file for no findings:
105
+
106
+ ```json
107
+ {"fingerprint":"src/parser.rs:42|correctness|null-handling","severity":"High","lane":"correctness","band":"leaf","component":"parser","location":"src/parser.rs:42","title":"Parser accepts empty owner","fix_locality":"local","raw_packet":"raw/correctness/leaf/parser.md","rationale":"Confirmed by reading the branch that bypasses owner validation."}
108
+ ```
109
+
110
+ The fingerprint is the idempotency key for in-campaign dedupe. The orchestrator writes
111
+ spot-checked survivors to `accepted/<lane>/<band>.jsonl`, preserving `raw_packet` and adding
112
+ `accepted_by: "orchestrator-spot-check"` plus any drop reason for rejected candidates in the gate or
113
+ campaign ledger.
114
+
115
+ ## Rollups
116
+
117
+ `rollups/<lane>/<band>.md` is the compact input for the next altitude band:
118
+
119
+ - counts by severity
120
+ - accepted finding fingerprints
121
+ - `file:line` + title + one-line rationale
122
+ - inherited parent/cross-cutting bucket assignment
123
+ - coverage gaps
124
+
125
+ The next band receives the relevant rollup excerpt, not every raw packet.
126
+
127
+ ## Gate Packets
128
+
129
+ Before Gate 1, concatenate accepted findings into `gate1-input.md` with links to raw packets and
130
+ rollups. Reviewers get that path. After each round, write `gate1-ledger.md`.
131
+
132
+ Before Gate 2, write the proposed fix epic and child item bodies to `fix-draft.md`. Reviewers get
133
+ that path. After each round, write `gate2-ledger.md`.
134
+
135
+ ## Cleanup Gate
136
+
137
+ Do not delete the artifact root until all of these are true:
138
+
139
+ - `fix-<goal>` exists or the user explicitly declined fix emission.
140
+ - The scan epic's `## Campaign record` includes counts by lane/severity, coverage gaps, contested
141
+ findings, gauntlet outcomes, and whether a fix epic was emitted.
142
+ - The fix epic's `## Audit summary` and `## Resolved finding index` contain the survivors that drove
143
+ the remediation plan.
144
+ - Each emitted fix feature/story cites the findings it resolves and the relevant gauntlet
145
+ constraints.
146
+
147
+ Then delete `.work/scan-artifacts/scan-<goal>/`. If the campaign is abandoned before collation,
148
+ either resume and collate first or ask the user before deleting the only raw evidence.
@@ -0,0 +1,91 @@
1
+ # Consolidation — turning findings into one intelligent fix epic
2
+
3
+ The deliverable is **one coherent remediation plan**, not a backlog flooded with one stub per
4
+ finding. A whole-codebase campaign can surface hundreds of findings; emitted raw, they're
5
+ unusable — duplicative, unsequenced, and impossible to prioritize. Consolidation is the step that
6
+ makes the campaign worth running.
7
+
8
+ This runs **after the pre-consolidation review gauntlet** (see
9
+ [review-gauntlet.md](review-gauntlet.md)) — you cluster only survivors, and the drafted fix epic
10
+ gets one more gauntlet pass before emission.
11
+
12
+ ## Step 1 — collect and dedupe
13
+
14
+ Gather every surviving finding from the artifact ledger's accepted findings and Gate 1 ledger, using
15
+ the altitude story bodies as the human-readable index. Dedupe by `file:line`:
16
+ - Same location flagged by two lanes → keep the higher severity, note "also flagged by: <lane>".
17
+ - A leaf finding subsumed by a module finding (the wider one's fix resolves the narrow one) → keep
18
+ the wider, reference the narrower.
19
+
20
+ ## Step 2 — cluster (the intelligence)
21
+
22
+ Group findings into **fix units** along whichever axis yields the most coherent, lowest-churn work.
23
+ Pick per cluster, don't force one axis on everything:
24
+
25
+ - **By fix locality** — `cross-cutting` findings that share a root cause become *one* feature (e.g.
26
+ "introduce a cancellation-aware task helper and adopt it" resolves 14 scattered findings). This is
27
+ the highest-value clustering: many findings, one well-designed change.
28
+ - **By component** — several findings in one module become one "harden `<module>`" feature, so the
29
+ fixer holds the module's context once instead of context-switching 6 times.
30
+ - **By theme / pattern** — all instances of one named anti-pattern become one feature with a
31
+ consistent remedy, so the fix is applied uniformly (and could seed a new `patterns/` entry).
32
+ - **Standalone** — a genuinely isolated Critical stays its own story; don't bundle it just to bundle.
33
+
34
+ A file or root cause with 5+ findings is almost always *one* "rework this" unit, not five stories —
35
+ mirror bug-scan's clustering instinct, scaled to the campaign.
36
+
37
+ ## Step 3 — shape the fix epic
38
+
39
+ ```
40
+ EPIC fix-<goal> kind: epic, scan_origin: scan-<goal>, release_binding: null
41
+ ├─ FEATURE <cluster-1> one fix unit; tag carries the lane(s) it resolves
42
+ │ ├─ STORY ... concrete, single-stride fixes
43
+ │ └─ STORY ...
44
+ └─ FEATURE <cluster-2>
45
+ ```
46
+
47
+ - **Sequence with `depends_on`:** a shared-abstraction feature (the helper everything adopts) lands
48
+ *before* the adopt-it features that depend on it. Foundational clusters first.
49
+ - **Severity drives stage, not the gauntlet:** Critical/High/Medium clusters → features at
50
+ `stage: drafting`, ready to design. (Findings already passed the gauntlet; staging is about
51
+ urgency, not confidence.) **Low findings do NOT become fix items at all** — they stay as advisory
52
+ notes in the scan epic / story bodies. Minting a backlog stub per Low finding is exactly the flood
53
+ this skill exists to avoid; if the user wants to action one later, they elevate it via
54
+ `/agile-workflow:scope`.
55
+ - **Every fix item carries `scan_origin: scan-<goal>`** so the remediation traces back to the audit,
56
+ and `tags:` reflect the lane(s) + the project taxonomy (route a behavior-preserving cluster
57
+ `[refactor]`, a perf cluster `[perf]`, etc., so downstream design routing is correct).
58
+ - **Each feature body cites its findings:** the `file:line` list it resolves, the cluster rationale,
59
+ and the gauntlet verdict. This is the grounding that lets `feature-design` design the fix without
60
+ re-discovering the problem.
61
+
62
+ ## Step 4 — gauntlet the plan, then emit (operator-confirmed)
63
+
64
+ Gate 2 needs a **concrete artifact** to review, not an idea in your head. Persist the proposed plan
65
+ as a **fix-epic draft packet** before reviewing it: write the would-be epic + every cluster
66
+ feature/story (frontmatter + bodies + the `file:line` lists each resolves) to
67
+ `.work/scan-artifacts/scan-<goal>/fix-draft.md`. Hand *that path* to the Gate 2 gauntlet reviewers so
68
+ a fresh-context, cross-model pass can judge the clustered plan (does it, as written, still pass
69
+ Reality / Context / Intent? did clustering bundle a finding into a feature that now fights a goal?).
70
+ See [review-gauntlet.md](review-gauntlet.md).
71
+
72
+ Once the packet clears Gate 2, **present it and ask before writing the real items** — like
73
+ `research-handoff`, never an auto-flood:
74
+
75
+ > "<N> findings survived review → <M> fix features (<k> foundational, sequenced). Top clusters:
76
+ > … . Emit `fix-<goal>` now?"
77
+
78
+ On confirmation, materialize the packet into real `.work/` items (epic + features/stories, each with
79
+ `scan_origin: scan-<goal>`) and commit:
80
+ `deep-code-scan: emit fix epic for <goal> (<N> findings -> <M> features)`.
81
+
82
+ After emission and the scan epic campaign record are updated, delete
83
+ `.work/scan-artifacts/scan-<goal>/`. The emitted `.work/` items are the durable record; the artifact
84
+ root is in-flight scratch space.
85
+
86
+ ## What the user gets back
87
+
88
+ A single epic they can hand straight to `/agile-workflow:autopilot --scope fix-<goal>` or reprioritize
89
+ via `/agile-workflow:scope` — sequenced, deduped, gauntlet-hardened, and traceable to the audit.
90
+ That is the whole point of the campaign: not "here are 300 things", but "here is the organized,
91
+ vetted work, in the order it should happen."
@@ -0,0 +1,127 @@
1
+ # Codebase Decomposition — building the altitude component map
2
+
3
+ The map is the campaign's spine: `band → [components]`, where every component is a concrete,
4
+ scannable scope. Get this right and every scanner downstream is tight and non-overlapping; get it
5
+ wrong and you either drown leaf scanners in the whole repo or miss whole regions.
6
+
7
+ ## The default altitude bands
8
+
9
+ | Band | Grain | What a component is | Typical count |
10
+ |---|---|---|---|
11
+ | `leaf` | functions / single files | one source file or a tight cluster of hot functions | tens–hundreds |
12
+ | `module` | directories / packages | a cohesive dir, package, or namespace | tens |
13
+ | `subsystem` | bounded contexts | a feature area / service / layer that spans dirs | a handful |
14
+ | `system` | cross-cutting / whole repo | the repo as one unit; cross-component concerns | 1 |
15
+
16
+ Trim or rename to fit. A single-file library is just `file → repo`. A monorepo may add a
17
+ `package` band between `module` and `subsystem`. Confirm the bands at kickoff (Phase 1).
18
+
19
+ ## Why small→large, and how findings flow up
20
+
21
+ Leaf scans are cheap, parallel, and precise — run them first. Each wider band then **inherits the
22
+ findings recorded by the band below it**, so a module scanner already knows "these three files have
23
+ stale-closure issues" and can reason about *interactions* between them rather than re-finding the
24
+ leaf bugs. System-level scanners focus purely on cross-cutting concerns (global invariants,
25
+ end-to-end data flow, architectural seams) because the local issues are already logged below.
26
+
27
+ This is why the altitude stories are `depends_on`-chained leaf→system: it is not just ordering, it
28
+ is information flow.
29
+
30
+ **The roll-up is deterministic by explicit component membership** (sub-agents are stateless, so the
31
+ orchestrator carries findings between bands). Naive path-prefix ownership breaks on real codebases —
32
+ a "CLI pipeline" subsystem spans several directories; a shared util belongs to two parents. So the
33
+ component map must carry, per component, an **explicit file set** and **parent-component edges** (not
34
+ just a directory glob). Then:
35
+
36
+ - a band-N component inherits every confirmed band-(N-1) finding whose `file` is in its file set;
37
+ - a finding may roll up to **multiple** parents (shared files), deterministically ordered by the
38
+ component map's declared order;
39
+ - a finding owned by no single higher component lands in an explicit **cross-cutting bucket** that the
40
+ `system` band always inherits, so nothing is silently dropped.
41
+
42
+ (Same rule referenced in `scanner-brief.md`.)
43
+
44
+ ## Detecting component boundaries
45
+
46
+ Use cheap structural signals before reading code. Prefer `ast-grep` for structure and `rg`/`git`
47
+ for breadth.
48
+
49
+ 1. **Repo skeleton** — `git ls-files | sed 's#/[^/]*$##' | sort -u` for the directory set;
50
+ `tokei`/`cloc` or `git ls-files | xargs wc -l` for size, to find the dense regions worth their
51
+ own component.
52
+ 2. **Declared modules** — manifests and module systems: workspace members (`Cargo.toml`
53
+ `[workspace]`, `package.json` workspaces, `go.mod`), package boundaries (`__init__.py`,
54
+ `mod.rs`, `index.ts`, Go packages), namespaces.
55
+ 3. **Entry points** (seed the subsystem band) — route registrations, CLI mains, workers, cron,
56
+ webhook handlers, exported public API. Each entry point usually anchors a subsystem.
57
+ 4. **Dependency cues** — import/`use`/`require` density. Files that import each other heavily are
58
+ one component; a dir nothing imports is a leaf island. `ast-grep` import patterns or a quick
59
+ `rg '^(import|use|from|require)'` histogram surfaces the clusters.
60
+ 5. **Co-change cues** (optional, powerful) — `git log --format= --name-only | sort | uniq -c | sort -rn`
61
+ and pairwise co-change reveal de-facto modules that the directory tree hides.
62
+
63
+ ## Keeping the leaf set tractable
64
+
65
+ The leaf band can explode. Bound it:
66
+
67
+ - **Cap per parallel wave.** Group leaf components so each scanner gets a *batch* of related files,
68
+ not one file each — aim for a handful of scanners per module's worth of leaves, not one per file.
69
+ - **Goal-filter early.** The scan `goal`/lane prunes irrelevant leaves: a correctness campaign skips
70
+ pure-config and generated files; a perf campaign skips cold setup code. Record what you skipped
71
+ and why — skipped scope is a reportable decision, not a silent gap.
72
+ - **Respect the lane's relevance test.** Reuse the specialist's own Phase-1 logic (e.g. bug-scan's
73
+ domain relevance) to decide whether a leaf even warrants a given lane's scanner.
74
+
75
+ ## Agent budget (compute it, surface it, get it approved)
76
+
77
+ The fan-out is roughly `components × (domains/lenses per lane)` summed over lanes and bands — and it
78
+ **will** explode if left unbounded (eight bug domains over 300 leaf files = 2,400 scanner calls). The
79
+ gauntlet adds more on top. The map is where you bound it. Before the Phase 3 checkpoint, compute and
80
+ write into the epic body (this is the same "agent budget" contract stated in SKILL.md):
81
+
82
+ - **max scanners per wave** (the parallel width of one band's fan-out),
83
+ - **leaf batch size** (files per scanner — the main lever for collapsing the leaf count),
84
+ - **estimated scanner calls**, **plus estimated verification/review calls** (Gate 1 + Gate 2 rounds,
85
+ and the `full`-rigor campaign evaluate — the gauntlet is not free),
86
+ - **estimated total agent calls** (scanners + verification).
87
+
88
+ If the total exceeds a sane threshold (rule of thumb: **>150 total agent calls or >20 scanners per
89
+ wave**), surface it at the checkpoint so the user can narrow lanes, coarsen bands, raise the batch
90
+ size, drop rigor, or accept the cost knowingly. The budget is part of what gets approved — a campaign
91
+ that quietly spawns thousands of agents is a footgun, not a feature.
92
+
93
+ ## Large-repo dispatch
94
+
95
+ For a big or polyglot repo, spawn **1–3 parallel exploratory sub-agents** to build the
96
+ map, each owning a slice of the tree, each returning `band → [components]` with a one-line role per
97
+ component and an approximate size. Merge their maps, dedupe overlaps at boundaries, and resolve any
98
+ component claimed by two slices to the lower (more specific) band. For a small repo, build the map
99
+ inline — don't pay for agents you don't need.
100
+
101
+ ## Output shape (what Phase 3 consumes)
102
+
103
+ A single structured map. Each component carries an **explicit file set** (or globs that resolve to
104
+ one) and its **parent edges**, so the roll-up is unambiguous — not just a label:
105
+
106
+ ```
107
+ goal: correctness lanes: [correctness, tests] bands: [leaf, module, subsystem, system]
108
+
109
+ leaf:
110
+ - args files: [work-view/src/cli/args.rs] parents: [substrate-layer, cli]
111
+ - query files: [work-view/src/substrate/query.rs] parents: [substrate-layer]
112
+ - board-view files: [board/views/board.js] parents: [board-app]
113
+ module:
114
+ - substrate-layer files: [work-view/src/substrate/**] parents: [cli]
115
+ - board-app files: [board/**] parents: []
116
+ subsystem:
117
+ - cli files: [work-view/src/**] (entry: main.rs) parents: [] # spans dirs
118
+ - board-app files: [board/**] parents: []
119
+ system:
120
+ - repo files: [**] role: cross-cutting — error model, IO/time/rand seams + the
121
+ cross-cutting bucket (findings owned by no single component)
122
+ ```
123
+
124
+ Note `args` rolls up to **two** parents (it's shared by the CLI and substrate layers) — that's why
125
+ membership is explicit, not path-prefix. Write each band's component list (with file sets) into the
126
+ matching altitude story body as that story's scan scope. The map — file sets, parent edges, and the
127
+ scanner budget — is the artifact you present at the Phase 3 checkpoint; it is what the user approves.
@@ -0,0 +1,263 @@
1
+ # Item Templates — scan scaffold + fix epic
2
+
3
+ All items follow the agile-workflow active-tier schema (see `docs/SPEC.md`). Two kinds of structure:
4
+
5
+ - **Scan scaffold** (`scan-<goal>` epic + lane features + altitude stories) — **engagement-owned
6
+ working state**. `deep-code-scan` creates it, drives it, and advances it to `done` *within the same
7
+ run*. It is not a queue for `autopilot` to drain (see the routing guardrail in SKILL.md).
8
+ - **Artifact ledger** (`.work/scan-artifacts/scan-<goal>/`) — temporary scanner-written raw packets,
9
+ candidate findings, per-scanner status files, orchestrator-accepted rollups, and gauntlet inputs.
10
+ The scan scaffold links to it while the campaign is running; Phase 6 deletes it after the scan/fix
11
+ item bodies contain the collated durable record.
12
+ - **Fix epic** (`fix-<goal>`) — the **durable deliverable**. Ordinary remediation work that plugs
13
+ straight into the normal design → implement → release machinery.
14
+
15
+ ## The `scan_origin` linkage field
16
+
17
+ `scan_origin: <campaign-slug>` is a **first-class linkage field**, mirroring `research_origin` —
18
+ queryable via `work-view --scan-origin <slug>`, inert (`null`/absent) when not set, no validation
19
+ warning when omitted. It records the scan campaign that produced an item:
20
+
21
+ - **Fix items** carry `scan_origin: scan-<goal>` so the remediation traces back to its audit, and
22
+ re-runs can dedupe against prior campaigns.
23
+ - **Scan items** also carry it (`scan_origin: scan-<goal>`) for symmetry and single-query recall of
24
+ the whole scaffold; their `parent` chain already nests them under the epic.
25
+
26
+ > **Paired scan-aware substrate (one rollout, modeled on `research_origin`).** This skill assumes a
27
+ > substrate patch that ships alongside it:
28
+ > 1. `scan_origin` in `docs/SPEC.md` + the `work-view` parser/model/filter + a `--scan-origin` flag + tests;
29
+ > 2. `scan` / lane / band tags registered in `.work/CONVENTIONS.md`;
30
+ > 3. **autopilot excludes `[scan]`-tagged items from its ready queue** — the structural guarantee the
31
+ > engagement-owned scaffold can't be misrouted to `implement-orchestrator`.
32
+ >
33
+ > Until that lands in a given install, `scan_origin` is inert (the parser ignores unknown frontmatter),
34
+ > `grep -rl 'scan-<goal>' .work/` is the fallback query, and scan items are autopilot-grabbable — so
35
+ > the Phase 0 preflight warns before creating them.
36
+
37
+ ## Canonical tags (slug-safe)
38
+
39
+ Tags must be single kebab tokens — never display names with slashes/spaces ("quality / holistic").
40
+ Scan items use the **scan-domain** tags below; fix items use the **project's** routing taxonomy
41
+ (`.work/CONVENTIONS.md`) so design routing works.
42
+
43
+ | Lane (display) | scan tag |
44
+ |---|---|
45
+ | correctness | `correctness` |
46
+ | tests | `tests` |
47
+ | performance | `performance` |
48
+ | security | `security` |
49
+ | quality / holistic | `quality` |
50
+ | structure / refactor | `structure` |
51
+ | architecture / bold | `architecture` |
52
+ | custom | `custom` |
53
+
54
+ Every scan item also carries the umbrella `scan` tag (the `[scan]` tag is the discriminator
55
+ `autopilot` uses to skip the engagement-owned scaffold); stories add a band tag (`leaf` / `module` /
56
+ `subsystem` / `system`). **Registering `scan` + the lane/band tags in `.work/CONVENTIONS.md` is a
57
+ rollout prerequisite**, shipped with the paired substrate change — not an optional aside.
58
+
59
+ **Fix items never carry `[scan]`.** They use the project's routing taxonomy so design routing works
60
+ *and* so `autopilot` drains them normally — a fix item tagged `[scan]` would be skipped.
61
+
62
+ ---
63
+
64
+ ## Scan scaffold
65
+
66
+ ### Scan campaign epic — `.work/active/epics/scan-<goal>.md`
67
+
68
+ ```yaml
69
+ ---
70
+ id: scan-<goal>
71
+ kind: epic
72
+ stage: drafting # drafting at creation; -> implementing once the Phase 3 plan is approved;
73
+ # -> done at end of the run (audit complete). NOT release-bound.
74
+ tags: [scan, <lane-tags...>]
75
+ parent: null
76
+ depends_on: []
77
+ release_binding: null # a scan is an input/audit, never a release-bundle member
78
+ gate_origin: null
79
+ scan_origin: scan-<goal> # self-anchor: the campaign id
80
+ created: YYYY-MM-DD
81
+ updated: YYYY-MM-DD
82
+ ---
83
+
84
+ # Scan campaign: <goal>
85
+
86
+ ## Goal
87
+ <verbatim user goal — the north star every scanner serves>
88
+
89
+ ## Dials
90
+ - lanes: [<lane>, ...]
91
+ - rigor: floor | standard | full
92
+ - altitude bands: [leaf, module, subsystem, system]
93
+
94
+ ## Agent budget <!-- approved at the Phase 3 checkpoint -->
95
+ - max scanners per wave: <n> · leaf batch size: <n files/scanner>
96
+ - est. scanner calls: <n> · est. verification/review calls (Gate 1 + Gate 2 rounds, evaluate): <n>
97
+ - **est. total agent calls: <n>**
98
+
99
+ ## Component map
100
+ <the band -> [components] map from decomposition; this is what was approved at the checkpoint>
101
+
102
+ ## Artifact ledger
103
+ - root: `.work/scan-artifacts/scan-<goal>/`
104
+ - manifest: `.work/scan-artifacts/scan-<goal>/manifest.jsonl`
105
+ - raw packets: `.work/scan-artifacts/scan-<goal>/raw/`
106
+ - scanner candidates: `.work/scan-artifacts/scan-<goal>/candidates/`
107
+ - scanner statuses: `.work/scan-artifacts/scan-<goal>/status/`
108
+ - accepted findings: `.work/scan-artifacts/scan-<goal>/accepted/`
109
+ - rollups: `.work/scan-artifacts/scan-<goal>/rollups/`
110
+ - lifecycle: temporary; removed after final collation
111
+
112
+ ## Campaign record <!-- filled at Phase 6; a done+unbound epic body is git-historical and may later
113
+ be pruned to a stub, so the DURABLE summary also rides fix-<goal> -->
114
+ - lanes run / components scanned / findings by severity
115
+ - review gauntlet: rounds, dropped (by lens) + why, contested/advisory items (Low findings live here)
116
+ - fix epic emitted: `fix-<goal>` (<M> features)
117
+ - artifact cleanup: `.work/scan-artifacts/scan-<goal>/` removed after collation
118
+ ```
119
+
120
+ ### Lane feature — `.work/active/features/scan-<goal>-<lane>.md`
121
+
122
+ ```yaml
123
+ ---
124
+ id: scan-<goal>-<lane>
125
+ kind: feature
126
+ stage: drafting # -> implementing as its bands run; -> done when the lane is scanned
127
+ tags: [scan, <lane>]
128
+ parent: scan-<goal>
129
+ depends_on: [] # lanes independent unless one informs another
130
+ release_binding: null
131
+ gate_origin: null
132
+ scan_origin: scan-<goal>
133
+ created: YYYY-MM-DD
134
+ updated: YYYY-MM-DD
135
+ ---
136
+
137
+ # Lane: <lane>
138
+
139
+ ## Scanner tier <!-- LOCKED at plan time from the kickoff dial; binds this lane's fan-out -->
140
+ opus | mixed | sonnet | codex-high | codex-xhigh
141
+
142
+ ## Reference knowledge
143
+ <paths from lane-catalog.md this lane's scanners load>
144
+
145
+ ## Altitude spine
146
+ <the bands this lane uses — may be a subset; see lane-catalog "story spine">
147
+ ```
148
+
149
+ ### Altitude story — `.work/active/stories/scan-<goal>-<lane>-<band>.md`
150
+
151
+ ```yaml
152
+ ---
153
+ id: scan-<goal>-<lane>-<band>
154
+ kind: story
155
+ stage: implementing # -> review -> done as the band is scanned + spot-checked
156
+ tags: [scan, <lane>, <band>]
157
+ parent: scan-<goal>-<lane>
158
+ depends_on: [scan-<goal>-<lane>-<band-below>] # leaf: [] ; module deps on leaf ; etc.
159
+ release_binding: null
160
+ gate_origin: null
161
+ scan_origin: scan-<goal>
162
+ created: YYYY-MM-DD
163
+ updated: YYYY-MM-DD
164
+ ---
165
+
166
+ # <lane> / <band>
167
+
168
+ ## Scope (components)
169
+ <this band's component list from the map>
170
+
171
+ ## Inherited findings
172
+ <file:line summaries rolled up from the band below by path ownership — or "leaf band, none">
173
+
174
+ ## Findings <!-- written after the fan-out + per-band spot-check -->
175
+ ### Critical / High / Medium
176
+ - `file:line` — <title> — fix locality: local|module|cross-cutting — <one line>
177
+ ### Low (advisory — stays here, NOT minted as backlog stubs)
178
+ - `file:line` — <title> — <one line>
179
+
180
+ ## Artifact links
181
+ - raw packets: `.work/scan-artifacts/scan-<goal>/raw/<lane>/<band>/`
182
+ - scanner candidates: `.work/scan-artifacts/scan-<goal>/candidates/<lane>/<band>/`
183
+ - scanner statuses: `.work/scan-artifacts/scan-<goal>/status/<lane>/<band>/`
184
+ - accepted findings: `.work/scan-artifacts/scan-<goal>/accepted/<lane>/<band>.jsonl`
185
+ - rollup: `.work/scan-artifacts/scan-<goal>/rollups/<lane>/<band>.md`
186
+
187
+ ## Coverage gaps
188
+ <components/domains skipped + why, scanner errors>
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Fix epic (the deliverable) — `.work/active/epics/fix-<goal>.md`
194
+
195
+ Emitted operator-confirmed at Phase 5, after the draft packet clears gauntlet Gate 2.
196
+
197
+ ```yaml
198
+ ---
199
+ id: fix-<goal>
200
+ kind: epic
201
+ stage: drafting # ready for epic/feature design + autopilot — ordinary work from here
202
+ tags: [<project taxonomy tags reflecting the clusters>]
203
+ parent: null
204
+ depends_on: []
205
+ release_binding: null # binds to a release later, via release-deploy, like any work
206
+ gate_origin: null
207
+ scan_origin: scan-<goal>
208
+ created: YYYY-MM-DD
209
+ updated: YYYY-MM-DD
210
+ ---
211
+
212
+ # Remediation: <goal>
213
+
214
+ **Source:** scan campaign `scan-<goal>` — <N> findings survived the review gauntlet.
215
+
216
+ ## Clusters
217
+ <one line per fix feature: what it resolves, how many findings, why grouped>
218
+
219
+ ## Audit summary (durable)
220
+ <finding counts by severity + lane; the persistent record, since the scan epic may be pruned>
221
+
222
+ ## Resolved finding index (durable — idempotency anchor)
223
+ <every finding this campaign actioned, as `file:line — <slug>` fingerprints, one per line. Lives on
224
+ the fix EPIC (not just child features) so re-runs match against one durable list even after the scan
225
+ scaffold and fix children are archived/pruned.>
226
+ ```
227
+
228
+ ### Fix feature / fix story
229
+
230
+ `.work/active/features/fix-<goal>-<cluster>.md` and `.work/active/stories/fix-<goal>-<cluster>-<n>.md`
231
+
232
+ ```yaml
233
+ ---
234
+ id: fix-<goal>-<cluster>
235
+ kind: feature # or story for a single-stride isolated fix
236
+ stage: drafting
237
+ tags: [<project routing tag: refactor / perf / bug / security / ...>]
238
+ parent: fix-<goal>
239
+ depends_on: [fix-<goal>-<foundational-cluster>] # foundational clusters land first
240
+ release_binding: null
241
+ gate_origin: null
242
+ scan_origin: scan-<goal>
243
+ created: YYYY-MM-DD
244
+ updated: YYYY-MM-DD
245
+ ---
246
+
247
+ # Fix: <cluster name>
248
+
249
+ ## Findings resolved
250
+ - `file:line` — <title> (severity)
251
+
252
+ ## Cluster rationale
253
+ <why these are one unit; the shared root cause or shared remedy>
254
+
255
+ ## Review gauntlet verdict
256
+ <survived rounds; context/intent constraints the fix MUST respect — e.g.
257
+ "keep the parser dependency-free per patterns/hand-rolled-peekable-flag-parser">
258
+ ```
259
+
260
+ > **Routing matters.** Tag each fix cluster with the project taxonomy so agile-workflow routes its
261
+ > design correctly: behavior-preserving cleanup → `[refactor]` (→ refactor-design), perf → `[perf]`
262
+ > (→ perf-design), correctness/security → its domain tag (→ feature-design). The gauntlet's
263
+ > Intent-lens constraints ride in the body so the designer can't undo a deliberate choice.