mandrel 1.88.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 (138) 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 +62 -27
  17. package/.agents/docs/configuration.md +4 -4
  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 +6 -6
  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/runners.js +13 -5
  45. package/.agents/scripts/lib/config/temp-paths.js +24 -0
  46. package/.agents/scripts/lib/config-settings-schema-delivery.js +21 -8
  47. package/.agents/scripts/lib/doc-tiers.js +291 -0
  48. package/.agents/scripts/lib/epic-body-sections.js +5 -2
  49. package/.agents/scripts/lib/epic-merge-lock.js +83 -0
  50. package/.agents/scripts/lib/epic-plan-clarity.js +3 -1
  51. package/.agents/scripts/lib/feedback-loop/audit-results-graduator.js +47 -15
  52. package/.agents/scripts/lib/feedback-loop/graduator-core.js +395 -86
  53. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +299 -72
  54. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +438 -0
  55. package/.agents/scripts/lib/gates/friction.js +15 -5
  56. package/.agents/scripts/lib/observability/perf-aggregator.js +30 -104
  57. package/.agents/scripts/lib/observability/perf-report-readers.js +1 -1
  58. package/.agents/scripts/lib/observability/signal-validator.js +204 -0
  59. package/.agents/scripts/lib/observability/signals-writer.js +157 -54
  60. package/.agents/scripts/lib/observability/tool-trace-hook.js +42 -4
  61. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
  62. package/.agents/scripts/lib/orchestration/code-review.js +74 -4
  63. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +213 -0
  64. package/.agents/scripts/lib/orchestration/doc-reader.js +4 -96
  65. package/.agents/scripts/lib/orchestration/docs-digest.js +34 -0
  66. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +56 -19
  67. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +22 -0
  68. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +188 -0
  69. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +6 -0
  70. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +69 -8
  71. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +11 -2
  72. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +47 -61
  73. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +39 -3
  74. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +144 -0
  75. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +258 -14
  76. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +6 -0
  77. package/.agents/scripts/lib/orchestration/merge-block-class.js +218 -0
  78. package/.agents/scripts/lib/orchestration/plan-review-routing.js +1 -1
  79. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -3
  80. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +63 -34
  81. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +167 -52
  82. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +49 -2
  83. package/.agents/scripts/lib/orchestration/retro-proposals.js +12 -55
  84. package/.agents/scripts/lib/orchestration/retro-runner.js +9 -0
  85. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -0
  86. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +419 -0
  87. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +35 -2
  88. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +353 -69
  89. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +66 -4
  90. package/.agents/scripts/lib/orchestration/spec-section-validator.js +60 -9
  91. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +7 -5
  92. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +24 -2
  93. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +167 -8
  94. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +163 -0
  95. package/.agents/scripts/lib/orchestration/ticketing/reads.js +20 -9
  96. package/.agents/scripts/lib/planning-corpus.js +306 -0
  97. package/.agents/scripts/lib/signals/detectors/common.js +10 -10
  98. package/.agents/scripts/lib/signals/detectors/index.js +4 -4
  99. package/.agents/scripts/lib/signals/detectors/retry.js +19 -18
  100. package/.agents/scripts/lib/signals/detectors/rework.js +1 -1
  101. package/.agents/scripts/lib/signals/schema.js +56 -81
  102. package/.agents/scripts/lib/signals/span-tree.js +6 -5
  103. package/.agents/scripts/lib/story-plan.js +3 -0
  104. package/.agents/scripts/lib/wave-runner/tick.js +10 -2
  105. package/.agents/scripts/lifecycle-emit.js +39 -8
  106. package/.agents/scripts/providers/github/issues.js +12 -1
  107. package/.agents/scripts/resolve-doc-tiers.js +83 -0
  108. package/.agents/scripts/retro-run.js +51 -0
  109. package/.agents/scripts/signals-view.js +1 -1
  110. package/.agents/scripts/single-story-close.js +20 -1
  111. package/.agents/scripts/standalone-feedback-rollup.js +188 -0
  112. package/.agents/scripts/story-close.js +48 -0
  113. package/.agents/scripts/story-plan.js +51 -12
  114. package/.agents/scripts/validate-docs-freshness.js +69 -15
  115. package/.agents/skills/core/documentation-and-adrs/SKILL.md +58 -0
  116. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +5 -3
  117. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +20 -7
  118. package/.agents/skills/core/scope-triage/SKILL.md +61 -0
  119. package/.agents/skills/skills.index.json +3 -3
  120. package/.agents/workflows/audit-documentation.md +82 -2
  121. package/.agents/workflows/helpers/code-review.md +116 -43
  122. package/.agents/workflows/helpers/deliver-epic.md +123 -54
  123. package/.agents/workflows/helpers/deliver-stories.md +26 -0
  124. package/.agents/workflows/helpers/epic-audit.md +116 -366
  125. package/.agents/workflows/helpers/epic-deliver-story.md +14 -0
  126. package/.agents/workflows/helpers/epic-plan-decompose.md +18 -200
  127. package/.agents/workflows/helpers/epic-plan-spec.md +18 -180
  128. package/.agents/workflows/helpers/plan-epic.md +141 -105
  129. package/.agents/workflows/helpers/plan-story.md +32 -0
  130. package/.agents/workflows/helpers/single-story-deliver.md +43 -0
  131. package/.agents/workflows/loops/nightly-audit.md +9 -7
  132. package/docs/CHANGELOG.md +22 -0
  133. package/lib/cli/doctor.js +44 -0
  134. package/package.json +4 -3
  135. package/.agents/scripts/epic-plan-spec-validate.js +0 -111
  136. package/.agents/scripts/lib/feedback-loop/code-review-graduator.js +0 -224
  137. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +0 -58
  138. package/.agents/scripts/lib/signals/detectors/hotspot.js +0 -292
