mandrel 1.81.0 โ†’ 1.83.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 (121) hide show
  1. package/.agents/README.md +46 -5
  2. package/.agents/docs/SDLC.md +97 -82
  3. package/.agents/docs/agentrc-reference.json +10 -2
  4. package/.agents/docs/configuration.md +4 -1
  5. package/.agents/docs/execution-reference.md +52 -0
  6. package/.agents/docs/workflows.md +1 -1
  7. package/.agents/instructions.md +85 -45
  8. package/.agents/personas/architect.md +8 -5
  9. package/.agents/personas/engineer-mobile.md +3 -2
  10. package/.agents/personas/engineer-web.md +3 -2
  11. package/.agents/personas/engineer.md +6 -5
  12. package/.agents/personas/product.md +19 -13
  13. package/.agents/personas/project-manager.md +9 -8
  14. package/.agents/personas/qa-engineer.md +10 -6
  15. package/.agents/personas/refactorer.md +3 -2
  16. package/.agents/personas/technical-writer.md +2 -1
  17. package/.agents/personas/ux-designer.md +2 -2
  18. package/.agents/schemas/agentrc.schema.json +41 -3
  19. package/.agents/schemas/qa-ledger.schema.json +2 -2
  20. package/.agents/scripts/acceptance-spec-reconciler.js +143 -59
  21. package/.agents/scripts/epic-deliver-prepare.js +40 -31
  22. package/.agents/scripts/epic-plan-decompose.js +2 -5
  23. package/.agents/scripts/epic-plan-spec.js +16 -19
  24. package/.agents/scripts/hierarchy-gate.js +11 -11
  25. package/.agents/scripts/lib/ITicketingProvider.js +4 -3
  26. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  27. package/.agents/scripts/lib/bdd-scenario-scanner.js +1 -1
  28. package/.agents/scripts/lib/cli-args.js +1 -5
  29. package/.agents/scripts/lib/codebase-snapshot.js +1 -1
  30. package/.agents/scripts/lib/config/explain.js +4 -1
  31. package/.agents/scripts/lib/config/temp-paths.js +1 -4
  32. package/.agents/scripts/lib/config-settings-schema.js +30 -1
  33. package/.agents/scripts/lib/epic-body-sections.js +310 -0
  34. package/.agents/scripts/lib/epic-plan-clarity.js +38 -1
  35. package/.agents/scripts/lib/epic-plan-ideation.js +15 -3
  36. package/.agents/scripts/lib/findings/promote-finding.js +3 -3
  37. package/.agents/scripts/lib/findings/severity.js +5 -6
  38. package/.agents/scripts/lib/label-constants.js +7 -17
  39. package/.agents/scripts/lib/label-taxonomy.js +4 -21
  40. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +65 -2
  41. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +105 -9
  42. package/.agents/scripts/lib/orchestration/doc-reader.js +29 -0
  43. package/.agents/scripts/lib/orchestration/docs-digest.js +134 -0
  44. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +23 -22
  45. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +7 -10
  46. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +4 -38
  47. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +8 -9
  48. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/authoring-context.js +11 -5
  49. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/cli-args.js +26 -5
  50. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +102 -304
  51. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +32 -29
  52. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +19 -20
  53. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-authoring-grounding.js +1 -1
  54. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +6 -9
  55. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +3 -4
  56. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +1 -1
  57. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +20 -27
  58. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +11 -5
  59. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +22 -59
  60. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
  61. package/.agents/scripts/lib/orchestration/planning-context-budget.js +1 -1
  62. package/.agents/scripts/lib/orchestration/preflight-cache.js +1 -1
  63. package/.agents/scripts/lib/orchestration/spec-freshness.js +3 -3
  64. package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
  65. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +15 -1
  66. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +122 -1
  67. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +5 -8
  68. package/.agents/scripts/lib/orchestration/ticketing/reads.js +2 -2
  69. package/.agents/scripts/lib/plan-phase-cleanup.js +1 -2
  70. package/.agents/scripts/lib/qa/console-allowlist.js +5 -4
  71. package/.agents/scripts/lib/qa/qa-context-hydrator.js +6 -85
  72. package/.agents/scripts/lib/qa/resolve-qa-contract.js +144 -8
  73. package/.agents/scripts/lib/templates/decomposer-prompts.js +14 -8
  74. package/.agents/scripts/lifecycle-emit.js +1 -1
  75. package/.agents/scripts/lint-label-vocabulary.js +2 -3
  76. package/.agents/scripts/providers/github/mappers.js +0 -3
  77. package/.agents/scripts/providers/github/tickets.js +7 -18
  78. package/.agents/scripts/single-story-init.js +0 -1
  79. package/.agents/scripts/story-init.js +1 -29
  80. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +43 -22
  81. package/.agents/skills/core/epic-plan-consolidate/examples.md +51 -0
  82. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +27 -40
  83. package/.agents/skills/core/epic-plan-decompose-author/examples.md +47 -0
  84. package/.agents/skills/core/epic-plan-premortem/SKILL.md +15 -13
  85. package/.agents/skills/core/epic-plan-premortem/examples.md +53 -0
  86. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +143 -151
  87. package/.agents/skills/core/epic-plan-spec-author/examples.md +91 -0
  88. package/.agents/skills/core/hydrate-context/SKILL.md +10 -5
  89. package/.agents/skills/core/knowledge-transfer/SKILL.md +3 -2
  90. package/.agents/skills/core/scope-triage/SKILL.md +2 -1
  91. package/.agents/skills/skills.index.json +8 -8
  92. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +52 -38
  93. package/.agents/templates/epic-from-idea.md +4 -0
  94. package/.agents/workflows/audit-to-stories.md +2 -2
  95. package/.agents/workflows/helpers/code-review.md +11 -9
  96. package/.agents/workflows/helpers/deliver-epic-reference.md +514 -0
  97. package/.agents/workflows/helpers/deliver-epic.md +173 -490
  98. package/.agents/workflows/helpers/epic-audit.md +11 -8
  99. package/.agents/workflows/helpers/epic-deliver-story.md +45 -27
  100. package/.agents/workflows/helpers/epic-plan-decompose.md +17 -12
  101. package/.agents/workflows/helpers/epic-plan-spec.md +68 -68
  102. package/.agents/workflows/helpers/parallel-tooling.md +2 -1
  103. package/.agents/workflows/helpers/plan-epic-reference.md +136 -0
  104. package/.agents/workflows/helpers/plan-epic.md +141 -256
  105. package/.agents/workflows/helpers/plan-story.md +31 -61
  106. package/.agents/workflows/helpers/qa-run-scenario.md +194 -0
  107. package/.agents/workflows/helpers/scope-triage-gate.md +97 -0
  108. package/.agents/workflows/helpers/single-story-deliver-reference.md +423 -0
  109. package/.agents/workflows/helpers/single-story-deliver.md +129 -393
  110. package/.agents/workflows/helpers/worktree-lifecycle.md +1 -1
  111. package/.agents/workflows/plan.md +8 -8
  112. package/.agents/workflows/qa-assist.md +2 -1
  113. package/.agents/workflows/qa-explore.md +63 -32
  114. package/.agents/workflows/qa-run.md +293 -130
  115. package/docs/CHANGELOG.md +35 -0
  116. package/package.json +1 -1
  117. package/.agents/schemas/qa-finding.schema.json +0 -133
  118. package/.agents/scripts/lib/issue-link-parser.js +0 -74
  119. package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +0 -116
  120. package/.agents/scripts/lib/orchestration/planning-state-manager.js +0 -318
  121. package/.agents/scripts/lib/story-init/hierarchy-tracer.js +0 -57
