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
@@ -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
@@ -293,12 +365,64 @@ For every finding, provide:
293
365
  fix worked. Keep it tight (≤ 5 sentences); the sub-agent will read the
294
366
  surrounding code itself.
295
367
 
368
+ ### The `## Fixed on-branch` section (Story #4399)
369
+
370
+ Findings that Step 4.5 remediated on `[HEAD_REF]` MUST be rendered under a
371
+ dedicated **`## Fixed on-branch`** heading, **not** in the severity groups
372
+ above. This is the contract seam that keeps remediated findings from
373
+ spawning ghost follow-up issues: the
374
+ [audit-results graduator](../../scripts/lib/feedback-loop/audit-results-graduator.js)
375
+ (the sole canonical reader of the unified comment)
376
+ skips every entry inside this section (both because a fixed entry is
377
+ rendered with a **✅ prefix** — so it carries no leading severity emoji the
378
+ parser would match — and because the parser has an explicit
379
+ Fixed-on-branch section guard).
380
+
381
+ Render each fixed finding as a `✅`-prefixed line naming its original
382
+ severity, the file path in backticks, and the remediating commit SHA, e.g.:
383
+
384
+ ```markdown
385
+ ## Fixed on-branch
386
+
387
+ - ✅ 🟡 Medium: `src/lib/foo.js` — missing edge-case guard added (a1b2c3d)
388
+ - ✅ 🟠 High: `src/api/users.js` — ownership check added (d4e5f6a)
389
+ ```
390
+
391
+ Open (escalated / unfixed) findings stay in their severity group with
392
+ their leading severity emoji so the graduator still files them.
393
+
296
394
  ## Step 4.5 — Focused-fix Routing (host LLM, no automated loop)
297
395
 
298
396
  There is **no runtime auto-fix function** at this phase. The host LLM is
299
- the executor: for each 🔴 / 🟠 finding from Step 4, decide between two
300
- paths and keep the `code-review` structured comment authoritative for
301
- anything not fixed in-place.
397
+ the executor: it decides, per finding, between a focused fix on
398
+ `[HEAD_REF]` and leaving the finding on the `verification-results`
399
+ structured comment for the operator.
400
+
401
+ ### Resolve the remediation threshold (Story #4399)
402
+
403
+ Read `delivery.codeReview.autoFixSeverity` from the resolved `.agentrc.json`
404
+ (default **`medium`**; the resolver in
405
+ [`config/runners.js`](../../scripts/lib/config/runners.js) supplies the
406
+ default when the key is absent). The threshold governs **which severities
407
+ route into on-branch remediation** — it never changes the halting rule (a
408
+ surviving 🔴 still stops) or the escalation classes:
409
+
410
+ - **`medium`** (default) — route 🔴 Critical, 🟠 High, **and 🟡 Medium**
411
+ findings into remediation. 🟢 Suggestions stay on the comment (never
412
+ auto-fixed).
413
+ - **`high`** — route only 🔴 Critical and 🟠 High findings, reproducing
414
+ the pre-4399 behavior exactly. 🟡 Medium and 🟢 Suggestion findings stay
415
+ on the comment.
416
+
417
+ Hard cutover per
418
+ [`rules/git-conventions.md`](../../rules/git-conventions.md) § Contract
419
+ Cutovers — no back-compat flag; `high` is opt-in to the old routing.
420
+
421
+ ### 🔴 / 🟠 findings — per-finding ceremony (unchanged)
422
+
423
+ For each 🔴 / 🟠 finding from Step 4, decide between two paths and keep the
424
+ `verification-results` structured comment authoritative for anything not fixed
425
+ in-place.
302
426
 
303
427
  1. **Apply a focused fix on `[HEAD_REF]`.** Permitted only when the