@@ -64,7 +64,7 @@ envelope via
64
64
 
65
65
  It is an **input-only** signal: it changes *how thorough* the review is, never
66
66
  the findings envelope (`{ status, severity, posted, report, halted,
67
- blockerReason }`) nor the posted `code-review` structured-comment body. An
67
+ blockerReason }`) nor the posted `verification-results` structured-comment body. An
68
68
  absent or malformed `depth` is treated as `standard`, so an Epic that skipped
69
69
  `/plan` still gets a passing review with no new failure mode.
70
70
 
@@ -117,13 +117,82 @@ The pipeline will:
117
117
  - Run a focused lint check on the change set.
118
118
  - Post a structured summary report to the `[TICKET_ID]` issue.
119
119
 
120
+ ### Step 1a — Story-scope local-lens pass (`scope: story` only, Epic #4405)
121
+
122
+ When `scope === 'story'`, the shared review spine
123
+ [`runStoryReviewCore`](../../scripts/lib/orchestration/story-close/phases/code-review.js)
124
+ runs a **shift-left local-lens pass** in the same close subprocess, *before*
125
+ returning the review envelope. It:
126
+
127
+ 1. Enumerates the actual Story diff (`baseRef...headRef` via
128
+ `git diff --name-only`).
129
+ 2. Selects the **local-tier** lenses that own a concern decidable from a single
130
+ Story's diff — `resolveLensTier(lens) === 'local'` **plus** the pure
131
+ `matchesAnyFilePattern` matcher against the diff (the audit-suite SDK's
132
+ [`selectLocalLenses`](../../scripts/lib/audit-suite/selector.js)). This is
133
+ deliberately **not** `selectAudits`: `selectAudits` unions in keyword and
134
+ gate matches and has no per-tier gate, so it would widen the roster past the
135
+ footprint-matched local set this tier owns.
136
+ 3. Materializes the matched roster at **`light`** depth
137
+ (`STORY_SCOPE_LENS_DEPTH`) via `runAuditSuite`, surfacing the outcome on the
138
+ review envelope's `localLensReview` field.
139
+
140
+ A diff that matches no local lens adds **no** lens work (the roster is empty and
141
+ `runAuditSuite` is never invoked). The pass is advisory and best-effort: a git
142
+ or materialization failure degrades to a skipped envelope and never blocks the
143
+ close.
144
+
145
+ Both close entry points —
146
+ [`runStoryCodeReview`](../../scripts/lib/orchestration/story-close/phases/code-review.js)
147
+ (Epic-attached) and
148
+ [`runStoryScopeReview`](../../scripts/lib/orchestration/single-story-close/phases/code-review.js)
149
+ (standalone) — reach this pass through the single `runStoryReviewCore` spine, so
150
+ standalone Stories gain local-lens coverage for the first time. Because the pass
151
+ lives inside the close subprocess (invoked after the delivering child exits), it
152
+ honors the maker-blind invariant above: a maker never runs its own local-lens
153
+ review. This step does not apply to `scope: epic`, whose lens roster is the
154
+ cumulative + global + risk-routed set resolved at Epic close (Step 1b).
155
+
156
+ ### Step 1b — Epic-close lens roster (`scope: epic` only, Epic #4405)
157
+
158
+ When `scope === 'epic'`, the cumulative Epic diff is walked **once** at close:
159
+ the Epic-close lens roster is executed as **dimensions of this same review
160
+ pass**, not as a separate Phase 4 walk (Story #4412 folded the standalone
161
+ epic-audit lens walk into this pass). Resolve and walk the roster inline:
162
+
163
+ 1. Resolve the roster via [`helpers/epic-audit.md`](epic-audit.md) Step 1 —
164
+ run `epic-audit-prepare.js --gate gate3` and take its **`epicCloseLenses`**
165
+ field: the slim roster of cumulative + global + risk-routed lenses, with
166
+ every local-tier change-set lens excluded (routed off `resolveLensTier` in
167
+ [`selectEpicCloseLenses`](../../scripts/lib/orchestration/code-review.js)).
168
+ Local-tier concerns are already verified shift-left (write-time checklists +
169
+ the Story-scope local-lens pass), so they are **not** re-run here.
170
+ 2. Materialize each rostered lens via
171
+ [`runAuditSuite`](../../scripts/lib/audit-suite/index.js) at the envelope's
172
+ `depth`, applying the `{{changedFiles}}` substitution, and walk each lens's
173
+ `.agents/workflows/audit-<lens>.md` procedure over the cumulative
174
+ `main..epic/<epicId>` diff. Global lenses (`globalLenses`, e.g.
175
+ `audit-navigability`) run against the WHOLE route tree, exempt from the
176
+ change-set narrowing.
177
+ 3. Fold the lens findings into this pass's severity aggregate **alongside** the
178
+ Step 2 review pillars — one walk of the cumulative diff, one aggregate, one
179
+ `verification-results` comment (Step 4). An empty `epicCloseLenses` roster
180
+ (docs-only, or every selected lens already covered shift-left, and no
181
+ risk-routed lens) adds no lens dimension — the pillars still run.
182
+
183
+ This step does not apply to `scope: story` (its roster is the local-tier set of
184
+ Step 1a) and is skipped when `epic-audit-prepare.js` returns `degraded: true`
185
+ (propagate the reason and STOP, per `epic-audit.md`).
186
+
120
187
  ## Step 2 — Review Pillars
121
188
 
122
189
  For each changed file, execute a strict review against four pillars. The
123
190
  second pillar (**Integration Review**) deliberately defers the security /
124
- performance / quality / coverage sweeps to the change-set-scoped audits
125
- that already ran upstream re-walking them here is duplication, not
126
- defense-in-depth.
191
+ performance / quality / coverage sweeps to the change-set-scoped lenses — at
192
+ `scope: story` those ran shift-left in the Story-scope local-lens pass (Step
193
+ 1a); at `scope: epic` they run inline in this same pass as the Epic-close lens
194
+ roster (Step 1b). Re-walking those sweeps a second time in this pillar is
195
+ duplication, not defense-in-depth.
127
196
 
128
197
  **Apply the `depth` lever** (see **Review depth** above) to how hard you walk
129
198
  these pillars: at `light`, focus on Pillar 1 and reduce Pillars 2–3 to a quick
@@ -151,11 +220,11 @@ The integration view depends on `scope`. The diff under review is always
151
220
  against differs:
152
221
 
153
222
  - **`scope: story`** — the diff is `epic/<epicId>..story-<storyId>` (i.e.
154
- one Story's contribution to the Epic). There is typically no
155
- `audit-results` comment on the Story; Phase 4 epic-level audits have not
156
- yet run for this change set. The integration view here focuses on
157
- cross-Task ripple within the Story and contract drift against the Epic
158
- branch tip. Look for:
223
+ one Story's contribution to the Epic). The Story-scope local-lens pass
224
+ (Step 1a) has already covered the local-tier concerns; the Epic-close lens
225
+ roster has not run for this change set (it runs once at Epic close). The
226
+ integration view here focuses on cross-Task ripple within the Story and
227
+ contract drift against the Epic branch tip. Look for:
159
228
  - Cross-Task contract drift inside the Story (one Task's API change vs.
160
229
  another Task's caller in the same branch).
161
230
  - Shared-module ripple effects from this Story onto siblings already
@@ -163,30 +232,31 @@ against differs:
163
232
  - Spec deviations that the per-Task commits papered over.
164
233
 
165
234
  - **`scope: epic`** — the diff is `main..epic/<epicId>` (the cumulative
166
- Epic change set). Read the **`audit-results` structured comment** posted
167
- on the Epic ticket by the [`epic-audit.md`](epic-audit.md) helper in
168
- Phase 4. That comment is the authoritative source of security, privacy,
169
- performance, code-quality, and test-coverage findings for this change
170
- set they were produced by the change-set-aware lens selector and
171
- per-lens audit workflows under `.agents/workflows/audit-*.md`. Do **not**
172
- re-derive those findings inline here.
235
+ Epic change set). The Epic-close lens roster (`epicCloseLenses`) is walked
236
+ **inline as part of this pass** (Step 1b) the cumulative diff is read once,
237
+ and the security, privacy, performance, code-quality, and test-coverage
238
+ findings the rostered lenses produce feed this pass's aggregate directly.
239
+ There is no separate `audit-results` comment to read (Story #4412 retired it);
240
+ the lens findings and the pillar findings share the single
241
+ `verification-results` comment this pass posts.
173
242
 
174
243
  The integration view at epic scope is what the per-lens audits cannot
175
244
  produce because each lens runs in isolation:
176
245
 
177
- - Cross-reference 🔴 / 🟠 audit findings against the spec deviations
246
+ - Cross-reference 🔴 / 🟠 lens findings against the spec deviations
178
247
  flagged in Pillar 1 — a finding that traces back to a deliberate
179
248
  Tech-Spec decision is different from one that traces back to an
180
249
  oversight.
181
250
  - Look for cross-cutting concerns no single lens owns: contract drift
182
251
  between Stories, shared-module ripple effects, boundary changes that
183
252
  thread security and performance implications together.
184
- - Note any audit finding that the operator's remediation flow should
253
+ - Note any lens finding that the operator's remediation flow should
185
254
  bundle (e.g. one refactor closes findings from multiple lenses).
186
255
 
187
- If the Epic has no `audit-results` comment (docs-only Epic, or Phase 4
188
- was skipped via `--skip-epic-audit`), record that explicitly in the
189
- findings report and proceed there is nothing to integrate.
256
+ If the Epic-close roster is empty (docs-only Epic, every selected lens
257
+ already covered shift-left, or the pass was skipped via
258
+ `--skip-epic-audit` / `--skip-code-review`), record that explicitly in the
259
+ findings report and proceed — there is no lens dimension to integrate.
190
260
 
191
261
  ### Pillar 3: Documentation Integrity
192
262
 
@@ -261,9 +331,11 @@ prior baseline before merging.
261
331
 
262
332
  ## Step 4 — Produce Findings Report
263
333
 
264
- Findings are **persisted as a `code-review` structured comment on the
265
- `[TICKET_ID]` issue** by `runCodeReview`. The target ticket is the Story
266
- when `scope === 'story'` and the Epic when `scope === 'epic'`. The comment
334
+ Findings are **persisted as a `verification-results` structured comment on
335
+ the `[TICKET_ID]` issue** by `runCodeReview` (the unified findings contract of
336
+ Story #4411; at `scope: epic` this single comment also carries the Step 1b
337
+ Epic-close lens findings). The target ticket is the Story when
338
+ `scope === 'story'` and the Epic when `scope === 'epic'`. The comment
267
339
  is idempotent — re-runs replace the prior one — and its body includes
268
340
  severity-tier counts plus the full findings list so downstream workflows
269
341
  (notably the retro helper) can summarise blockers/high findings without
@@ -299,7 +371,8 @@ Findings that Step 4.5 remediated on `[HEAD_REF]` MUST be rendered under a
299
371
  dedicated **`## Fixed on-branch`** heading, **not** in the severity groups
