mandrel 1.87.0 → 1.89.0

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 (140) hide show
  1. package/.agents/README.md +18 -13
  2. package/.agents/audit-checklists/architecture.md +24 -0
  3. package/.agents/audit-checklists/clean-code.md +24 -0
  4. package/.agents/audit-checklists/dependencies.md +14 -0
  5. package/.agents/audit-checklists/devops.md +17 -0
  6. package/.agents/audit-checklists/documentation.md +22 -0
  7. package/.agents/audit-checklists/lighthouse.md +15 -0
  8. package/.agents/audit-checklists/navigability.md +14 -0
  9. package/.agents/audit-checklists/performance.md +22 -0
  10. package/.agents/audit-checklists/privacy.md +21 -0
  11. package/.agents/audit-checklists/quality.md +18 -0
  12. package/.agents/audit-checklists/security.md +22 -0
  13. package/.agents/audit-checklists/seo.md +16 -0
  14. package/.agents/audit-checklists/sre.md +24 -0
  15. package/.agents/audit-checklists/ux-ui.md +21 -0
  16. package/.agents/docs/SDLC.md +63 -16
  17. package/.agents/docs/configuration.md +5 -3
  18. package/.agents/instructions.md +51 -21
  19. package/.agents/personas/architect.md +10 -7
  20. package/.agents/personas/engineer.md +4 -3
  21. package/.agents/personas/project-manager.md +5 -2
  22. package/.agents/personas/refactorer.md +5 -3
  23. package/.agents/rules/git-conventions.md +77 -0
  24. package/.agents/schemas/agentrc.schema.json +16 -4
  25. package/.agents/schemas/audit-rules.json +16 -2
  26. package/.agents/schemas/audit-rules.schema.json +7 -6
  27. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +38 -0
  28. package/.agents/schemas/signal-event.schema.json +28 -13
  29. package/.agents/scripts/acceptance-spec-reconciler.js +6 -4
  30. package/.agents/scripts/check-context-budget.js +320 -0
  31. package/.agents/scripts/diagnose-friction.js +4 -4
  32. package/.agents/scripts/epic-audit-prepare.js +30 -2
  33. package/.agents/scripts/epic-audit-recheck.js +46 -13
  34. package/.agents/scripts/epic-deliver-prepare.js +80 -8
  35. package/.agents/scripts/epic-plan-spec.js +4 -8
  36. package/.agents/scripts/generate-lens-checklists.js +180 -0
  37. package/.agents/scripts/lib/audit-suite/checklist-threading.js +300 -0
  38. package/.agents/scripts/lib/audit-suite/findings.js +27 -0
  39. package/.agents/scripts/lib/audit-suite/index.js +9 -0
  40. package/.agents/scripts/lib/audit-suite/lens-checklist.js +212 -0
  41. package/.agents/scripts/lib/audit-suite/selector.js +136 -5
  42. package/.agents/scripts/lib/checks/loop-health.js +340 -0
  43. package/.agents/scripts/lib/cli-args.js +8 -0
  44. package/.agents/scripts/lib/config/explain.js +4 -0
  45. package/.agents/scripts/lib/config/runners.js +21 -2
  46. package/.agents/scripts/lib/config/temp-paths.js +24 -0
  47. package/.agents/scripts/lib/config-settings-schema-delivery.js +23 -3
  48. package/.agents/scripts/lib/config-settings-schema-quality.js +7 -0
  49. package/.agents/scripts/lib/doc-tiers.js +291 -0
  50. package/.agents/scripts/lib/epic-body-sections.js +5 -2
  51. package/.agents/scripts/lib/epic-merge-lock.js +83 -0
  52. package/.agents/scripts/lib/epic-plan-clarity.js +3 -1
  53. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +66 -20
  54. package/.agents/scripts/lib/feedback-loop/graduator-core.js +395 -86
  55. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +299 -72
  56. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +438 -0
  57. package/.agents/scripts/lib/gates/friction.js +15 -5
  58. package/.agents/scripts/lib/observability/perf-aggregator.js +30 -104
  59. package/.agents/scripts/lib/observability/perf-report-readers.js +1 -1
  60. package/.agents/scripts/lib/observability/signal-validator.js +204 -0
  61. package/.agents/scripts/lib/observability/signals-writer.js +157 -54
  62. package/.agents/scripts/lib/observability/tool-trace-hook.js +42 -4
  63. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
  64. package/.agents/scripts/lib/orchestration/code-review.js +74 -4
  65. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +213 -0
  66. package/.agents/scripts/lib/orchestration/doc-reader.js +4 -96
  67. package/.agents/scripts/lib/orchestration/docs-digest.js +34 -0
  68. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +56 -19
  69. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +22 -0
  70. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +188 -0
  71. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +6 -0
  72. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +69 -8
  73. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +11 -2
  74. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +47 -61
  75. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +39 -3
  76. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +144 -0
  77. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +258 -14
  78. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +6 -0
  79. package/.agents/scripts/lib/orchestration/merge-block-class.js +218 -0
  80. package/.agents/scripts/lib/orchestration/plan-review-routing.js +1 -1
  81. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -3
  82. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +63 -34
  83. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +167 -52
  84. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +49 -2
  85. package/.agents/scripts/lib/orchestration/retro-proposals.js +12 -55
  86. package/.agents/scripts/lib/orchestration/retro-runner.js +9 -0
  87. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -0
  88. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +419 -0
  89. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +35 -2
  90. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +353 -69
  91. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +66 -4
  92. package/.agents/scripts/lib/orchestration/spec-section-validator.js +60 -9
  93. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +7 -5
  94. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +24 -2
  95. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +167 -8
  96. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +163 -0
  97. package/.agents/scripts/lib/orchestration/ticketing/reads.js +20 -9
  98. package/.agents/scripts/lib/planning-corpus.js +306 -0
  99. package/.agents/scripts/lib/signals/detectors/common.js +10 -10
  100. package/.agents/scripts/lib/signals/detectors/index.js +4 -4
  101. package/.agents/scripts/lib/signals/detectors/retry.js +19 -18
  102. package/.agents/scripts/lib/signals/detectors/rework.js +1 -1
  103. package/.agents/scripts/lib/signals/schema.js +56 -81
  104. package/.agents/scripts/lib/signals/span-tree.js +6 -5
  105. package/.agents/scripts/lib/story-plan.js +3 -0
  106. package/.agents/scripts/lib/wave-runner/tick.js +10 -2
  107. package/.agents/scripts/lifecycle-emit.js +39 -8
  108. package/.agents/scripts/providers/github/issues.js +12 -1
  109. package/.agents/scripts/resolve-doc-tiers.js +83 -0
  110. package/.agents/scripts/retro-run.js +51 -0
  111. package/.agents/scripts/signals-view.js +1 -1
  112. package/.agents/scripts/single-story-close.js +20 -1
  113. package/.agents/scripts/standalone-feedback-rollup.js +188 -0
  114. package/.agents/scripts/story-close.js +48 -0
  115. package/.agents/scripts/story-plan.js +51 -12
  116. package/.agents/scripts/validate-docs-freshness.js +69 -15
  117. package/.agents/skills/core/documentation-and-adrs/SKILL.md +58 -0
  118. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +5 -3
  119. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +20 -7
  120. package/.agents/skills/core/scope-triage/SKILL.md +61 -0
  121. package/.agents/skills/skills.index.json +3 -3
  122. package/.agents/workflows/audit-documentation.md +82 -2
  123. package/.agents/workflows/helpers/code-review.md +193 -44
  124. package/.agents/workflows/helpers/deliver-epic.md +128 -39
  125. package/.agents/workflows/helpers/deliver-stories.md +26 -0
  126. package/.agents/workflows/helpers/epic-audit.md +116 -283
  127. package/.agents/workflows/helpers/epic-deliver-story.md +14 -0
  128. package/.agents/workflows/helpers/epic-plan-decompose.md +18 -200
  129. package/.agents/workflows/helpers/epic-plan-spec.md +18 -180
  130. package/.agents/workflows/helpers/plan-epic.md +141 -105
  131. package/.agents/workflows/helpers/plan-story.md +32 -0
  132. package/.agents/workflows/helpers/single-story-deliver.md +43 -0
  133. package/.agents/workflows/loops/nightly-audit.md +9 -7
  134. package/docs/CHANGELOG.md +29 -0
  135. package/lib/cli/doctor.js +44 -0
  136. package/package.json +4 -3
  137. package/.agents/scripts/epic-plan-spec-validate.js +0 -111
  138. package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +0 -207
  139. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +0 -58
  140. package/.agents/scripts/lib/signals/detectors/hotspot.js +0 -292