@@ -12,6 +12,14 @@ description: >-
12
12
 
13
13
  # helpers/deliver-epic โ€” Epic delivery path (invoked by /deliver)
14
14
 
15
+ > **Runtime core.** This file is the always-ingested Epic-delivery path:
16
+ > phase flow, commands, gate contracts, and return shapes. The recovery
17
+ > procedures, historical rationale, and troubleshooting detail live in the
18
+ > sibling [`deliver-epic-reference.md`](deliver-epic-reference.md); each
19
+ > moved procedure keeps a one-line pointer at its trigger point below. The
20
+ > reference is not projected to `.claude/commands/` โ€” it is consulted on
21
+ > demand.
22
+
15
23
  ## Overview
16
24
 
17
25
  This helper is the **Epic delivery path** behind `/deliver` โ€” the router
@@ -65,11 +73,10 @@ spawned.
65
73
  - `--full-retro` โ€” force the six-section retro regardless of manifest
66
74
  cleanliness. `--skip-retro` wins over `--full-retro`.
67
75
  - `--skip-integration-gate` โ€” skip Phase 6.5 (log the override). The
68
- explicit operator override for the post-wave integration gate, consistent
69
- with `--skip-epic-audit`. Use only when the deliberately-global checks are
70
- known to be irrelevant for this Epic (e.g., a docs-only Epic that adds no
71
- surface). Skipping the gate is recorded as a manual intervention and
72
- disqualifies auto-merge, exactly like the other `--skip-*` overrides.
76
+ explicit operator override for the post-wave integration gate,
77
+ consistent with `--skip-epic-audit`. Skipping the gate is recorded as a
78
+ manual intervention and disqualifies auto-merge, exactly like the other
79
+ `--skip-*` overrides.
73
80
 
74
81
  Every other runtime modifier is sourced from the Epic's labels or from
75
82
  `delivery.deliverRunner` in `.agentrc.json`.
@@ -97,10 +104,7 @@ Every other runtime modifier is sourced from the Epic's labels or from
97
104
  (`--event epic.close.end` / `--event epic.automerge.start` /
98
105
  `--event epic.merge.armed`); the matching listener chain runs the
99
106
  bus-driven side effects (acceptance reconcile, automerge-armer,
100
- branch cleanup). PR open + planning-artifact close are
101
- operator-/host-LLM-driven in the current wiring โ€” see Phase 7.1 for
102
- the canonical manual sequence and `finalizer.js` for the listener's
103
- no-op disclaimer. The append-only NDJSON ledger at
107
+ branch cleanup). The append-only NDJSON ledger at
104
108
  `temp/epic-<id>/lifecycle.ndjson` is the resume target. See
105
109
  [`docs/LIFECYCLE.md`](../../../docs/LIFECYCLE.md) for the bus
106
110
  contract, event taxonomy, ledger format, and listener model.
@@ -152,9 +156,7 @@ run upserts the same comment in place.
152
156
 
153
157
  Threshold defaults live in `delivery.preflight.*` in `.agentrc.json`
154
158
  (all keys default to "no cap" โ€” the gate is opt-in until an operator
155
- configures `maxStories` etc.). The CI-firehose mitigation
156
- (`delivery.ci.skipForStoryPushes: true`) and these threshold keys are
157
- the two operator-tunable knobs F13 ships.
159
+ configures `maxStories` etc.).
158
160
 
159
161
  ### Phase 1 main โ€” Seed the wave plan
160
162
 
@@ -167,71 +169,31 @@ Validates `type::epic`, enumerates `type::story` descendants, parses
167
169
  (to enumerate the open Story set), and upserts the `epic-run-state`
168
170
  checkpoint in the per-Story-status shape (a flat `stories` map seeded at
169
171
  `pending`, plus the global `concurrencyCap`). Treat the printed JSON as
170
- `state`: `{ epicId, storyCount, concurrencyCap, stories, prdId, techSpecId, checkpointInitializedAt }`.
172
+ `state`: `{ epicId, storyCount, concurrencyCap, stories, checkpointInitializedAt, docsDigestPath }`.
171
173
  `stories` is the flat dispatch hint (`{ storyId, worktree, title }` per open
172
174
  Story); the ready-set `tick` (Phase 2) decides which to dispatch on each