300
372
  above. This is the contract seam that keeps remediated findings from
301
373
  spawning ghost follow-up issues: the
302
- [`code-review` graduator](../../scripts/lib/feedback-loop/code-review-graduator.js)
374
+ [audit-results graduator](../../scripts/lib/feedback-loop/audit-results-graduator.js)
375
+ (the sole canonical reader of the unified comment)
303
376
  skips every entry inside this section (both because a fixed entry is
304
377
  rendered with a **✅ prefix** — so it carries no leading severity emoji the
305
378
  parser would match — and because the parser has an explicit
@@ -322,8 +395,8 @@ their leading severity emoji so the graduator still files them.
322
395
 
323
396
  There is **no runtime auto-fix function** at this phase. The host LLM is
324
397
  the executor: it decides, per finding, between a focused fix on
325
- `[HEAD_REF]` and leaving the finding on the `code-review` structured
326
- comment for the operator.
398
+ `[HEAD_REF]` and leaving the finding on the `verification-results`
399
+ structured comment for the operator.
327
400
 
328
401
  ### Resolve the remediation threshold (Story #4399)
329
402
 
@@ -348,7 +421,7 @@ Cutovers — no back-compat flag; `high` is opt-in to the old routing.
348
421
  ### 🔴 / 🟠 findings — per-finding ceremony (unchanged)
349
422
 
350
423
  For each 🔴 / 🟠 finding from Step 4, decide between two paths and keep the
351
- `code-review` structured comment authoritative for anything not fixed
424
+ `verification-results` structured comment authoritative for anything not fixed
352
425
  in-place.
353
426
 
354
427
  1. **Apply a focused fix on `[HEAD_REF]`.** Permitted only when the
@@ -401,8 +474,8 @@ ceremony above:
401
474
  findings stay on the comment for Step 5.
402
475
 
403
476
  Record every remediated finding (🟠 or 🟡) in the **"Fixed on-branch"**
404
- section of the `code-review` comment (Step 4) so it does not graduate to a
405
- follow-up issue.
477
+ section of the `verification-results` comment (Step 4) so it does not graduate
478
+ to a follow-up issue.
406
479
 
407
480
  Do not invent a programmatic retry budget. The host LLM applies *at most
408
481
  one* focused-fix attempt per finding (or per batched finding) before
@@ -413,18 +486,18 @@ consumers) see exactly why each one was not auto-remediated.
413
486
  ## Step 4.6 — Cross-phase re-check trigger