@@ -1,44 +1,53 @@
1
1
  ---
2
2
  description: >-
3
- Run smart change-set audits at Epic finalize. Consumes the epic-audit-prepare
4
- envelope, dispatches each selected lens (inline, or via a single
5
- audit-orchestrator sub-agent that fans the lenses out as parallel level-2
6
- agents) through runAuditSuite, and posts an audit-results structured comment
7
- back onto the Epic ticket.
3
+ Resolve the slim Epic-close (gate3) lens roster for /deliver. Consumes the
4
+ epic-audit-prepare envelope, restricts it to the cumulative + global +
5
+ risk-routed lenses the Epic-close tier owns, and hands that roster to the
6
+ Phase 5 code-review pass which walks the cumulative Epic diff once and folds
7
+ the lens findings into its single verification-results comment.
8
8
  ---
9
9
 
10
- # Epic Audit (helper)
10
+ # Epic-close lens roster (helper)
11
11
 
12
12
  > **Helper module.** Not a slash command. Invoked automatically from
13
13
  > `/deliver` Phase 4 once the wave loop completes (all Stories at
14
- > `agent::done`). To run an audit directly, use `/deliver [Epic_ID]` it
15
- > delegates here (or pass `--skip-epic-audit` to bypass).
14
+ > `agent::done`) to resolve the Epic-close lens roster that the Phase 5
15
+ > code-review pass walks. To run delivery, use `/deliver [Epic_ID]` (or pass
16
+ > `--skip-epic-audit` to bypass the Epic-close lens tier).
17
+
18
+ This helper resolves the **change-set-aware Epic-close lens roster** for an
19
+ Epic branch. Under the three-tier verification model (Epic #4405) it does
20
+ **not** run a standalone lens walk of its own and does **not** post its own
21
+ structured comment. It selects the slim roster and hands it to the Phase 5
22
+ [`code-review.md`](code-review.md) pass, which walks the cumulative Epic diff
23
+ **once** — executing these lenses as review dimensions — and folds their
24
+ findings into the single `verification-results` comment it posts on the Epic.
25
+
26
+ > **Three-tier model — where each lens concern is verified.** A lens's `scope`
27
+ > field in [`audit-rules.json`](../../schemas/audit-rules.json) (resolved by
28
+ > [`resolveLensTier`](../../scripts/lib/audit-suite/selector.js)) decides the
29
+ > **one** tier that owns its concern:
30
+ >
31
+ > - **`local`** — decidable from a single Story's diff. Verified **write-time**
32
+ > (the distilled checklist threaded into the Story implementation prompt,
33
+ > Story #4410) and at **Story-scope** (the maker-blind local-lens pass in
34
+ > `story-close`, Story #4409). **Not** re-run at Epic close.
35
+ > - **`cumulative`** — only decidable across the Epic's combined diff. Verified
36
+ > at **Epic close** only.
37
+ > - **`global`** — evaluates a whole-product property regardless of the diff
38
+ > (exempt from the cross-epic-leak change-set narrowing). Verified at **Epic
39
+ > close** only.
40
+ >
41
+ > No lens concern is verified at more than one tier: the Epic-close roster
42
+ > below deliberately **excludes every local-tier change-set lens** and keeps
43
+ > only cumulative + global + risk-routed lenses.
16
44
 
17
- This helper runs the **change-set-aware audit pass** on an Epic branch
18
- before the code-review helper opens its review. Unlike code-review (which
19
- walks the full diff against `main` through six fixed pillars), epic-audit
20
- asks the [`selectAudits`](../../scripts/lib/audit-suite/index.js) SDK which
21
- lenses are actually relevant to **this** Epic's change set, then dispatches
22
- only the matching audit workflows. Docs-only Epics select zero lenses and
23
- exit cleanly.
45
+ **When to run**: After Phase 3 close-validation passes, to resolve the roster
46
+ the Phase 5 code-review pass consumes. `/deliver` invokes this automatically
47
+ once the wave loop completes and all Stories reach `agent::done`.
24
48
 
25
- In addition to the change-set selection, `epic-audit-prepare.js` unions in
26
- the **risk-routed lenses** (Story #3889): it reads the Epic's model-judged
27
- `planningRisk` envelope off the `epic-plan-state` checkpoint and routes each
28
- high-risk axis to its mapped lens via
29
- [`resolveAuditLenses`](../../scripts/lib/orchestration/code-review.js)
30
- (`security` → `audit-security`, `public-api` → `audit-architecture`). A
31
- high-risk Epic therefore auto-runs its mapped lenses even when the change
32
- set alone did not select them; a low-risk Epic adds nothing beyond the
33
- change-set selection. Both lens sources fire through the **same**
34
- `runAuditSuite` dispatch below — no new audit machinery.
35
-
36
- > **When to run**: After Phase 3 close-validation passes and before Phase 5
37
- > code-review. `/deliver` invokes this automatically once the wave loop
38
- > completes and all Stories reach `agent::done`.
39
- >
40
- > **Persona**: `architect` · **Skills**: `core/code-review-and-quality`,
41
- > `core/security-and-hardening`
49
+ **Persona**: `architect` · **Skills**: `core/code-review-and-quality`,
50
+ `core/security-and-hardening`
42
51
 
43
52
  ## Step 0 — Resolve Context
44
53
 
@@ -53,14 +62,13 @@ change-set selection. Both lens sources fire through the **same**
53
62
  - **Tech Spec** — the folded Tech Spec sections (opening with
54
63
  `## Delivery Slicing`) inside the body's managed region.
55
64
  5. Read the Epic body fully (including its Tech Spec sections) to
56
- understand the intended
57
- scope, selected lenses, and acceptance criteria.
65
+ understand the intended scope, selected lenses, and acceptance criteria.
58
66
 
59
67
  ## Step 1 — Prepare (`epic-audit-prepare.js`)
60
68
 
61
69
  Run the prepare CLI to compute the change-set, ask `selectAudits` which
62
- lenses fire at `gate3` (the Epic close gate), and emit the helper-consumable
63
- JSON envelope on stdout:
70
+ lenses fire at `gate3` (the Epic close gate), union in the model-judged
71
+ risk-routed lenses, and emit the helper-consumable JSON envelope on stdout:
64
72
 
65
73
  ```bash
66
74
  node .agents/scripts/epic-audit-prepare.js \
@@ -76,55 +84,54 @@ envelope:
76
84
  "epicId": 2586,
77
85
  "epicBranch": "epic/2586",
78
86
  "depth": "deep",
79
- "selectedAudits": ["audit-security", "audit-privacy"],
80
- "changeSetAudits": ["audit-privacy"],
87
+ "selectedAudits": ["audit-security", "audit-architecture", "audit-privacy"],
88
+ "epicCloseLenses": ["audit-architecture", "audit-security"],
89
+ "changeSetAudits": ["audit-architecture", "audit-privacy"],
81
90
  "riskRoutedAudits": ["audit-security"],
91
+ "globalLenses": [],
82
92
  "changedFiles": ["src/api/admin/users.ts", "..."],
83
93
  "changedFilesCount": 47,
84
94
  "substitutionsPayload": "src/api/admin/users.ts\n..."
85
95
  }
86
96
  ```
87
97
 
88
- `selectedAudits` is the de-duplicated **union** of `changeSetAudits` (the
89
- change-set selection) and `riskRoutedAudits` (the model-judged risk-routed
90
- lenses). The two source arrays are surfaced for observability so the
91
- operator can see why each lens fired. Dispatch the `selectedAudits` union
92
- in Step 2.
98
+ - **`epicCloseLenses` is the roster you hand to Phase 5.** It is the slim
99
+ Epic-close roster `selectedAudits` restricted to the tiers the Epic-close
100
+ tier owns via
101
+ [`selectEpicCloseLenses`](../../scripts/lib/orchestration/code-review.js):
102
+ every **cumulative** and **global** change-set lens, plus every **risk-routed**
103
+ lens, with every **local-tier** change-set lens dropped (its concern is
104
+ already verified shift-left). In the example above `audit-privacy` (local) is
105
+ excluded from `epicCloseLenses` while `audit-architecture` (cumulative) is
106
+ kept and `audit-security` (risk-routed) is kept.
107
+ - **`selectedAudits` / `changeSetAudits` / `riskRoutedAudits`** are surfaced for
108
+ observability so the operator can see the pre-slim union and why each lens
109
+ fired. Do **not** walk `selectedAudits` — walk `epicCloseLenses`.
110
+ - **`globalLenses`** is the subset of the roster on the global-lens allowlist
111
+ (e.g. `audit-navigability`) that the Phase 5 walk runs against the WHOLE
112
+ route tree, exempt from the cross-epic-leak guard's change-set narrowing
113
+ (Epic #4131, F2).
93
114
 
94
115
  ### The `depth` field (Story #3939)
95
116
 
96
117
  `depth` is `light`, `standard`, or `deep` — an **orthogonal** signal that
97
- tells you how thorough each **selected** lens should be on this Epic. It is
98
- resolved by the shared `resolveDepth` resolver from the Epic's model-judged
99
- risk envelope (`overallLevel` off the `epic-plan-state` checkpoint) folded
100
- with `changedFilesCount`: a high-risk **or** wide-footprint Epic resolves to
101
- `deep`, a low-risk small one to `light`, and everything else — including an
102
- Epic that skipped `/plan` and has no checkpoint — to `standard`.
103
-
104
- Depth changes **how deeply** each lens runs, never **which** lenses fire:
105
-
106
- - **`light`** — run each selected lens against the **changed surface only**
107
- and report **only Critical/High** findings. Skip the Medium/Suggestion
108
- sweep. Light **never skips a selected lens** — including the alwaysRun
109
- floor (`audit-clean-code`, `audit-architecture`) — it only **shrinks the
110
- selected lens's sweep**. An easy, low-risk Epic still runs its audits;
111
- they just run lighter.
112
- - **`standard`** — today's behavior: walk each selected lens's full
113
- procedure over the changed surface at every severity.
114
- - **`deep`** — run the full lens procedure over the changed surface **plus
115
- the modules it directly touches**, at every severity.
116
-
117
- Depth never changes the severity taxonomy, the findings-report shape, or the
118
- Phase 4 halting rule below (a 🔴 Critical Blocker halts at every depth;
119
- everything else logs). Thread the resolved `depth` into each lens walk in
120
- Step 2 — it scopes the sweep, it does not gate the roster.
118
+ tells the Phase 5 pass how thorough each **rostered** lens should be on this
119
+ Epic. It is resolved by the shared `resolveDepth` resolver from the Epic's
120
+ model-judged risk envelope (`overallLevel` off the `epic-plan-state`
121
+ checkpoint) folded with `changedFilesCount`: a high-risk **or** wide-footprint
122
+ Epic resolves to `deep`, a low-risk small one to `light`, and everything else
123
+ — including an Epic that skipped `/plan` and has no checkpoint — to `standard`.
124
+ Depth changes **how deeply** each lens runs, never **which** lenses fire.
121
125
 
122
126
  ### Outcomes
123
127
 
124
- - **`selectedAudits` is non-empty** — continue to Step 2.
125
- - **`selectedAudits` is empty** (docs-only or no-lens change set, and no
126
- high-risk axis routed a lens) skip Step 2 and write the docs-only
127
- marker described in Step 4.
128
+ - **`epicCloseLenses` is non-empty** — pass the roster (and `depth`,
129
+ `globalLenses`, `substitutionsPayload`) to the Phase 5 code-review pass.
130
+ - **`epicCloseLenses` is empty** (docs-only change set, or every selected lens
131
+ was a local-tier change-set lens already covered shift-left, and no high-risk
132
+ axis routed a lens) — there is no Epic-close lens dimension to walk; the
133
+ Phase 5 pass still runs its review pillars and posts the
134
+ `verification-results` comment.
128
135
  - **`degraded: true`** — the selector aborted. Possible `reason` codes:
129
136
  `GIT_DIFF_TIMEOUT` (git-diff timed out), `HEAD_REF_UNRESOLVED` (the
130
137
  Epic's branch `refs/heads/epic/<id>` is not present in this checkout),
@@ -135,222 +142,48 @@ Step 2 — it scopes the sweep, it does not gate the roster.
135
142
  roster — that defeats the change-set scoping, and an unresolved/mismatched
136
143
  ref means the change set would belong to the wrong Epic.
137
144
 
138
- ## Step 2 — Walk Selected Lenses (`runAuditSuite`)
139
-
140
- > **Execution model the host LLM is the executor, not the CLI.**
141
- > `run-audit-suite.js` is a **prompt-assembly runner**, not a findings
142
- > generator. It resolves each lens to its workflow markdown, applies
143
- > the `{{ticketId}}` / `{{baseBranch}}` / `{{changedFiles}}` (and any
144
- > per-audit) substitutions, and returns one *workflow descriptor* per
145
- > lens. Its return envelope intentionally carries `findings: []` and
146
- > `summary: { critical:0, high:0, medium:0, low:0 }` because no lens
147
- > has been *executed* yet — the host LLM walks each workflow's
148
- > procedure inline against the substitution payload, severity-rates
149
- > what it finds, and assembles the aggregate report in Step 4. If you
150
- > expected `findings[]` to be populated by the CLI, the rest of this
151
- > helper will surprise you; stop and re-read this paragraph.
152
-
153
- For each lens name in `selectedAudits`, invoke
154
- [`runAuditSuite`](../../scripts/lib/audit-suite/index.js) (or its CLI
155
- wrapper) with the prepare envelope as the substitution source. The
156
- runner loads the matching `.agents/workflows/audit-<lens>.md` file,
157
- applies the substitutions, and when `--run-id` is supplied — writes
158
- the substituted body to a per-lens artifact at
159
- `<auditOutputDir>/audit-<run-id>-<lens>.md` (default `auditOutputDir`
160
- is `temp/audits/`):
161
-
162
- ```bash
163
- node .agents/scripts/run-audit-suite.js \
164
- --audits audit-security,audit-privacy \
165
- --ticket [EPIC_ID] \
166
- --base-branch [BASE_BRANCH] \
167
- --substitution changedFiles="[substitutionsPayload]" \
168
- --run-id epic-[EPIC_ID]
169
- ```
170
-
171
- CLI shape notes:
172
-
173
- - `--audits` is **comma-separated**, not space-separated. Passing each
174
- lens as a separate positional arg only captures the first one.
175
- - `--substitution` is **repeatable** (`key=value` per occurrence); the
176
- legacy `--substitutions '<json>'` flag is not supported.
177
- - `--run-id` is the per-lens artifact prefix (the legacy
178
- `--artifact-prefix` flag is not supported). When omitted, no
179
- artifact is written and the host LLM must walk the workflow body in
180
- memory.
181
-
182
- After the runner returns:
183
-
184
- 1. **Read the descriptor stream** — confirm every requested lens
185
- appears in `metadata.auditsRun`, then walk each entry in
186
- `workflows[]` (or each on-disk artifact when `--run-id` was set).
187
- 2. **Execute the lens inline at the run's `depth`.** Open the lens
188
- workflow at `path` (or the per-lens artifact file when `--run-id`
189
- produced one) and follow its procedure verbatim against the
190
- substituted change set, scoping the sweep to the envelope's `depth`
191
- (see "The `depth` field" above): `light` walks the changed surface and
192
- reports only 🔴/🟠 findings, `standard` walks the full procedure at
193
- every severity, and `deep` extends the sweep to the directly-touched
194
- modules. Depth scopes the sweep only — never skip a selected lens.
195
- Each lens declares its own pillars, severity rubric, and remediation
196
- prose; treat its body as the canonical execution contract for that
197
- pass.
198
- 3. **Aggregate** by severity (🔴 Critical Blocker / 🟠 High /
199
- 🟡 Medium / 🟢 Suggestion). Hold the aggregate for Step 3
200
- (auto-fix) and Step 4 (the `audit-results` structured comment).
201
-
202
- ### Optional: delegate the roster walk to an audit-orchestrator sub-agent
203
-
204
- The Step 2 loop above walks the `selectedAudits` roster **serially in the
205
- host's own context**. When the roster carries more than one lens, `/deliver`
206
- Phase 4 MAY instead delegate the whole walk to a **single audit-orchestrator
207
- sub-agent** — one level-1 `Agent` call (`subagent_type: general-purpose`) — that:
208
-
209
- 1. Receives the **already-selected** `selectedAudits` roster, the run's
210
- `depth`, and the prepare envelope's substitution payload. It does **not**
211
- re-run `selectAudits` and does **not** widen the roster — the roster is
212
- fixed upstream by Step 1 (see the Constraints below).
213
- 2. Fans the roster out as **parallel level-2 agents, one per lens** (nested
214
- `Agent` dispatch — verified depth 2, announced max depth 5, per
215
- [#2870](https://github.com/dsj1984/mandrel/issues/2870) and the
216
- "Flat Story dispatch by design" note in
217
- [`deliver-epic.md`](deliver-epic.md)). Each level-2 agent executes exactly
218
- one lens's workflow procedure at the run's `depth`, isolated from the main
219
- context.
220
- 3. Collects the per-lens findings, **aggregates them by severity** (🔴 / 🟠 /
221
- 🟡 / 🟢), and returns **only the aggregated audit-results** to the host —
222
- the per-lens reasoning transcripts stay in the level-2 leaves and never
223
- enter the main context. The host resumes at Step 3 (remediation routing)
224
- with the aggregate exactly as if it had walked the roster itself, and Step 4
225
- posts the identical `audit-results` comment.
226
-
227
- This delegation is a **cross-lens parallelization of the roster walk only**. It
228
- is orthogonal to — and MUST NOT be conflated with — the *per-lens execution
229
- strategy*:
230
-
231
- - **The per-lens cost/precision gate is preserved.** Each level-2 lens agent
232
- still runs its own lens at whatever strategy that lens's cost/precision gate
233
- dictates (`docs/roadmap.md` § "The per-lens cost / precision gate"): an
234
- orchestrated lens fans its own analysis dimensions out under
235
- `runAuditOrchestration`, a sequential-only lens runs turn-by-turn. Fanning
236
- the *roster* out in parallel changes **which context** runs a lens, never
237
- **how** that lens runs internally, so no per-lens cost gate is bypassed or
238
- altered.
239
- - **The "do not batch-convert the sequential-only lenses" rule is preserved.**
240
- The seven sequential-only lenses (`audit-dependencies`, `audit-devops`,
241
- `audit-sre`, `audit-privacy`, `audit-seo`, `audit-ux-ui`,
242
- `audit-lighthouse`) stay sequential **inside** their level-2 agent.
243
- Dispatching them as parallel level-2 agents is **not** a batch-conversion of
244
- their internal execution — a sequential-only lens remains sequential-only
245
- (`docs/roadmap.md` § "Remaining orchestration surface"). Generalizing any of
246
- those lenses to orchestrated is still a separate, gated, lens-by-lens
247
- decision that this roster fan-out neither performs nor pre-empts.
248
-
249
- Weigh the whole subtree's token cost before delegating
250
- ([`.agents/instructions.md` § 4](../../instructions.md) — cost compounds with
251
- nesting depth): the level-1 orchestrator plus one level-2 agent per lens
252
- re-pays the always-loaded context at each level. For a single-lens roster the
253
- serial host walk is cheaper; the delegation pays off when several lenses fan
254
- out at once. Either path produces the identical Step 4 `audit-results` comment,
255
- so the delegation is a performance/context-isolation choice, never a change to
256
- what gets audited or reported.
257
-
258
- If a future Story lifts per-lens execution out of the host-LLM walk
259
- into the CLI itself, the runner will populate `findings[]` and this
260
- section will collapse to a "read the structured findings off the
261
- envelope" bullet. Until then, the host LLM is the gate.
262
-
263
- ## Step 3 — Remediation Routing (host LLM, no automated loop)
264
-
265
- There is **no runtime auto-fix function** at this phase. The host LLM is
266
- the executor: it inspects the aggregated 🔴 / 🟠 findings from Step 2 and
267
- either applies a focused fix on the Epic branch or escalates the finding
268
- to the operator via the `audit-results` comment in Step 4.
269
-
270
- For each 🔴 / 🟠 finding, the host LLM MUST decide between two paths:
271
-
272
- 1. **Apply a focused fix on `[EPIC_BRANCH]`.** Permitted only when the
273
- finding is unambiguously *fixable* (clean remediation, no scope creep,
274
- no spec deviation, no secret exposure):
275
- - Call [`assert-branch.js`](../../scripts/assert-branch.js) with
276
- `--expected [EPIC_BRANCH]` before touching the working tree.
277
- - Stage explicit paths only (never `git add .`).
278
- - Make one focused conventional commit per finding
279
- (`fix(<scope>): <description> (audit finding)`).
280
- - Re-run the owning lens (re-invoke `run-audit-suite.js` for that
281
- single lens) and confirm the finding is gone before moving on.
282
- - Run the lens-appropriate validation subset (`npm run lint` plus the
283
- relevant `npm test` slice) to confirm the fix did not regress
284
- anything.
285
- - If the rescan still surfaces the same finding, or validation
286
- regresses, **stop fixing** — route the finding to escalation
287
- (path 2) and record the attempt context in Step 4.
288
- 2. **Escalate to the operator via Step 4.** Required when the finding
289
- falls into any of the following classes:
290
- - `spec-deviation` — the change diverges from the Epic/Tech Spec.
291
- - `secrets` — credentials, tokens, or PII surfaced in the diff.
292
- - `test-deletion` — coverage was removed without an explicit
293
- decision in the spec.
294
- - `scope-exceeded` — the remediation would touch more files than the
295
- change set warrants.
296
- - Any finding the host LLM cannot remediate after one focused
297
- attempt (the equivalent of the prior loop's
298
- `validation-regression` / `thrash-detected` exits).
299
-
300
- Do not invent a programmatic retry budget. The host LLM applies *at most
301
- one* focused-fix attempt per finding before escalating; any further
302
- remediation is the operator's call after reading the `audit-results`
303
- comment.
304
-
305
- Escalated findings flow through to Step 4 unchanged with their
306
- escalation reason recorded — the audit pass does not delete them, it
307
- just stops trying to fix them automatically. Surface the escalation
308
- reason for each in the `audit-results` comment so the operator sees
309
- exactly why the finding was not auto-remediated.
310
-
311
- ## Step 4 — Post `audit-results` Structured Comment
312
-
313
- Persist the findings as an `audit-results` structured comment on the Epic
314
- issue. The comment is idempotent — re-runs replace the prior one. Build the
315
- body in a temp file under `[TEMP_ROOT]/epic-[EPIC_ID]/audit-results.md`,
316
- then upsert via [`post-structured-comment.js`](../../scripts/post-structured-comment.js):
317
-
318
- ```bash
319
- node .agents/scripts/post-structured-comment.js \
320
- --ticket [EPIC_ID] \
321
- --marker audit-results \
322
- --body-file [TEMP_ROOT]/epic-[EPIC_ID]/audit-results.md
323
- ```
324
-
325
- The body MUST include:
326
-
327
- - the `selectedAudits` roster (or `Lenses applied: none (docs-only)` when
328
- the prepare envelope returned an empty list),
329
- - the per-severity counts (🔴 critical / 🟠 high / 🟡 medium / 🟢 suggestion),
330
- - the per-lens findings grouped under the lens name, each carrying file
331
- path + line range + pillar + recommended fix,
332
- - a link to the per-lens artifact files under `<auditOutputDir>` so the
333
- operator (and downstream retro) can re-read the full prompt body.
334
-
335
- ### Severity gating
336
-
337
- - **Any 🔴 Critical Blocker** → STOP. Relay to the operator and let
338
- `/deliver` Phase 4 record a manual intervention.
339
- - **Only 🟠/🟡/🟢** → log as non-blocking and return to `/deliver`
340
- Phase 5 (code-review).
145
+ ## Step 2 — Hand the roster to the Phase 5 code-review pass
146
+
147
+ The cumulative Epic diff is walked **once** at Epic close. The
148
+ `epicCloseLenses` roster is executed as **dimensions of the Phase 5
149
+ code-review pass** ([`code-review.md`](code-review.md) with `scope: epic`),
150
+ not as a standalone walk here. The Phase 5 pass:
151
+
152
+ 1. Loads each rostered lens's `.agents/workflows/audit-<lens>.md` via
153
+ [`runAuditSuite`](../../scripts/lib/audit-suite/index.js) (the
154
+ prompt-assembly runner), applying the `{{changedFiles}}` / `{{ticketId}}` /
155
+ `{{baseBranch}}` substitutions from this envelope.
156
+ 2. Executes each lens inline at the run's `depth` over the cumulative diff,
157
+ folding its findings into the pass's severity aggregate alongside the review
158
+ pillars one walk of `main..epic/<id>`, one aggregate.
159
+ 3. Posts the single `verification-results` structured comment (there is **no**
160
+ separate `audit-results` comment; that producer was retired in Story #4412).
161
+
162
+ Remediation of the aggregate is **tier-aware** (Story #4412): the Epic-close
163
+ tier reads `delivery.epicAudit.autoFixSeverity` (default **`high`** — see
164
+ [`config/runners.js`](../../scripts/lib/config/runners.js)) and routes only
165
+ 🔴 Critical + 🟠 High findings into on-branch remediation; 🟡 Medium and
166
+ 🟢 Suggestion findings graduate to follow-up issues, because 🟡 Medium concerns
167
+ are already remediated shift-left at the write-time and Story-scope tiers.
168
+ Setting `medium` opts back into routing 🔴/🟠/🟡 on-branch. The severity gate
169
+ is **unchanged** — a surviving 🔴 Critical Blocker halts the run.
341
170
 
342
171
  ## Constraints
343
172
 
344
173
  - **Always** diff against `[BASE_BRANCH]`, not against individual Story
345
- branches. The audit examines the cumulative effect of the entire Epic.
346
- - **Always** read the Epic body and Tech Spec before walking lenses. Findings
174
+ branches. The Epic-close pass examines the cumulative effect of the entire
175
+ Epic.
176
+ - **Always** read the Epic body and Tech Spec before the Phase 5 walk. Findings
347
177
  without spec context are noise.
348
- - **Always** cap focused fixes at one attempt per finding (Step 3). The
349
- host LLM is the executor; there is no shared retry/anti-thrash module
350
- to call. Any finding that does not resolve cleanly on the first attempt
351
- routes to escalation in Step 4.
352
- - **Never** widen the lens roster past `selectedAudits`. The whole point of
353
- the change-set selector is to avoid running irrelevant audits on a
354
- scoped Epic running extras defeats the gate.
178
+ - **Never** walk `selectedAudits`. The roster the Phase 5 pass executes is
179
+ `epicCloseLenses` the slim cumulative + global + risk-routed set. Walking
180
+ the pre-slim union re-verifies local-tier concerns already covered
181
+ shift-left, defeating the three-tier model.
182
+ - **Never** widen the roster past `epicCloseLenses`. The whole point of the
183
+ change-set selector plus the tier filter is to avoid re-running irrelevant or
184
+ already-verified lenses on a scoped Epic.
185
+ - **Never** post an `audit-results` structured comment. The Epic-close lens
186
+ findings are folded into the Phase 5 `verification-results` comment — the
187
+ single findings surface on the Epic.
355
188
  - **Always** propagate `degraded` envelopes verbatim. Do not paper over a
356
189
  selector failure with a full-roster fallback.
@@ -180,6 +180,20 @@ Run a single Story-implementation phase against the inline `acceptance[]`
180
180
  Story's own context points you at one. See
181
181
  [`.agents/instructions.md` § 3](../../instructions.md).
182
182
 
183
+ **Write-time audit checklists — read the threaded `checklistPath`.**
184
+ When the parent prompt passes a `checklistPath` (Story #4410 — the
185
+ repo-relative path to this Story's footprint-matched **local**-lens
186
+ authoring checklists at
187
+ `temp/epic-<epicId>/checklists/story-<storyId>.md`), read it before you
188
+ write and self-check your change against each listed concern as you
189
+ author it. These are the local audit lenses whose `filePatterns` matched
190
+ the Story's predicted footprint, distilled to a short checklist and
191
+ capped at a hard token budget by `epic-deliver-prepare.js` (matched via
192
+ `resolveLensTier(lens) === 'local'` + `matchesAnyFilePattern`, never
193
+ `selectAudits` — no provider or git diff runs on that path). When
194
+ `checklistPath` is null or absent, the Story's footprint matched no local
195
+ lens and there is nothing extra to read.
196
+
183
197
  3. Implement the work as one or more commits on `story-<storyId>`.
184
198
  Author commits directly with the project's editor / `git commit`,
185
199
  following