173
- beat. Flip the Epic to `agent::executing` (idempotent) after the CLI returns.
174
-
175
- **Epic linkages resolved once (Story #4253).** The envelope also carries
176
- `prdId` and `techSpecId` โ€” the Epic's linked PRD / Tech-Spec issue ids,
177
- resolved a **single** time here from the Epic snapshot prepare already
178
- holds (no extra fetch). Capture both and thread them into **every**
179
- per-Story `story-init.js` invocation (ยง 2b โ†’ `epic-deliver-story` Step 0)
180
- as `--prd <prdId> --tech-spec <techSpecId>`. This collapses the N
181
- per-Story `getEpic` round-trips (one per child, each in its own process
182
- with its own provider cache) to this one parent-side resolution โ€” the
183
- immutable Epic issue is invariant for the lifetime of a delivery run.
184
- When a linkage is `null` (the Epic links no PRD or Tech Spec), omit the
185
- corresponding flag; the child's `story-init.js` then falls back to its
186
- own `getEpic` resolution for the missing id, preserving graceful
187
- degradation.
188
-
189
- > **Preflight guards (Story #3482 / F-workflow-guards).** Before the
190
- > snapshot phase runs โ€” and before any worktree is created โ€” prepare runs
191
- > two **fail-closed** guards
192
- > ([`lib/orchestration/epic-deliver-lease-guard.js`](../../scripts/lib/orchestration/epic-deliver-lease-guard.js)):
193
- >
194
- > 1. **Checkout safety.** Prepare refuses to start when the working tree is
195
- > dirty or HEAD is on a branch other than the expected one (`epic/<id>`
196
- > on a resume, or the project base branch on a fresh run). It will
197
- > **not** check `epic/<id>` out over your work โ€” the historic
198
- > HEAD-yank footgun. Remediation: commit/stash/clean the tree, or
199
- > switch to the expected branch, then re-run.
200
- > 2. **Epic lease.** Prepare acquires the assignee-as-lease on the Epic
201
- > ticket (`ticket-lease.acquireLease`). On a **live foreign claim**
202
- > (a teammate's run with a fresh `story.heartbeat` within
203
- > `delivery.lease.ttlMs`) it exits non-zero and names the current owner;
204
- > a **stale** claim is silently reclaimed. The operator identity is
205
- > resolved from `--as <handle>` โ†’ `github.operatorHandle` โ†’
206
- > `git config user.email`. Pass `--steal` to forcibly transfer a live
207
- > foreign claim (the takeover is logged for auditability). The committed
208
- > `github.operatorHandle` is the non-personal `@[USERNAME]` placeholder,
209
- > which resolves to null โ€” so when none of the three sources yields a real
210
- > identity the guard **fails closed** (throws after the checkout guard
211
- > runs) rather than driving an ownerless, unguarded delivery. Set your own
212
- > handle in `.agentrc.local.json`, pass `--as <handle>`, or configure
213
- > `git user.email`. The lease is the cross-clone coordination layer, while
214
- > `epic-merge-lock.js` continues to serialize same-machine sessions.
215
- >
216
- > Both guards throw on failure, which `runAsCli` maps to `process.exit(1)`
217
- > per [`orchestration-error-handling.md`](../../rules/orchestration-error-handling.md).
218
-
219
- Once the preflight guards pass, the snapshot phase applies one more gate:
220
-
221
- > **Acceptance-spec start gate.** Before the wave loop fans out, the
222
- > snapshot phase
223
- > ([`lib/orchestration/epic-runner/phases/snapshot.js`](../../scripts/lib/orchestration/epic-runner/phases/snapshot.js))
224
- > asserts that the Epic either carries the `acceptance::n-a` waiver
225
- > label **or** has a linked `context::acceptance-spec` ticket. The
226
- > ticket's GitHub state (open / closed) is **not** checked โ€”
227
- > presence is sufficient, matching the PRD and Tech Spec contract.
228
- > The reviewer's OK during `/plan` Phase 7 is the approval
229
- > signal, not a manual ticket-close action. Neither condition met โ†’
230
- > the snapshot throws a clear error
231
- > (`[epic-deliver] Epic #<id> cannot launch: โ€ฆ`) and `runAsCli`
232
- > maps it to `process.exit(1)`. Operator remediation: either run
233
- > `/plan` Phase 7 to author the spec, or apply the
234
- > `acceptance::n-a` label to opt out.
175
+ beat. `docsDigestPath` is the repo-relative path to the per-Epic docs digest
176
+ (`temp/epic-<epicId>/docs-digest.md`) that prepare writes from
177
+ `project.docsContextFiles` โ€” thread it into every child prompt (ยง 2b, item 6).
178
+ It is `null` when the project configured no `docsContextFiles` (no digest is
179
+ written). Flip the Epic to `agent::executing` (idempotent) after the CLI
180
+ returns.
181
+
182
+ **No spec-ticket linkage to resolve (Story #4324).** The Tech Spec lives
183
+ as managed sections of the Epic body itself โ€” there is no separate
184
+ Tech-Spec issue id in the envelope and no `--tech-spec` flag to thread
185
+ into the per-Story `story-init.js` invocations. Story agents receive the
186
+ Tech Spec via context hydration, which embeds the Epic body (with the
187
+ `## Acceptance Table` section stripped) directly into each Story prompt.
188
+
189
+ > **Preflight guards + acceptance-table start gate.** Before the snapshot
190
+ > phase runs โ€” and before any worktree is created โ€” prepare runs two
191
+ > **fail-closed** guards (checkout safety + Epic lease), then the snapshot
192
+ > phase asserts the Epic carries an `acceptance::n-a` waiver or a
193
+ > `## Acceptance Table` section. Both throw on failure. See
194
+ > [`deliver-epic-reference.md` ยง Phase 1 โ€” Preflight guards](deliver-epic-reference.md#phase-1--preflight-guards-story-3482--f-workflow-guards)
195
+ > for the remediation detail (dirty-tree recovery, `--steal`, waiver
196
+ > options).
235
197
 
236
198
  ---
237
199
 
@@ -291,11 +253,10 @@ spans). The [`signals` helper](signals.md)
291
253
  (`node .agents/scripts/signals-view.js`) renders the forensics signals in the
292
254
  span-tree view.
293
255
 
294
- > **Fail-closed on an old-shape checkpoint.** If the Epic still carries a
295
- > pre-ready-set (`plan` / `currentWave` / `totalWaves`) `epic-run-state`
296
- > checkpoint, the tick **refuses to run** and throws an explicit operator
297
- > message โ€” re-run `epic-deliver-prepare.js --epic <id>` to re-seed the
298
- > checkpoint in the per-Story-status shape, then re-run `/deliver`.
256
+ > **Old-shape checkpoint โ†’ fail-closed.** A pre-ready-set
257
+ > (`plan` / `currentWave` / `totalWaves`) checkpoint makes the tick refuse
258
+ > to run and throw; re-seed via `epic-deliver-prepare.js`. See
259
+ > [`deliver-epic-reference.md` ยง Fail-closed on an old-shape checkpoint](deliver-epic-reference.md#fail-closed-on-an-old-shape-checkpoint).
299
260
 
300
261
  ### 2b. Dispatch โ€” fan out per-Story Agent calls
301
262
 
@@ -313,15 +274,12 @@ Dispatch the ready set as background calls (`run_in_background: true`) and,
313
274
  as each child returns, record it (ยง 2c) and **re-tick** (ยง 2a) to pull the
314
275
  next ready set โ€” never wait for the whole set before refilling.
315
276
 
316
- > **Throughput tradeoff.** The default `concurrencyCap` of 3 is the GLOBAL
317
- > in-flight cap, intentionally conservative โ€” it keeps host-quota
318
- > consumption low and avoids flooding the GitHub API. For Epics with wide
319
- > dependency-free fronts where the host has adequate parallel-agent quota,
320
- > raising `delivery.deliverRunner.concurrencyCap` in `.agentrc.json` reduces
321
- > wall-clock time proportionally to the extra concurrency. The safe default
322
- > is left in place; this is a deliberate operator-tuning knob, not a hidden
323
- > performance ceiling. See `agentrc-reference.json`
324
- > `delivery.deliverRunner.concurrencyCap` for the configuration surface.
277
+ > **Throughput + capability tuning.** The default `concurrencyCap` of 3 is
278
+ > a deliberate operator-tuning knob (raise
279
+ > `delivery.deliverRunner.concurrencyCap`), and the optional per-call
280
+ > `model:` escape hatch lets mechanical Stories run on a cheaper capability.
281
+ > See [`deliver-epic-reference.md` ยง Throughput tradeoff](deliver-epic-reference.md#throughput-tradeoff)
282
+ > and [ยง Sub-agent dispatch capability](deliver-epic-reference.md#sub-agent-dispatch-capability).
325
283
 
326
284
  **Ledger the dispatch BEFORE the Agent call.** Immediately before each
327
285
  per-Story `Agent` tool call (one shell-out per Story, every attempt โ€”
@@ -348,20 +306,28 @@ matching `story.dispatch.end` record is appended later by
348
306
  `epic-execute-record-wave.js` (via `emit-story-dispatch-end.js`, Story #3900)
349
307
  after the Agent return is recorded in ยง 2c.
350
308
 
351
- Each Agent call's prompt must (1) name the Story + Epic ids **and the
352
- `prdId` / `techSpecId` from the Phase 1 prepare envelope** (Story #4253) so
353
- the child can thread `--prd <prdId> --tech-spec <techSpecId>` into its
354
- `story-init.js` Step 0 โ€” omit whichever flag is `null`, (2)
309
+ Each Agent call's prompt must (1) name the Story + Epic ids, (2)
355
310
  instruct the child to invoke `helpers/epic-deliver-story <storyId>`
356
311
  (whose Step 4 defines the child's return shape), (3) remind the child
357
312
  of the **non-interactive contract** (no clarifying questions;
358
- transition to `agent::blocked` and exit if stuck), (4) ask the child to
359
- suppress per-Story chat relay, and (5) require the child to emit a
360
- `story.heartbeat` lifecycle event at least once per Story-level phase
361
- transition via `node .agents/scripts/story-phase.js` (or whenever it
362
- stalls on a long-running step), and if it cannot make progress to
363
- transition to `agent::blocked` rather than fall silent. The pairing of
364
- `story.heartbeat` and `agent::blocked` is what lets the ยง2e Idle
313
+ transition to `agent::blocked` and exit if stuck), (4) tell the child to
314
+ suppress per-Story chat relay and instead relay **one line per phase
315
+ transition** (e.g. `Story #<id>: implementing โ†’ closing`) โ€” the child's
316
+ authoritative progress lands in the `story-run-progress` snapshot the
317
+ `story-phase.js` CLI upserts, not in a verbatim body dump, (5) require the
318
+ child to emit a `story.heartbeat` lifecycle event at least once per
319
+ Story-level phase transition via `node .agents/scripts/story-phase.js` (or
320
+ whenever it stalls on a long-running step), and if it cannot make progress
321
+ to transition to `agent::blocked` rather than fall silent, and (6) pass the
322
+ **docs digest path** โ€” the `docsDigestPath` field from the
323
+ `epic-deliver-prepare.js` envelope (ยง Phase 1 main), which points at
324
+ `temp/epic-<epicId>/docs-digest.md`. Instruct the child to read that
325
+ digest instead of re-reading the full `project.docsContextFiles` set,
326
+ and to pull individual docs files on demand (per
327
+ [`.agents/instructions.md` ยง 3](../../instructions.md)). When
328
+ `docsDigestPath` is null (the project configured no `docsContextFiles`),
329
+ say so โ€” the child then has no per-Story docs mandate. The pairing of
330
+ `story.heartbeat` and `agent::blocked` is what lets the ยง 2d Idle
365
331
  Watchdog distinguish a working child from a dead one; a silent child
366
332
  with no recent heartbeat and no blocker label is the failure mode the
367
333
  watchdog is built to catch.
@@ -372,13 +338,6 @@ to enforce. GitHub state is the contract: `epic-execute-record-wave.js`
372
338
  hint and reconciles any unparseable, empty, or missing return directly
373
339
  from the Story's live labels and comments.
374
340
 
375
- **Sub-agent dispatch.** `Agent` calls emit no `model:` argument by
376
- default โ€” children inherit from the `general-purpose` sub-agent
377
- definition and the parent's worktree context. No
378
- `--dangerously-skip-permissions` (no subprocess is spawned). If a
379
- specific call needs to override the inherited model, pass `model:` as a
380
- per-call literal at the `Agent(...)` site.
381
-
382
341
  ### 2c. Record the Story outcomes
383
342
 
384
343
  As dispatched Stories return (record them as they land โ€” you need not wait
@@ -407,14 +366,11 @@ Print `renderedBody` verbatim, then optionally append a short **Notable**
407
366
  section (0โ€“5 bullets on newly blocked / failed / slow Stories, friction,
408
367
  elapsed-time surprises).
409
368
 
410
- > **Crash recovery.** If the host crashed *after* a child finished but
411
- > *before* its return was recorded, the next `tick` re-derives that Story's
412
- > state directly from its live label (the tick reads labels every beat), so a
413
- > done-but-unrecorded Story is recognised as done and never re-dispatched โ€”
414
- > there is no falsely-`complete` empty wave to recover from. If you want to
415
- > reconcile a known-completed Story whose return text was lost, re-record it
416
- > from its live state by passing `--results '[{"storyId":<n>,"status":"done"}]'`
417
- > (verification re-checks the live label before recording `done`).
369
+ > **Crash recovery.** A child that finished but was never recorded is
370
+ > re-derived from its live label on the next `tick` and never
371
+ > re-dispatched. See
372
+ > [`deliver-epic-reference.md` ยง Crash recovery (record step)](deliver-epic-reference.md#crash-recovery-record-step)
373
+ > for the manual re-record command.
418
374
 
419
375
  ### 2d. Loop on `nextAction`
420
376
 
@@ -440,69 +396,14 @@ After `2c`, re-run `wave-tick.js`. Branch on the new envelope:
440
396
  the done count equals the in-scope Story count โ€” a stuck Story surfaces
441
397
  as `halt`, not a false `epic-complete`.)
442
398
 
443
- ### 2e. Idle Watchdog
444
-
445
- A Story's implementation loop can run for many minutes between
446
- `story.dispatch.start` and the post-merge `story.merged` record. While
447
- `observe` keeps the host LLM polling the Epic, it does **not** flag a
448
- sub-agent that has gone silent (host crash, mid-Story stall, lost
449
- return). The Idle Watchdog closes that gap.
450
-
451
- **Cadence.** While any Story is in flight (i.e. `nextAction.kind` is
452
- `observe` or the most recent dispatch's `in-flight` list is non-empty),
453
- re-tick every **30 minutes** with the watchdog flag:
454
-
455
- ```bash
456
- node .agents/scripts/wave-tick.js --epic <epicId> --check-idle 30
457
- ```
458
-
459
- > **Why 30, not 10 (Story #3900).** Heartbeats fire only at Story-level
460
- > phase transitions, and `implementing โ†’ closing` routinely exceeds 10
461
- > minutes for a healthy Story. A 10-minute threshold therefore tripped the
462
- > watchdog on every long-running Story, whose prescribed remediation โ€”
463
- > re-dispatch โ€” put two agents on one `story-<id>` branch (the worst
464
- > failure mode in the system). The threshold is widened to 30 minutes and
465
- > the staleness test now also consults a deterministic branch-commit
466
- > signal (below), so a Story still gaining commits is never flagged.
467
-
468
- The `--check-idle <minutes>` mode scans the per-Epic lifecycle ledger
469
- (`temp/epic-<epicId>/lifecycle.ndjson`) for Stories that carry a
470
- `story.dispatch.start` without a matching `story.dispatch.end` (the
471
- canonical in-flight list โ€” see ยง 2a's `nextAction['in-flight']`), and
472
- compares each in-flight Story's most recent ledger event (any
473
- `story.*` event, notably the `story.heartbeat` records emitted by
474
- `story-phase.js` at each Story-level phase transition) against the
475
- threshold. **Before flagging a stall, it also checks the last commit on
476
- `story-<id>` via `git log` (Story #3900): a Story whose branch carries a
477
- commit newer than the threshold is making forward progress and is left
478
- in-flight, never stalled โ€” deterministic protection against the
479
- false-positive re-dispatch hazard even when no heartbeat has landed.**
480
- The CLI emits one envelope on stdout and exits non-zero when at least
481
- one in-flight Story has been silent for โ‰ฅ the threshold:
482
-
483
- ```json
484
- {
485
- "kind": "wave-stall",
486
- "epicId": <n>,
487
- "thresholdMinutes": <n>,
488
- "checkedAt": "<ISO-8601>",
489
- "stalled": [{ "storyId": <n>, "lastEventAt": "<ISO-8601>", "idleMinutes": <n> }],
490
- "inFlight": [<storyId>, ...]
491
- }
492
- ```
493
-
494
- **On a stall.** When the watchdog exits non-zero, post the envelope
495
- verbatim as a `wave-stall` structured comment on the Epic (use
496
- [`post-structured-comment.js`](../../scripts/post-structured-comment.js)
497
- with `--kind wave-stall`), then re-evaluate the affected Stories: if a
498
- child sub-agent has crashed (no `story.dispatch.end`, no recent
499
- heartbeat, no commit on `story-<id>`), re-dispatch the Story per ยง 2b
500
- incrementing the `--attempt` counter; if the child is alive but
501
- genuinely blocked, flip the Story to `agent::blocked` and proceed per
502
- ยง 2d's `observe` branch.
503
-
504
- Stop the watchdog cadence once `wave-tick.js` returns `epic-complete` โ€”
505
- there are no in-flight Stories left to monitor.
399
+ > **Idle Watchdog.** While any Story is in flight, re-tick every 30 minutes
400
+ > with `wave-tick.js --epic <epicId> --check-idle 30` so a silent child
401
+ > (crashed host, lost return) is surfaced as a `wave-stall` and
402
+ > re-dispatched or blocked. The full cadence, staleness test (heartbeat +
403
+ > deterministic branch-commit signal), stall envelope, and the "why 30 not
404
+ > 10" rationale are in
405
+ > [`deliver-epic-reference.md` ยง 2e. Idle Watchdog](deliver-epic-reference.md#2e-idle-watchdog).
406
+ > Stop the cadence once the tick returns `epic-complete`.
506
407
 
507
408
  ---
508
409
 
@@ -603,221 +504,91 @@ canonical compose-and-post surface at
603
504
  Propagate `--full-retro` to bypass the compact-path heuristic.
604
505
 
605
506
  Retro fires here (before the PR opens) so it stays in the operator's
606
- local session with full env access (env vars, credentials, MCP).
607
-
608
- After the GitHub upsert succeeds, the retro body is also **mirrored
609
- locally** to the per-Epic temp tree at `temp/epic-<epicId>/retro.md`
610
- (path resolved via
507
+ local session with full env access (env vars, credentials, MCP). After
508
+ the GitHub upsert succeeds, the retro body is also **mirrored locally** to
509
+ `temp/epic-<epicId>/retro.md` (path resolved via
611
510
  [`lib/config/temp-paths.js`](../../scripts/lib/config/temp-paths.js)'s
612
- `epicRetroMirrorPath`, which honours `project.paths.tempRoot`).
613
- Operators can read the retro without re-fetching from GitHub. GitHub
614
- remains the source of truth โ€” a mirror-write failure only logs a warn
615
- and never fails the phase.
511
+ `epicRetroMirrorPath`). GitHub remains the source of truth โ€” a
512
+ mirror-write failure only logs a warn and never fails the phase.
616
513
 
617
514
  ---
618
515
 
619
516
  ## Phase 6.5 โ€” Post-wave integration gate (Epic #4131, F1/F4)
620
517
 
621
518
  This phase runs **after** the Phase 2 wave loop reports `epic-complete` and
622
- **before** the Phase 7 finalize emit opens the PR to `main`. Where every gate
623
- before it (`selectAudits`, the cross-epic-leak guard `#3362`, diff-against-base,
624
- the file-assumption gate) is **change-set-scoped** โ€” it narrows its evidence to
625
- the Epic's `changedFiles` โ€” this is the one **deliberately-global** gate: its
626
- evidence spans the **whole product**, not just the change set. The disease it
627
- catches is the surface that each Story shipped correctly in isolation yet that
628
- the assembled product cannot reach: a route nobody can navigate to, or a
629
- persona journey that the integrated waves silently broke. A change-set lens
630
- never sees that, because no single Story's diff contains the orphan.
519
+ **before** the Phase 7 finalize emit opens the PR to `main`. It is the one
520
+ **deliberately-global** gate โ€” its evidence spans the whole product, not just
521
+ the Epic's change set โ€” so it catches the surface each Story shipped correctly
522
+ in isolation yet the assembled product cannot reach (an unnavigable route, a
523
+ broken persona journey).
631
524
 
632
525
  Skip when `--skip-integration-gate` (log the override; record a manual
633
- intervention per [ยง Recording manual interventions](#recording-manual-interventions)).
634
- The gate is otherwise **always evaluated**, but it is a **silent no-op when
635
- unconfigured** (see *No-op when unconfigured* below) so consumers who ship no
636
- nav config and no journey suite are unaffected.
637
-
638
- ### 6.5a โ€” Whole-product navigability (reuses the Phase 4 lens)
639
-
640
- Run the `navigability` lens
641
- ([`audit-navigability.md`](../audit-navigability.md), the deliberately-global
642
- lens delivered in this Epic's wave 0) in **whole-route mode** against the
643
- integrated `epic/<epicId>` tip. Unlike its Phase 4 change-set-routed invocation,
644
- here it is driven explicitly over the consumer's **entire** route tree + nav
645
- registry โ€” it is on the global-lens allowlist (`GLOBAL_LENS_ALLOWLIST` in
646
- [`lib/audit-suite/selector.js`](../../scripts/lib/audit-suite/selector.js)) and
647
- exempt from the cross-epic-leak guard `#3362`, so a route orphaned anywhere in
648
- the product is in scope even when no Story in this Epic touched it.
649
-
650
- The lens reads the consumer's navigability config (resolved from `.agentrc.json`):
651
-
652
- - `delivery.quality.navigability.routeGlobs` โ€” the route-tree SSOT the lens
653
- enumerates.
654
- - `delivery.quality.navigability.navRegistry` โ€” the nav-registry SSOT the lens
655
- cross-checks every route against.
656
-
657
- A whole-product finding is a **hard failure**: an **orphaned route** (a route
658
- with no nav door for any entitled persona) or a **dead nav href** (a nav door
659
- pointing at a route that no longer exists). On a hard failure the gate **blocks
660
- finalize** and names the orphaned surface (route identifier / nav-door
661
- identifier only โ€” never the route body or persona PII, per
662
- `security-baseline.md`). Do **not** proceed to Phase 7.
663
-
664
- ### 6.5b โ€” Consumer journey suite
665
-
666
- Run the consumer's per-persona journey suite โ€” the integrated, cross-Epic
667
- persona-journey verification โ€” over the `epic/<epicId>` tip:
668
-
669
- - `delivery.quality.navigability.journeySuite` โ€” path/command for the journey
670
- suite. Absent โ‡’ this sub-step is skipped (no-op).
671
-
672
- A failing journey (a persona cannot complete an end-to-end journey through the
673
- assembled product) is a **hard failure**: block finalize and name the broken
674
- journey. This is the runtime complement to 6.5a's static reachability check โ€”
675
- 6.5a proves a door exists; the journey suite proves the door, and everything
676
- behind it, actually works when the waves are integrated.
677
-
678
- ### 6.5c โ€” `@pending` โ‰  green for surface-adding Epics (F4)
679
-
680
- The Phase 7 finalize chain runs the acceptance-spec reconciler
681
- ([`acceptance-spec-reconciler.js`](../../scripts/acceptance-spec-reconciler.js)),
682
- which classifies every AC ID into `satisfied` (covered by a non-pending
683
- scenario), `pending` (covered **only** by scenarios tagged `@pending`), or
684
- `missing`. For a **surface-adding Epic** โ€” one whose risk verdict carries a
685
- surface-adding signal (a route-adding change set, the `navigability` lens routed
686
- in Phase 4, or a configured `routeGlobs` match) โ€” this phase tightens that
687
- contract: an AC whose coverage is **only `@pending`** is treated as
688
- **unsatisfied**, not green. A surface-adding Epic whose acceptance coverage is
689
- **only `@pending`** therefore **fails the close gate** instead of passing โ€”
690
- shipping a new surface behind a deferred-forever `@pending` scenario is exactly
691
- the late-gate gap this Epic exists to close.
692
-
693
- This is **purely additive** and **scoped to surface-adding Epics**:
694
- refactor-only and docs-only Epics (no surface-adding signal) are **unaffected** โ€”
695
- their `@pending` handling is exactly as before, and the existing
696
- `satisfied` / `missing` reconciliation is **not** de-scoped for any Epic.
697
-
698
- ### No-op when unconfigured
699
-
700
- With **no** navigability config (`routeGlobs` / `navRegistry`) and **no**
701
- `journeySuite` present in `.agentrc.json`, this entire phase degrades to a
702
- **silent no-op**: 6.5a and 6.5b skip (nothing to enumerate or run) and 6.5c's
703
- surface-adding signal cannot fire without route globs, so the `@pending`
704
- tightening never engages. The gate adds **zero** behaviour to an unconfigured
705
- consumer โ€” it neither blocks finalize nor changes the existing change-set-scoped
706
- gates. The override flag `--skip-integration-gate` is the explicit operator
707
- escape hatch (consistent with `--skip-epic-audit`) for a configured consumer who
708
- wants to bypass the gate for a specific run.
709
-
710
- ### Fail safe and loud
711
-
712
- Per the Tech Spec's security note, this gate sits on the **critical path** to
713
- finalize. It MUST fail **safe and loud**: a hard failure **blocks** finalize and
714
- **names the cause** (the orphaned route, dead href, broken journey, or
715
- `@pending`-only AC), never silently passes. A genuinely unconfigured consumer is
716
- the **only** silent path, and that path **passes** (it is a no-op, not a block).
717
- On a hard failure, post a friction structured comment naming the surface, flip
718
- the Epic to `agent::blocked`, and park for the operator โ€” do **not** open the PR.
526
+ intervention per
527
+ [`deliver-epic-reference.md` ยง Recording manual interventions](deliver-epic-reference.md#recording-manual-interventions)).
528
+ The gate is otherwise **always evaluated** but a **silent no-op when
529
+ unconfigured** (no `routeGlobs` / `navRegistry` / `journeySuite` in
530
+ `.agentrc.json`).
531
+
532
+ Sub-steps and hard-failure semantics:
533
+
534
+ - **6.5a โ€” Whole-product navigability**: run the `navigability` lens in
535
+ whole-route mode over the `epic/<epicId>` tip. An **orphaned** route (no
536
+ nav door for any entitled persona) or a dead nav href is a hard failure
537
+ that **blocks finalize** and names the surface.
538
+ - **6.5b โ€” Consumer journey suite**: run
539
+ `delivery.quality.navigability.journeySuite` over the tip. A failing
540
+ persona journey is a hard failure that **blocks finalize** and names the
541
+ broken journey.
542
+ - **6.5c โ€” `@pending` โ‰  green for surface-adding Epics (F4)**: for a
543
+ **surface-adding** Epic, an AC covered **only** by `@pending` scenarios is
544
+ treated as unsatisfied and **fails the close gate** instead of passing
545
+ green. This is **purely additive** and scoped to surface-adding Epics โ€”
546
+ refactor-only and docs-only Epics are **unaffected** and the existing
547
+ `satisfied` / `missing` reconciliation is **not de-scoped** for any Epic.
548
+
549
+ On any hard failure, post a friction structured comment naming the surface
550
+ (route / nav-door identifier only โ€” never route bodies or persona PII per
551
+ `security-baseline.md`), flip the Epic to `agent::blocked`, and **do not**
552
+ open the PR โ€” the gate fails safe and loud. See
553
+ [`deliver-epic-reference.md` ยง Phase 6.5](deliver-epic-reference.md#phase-65--post-wave-integration-gate-epic-4131-f1f4)
554
+ for the full lens config, the surface-adding-signal derivation, the no-op
555
+ degradation contract, and the fail-safe-and-loud security note.
719
556
 
720
557
  ---
721
558
 
722
559
  ## Phase 7 โ€” Finalize (open PR to main)
723
560
 
724
- ### 7.0 โ€” Sync Epic branch from `main` (Story #2580)
725
-
726
- Before firing the close-tail emit, sync the Epic branch with
727
- `origin/main` so the PR opens with the latest base commits already
728
- integrated. The Epic branch may be behind `main` if other PRs merged
729
- during the wave loop; without this step, the Epicโ†’`main` PR opens
730
- "behind base" and (with branch-protection's `up-to-date branch` rule)
731
- stalls at the merge gate.
561
+ Before the close-tail emit, sync the Epic branch with `origin/main` so the
562
+ PR opens with the latest base commits already integrated (a stale base
563
+ stalls at branch-protection's `up-to-date branch` rule):
732
564
 
733
565
  ```bash
734
566
  git checkout epic/<epicId>
735
567
  node .agents/scripts/sync-branch-from-base.js \
736
568
  --branch epic/<epicId> --base main
569
+ git push origin epic/<epicId>
737
570
  ```
738
571
 
739
- Outcomes:
740
-
741
- - **`fast-forward` / `merge-commit` / `noop-already-current`** โ†’ push
742
- the resulting tip and continue to Phase 7.1: `git push origin epic/<epicId>`.
743
- - **`conflict`** โ†’ resolve in the Epic checkout (`git merge --no-edit
744
- origin/main`, fix conflicts, `git commit --no-edit`), then re-run the
745
- sync command. Once it exits 0, continue. Operator-recoverable; not an
746
- agent loop.
747
- - **`fetch-failed`** โ†’ re-check network / `origin` access and re-run.
748
-
749
- This is a workflow-level step (operator-driven), not part of the
750
- close-tail listener chain. The sync runs from the main checkout so
751
- the resulting tip lands on `epic/<epicId>` before Phase 7.1 fires
752
- the bus-driven close-tail.
753
-
754
- ### 7.1 โ€” Fire the close-tail emit
572
+ Then fire the close-tail emit:
755
573
 
756
574
  ```bash
757
575
  node .agents/scripts/lifecycle-emit.js --epic <epicId> --event epic.close.end
758
576
  ```
759
577
 
760
- Emits `epic.close.end` onto the lifecycle bus. **Every close-time
761
- responsibility below runs inside the listener chain โ€” the operator
762
- shells nothing manually. The `Finalizer` listener (Story #2894 โ€”
763
- bus-owned finalize) composes three helpers under
764
- `.agents/scripts/lib/orchestration/finalize/` and emits the canonical
765
- chain.** Treat this section as a runtime contract โ€” `/deliver`
766
- just fires the emit and reads the resulting ledger.
767
-
768
- 1. **Acceptance-spec reconciliation โ€” bus-driven.** The
769
- `AcceptanceReconciler` listener invokes
770
- [`acceptance-spec-reconciler.js`](../../scripts/acceptance-spec-reconciler.js)
771
- to diff the AC IDs declared in the linked `context::acceptance-spec`
772
- body against `@ac-*` / `@pending` tags in `tests/features/**`. A
773
- non-OK reconciliation throws (per
774
- [`rules/orchestration-error-handling.md`](../../rules/orchestration-error-handling.md)),
775
- aborting finalize **before** any PR is opened or planning artifacts
776
- are closed โ€” so the PRD, Tech Spec, and Acceptance Spec stay open
777
- until the AC coverage gap is fixed. The reconciler returns
778
- `status: 'waived'` without scanning features when the Epic carries
779
- `acceptance::n-a`, and defends against direct CLI invocation by
780
- refusing to run when no spec is linked and no waiver is set (the
781
- start gate in Phase 1 would normally catch that first).
782
- 2. **PR open โ€” bus-driven (Story #2894).** On
783
- `acceptance.reconcile.ok` the `Finalizer` listener invokes
784
- [`openOrLocatePr`](../../scripts/lib/orchestration/finalize/open-or-locate-pr.js)
785
- with `{ epicId, headBranch: 'epic/<id>', baseBranch: 'main' }`.
786
- The helper probes for an existing open PR on the head branch
787
- first (idempotent locate path โ€” a re-run of `/deliver`
788
- on the same branch short-circuits without opening a duplicate)
789
- and only opens a new PR when none exists. The listener then
790
- emits `pr.created` โ†’ `epic.finalize.end` and **stops** (Story
791
- #3367). It does **not** emit `epic.merge.ready`: that event is
792
- the sole `AutomergeArmer` trigger, and emitting it from finalize
793
- would cascade `epic.close.end` synchronously through the arm โ†’
794
- `MergeWatcher` โ†’ `Cleaner` โ†’ `BranchCleaner` reap, deleting the
795
- `epic/<id>` branch before the PR merged and bypassing the
796
- `AutomergePredicate` disqualification gate. The auto-merge arm is
797
- driven later from the gated watch path (`pr.created` โ†’ `Watcher`
798
- โ†’ `epic.watch.end` โ†’ `AutomergePredicate` โ†’ `epic.merge.ready` โ†’
799
- `AutomergeArmer`) re-entered in Phase 8.5. The merge-lockout rule
800
- in [`check-lifecycle-lint.js`](../../scripts/check-lifecycle-lint.js)
801
- keeps `gh pr merge --auto --squash --delete-branch` confined to
802
- `AutomergeArmer` โ€” Phase 7 never shells the merge command.
803
- 3. **Planning-artifact close + hand-off โ€” bus-driven (Story
804
- #2894).** After `openOrLocatePr` returns, the `Finalizer` chains
805
- [`closePlanningTickets`](../../scripts/lib/orchestration/finalize/close-planning-tickets.js)
806
- to close the three planning context tickets
807
- (`context::prd`, `context::tech-spec`, `context::acceptance-spec`)
808
- so the Epic's `Closes #<id>` auto-close path is not blocked by
809
- open sub-issues, then
810
- [`postHandoffComment`](../../scripts/lib/orchestration/finalize/post-handoff-comment.js)
811
- to upsert the canonical `epic-handoff` structured comment naming
812
- the PR URL. Both helpers are idempotent โ€” already-closed tickets
813
- are counted under `alreadyClosed`, and the handoff comment is
814
- edited in place via `upsertStructuredComment` rather than
815
- appending a duplicate. When the `acceptance::n-a` waiver is set
816
- and no Acceptance Spec ticket was ever opened, the third
817
- planning-ticket close is recorded as `skipped`.
818
-
819
- Branch cleanup is out-of-band (Phase 9 reaps local refs after merge; the
820
- rare "scrap and reset" case for an unmerged Epic is handled manually).
578
+ `epic.close.end` drives the bus-owned `Finalizer` chain: acceptance-table
579
+ reconciliation (throws and aborts finalize on a coverage gap, `waived` under
580
+ `acceptance::n-a`), idempotent PR open/locate against `main`, and the
581
+ `epic-handoff` comment naming the PR URL. The chain emits `pr.created` โ†’
582
+ `epic.finalize.end` and **stops** โ€” it never emits `epic.merge.ready` (the
583
+ auto-merge arm is driven later from the Phase 8.5 gated watch path). The
584
+ operator shells nothing beyond the sync and the single emit.
585
+
586
+ See
587
+ [`deliver-epic-reference.md` ยง Phase 7 โ€” Finalize](deliver-epic-reference.md#phase-7--finalize-close-tail-listener-chain)
588
+ for the branch-sync outcome table (conflict / fetch-failed recovery) and the
589
+ full three-step listener contract (why finalize must not emit
590
+ `epic.merge.ready`, the merge-lockout lint rule, the no planning-ticket close
591
+ sweep).
821
592
 
822
593
  ---
823
594
 
@@ -840,43 +611,13 @@ node <agentRoot>/scripts/pr-watch-with-update.js --pr <prNumber>
840
611
  calls per session and `--poll-interval-ms MS` (default 10000) to
841
612
  override the polling cadence.
842
613
 
843
- Exit 0 โ†’ proceed to Phase 8.5. Non-zero โ†’ remediate (below) and re-run
844
- the helper. Auto-merge stays armed across retries; the
845
- `epic.automerge.start` emit in Phase 8.5 re-runs the `AutomergeArmer`
846
- listener, which re-checks `mergeStateStatus` before firing merge, so a
847
- second BEHIND that arrives between the helper exiting clean and Phase
848
- 8.5 starting is also caught.
849
-
850
- ### 8.1 Remediation
851
-
852
- For each failed required check: fetch the log
853
- (`gh run view <runId> --log-failed`), classify and fix:
854
-
855
- - **lint / format** โ†’ `npm run lint` + `npx biome check --apply` (or
856
- `format --write`); commit, push.
857
- - **maintainability / crap baseline drift** โ†’ re-run the ratcheted
858
- script. Refresh the baseline only when drift is justified by the
859
- diff; otherwise fix at source.
860
- - **test failure** โ†’ reproduce with `npm test`, fix source or test.
861
- - **coverage threshold** โ†’ add tests (preferred); refresh baseline only
862
- when the diff demonstrably can't be covered.
863
- - **anything else** โ†’ read the log, fix at source.
864
-
865
- Push to `epic/<epicId>` and re-run
866
- `node <agentRoot>/scripts/pr-watch-with-update.js --pr <prNumber>`.
614
+ Exit 0 โ†’ proceed to Phase 8.5. Non-zero โ†’ remediate and re-run the helper
615
+ (push fixes to `epic/<epicId>`; auto-merge stays armed across retries).
867
616
 
868
- ### 8.2 When to halt
869
-
870
- Three consecutive iterations on the same failure class without
871
- convergence โ†’ friction comment, flip to `agent::blocked`, park. Unknown
872
- failure class on first encounter โ†’ attempt source-level fix; log
873
- friction if diagnosis takes more than one round.
874
-
875
- ### 8.3 Hard prohibitions
876
-
877
- **Never** `gh pr merge` from Phase 8 (Phase 8.5 is the only merge
878
- site). **Never** force-push to `main`. **Never** push empty commits or
879
- refresh baselines to dodge a red check.
617
+ > **Remediation + hard prohibitions.** For the per-check fix table (lint,
618
+ > baseline drift, test, coverage), the three-strikes halt rule, and the
619
+ > never-merge / never-force-push / never-dodge prohibitions, see
620
+ > [`deliver-epic-reference.md` ยง Phase 8 โ€” Watch-and-iterate remediation](deliver-epic-reference.md#phase-8--watch-and-iterate-remediation).
880
621
 
881
622
  ---
882
623
 
@@ -890,57 +631,29 @@ node .agents/scripts/lifecycle-emit.js --epic <epicId> \
890
631
  --event epic.automerge.start --pr-url <prUrl>
891
632
  ```
892
633
 
893
- `AutomergePredicate` subscribes to `epic.automerge.start` (Story #3901 โ€”
894
- before that this event had **zero** subscribers and the entire Phase 8.5
895
- gate was a dead wire). It evaluates the structured-signal verdict and
896
- emits `epic.merge.ready` on a clean verdict or `epic.merge.blocked`
897
- otherwise. The CI-freshness gate is skipped on this event because Phase 8
898
- already polled every required check to green โ€” `epic.automerge.start`
899
- carries `prUrl` but no `checkOutcomes`.
900
-
901
- The `AutomergeArmer` listener subscribes to the downstream
902
- `epic.merge.ready` outcome and fires `gh pr merge --auto --squash
903
- --delete-branch` only when `clean: true`. `clean: true` requires
904
- **all** of:
905
-
906
- - `state.manualInterventions[]` is empty;
907
- - every wave's `status === "complete"`;
908
- - no story envelope carries a `blockerCommentId` or non-`done` status;
909
- - code-review reports `0` ๐Ÿ”ด + `0` ๐ŸŸ  findings;
910
- - the retro's machine-readable `automerge-verdict` trailer reports
911
- `cleanSprint: true` (Story #3901 โ€” the predicate reads the parsed JSON
912
- trailer `retro-run.js` writes into the retro body, **not** an emoji
913
- string-match on the human-facing "๐ŸŸข Clean sprint" prose).
914
-
915
- When clean, the listener fires `gh pr merge --squash --delete-branch`.
916
- Otherwise the listener records disqualifying reasons via
917
- `epic.merge.blocked` and exits without merging โ€” operator merges
918
- manually.
919
-
920
- Close the phase wrapper by emitting `epic.automerge.end` (records the
921
- arm outcome on the ledger; `merged: true` once GitHub completes the
922
- squash, `merged: false` with a reason for predicate-blocked or
923
- armed-but-pending):
634
+ `AutomergePredicate` evaluates the structured-signal verdict and emits
635
+ `epic.merge.ready` on a clean verdict or `epic.merge.blocked` otherwise. The
636
+ downstream `AutomergeArmer` fires `gh pr merge --auto --squash
637
+ --delete-branch` **only** when `clean: true` (empty manual-interventions,
638
+ every wave complete, no story blocked, `0` ๐Ÿ”ด + `0` ๐ŸŸ  review findings, and
639
+ the retro's `automerge-verdict` trailer reports `cleanSprint: true`).
640
+ Otherwise it records disqualifying reasons and exits without merging โ€” the
641
+ operator merges manually.
642
+
643
+ Close the phase wrapper by emitting `epic.automerge.end` (records the arm
644
+ outcome on the ledger; `merged: true` once GitHub completes the squash,
645
+ `merged: false` with a reason otherwise):
924
646
 
925
647
  ```bash
926
648
  node .agents/scripts/lifecycle-emit.js --epic <epicId> \
927
649
  --event epic.automerge.end --pr-url <prUrl> --merged <true|false>
928
650
  ```
929
651
 
930
- ### Recording manual interventions
931
-
932
- Whenever you step outside the happy path during a delivery, record it
933
- (each entry disqualifies auto-merge):
934
-
935
- ```bash
936
- node .agents/scripts/epic-deliver-note-intervention.js \
937
- --epic <epicId> --reason "<one-line description>"
938
- ```
939
-
940
- Triggers: `AskUserQuestion` mid-run; `git restore`/`reset` against the
941
- tree; child-reported `--no-ff` recovery, stash dance, or out-of-band
942
- merge surgery; child closes via `--skipValidation`; force-pushing or
943
- empty-committing to dodge CI diagnosis.
652
+ > **Predicate wiring + manual-intervention recording.** For the full
653
+ > `clean: true` predicate contract (Story #3901 โ€” the trailer read, the
654
+ > CI-freshness skip) and the `epic-deliver-note-intervention.js` command +
655
+ > its trigger list, see
656
+ > [`deliver-epic-reference.md` ยง Phase 8.5 โ€” Auto-merge predicate detail](deliver-epic-reference.md#phase-85--auto-merge-predicate-detail).
944
657
 
945
658
  ---
946
659
 
@@ -948,54 +661,24 @@ empty-committing to dodge CI diagnosis.
948
661
 
949
662
  Phase 9 runs **automatically** inside the lifecycle bus once auto-merge
950
663
  arms: the `BranchCleaner` listener subscribes to `epic.cleanup.start`
951
- and reaps local refs before `Cleaner` archives the `temp/epic-<id>/`
952
- tree. No operator step is required on the auto-merge path.
953
-
954
- What gets reaped (in order, all in-process):
955
-
956
- 1. The main checkout is switched off `epic/<id>` to `baseBranch` when
957
- needed (otherwise `git branch -D epic/<id>` is refused).
958
- 2. Every `story-<id>` listed in the `epic-run-state` checkpoint, plus
959
- `epic/<id>`. Attached worktrees are removed with the standard
960
- `git worktree remove` โ†’ `--force` โ†’ filesystem-rm fallback (the
961
- last step covers Windows file-locks).
962
- 3. `git remote prune <remote>` drops stale `<remote>/...` tracking
963
- refs left behind by `gh pr merge --delete-branch`.
964
- 4. The `wt-branch` scratch ref left by `story-close.js`'s internal
965
- merge worktree is deleted when no worktree still points at it.
966
-
967
- Per-branch failures aggregate into the listener's classification log
968
- (`reaped` / `failed` / `no-state` / `skipped-duplicate`) and are
969
- visible in `temp/epic-<id>/lifecycle.ndjson`. They do not block the
970
- rest of cleanup.
664
+ and reaps local refs (the `epic/<id>` branch, every `story-<id>` in the
665
+ checkpoint, attached worktrees, and stale tracking refs) before `Cleaner`
666
+ archives the `temp/epic-<id>/` tree. No operator step is required on the
667
+ auto-merge path.
971
668
 
972
669
  For out-of-band cleanup re-entry (resume after a crash, or operator
973
- override), fire `epic.merge.armed` via the lifecycle-emit helper:
670
+ override), fire `epic.merge.armed`:
974
671
 
975
672
  ```bash
976
673
  node .agents/scripts/lifecycle-emit.js --epic <epicId> \
977
674
  --event epic.merge.armed --pr-url <prUrl>
978
675
  ```
979
676
 
980
- If Phase 8.5 fell back to the operator-merges-button path (`gh pr
981
- merge --auto` was declined), the `epic.merge.armed` event never fires
982
- inside this run and Phase 9 will not run automatically. After the
983
- operator merges the PR, `epic/<epicId>` and each `story-<id>` ref can
984
- be reaped manually:
985
-
986
- ```bash
987
- git checkout main
988
- git pull --ff-only origin main
989
- git branch -D epic/<epicId>
990
- git branch -D story-<id1> story-<id2> ...
991
- git remote prune origin
992
- ```
993
-
994
- Note that `git-cleanup.js` alone will not catch `story-<id>` refs in
995
- this case because the epic PR squash-merges break the `git branch
996
- --merged main` signal and the stories never had their own PRs. Wiring
997
- a CLI surface that drives the BranchCleaner listener for this
998
- fallback is tracked as follow-up to Story #2398.
677
+ > **Reap order + operator-merges fallback.** For the full in-process reap
678
+ > order, the per-branch classification log, and the manual reap sequence
679
+ > when Phase 8.5 fell back to the operator-merges-button path (auto-merge
680
+ > declined, `epic.merge.armed` never fired), see
681
+ > [`deliver-epic-reference.md` ยง Phase 9 โ€” Local branch cleanup detail](deliver-epic-reference.md#phase-9--local-branch-cleanup-detail).
999
682
 
1000
683
  ---
1001
684
 
@@ -1011,8 +694,8 @@ the `epic-run-progress` structured comment.
1011
694
  ## Constraints
1012
695
 
1013
696
  - **Never** merge `epic/<epicId>` to `main` outside Phase 8.5.
1014
- - **Never** dispatch more than one wave at a time; concurrency lives
1015
- inside a single wave's fan-out, capped at `concurrencyCap`.
697
+ - **Never** dispatch more than the global `concurrencyCap` allows;
698
+ concurrency lives inside the ready-set fan-out.
1016
699
  - **Never** flip Story-level labels from this skill; **never** invoke
1017
700
  `helpers/epic-deliver-story` yourself (children run it via Agent fan-out,
1018
701
  even for single-Story waves); **never** spawn a subprocess for dispatch.