414
487
 
415
488
  After the focused-fix routing in Step 4.5 completes, any host-LLM-applied
416
- fix commits have modified files on `[HEAD_REF]` that the Phase 4 audit
417
- lenses already walked. Some of those edits may overlap the `filePatterns`
418
- of one or more lenses (e.g. a fix landing in `**/auth/*.js` overlaps the
419
- `audit-security` lens). When that happens, the prior `audit-results`
420
- structured comment is **stale for the overlapping lenses only** — the
421
- non-overlapping findings remain authoritative and MUST NOT be
489
+ fix commits have modified files on `[HEAD_REF]` that the Epic-close lens
490
+ roster already walked (Step 1b). Some of those edits may overlap the
491
+ `filePatterns` of one or more lenses (e.g. a fix landing in `**/auth/*.js`
492
+ overlaps the `audit-security` lens). When that happens, the lens findings in
493
+ the `verification-results` comment are **stale for the overlapping lenses
494
+ only** — the non-overlapping findings remain authoritative and MUST NOT be
422
495
  re-derived.
423
496
 
424
497
  > **Scope note.** This cross-phase re-check applies only when
425
- > `scope === 'epic'`. Story-scope reviews run before Phase 4 epic audits
426
- > exist, so there is no `audit-results` comment to invalidate; skip this
427
- > step entirely for `scope === 'story'`.
498
+ > `scope === 'epic'`. Story-scope reviews carry no Epic-close lens roster,
499
+ > so there is nothing to invalidate; skip this step entirely for
500
+ > `scope === 'story'`.
428
501
 
429
502
  Invoke the re-check selector with the cumulative set of paths touched by
430
503
  the focused-fix commits:
@@ -445,11 +518,11 @@ no-op.
445
518
  When `selectedAudits` is non-empty:
446
519
 
447
520
  1. Re-invoke each listed lens prompt under
448
- [`../audit-*.md`](../) the same way Phase 4's `epic-audit.md` does —
521
+ [`../audit-*.md`](../) the same way the Step 1b Epic-close walk does —
449
522
  one lens at a time, against the current `[HEAD_REF]` tip.