304
428
  finding is unambiguously *fixable* (clean remediation, no scope
@@ -328,27 +452,52 @@ anything not fixed in-place.
328
452
  attempt (the equivalent of the prior loop's
329
453
  `validation-regression` / `thrash-detected` exits).
330
454
 
455
+ ### 🟡 Medium findings — batched per-lens ceremony (only when `autoFixSeverity: medium`)
456
+
457
+ When the threshold is `medium`, remediate the fixable 🟡 Medium findings in
458
+ a **batch keyed by owning review lens/pillar** rather than the per-finding
459
+ ceremony above:
460
+
461
+ 1. Group the fixable Mediums by owning lens (the pillar or audit family
462
+ that produced them). A Medium is fixable on the same terms as a 🟠; a
463
+ Medium in any escalation class stays on the comment exactly like a 🟠.
464
+ 2. For each lens, call `assert-branch.js --expected [HEAD_REF]`, stage
465
+ explicit paths only, and make **one focused conventional commit per
466
+ lens** (`fix(<scope>): <description> (review findings batch)`).
467
+ 3. Bounded-attempt semantics extend to the batch: each finding gets **at
468
+ most one** attempt, and a lens's batch commit that would exceed
469
+ `delivery.codeReview.maxFixScopeFiles` routes that lens's findings to
470
+ escalation (`scope-exceeded`) instead of committing.
471
+ 4. After **all** lens batches are committed, run a **single** validation
472
+ pass (`npm run lint` plus the relevant `npm test` slice) and a
473
+ **single** targeted rescan over the touched files. Surviving batched
474
+ findings stay on the comment for Step 5.
475
+
476
+ Record every remediated finding (🟠 or 🟡) in the **"Fixed on-branch"**
477
+ section of the `verification-results` comment (Step 4) so it does not graduate
478
+ to a follow-up issue.
479
+
331
480
  Do not invent a programmatic retry budget. The host LLM applies *at most
332
- one* focused-fix attempt per finding before escalating to the operator.
333
- Escalated findings remain on the `code-review` structured comment with
334
- their reason recorded, so Step 5 (and downstream consumers) see exactly
335
- why each one was not auto-remediated.
481
+ one* focused-fix attempt per finding (or per batched finding) before
482
+ escalating to the operator. Escalated findings remain on the `code-review`
483
+ structured comment with their reason recorded, so Step 5 (and downstream
484
+ consumers) see exactly why each one was not auto-remediated.
336
485
 
337
486
  ## Step 4.6 — Cross-phase re-check trigger
338
487
 
339
488
  After the focused-fix routing in Step 4.5 completes, any host-LLM-applied
340
- fix commits have modified files on `[HEAD_REF]` that the Phase 4 audit
341
- lenses already walked. Some of those edits may overlap the `filePatterns`
342
- of one or more lenses (e.g. a fix landing in `**/auth/*.js` overlaps the
343
- `audit-security` lens). When that happens, the prior `audit-results`
344
- structured comment is **stale for the overlapping lenses only** — the
345
- 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
346
495
  re-derived.
347
496
 
348
497
  > **Scope note.** This cross-phase re-check applies only when
349
- > `scope === 'epic'`. Story-scope reviews run before Phase 4 epic audits
350
- > exist, so there is no `audit-results` comment to invalidate; skip this
351
- > 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'`.
352
501
 
353
502
  Invoke the re-check selector with the cumulative set of paths touched by
354
503
  the focused-fix commits:
@@ -369,11 +518,11 @@ no-op.
369
518
  When `selectedAudits` is non-empty:
370
519
 
371
520
  1. Re-invoke each listed lens prompt under
372
- [`../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 —
373
522
  one lens at a time, against the current `[HEAD_REF]` tip.
374
523
  2. **Append** a `## Cross-phase re-check` section to the **existing**
375
- `audit-results` structured comment on the Epic ticket. Do **not** post
376
- 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
377
526
  (the code-review trim, `/deliver` Pillar 2, the retro helper)
378
527
  read it once. The append carries the re-checked lens names, the new
379
528
  findings (if any), and the focused-fix commit SHAs that triggered the
@@ -383,7 +532,7 @@ When `selectedAudits` is non-empty:
383
532
  through Step 4.5's focused-fix routing. Findings that already
384
533
  received a focused-fix attempt in the first pass do not get a fresh
385
534
  attempt when the cross-phase re-check resurfaces an adjacent one —
386
- leave them on the `code-review` comment for the operator.
535
+ leave them on the `verification-results` comment for the operator.
387
536
 
388
537
  If `selectedAudits` is empty, skip silently and proceed to Step 5. The
389
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,51 +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 walks the selected roster **serially in-context by default**; when
493
- the roster carries more than one lens it **may delegate the walk to a single
494
- audit-orchestrator sub-agent** that fans the already-selected lenses out as
495
- parallel level-2 agents and returns only the aggregated `audit-results` (see
496
- [`epic-audit.md` § "Optional: delegate the roster walk to an audit-orchestrator
497
- sub-agent"](epic-audit.md), within the sub-agent depth budget noted under
498
- "Flat Story dispatch by design" above). The roster stays fixed upstream, every
499
- per-lens cost gate is preserved, and the seven sequential-only lenses are **not**
500
- batch-converted the fan-out parallelizes across lenses only and never changes
501
- how any single lens runs internally.
502
-
503
- - **Any 🔴 Critical Blocker** STOP. Relay to the operator.
504
- - **Only 🟠/🟡/🟢** log as non-blocking and continue.
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 🔴/🟠/🟡.
533
+
505
534
  - **Selector reports `degraded: true`** — STOP. Propagate the
506
535
  `reason`/`detail`, post a friction comment, do not fall back to a
507
536
  full-roster audit.
508
- - **`selectedAudits` is empty** (docs-only change set) log the
509
- 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.
510
541
 
511
542
  ---
512
543
 
513
- ## Phase 5 — Code review
544
+ ## Phase 5 — Code review (cumulative diff walked once)
514
545
 
515
546
  Skip when `--skip-code-review`. Otherwise resolve the **risk-derived review
516
547
  depth** for this Epic, then auto-invoke
517
548
  [`helpers/code-review.md`](code-review.md) inline (read-only audit)
518
549
  with the argument envelope `{ scope: 'epic', ticketId: <epicId>, baseRef:
519
- 'main', headRef: 'epic/<epicId>', depth: <reviewDepth> }`. The helper
520
- 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).
521
558
 
522
559
  The `depth` is the live epic-scope producer for Story #3876's review-depth
523
560
  lever (Story #3937). Resolve it from the Epic's judged risk envelope the same
@@ -533,8 +570,20 @@ emit so a high-risk Epic gets a deeper adversarial pass and a low-risk one a
533
570
  lighter one. Depth is **input-only** — it never changes the findings envelope
534
571
  or the posted comment shape.
535
572
 
536
- - **Any 🔴 Critical Blocker** STOP. Relay to the operator.
537
- - **Only 🟠/🟡/🟢** log as non-blocking and continue.
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
583
+ **unchanged** — it keys off the surviving (unfixed) findings.
584
+
585
+ - **Any surviving 🔴 Critical Blocker** — STOP. Relay to the operator.
586
+ - **Only 🟠/🟡/🟢 surviving** — log as non-blocking and continue.
538
587
 
539
588
  ---
540
589
 
@@ -565,6 +614,23 @@ the GitHub upsert succeeds, the retro body is also **mirrored locally** to
565
614
  `epicRetroMirrorPath`). GitHub remains the source of truth — a
566
615
  mirror-write failure only logs a warn and never fails the phase.
567
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
+
568
634
  ---
569
635
 
570
636
  ## Phase 6.5 — Post-wave integration gate (Epic #4131, F1/F4)
@@ -720,13 +786,36 @@ run. Passing `--epic <epicId>` scopes the red-path failure digest to
720
786
  ## Phase 8.5 — Auto-merge gate
721
787
 
722
788
  After Phase 8 exits 0, evaluate the auto-merge predicate by emitting
723
- `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:
724
793
 
725
794
  ```bash
726
795
  node .agents/scripts/lifecycle-emit.js --epic <epicId> \
727
- --event epic.automerge.start --pr-url <prUrl>
796
+ --event epic.automerge.start --pr-url <prUrl> [--headless true]
728
797
  ```
729
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
+
730
819
  `AutomergePredicate` first runs a **live `gh pr checks --required` probe**
731
820
  (Story #4361): green required CI is the arming signal, so if any required
732
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