450
523
  2. **Append** a `## Cross-phase re-check` section to the **existing**
451
- `audit-results` structured comment on the Epic ticket. Do **not** post
452
- a new comment; the comment is idempotent and downstream consumers
524
+ `verification-results` structured comment on the Epic ticket. Do **not**
525
+ post a new comment; the comment is idempotent and downstream consumers
453
526
  (the code-review trim, `/deliver` Pillar 2, the retro helper)
454
527
  read it once. The append carries the re-checked lens names, the new
455
528
  findings (if any), and the focused-fix commit SHAs that triggered the
@@ -459,7 +532,7 @@ When `selectedAudits` is non-empty:
459
532
  through Step 4.5's focused-fix routing. Findings that already
460
533
  received a focused-fix attempt in the first pass do not get a fresh
461
534
  attempt when the cross-phase re-check resurfaces an adjacent one —
462
- leave them on the `code-review` comment for the operator.
535
+ leave them on the `verification-results` comment for the operator.
463
536
 
464
537
  If `selectedAudits` is empty, skip silently and proceed to Step 5. The
465
538
  re-check trigger is **read-only signal** — it never mutates the Epic
@@ -1,8 +1,10 @@
1
1
  ---
2
2
  description: >-
3
3
  Drive an Epic from `agent::ready` to a merged pull request against `main`.
4
- The ten-phase flow runs the wave loop, close-validation, epic-audit,
5
- code-review, retro, finalize, watch-and-iterate, conditional auto-merge,
4
+ The ten-phase flow runs the wave loop, close-validation, the Epic-close
5
+ lens-roster resolve, the code-review pass (which walks the cumulative diff
6
+ once with the lens roster folded in), retro, finalize, watch-and-iterate,
7
+ conditional auto-merge,
6
8
  and local branch cleanup. When the run is end-to-end clean (zero manual
7
9
  interventions, zero 🔴/🟠 review findings, compact retro) the PR
8
10
  auto-merges via `gh pr merge --squash --delete-branch`; otherwise the
@@ -36,8 +38,8 @@ back to the operator-merges-button path.
36
38
  → Phase 1 — prepare (epic-deliver-prepare.js)
37
39
  → Phase 2 — ready-set loop (wave-tick.js → dispatch ready set → observe → re-tick)
38
40
  → Phase 3 — close-validation (lint + test + ratchets on epic/<id>)
39
- → Phase 4 — epic-audit (helpers/epic-audit.md — change-set audits via selectAudits)
40
- → Phase 5 — code-review (helpers/code-review.md with scope: epic)
41
+ → Phase 4 — epic-close lens roster (helpers/epic-audit.md — resolve the slim cumulative+global+risk-routed roster)
42
+ → Phase 5 — code-review (helpers/code-review.md scope: epic — walks the cumulative diff once, lens roster folded in)
41
43
  → Phase 6 — retro (.agents/scripts/lib/orchestration/retro-runner.js)
42
44
  → Phase 6.5 — integration gate (whole-product navigability + journey suite; @pending ≠ green for surface-adding Epics — blocks finalize)
43
45
  → Phase 7 — finalize (lifecycle-emit → epic.close.end → open PR to main)
@@ -81,6 +83,19 @@ spawned.
81
83
  Every other runtime modifier is sourced from the Epic's labels or from
82
84
  `delivery.deliverRunner` in `.agentrc.json`.
83
85
 
86
+ - `--yes` — the top-level `/deliver` unattended flag (suppresses the
87
+ operator confirmation gate; see [`deliver.md`](../deliver.md)). This
88
+ helper reads it as the **headless signal** for Phase 8.5's must-land
89
+ terminal step (Story #4427): when `/deliver` was invoked with `--yes`,
90
+ thread `--headless true` into the Phase 8.5
91
+ `epic.automerge.start` `lifecycle-emit.js` call (omit the flag, or
92
+ pass `--headless false`, for an attended run). The runtime reads this
93
+ as an explicit `headless` input threaded through
94
+ `runLifecycleEmit` → `buildDefaultListenerChain` →
95
+ `MergeWatcher({ headless })` — never an ambient/undefined global.
96
+ Attended-mode behavior (no `--headless` flag) is byte-for-byte
97
+ unchanged: `MergeWatcher` defaults `headless` to `false`.
98
+
84
99
  ---
85
100
 
86
101
  ## Contract
@@ -178,9 +193,14 @@ Validates `type::epic`, enumerates `type::story` descendants, parses
178
193
  checkpoint in the per-Story-status shape (a flat `stories` map seeded at
179
194
  `pending`, plus the global `concurrencyCap`). Treat the printed JSON as
180
195
  `state`: `{ epicId, storyCount, concurrencyCap, stories, checkpointInitializedAt, docsDigestPath }`.
181
- `stories` is the flat dispatch hint (`{ storyId, worktree, title }` per open
182
- Story); the ready-set `tick` (Phase 2) decides which to dispatch on each
183
- beat. `docsDigestPath` is the repo-relative path to the per-Epic docs digest
196
+ `stories` is the flat dispatch hint (`{ storyId, worktree, title,
197
+ checklistPath }` per open Story); the ready-set `tick` (Phase 2) decides
198
+ which to dispatch on each beat. `checklistPath` is the repo-relative path to
199
+ that Story's footprint-matched local-lens authoring checklist
200
+ (`temp/epic-<epicId>/checklists/story-<storyId>.md`, Story #4410) — thread it
201
+ into that child's prompt (§ 2b, item 7); it is `null` when the Story's
202
+ predicted footprint matched no local lens. `docsDigestPath` is the
203
+ repo-relative path to the per-Epic docs digest
184
204
  (`temp/epic-<epicId>/docs-digest.md`) that prepare writes from
185
205
  `project.docsContextFiles` — thread it into every child prompt (§ 2b, item 6).
186
206
  It is `null` when the project configured no `docsContextFiles` (no digest is
@@ -369,7 +389,14 @@ digest instead of re-reading the full `project.docsContextFiles` set,
369
389
  and to pull individual docs files on demand (per
370
390
  [`.agents/instructions.md` § 3](../../instructions.md)). When
371
391
  `docsDigestPath` is null (the project configured no `docsContextFiles`),
372
- say so — the child then has no per-Story docs mandate. The pairing of
392
+ say so — the child then has no per-Story docs mandate. (7) pass the
393
+ **checklist path** — the `checklistPath` field from that Story's entry in
394
+ the `stories` dispatch hint (Story #4410), which points at
395
+ `temp/epic-<epicId>/checklists/story-<storyId>.md`. Instruct the child to
396
+ read that file (footprint-matched local-lens authoring checklists) and
397
+ self-check its change against those concerns while writing. When
398
+ `checklistPath` is null (the Story's predicted footprint matched no local
399
+ lens), there is nothing to read. The pairing of
373
400
  `story.heartbeat` and `agent::blocked` is what lets the § 2d Idle
374
401
  Watchdog distinguish a working child from a dead one; a silent child
375
402
  with no recent heartbeat and no blocker label is the failure mode the
@@ -473,61 +500,61 @@ the Epic branch; if any drifts, refresh and commit
473
500
 
474
501
  ---
475
502
 
476
- ## Phase 4 — Epic audit (change-set lenses)
503
+ ## Phase 4 — Epic-close lens roster
477
504
 
478
505
  Skip when `--skip-epic-audit`. Otherwise auto-invoke
479
- [`helpers/epic-audit.md`](epic-audit.md) inline. The helper runs
506
+ [`helpers/epic-audit.md`](epic-audit.md) inline to **resolve** (not walk) the
507
+ slim Epic-close lens roster. The helper runs
480
508
  [`epic-audit-prepare.js`](../../scripts/epic-audit-prepare.js) to ask the
481
509
  [`selectAudits`](../../scripts/lib/audit-suite/index.js) SDK which lenses fire
482
510
  at the `gate3` close gate, **unions in the model-judged risk-routed lenses**
483
511
  (Story #3889 — `epic-audit-prepare.js` reads the Epic's `planningRisk`
484
512
  envelope off the `epic-plan-state` checkpoint and maps each high-risk axis to
485
- its lens via `resolveAuditLenses`), then dispatches each selected lens through
486
- [`runAuditSuite`](../../scripts/lib/audit-suite/index.js). A high-risk Epic
487
- therefore auto-runs its mapped lenses (e.g. a `security`-axis Epic runs
488
- `audit-security`) even when the change set alone did not select them; a
489
- low-risk Epic adds nothing. Findings are persisted as an `audit-results`
490
- structured comment on the Epic.
491
-
492
- The helper's Step 3 remediation is **threshold-aware** (Story #4399): it
493
- reads `delivery.epicAudit.autoFixSeverity` (default **`medium`**) and, at
494
- `medium`, routes 🔴/🟠/**🟡** findings into on-branch remediation (Mediums
495
- batched per lens one commit per lens, a single validation + overlapping-
496
- lens rescan at the end) while 🟢 Suggestions still graduate; `high`
497
- reproduces the pre-4399 Critical/High-only routing. Remediated findings are
498
- rendered under the comment's `## Fixed on-branch` section so they never
499
- graduate to follow-up issues. The severity gate below is **unchanged**
500
- it keys off the surviving (unfixed) findings.
501
-
502
- The helper walks the selected roster **serially in-context by default**; when
503
- the roster carries more than one lens it **may delegate the walk to a single
504
- audit-orchestrator sub-agent** that fans the already-selected lenses out as
505
- parallel level-2 agents and returns only the aggregated `audit-results` (see
506
- [`epic-audit.md` § "Optional: delegate the roster walk to an audit-orchestrator
507
- sub-agent"](epic-audit.md), within the sub-agent depth budget noted under
508
- "Flat Story dispatch by design" above). The roster stays fixed upstream, every
509
- per-lens cost gate is preserved, and the seven sequential-only lenses are **not**
510
- batch-converted — the fan-out parallelizes across lenses only and never changes
511
- how any single lens runs internally.
513
+ its lens via `resolveAuditLenses`), then restricts that union to the tiers the
514
+ Epic-close tier owns via
515
+ [`selectEpicCloseLenses`](../../scripts/lib/orchestration/code-review.js): the
516
+ envelope's **`epicCloseLenses`** field is the slim roster of **cumulative +
517
+ global + risk-routed** lenses, with every **local-tier** change-set lens
518
+ excluded (its concern is already verified shift-left at the write-time and
519
+ Story-scope tiers). A high-risk Epic still auto-runs its risk-routed lenses
520
+ even when the change set alone did not select them; a docs-only or
521
+ already-shift-left-covered change set yields an empty roster.
522
+
523
+ **The lens roster is not walked here.** Story #4412 folded the standalone
524
+ Phase 4 lens walk into the Phase 5 code-review pass so the cumulative Epic diff
525
+ is walked **once**. Phase 4 resolves `epicCloseLenses` (plus `depth`,
526
+ `globalLenses`, `substitutionsPayload`) and hands it to Phase 5; there is no
527
+ separate `audit-results` comment. Remediation of the lens findings happens in
528
+ Phase 5 and is **tier-aware** (Story #4412): the Epic-close tier reads
529
+ `delivery.epicAudit.autoFixSeverity` (default **`high`**) and routes only
530
+ 🔴 Critical + 🟠 High lens findings into on-branch remediation while 🟡 Medium
531
+ and 🟢 Suggestion findings graduate (🟡 Medium concerns are already remediated
532
+ shift-left); setting `medium` opts back into routing 🔴/🟠/🟡.
512
533
 
513
- - **Any surviving 🔴 Critical Blocker** — STOP. Relay to the operator.
514
- - **Only 🟠/🟡/🟢 surviving** — log as non-blocking and continue.
515
534
  - **Selector reports `degraded: true`** — STOP. Propagate the
516
535
  `reason`/`detail`, post a friction comment, do not fall back to a
517
536
  full-roster audit.
518
- - **`selectedAudits` is empty** (docs-only change set) log the
519
- short-circuit and continue to Phase 5.
537
+ - **`epicCloseLenses` is empty** (docs-only change set, or every selected lens
538
+ already covered shift-left, and no risk-routed lens) — there is no lens
539
+ dimension for Phase 5 to walk; continue to Phase 5, which still runs its
540
+ review pillars.
520
541
 
521
542
  ---
522
543
 
523
- ## Phase 5 — Code review
544
+ ## Phase 5 — Code review (cumulative diff walked once)
524
545
 
525
546
  Skip when `--skip-code-review`. Otherwise resolve the **risk-derived review
526
547
  depth** for this Epic, then auto-invoke
527
548
  [`helpers/code-review.md`](code-review.md) inline (read-only audit)
528
549
  with the argument envelope `{ scope: 'epic', ticketId: <epicId>, baseRef:
529
- 'main', headRef: 'epic/<epicId>', depth: <reviewDepth> }`. The helper
530
- persists findings as a `code-review` structured comment on the Epic.
550
+ 'main', headRef: 'epic/<epicId>', depth: <reviewDepth> }`, threading the
551
+ Phase 4 `epicCloseLenses` roster through. The pass walks the cumulative
552
+ `main..epic/<epicId>` diff **once**: it executes the Epic-close lens roster as
553
+ review dimensions (helper Step 1b) **and** the review pillars, folding both
554
+ into a single aggregate. Findings persist as the unified `verification-results`
555
+ structured comment on the Epic (the single findings surface — Story #4411
556
+ unified the former `code-review` and `audit-results` contracts, and Story #4412
557
+ folded the lens walk into this pass).
531
558
 
532
559
  The `depth` is the live epic-scope producer for Story #3876's review-depth
533
560
  lever (Story #3937). Resolve it from the Epic's judged risk envelope the same
@@ -543,14 +570,16 @@ emit so a high-risk Epic gets a deeper adversarial pass and a low-risk one a
543
570
  lighter one. Depth is **input-only** — it never changes the findings envelope
544
571
  or the posted comment shape.
545
572
 
546
- The helper's Step 4.5 focused-fix routing is **threshold-aware**
547
- (Story #4399): it reads `delivery.codeReview.autoFixSeverity` (default
548
- **`medium`**) and, at `medium`, routes 🔴/🟠/**🟡** findings into on-branch
549
- remediation (Mediums batched per lens one commit per lens, a single
550
- validation + rescan at the end) while 🟢 Suggestions stay on the comment;
551
- `high` reproduces the pre-4399 Critical/High-only routing. Remediated
552
- findings are rendered under the comment's `## Fixed on-branch` section so
553
- they never graduate to follow-up issues. The severity gate below is
573
+ Remediation in this pass is **tier-aware and split by finding class**
574
+ (Story #4412). The **review-pillar** findings (Pillars 1–4) route off
575
+ `delivery.codeReview.autoFixSeverity` (default **`medium`** 🔴/🟠/🟡
576
+ on-branch, Mediums batched per lens, 🟢 stays on the comment). The **Epic-close
577
+ lens** findings (Step 1b) route off `delivery.epicAudit.autoFixSeverity`
578
+ (default **`high`** only 🔴/🟠 on-branch; 🟡 Medium + 🟢 graduate, because
579
+ 🟡 Medium concerns were already remediated shift-left). Setting either key to
580
+ its other value re-widens or narrows that class. Remediated findings are
581
+ rendered under the single `verification-results` comment's `## Fixed on-branch`
582
+ section so they never graduate to follow-up issues. The severity gate below is
554
583
  **unchanged** — it keys off the surviving (unfixed) findings.
555
584
 
556
585
  - **Any surviving 🔴 Critical Blocker** — STOP. Relay to the operator.
@@ -585,6 +614,23 @@ the GitHub upsert succeeds, the retro body is also **mirrored locally** to
585
614
  `epicRetroMirrorPath`). GitHub remains the source of truth — a
586
615
  mirror-write failure only logs a warn and never fails the phase.
587
616
 
617
+ **Auto-file routed proposals (Story #4418).** Between gathering the retro
618
+ signals and composing the body, the runner files the retro's **actionable
619
+ routed proposals** (the `framework` / `consumer` friction categories that
620
+ recurred ≥ 2× or were force-flagged by an unresolved `agent::blocked`) as
621
+ GitHub follow-up issues via the graduator pre-parsed-findings seam
622
+ ([`retro-proposals-graduator.js`](../../scripts/lib/feedback-loop/retro-proposals-graduator.js)).
623
+ Each filed issue carries `meta::<framework-gap|consumer-improvement>` +
624
+ `friction::<category>` labels — the join key `/plan` Phase 0's
625
+ prior-feedback fetcher reads back — and the rendered retro sections then
626
+ list the **real filed issue numbers** instead of paste-ready `gh issue
627
+ create` command stanzas. Filing is idempotent (a content-hash marker probe
628
+ skips already-filed categories) and respects the graduator per-run filing
629
+ cap. The beat runs behind `delivery.feedbackLoop.retroProposals` (default
630
+ **ON**); set it to `false` to suppress auto-filing and fall back to the
631
+ command stanzas. Filing never fails the phase — a filing error degrades to
632
+ the command stanzas.
633
+
588
634
  ---
589
635
 
590
636
  ## Phase 6.5 — Post-wave integration gate (Epic #4131, F1/F4)
@@ -740,13 +786,36 @@ run. Passing `--epic <epicId>` scopes the red-path failure digest to
740
786
  ## Phase 8.5 — Auto-merge gate
741
787
 
742
788
  After Phase 8 exits 0, evaluate the auto-merge predicate by emitting
743
- `epic.automerge.start`:
789
+ `epic.automerge.start`. When this `/deliver` run was invoked with `--yes`,
790
+ add `--headless true` so the downstream `MergeWatcher` engages the
791
+ must-land terminal step (Story #4427, § Arguments above); omit the flag
792
+ (or pass `--headless false`) for an attended run:
744
793
 
745
794
  ```bash
746
795
  node .agents/scripts/lifecycle-emit.js --epic <epicId> \
747
- --event epic.automerge.start --pr-url <prUrl>
796
+ --event epic.automerge.start --pr-url <prUrl> [--headless true]
748
797
  ```
749
798
 
799
+ **Must-land terminal step (headless only).** `MergeWatcher` polls
800
+ `epic.merge.armed` to confirmation as usual. If its poll budget is
801
+ exhausted, a headless (`--headless true`) run does not exit silently —
802
+ it classifies the block (`classifyMergeBlock`, the shared classifier
803
+ from Story #4426) and applies one bounded retry before giving up:
804
+
805
+ - `checks-pending-timeout` (required checks still progressing) — extend
806
+ the watch budget once and keep polling.
807
+ - `api-race-other` (no definitive block signal) — re-arm once by
808
+ re-emitting `epic.merge.ready` on the bus (never a direct `gh pr merge`
809
+ call — `AutomergeArmer` remains the sole authorized call site).
810
+ - `branch-protection-human-required`, or both bounded retries already
811
+ spent — terminal: emit `merge.unlanded` (`scope: "epic"`, carrying the
812
+ block class) and fall through to the existing single `epic.blocked`
813
+ emit — one blocked path, never a duplicate `agent::blocked` transition.
814
+
815
+ Attended runs (no `--headless` flag) keep today's exact behavior: budget
816
+ exhaustion emits `epic.blocked` immediately, with no classification, no
817
+ retry, and no `merge.unlanded`.
818
+
750
819
  `AutomergePredicate` first runs a **live `gh pr checks --required` probe**
751
820
  (Story #4361): green required CI is the arming signal, so if any required
752
821
  check is red, pending, or the probe is unreadable it emits
@@ -336,6 +336,32 @@ auto-merge. Report its one-line result in the summary. Merged Story branches
336
336
  themselves are reaped by the boot sweep at the next `/plan` / `/deliver` boot —
337
337
  see [`.agents/rules/git-conventions.md` § Local checkout hygiene](../../rules/git-conventions.md).
338
338
 
339
+ ### Feedback rollup
340
+
341
+ Then run the **end-of-run feedback rollup** over the delivered Story set so
342
+ the write-only standalone signals tree finally has a consumer. Each per-Story
343
+ sub-agent appends `friction` records to its standalone stream
344
+ (`temp/standalone/stories/story-<sid>/signals.ndjson`); the rollup scans those
345
+ streams and aggregates friction by category:
346
+
347
+ ```bash
348
+ node .agents/scripts/standalone-feedback-rollup.js --stories <all delivered story ids, comma-separated>
349
+ ```
350
+
351
+ The CLI prints one JSON object
352
+ (`{ kind, stories, totalFriction, byCategory, perStory }`) and **always exits
353
+ 0** — missing or empty streams degrade to an empty summary, so this beat never
354
+ fails the run. Surface `totalFriction` and the `byCategory` totals in the run
355
+ summary so the operator sees where friction clustered across the delivery:
356
+
357
+ ```text
358
+ Feedback rollup — 4 friction signals across 3 Stories
359
+ Execution Error: 2, Tool Limitation: 1, Missing Skill: 1
360
+ ```
361
+
362
+ When `totalFriction` is 0 (or every stream was missing), report a single
363
+ "no friction signals recorded" line and move on — it is not an error.
364
+
339
365
  When some Stories are blocked or failed, list them explicitly with the
340
366
  `blockerCommentId` or failure detail so the operator knows where to look.
341
367