mandrel 1.94.0 → 2.1.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 (560) hide show
  1. package/.agents/README.md +116 -99
  2. package/.agents/agents/acceptance-critic.md +9 -7
  3. package/.agents/agents/story-worker.md +45 -51
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +498 -1287
  6. package/.agents/docs/agentrc-reference.json +185 -80
  7. package/.agents/docs/configuration.md +132 -190
  8. package/.agents/docs/execution-reference.md +51 -25
  9. package/.agents/docs/quality-gates.md +25 -33
  10. package/.agents/docs/workflows.md +8 -8
  11. package/.agents/instructions.md +113 -139
  12. package/.agents/rules/ci-remediation.md +11 -15
  13. package/.agents/rules/git-conventions-reference.md +48 -58
  14. package/.agents/rules/git-conventions.md +16 -22
  15. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  16. package/.agents/schemas/agentrc.schema.json +83 -254
  17. package/.agents/schemas/audit-rules.json +59 -1
  18. package/.agents/schemas/audit-rules.schema.json +33 -1
  19. package/.agents/schemas/lifecycle/README.md +1 -2
  20. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  21. package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
  22. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
  23. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  24. package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
  25. package/.agents/schemas/signal-event.schema.json +3 -3
  26. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  27. package/.agents/schemas/validation-evidence.schema.json +1 -1
  28. package/.agents/scripts/acceptance-eval.js +22 -66
  29. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  30. package/.agents/scripts/audit-to-stories.js +7 -7
  31. package/.agents/scripts/boot-sweep.js +1 -1
  32. package/.agents/scripts/bootstrap.js +3 -3
  33. package/.agents/scripts/check-dead-exports.js +43 -104
  34. package/.agents/scripts/check-doc-links.js +2 -2
  35. package/.agents/scripts/check-lifecycle-lint.js +7 -10
  36. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  37. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  38. package/.agents/scripts/deliver-recover.js +122 -0
  39. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  40. package/.agents/scripts/evidence-gate.js +20 -50
  41. package/.agents/scripts/generate-skills-index.js +17 -1
  42. package/.agents/scripts/generate-workflows-doc.js +4 -4
  43. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  44. package/.agents/scripts/lib/Logger.js +6 -10
  45. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  46. package/.agents/scripts/lib/audit-suite/selector.js +328 -28
  47. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  48. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  49. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
  50. package/.agents/scripts/lib/baselines/reader.js +1 -6
  51. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  52. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  53. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  54. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  55. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  56. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  57. package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
  58. package/.agents/scripts/lib/checks/index.js +3 -2
  59. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  60. package/.agents/scripts/lib/checks/state.js +17 -248
  61. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
  62. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  63. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  64. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  65. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  66. package/.agents/scripts/lib/cli-args.js +39 -9
  67. package/.agents/scripts/lib/close-validation/gates.js +15 -15
  68. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  69. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  70. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  71. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  72. package/.agents/scripts/lib/config/ci.js +6 -31
  73. package/.agents/scripts/lib/config/delivery-routing.js +52 -35
  74. package/.agents/scripts/lib/config/explain.js +61 -48
  75. package/.agents/scripts/lib/config/github.js +7 -5
  76. package/.agents/scripts/lib/config/limits.js +29 -80
  77. package/.agents/scripts/lib/config/paths.js +0 -2
  78. package/.agents/scripts/lib/config/quality.js +12 -15
  79. package/.agents/scripts/lib/config/runners.js +20 -66
  80. package/.agents/scripts/lib/config/temp-paths.js +30 -63
  81. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  82. package/.agents/scripts/lib/config-resolver.js +2 -7
  83. package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
  84. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
  85. package/.agents/scripts/lib/config-settings-schema.js +100 -60
  86. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  87. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  88. package/.agents/scripts/lib/dependency-parser.js +3 -2
  89. package/.agents/scripts/lib/doc-tiers.js +2 -2
  90. package/.agents/scripts/lib/duplicate-search.js +242 -41
  91. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  92. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  93. package/.agents/scripts/lib/format-generated-json.js +97 -0
  94. package/.agents/scripts/lib/framework-version.js +19 -190
  95. package/.agents/scripts/lib/gh-exec.js +8 -0
  96. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  97. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
  98. package/.agents/scripts/lib/git-utils.js +32 -20
  99. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  100. package/.agents/scripts/lib/json-utils.js +1 -2
  101. package/.agents/scripts/lib/label-constants.js +10 -38
  102. package/.agents/scripts/lib/label-taxonomy.js +10 -55
  103. package/.agents/scripts/lib/observability/active-story-env.js +44 -165
  104. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  105. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  106. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  107. package/.agents/scripts/lib/observability/source-classifier.js +5 -5
  108. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  109. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  110. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  111. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  112. package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
  113. package/.agents/scripts/lib/orchestration/code-review.js +78 -436
  114. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  115. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
  116. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  117. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  118. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  119. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  120. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  121. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  122. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  123. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  124. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  125. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
  126. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  127. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  128. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  129. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
  130. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  131. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  132. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  133. package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
  134. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  135. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  136. package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
  137. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
  138. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
  139. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  140. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  141. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
  142. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
  143. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  144. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
  145. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
  146. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
  147. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  148. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  149. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  150. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
  151. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  152. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
  153. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  154. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  155. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  156. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  157. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  158. package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
  159. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  160. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  161. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  162. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  163. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  164. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  165. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  166. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  167. package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
  168. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
  169. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  170. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  171. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
  172. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  173. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
  174. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  175. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  176. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
  177. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  178. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  179. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
  180. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  181. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  182. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  183. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  184. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  185. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  186. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  187. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
  188. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  189. package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
  190. package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
  191. package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
  192. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  193. package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
  194. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  195. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
  196. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
  197. package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
  198. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
  199. package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
  200. package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
  201. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  202. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  203. package/.agents/scripts/lib/planning-corpus.js +12 -281
  204. package/.agents/scripts/lib/preflight-runner.js +4 -4
  205. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  206. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  207. package/.agents/scripts/lib/signals/index.js +4 -17
  208. package/.agents/scripts/lib/signals/read.js +35 -35
  209. package/.agents/scripts/lib/signals/schema.js +8 -11
  210. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  211. package/.agents/scripts/lib/signals/write.js +0 -1
  212. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  213. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  214. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  215. package/.agents/scripts/lib/story-adjacency.js +17 -19
  216. package/.agents/scripts/lib/story-body/story-body.js +130 -75
  217. package/.agents/scripts/lib/story-plan.js +2 -4
  218. package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
  219. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  220. package/.agents/scripts/lib/test-env.js +14 -1
  221. package/.agents/scripts/lib/test-tiers.js +0 -3
  222. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
  223. package/.agents/scripts/lib/validation-evidence.js +32 -60
  224. package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
  225. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  226. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  227. package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
  228. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  229. package/.agents/scripts/lint-issue-body.js +71 -40
  230. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  231. package/.agents/scripts/notify.js +4 -3
  232. package/.agents/scripts/plan-context.js +102 -80
  233. package/.agents/scripts/plan-persist.js +230 -279
  234. package/.agents/scripts/plan-run-epilogue.js +142 -0
  235. package/.agents/scripts/post-structured-comment.js +0 -38
  236. package/.agents/scripts/pr-watch-with-update.js +43 -22
  237. package/.agents/scripts/providers/github/compose.js +0 -1
  238. package/.agents/scripts/providers/github/errors.js +0 -19
  239. package/.agents/scripts/providers/github/issues.js +13 -39
  240. package/.agents/scripts/providers/github/mappers.js +5 -12
  241. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  242. package/.agents/scripts/providers/github/tickets.js +33 -156
  243. package/.agents/scripts/providers/github.js +17 -6
  244. package/.agents/scripts/resolve-stories.js +236 -0
  245. package/.agents/scripts/run-coverage.js +4 -1
  246. package/.agents/scripts/run-lint.js +2 -2
  247. package/.agents/scripts/run-verify.js +31 -2
  248. package/.agents/scripts/signals-view.js +25 -21
  249. package/.agents/scripts/single-story-close.js +178 -26
  250. package/.agents/scripts/single-story-confirm-merge.js +313 -24
  251. package/.agents/scripts/single-story-init.js +35 -30
  252. package/.agents/scripts/stories-wave-tick.js +85 -10
  253. package/.agents/scripts/story-plan.js +28 -49
  254. package/.agents/scripts/update-ticket-state.js +14 -65
  255. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  256. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  257. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  258. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  259. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  260. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  261. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  262. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  263. package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
  264. package/.agents/skills/core/scope-triage/SKILL.md +31 -172
  265. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  266. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  267. package/.agents/skills/skills.index.json +3 -53
  268. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  269. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  270. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  271. package/.agents/starter-agentrc.json +0 -5
  272. package/.agents/templates/agent-protocol.md +9 -10
  273. package/.agents/workflows/audit-architecture.md +6 -7
  274. package/.agents/workflows/audit-clean-code.md +7 -7
  275. package/.agents/workflows/audit-dependencies.md +3 -3
  276. package/.agents/workflows/audit-devops.md +3 -3
  277. package/.agents/workflows/audit-documentation.md +9 -10
  278. package/.agents/workflows/audit-lighthouse.md +11 -3
  279. package/.agents/workflows/audit-navigability.md +13 -2
  280. package/.agents/workflows/audit-performance.md +5 -6
  281. package/.agents/workflows/audit-privacy.md +3 -3
  282. package/.agents/workflows/audit-quality.md +11 -12
  283. package/.agents/workflows/audit-security.md +4 -5
  284. package/.agents/workflows/audit-seo.md +13 -3
  285. package/.agents/workflows/audit-sre.md +3 -3
  286. package/.agents/workflows/audit-to-stories.md +20 -20
  287. package/.agents/workflows/audit-ux-ui.md +10 -3
  288. package/.agents/workflows/deliver.md +177 -176
  289. package/.agents/workflows/git-cleanup.md +5 -6
  290. package/.agents/workflows/git-deliver.md +1 -1
  291. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  292. package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
  293. package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
  294. package/.agents/workflows/helpers/code-review.md +75 -196
  295. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
  296. package/.agents/workflows/helpers/deliver-story.md +606 -0
  297. package/.agents/workflows/helpers/diagnose.md +10 -10
  298. package/.agents/workflows/helpers/parallel-tooling.md +3 -3
  299. package/.agents/workflows/helpers/signals.md +16 -16
  300. package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
  301. package/.agents/workflows/mandrel-update.md +2 -1
  302. package/.agents/workflows/plan.md +277 -145
  303. package/.agents/workflows/qa-assist.md +27 -33
  304. package/.agents/workflows/qa-explore.md +29 -38
  305. package/.agents/workflows/qa-run.md +2 -6
  306. package/README.md +9 -8
  307. package/bin/mandrel.js +12 -1
  308. package/docs/CHANGELOG.md +70 -0
  309. package/lib/cli/registry.js +262 -19
  310. package/lib/cli/sync-agents.js +157 -0
  311. package/lib/cli/sync-commands.js +115 -6
  312. package/lib/cli/sync.js +168 -6
  313. package/lib/cli/update.js +105 -8
  314. package/lib/cli/version-helpers.js +131 -0
  315. package/lib/migrations/README.md +7 -5
  316. package/lib/migrations/index.js +12 -8
  317. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  318. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  319. package/package.json +2 -2
  320. package/.agents/agents/retro.md +0 -42
  321. package/.agents/personas/architect.md +0 -113
  322. package/.agents/personas/devops-engineer.md +0 -38
  323. package/.agents/personas/engineer.md +0 -33
  324. package/.agents/personas/project-manager.md +0 -114
  325. package/.agents/personas/qa-engineer.md +0 -95
  326. package/.agents/personas/security-engineer.md +0 -111
  327. package/.agents/personas/technical-writer.md +0 -101
  328. package/.agents/schemas/dispatch-manifest.json +0 -232
  329. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  330. package/.agents/schemas/epic-spec.schema.json +0 -153
  331. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  332. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  333. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  334. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  335. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  336. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  337. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  338. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  339. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  340. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  341. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  342. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  343. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  344. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  345. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  346. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  347. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  348. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  349. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  350. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  351. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  352. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  353. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  354. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  355. package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
  356. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
  357. package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
  358. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  359. package/.agents/schemas/risk-verdict.schema.json +0 -66
  360. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  361. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  362. package/.agents/scripts/analyze-execution.js +0 -444
  363. package/.agents/scripts/bookkeeping-reconcile.js +0 -117
  364. package/.agents/scripts/check-prepush-recovery.js +0 -90
  365. package/.agents/scripts/dispatcher.js +0 -295
  366. package/.agents/scripts/epic-audit-prepare.js +0 -497
  367. package/.agents/scripts/epic-audit-recheck.js +0 -274
  368. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  369. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  370. package/.agents/scripts/epic-deliver-prepare.js +0 -852
  371. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  372. package/.agents/scripts/epic-plan-clarity.js +0 -211
  373. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  374. package/.agents/scripts/epic-reconcile.js +0 -625
  375. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  376. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  377. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  378. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  379. package/.agents/scripts/lib/config/preflight.js +0 -58
  380. package/.agents/scripts/lib/config/retro.js +0 -77
  381. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  382. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  383. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  384. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  385. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  386. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
  387. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  388. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  389. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  390. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
  391. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
  392. package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
  393. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  394. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  395. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  396. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  397. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  398. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  399. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  400. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  401. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  402. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  403. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  404. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  405. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  406. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  407. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  408. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
  409. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  410. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  411. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  412. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
  413. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  414. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  415. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  416. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  417. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  418. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  419. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  420. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  421. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  422. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  423. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  424. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  425. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  426. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
  427. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  428. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  429. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  430. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  431. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  432. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  433. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  434. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  435. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  436. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  437. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  438. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
  439. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  440. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  441. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  442. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
  443. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  444. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  445. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  446. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
  447. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  448. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  449. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  450. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  451. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  452. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  453. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  454. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  455. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  456. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  457. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  458. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  459. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  460. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  461. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
  462. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  463. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  464. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  465. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
  466. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  467. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  468. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  469. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  470. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  471. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  472. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  473. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  474. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  475. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  476. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  477. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  478. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  479. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  480. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  481. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  482. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  483. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  484. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  485. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  486. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  487. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  488. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  489. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  490. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  491. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  492. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  493. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  494. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  495. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  496. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  497. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  498. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  499. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  500. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  501. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
  502. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  503. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  504. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  505. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  506. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  507. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  508. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  509. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  510. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  511. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  512. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  513. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  514. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  515. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  516. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  517. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  518. package/.agents/scripts/lib/spec/index.js +0 -36
  519. package/.agents/scripts/lib/spec/loader.js +0 -425
  520. package/.agents/scripts/lib/spec/state.js +0 -208
  521. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  522. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  523. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  524. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  525. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  526. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  527. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  528. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  529. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  530. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  531. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  532. package/.agents/scripts/lifecycle-emit.js +0 -510
  533. package/.agents/scripts/retro-run.js +0 -218
  534. package/.agents/scripts/slice-phase.js +0 -361
  535. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  536. package/.agents/scripts/story-close.js +0 -294
  537. package/.agents/scripts/story-init.js +0 -599
  538. package/.agents/scripts/story-phase.js +0 -369
  539. package/.agents/scripts/wave-tick.js +0 -464
  540. package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
  541. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  542. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  543. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  544. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  545. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  546. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  547. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
  548. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  549. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
  550. package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
  551. package/.agents/workflows/helpers/deliver-epic.md +0 -998
  552. package/.agents/workflows/helpers/deliver-stories.md +0 -450
  553. package/.agents/workflows/helpers/epic-audit.md +0 -189
  554. package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
  555. package/.agents/workflows/helpers/epic-testing.md +0 -125
  556. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  557. package/.agents/workflows/helpers/plan-epic.md +0 -353
  558. package/.agents/workflows/helpers/plan-story.md +0 -251
  559. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  560. package/.agents/workflows/helpers/single-story-deliver.md +0 -557
@@ -1,10 +1,18 @@
1
1
  # Software Development Life Cycle (SDLC) Workflow
2
2
 
3
- Mandrel uses **Epic-Centric GitHub Orchestration** — GitHub Issues,
4
- Labels, and Projects V2 are the Single Source of Truth, fronted by a
5
- declarative `epic.yaml` artifact that makes plans diff-able and
6
- reconcilable. No per-iteration directories, no JSON state files for
7
- ticket data.
3
+ Mandrel uses **Story-centric GitHub orchestration** — GitHub Issues,
4
+ Labels, and Projects V2 are the Single Source of Truth. Plans persist as
5
+ `type::story` tickets ordered by `depends_on` edges;
6
+ each Story is delivered on its own `story-<id>` branch and reaches `main`
7
+ through its own PR.
8
+
9
+ An Epic may still exist as an **optional untyped human umbrella issue**
10
+ (no `type::epic` label and no shipped Epic issue form — only
11
+ `.github/ISSUE_TEMPLATE/story.yml`), but **delivery and planning
12
+ orchestration are Story-only**: there is no Epic wave loop, no
13
+ `epic/<id>` integration branch, no `epic.yaml` reconciler, and any ticket
14
+ that still carries an `Epic: #N` footer is **refused** by `/deliver`
15
+ (close it or re-plan it as a v2 Story).
8
16
 
9
17
  The framework is **Claude Code-first**: `.claude/`, hooks, skills, and
10
18
  the slash-command surface lean in on Claude Code as the reference
@@ -18,185 +26,143 @@ ADR 20260512-coupling-stance in [`../docs/decisions.md`](../../docs/decisions.md
18
26
 
19
27
  From zero to shipped:
20
28
 
21
- 1. **Plan the work.** Run `/plan` in your agentic IDE. The framework
22
- authors a Tech Spec and an Acceptance Table and folds both into the
23
- Epic body as managed sections (the Epic is the single planning
24
- document — the PRD and context-ticket artifact classes were
25
- retired), decomposes the work into the flat Story backlog under the
26
- Epic, and transitions the Epic to `agent::ready`.
27
-
28
- The entry point you use selects where the run begins:
29
- - With **no arguments** (or `--idea "<seed>"`), the workflow enters the
30
- ideation form of the interrogate step; the Epic Issue itself is opened
31
- by the persist step.
32
- - With **`<epicId>`**, the workflow interrogates against an Epic Issue
33
- you have already opened.
34
-
35
- > **Step note.** `/plan`'s Epic path runs **three steps** (Epic #4474):
36
- > all GitHub reads happen in `plan-context.js`, all GitHub writes in
37
- > `plan-persist.js`, and two HITL gates bracket the authoring middle.
38
-
39
- 1. **Interrogate** — the `idea-refinement` skill (ideation) or the
40
- envelope's clarity/re-plan signals (existing Epic) drive a
41
- question-at-a-time interrogation; `plan-context.js` emits the single
42
- authoring envelope (duplicate candidates, clarity rubric, re-plan
43
- signals, codebase snapshot, system prompts, delivery-shape signal).
44
- The scope-triage verdict, duplicate review, and re-plan / refined-body
45
- decisions fold into **gate #1**, one operator confirm at the step's
46
- exit.
47
- 2. **Author** the `epic-plan-spec-author` skill writes the Tech Spec
48
- (opening `## Delivery Slicing`), the risk verdict (with its
49
- `deliveryShape`), and the Acceptance Table; the
50
- `epic-plan-decompose-author` skill writes `tickets.json` in fan-out
51
- shape (a single-delivery plan authors no tickets). Fresh-context
52
- consolidation and pre-mortem critics run conditionally before the
53
- review.
54
- 3. **Persist** — **gate #2** (risk-routed) shows spec + tickets + risk +
55
- `deliveryShape` in one view; then `plan-persist.js` runs every
56
- deterministic gate in one ordered, fail-closed pass (section gate,
57
- ticket validator, file-assumption gate, DAG, budget, Epic lease,
58
- managed sections, story creation or the `delivery::single` marker,
59
- inline healthcheck) and flips the Epic to `agent::ready` **once**
60
- no intermediate `agent::review-spec` closing with a `plan-summary`
61
- comment that carries the dry-run wave table.
62
-
63
- 2. **Deliver the Epic.** Run `/deliver <epicId>` in your IDE. The
64
- skill drives the merged execute + close flow end-to-end.
65
-
66
- > **Phase numbering note.** The numbered phases below refer to
67
- > `/deliver`'s **internal** phases (1–9), not the SDLC-level
68
- > Phase 0–4 used by the Mermaid diagram in End-to-End
69
- > Process](#end-to-end-process). When prose elsewhere in this
70
- > document says "Phase 7", it always means the internal
71
- > `/deliver` phase unless explicitly prefixed with "SDLC".
72
-
73
- 1. **Phase 1 — prepare** — snapshot the Epic, build the wave DAG,
74
- initialise the `epic-run-state` checkpoint.
75
- 2. **Phase 2 wave loop** fan one `/deliver` Agent-tool
76
- sub-agent out per Story per wave (capped at `concurrencyCap`).
77
- Stories run in parallel inside the operator's Claude session
78
- against your Max subscription quota; no subprocess worker sessions
79
- for Story execution, no GitHub Actions minutes. Deterministic
80
- Node CLIs remain the state-mutation contract.
81
- 3. **Phase 3 — close-validation** — lint, test, and the project's
82
- ratcheted baselines run against the Epic branch. Evidence is
83
- cached by HEAD SHA so re-runs short-circuit.
84
- 4. **Phase 4 — Epic-close lens roster** resolves (does not walk) the
85
- slim Epic-close lens roster: the change-set `gate3` selection plus the
86
- risk-routed lenses, restricted to the **cumulative + global +
87
- risk-routed** tiers via `selectEpicCloseLenses` (every local-tier
88
- change-set lens is excluded already verified shift-left). The roster is
89
- handed to Phase 5.
90
- 5. **Phase 5 code-review** auto-invokes the in-process
91
- `lib/orchestration/code-review.js`; walks the cumulative Epic diff
92
- **once**, executing the Phase 4 lens roster as review dimensions
93
- **alongside** the review pillars (Story #4412 folded the standalone lens
94
- walk into this pass). Findings persist as the single unified
95
- `verification-results` structured comment on the Epic. Critical findings
96
- halt the run.
97
- 6. **Phase 6 — retro** — auto-invokes the in-process
98
- `lib/orchestration/retro-runner.js` (extracted from the old
99
- retro helper) and posts the structured retro comment on the
100
- Epic. The retro fires **before** the PR is opened so it has
101
- full env access in the operator's local session.
102
- 7. **Phase 7 — finalize** — pushes `epic/<epicId>` to `origin`,
103
- opens a pull request to `main`, sets the required-checks
104
- expectation from `github.branchProtection.requiredChecks`, and
105
- posts the hand-off comment naming the PR URL. The Epic stays
106
- at `agent::executing` until the PR merges; the standard
107
- label-transition pathway flips it to `agent::done` on merge.
108
- Finalize hands off to the watch / auto-merge / cleanup tail
109
- below — it does **not** stop the run.
110
- 8. **Phase 8 — watch-and-iterate** — watches CI on the open PR
111
- until checks turn green (or a failure surfaces for human
112
- remediation).
113
- 9. **Phase 8.5 — auto-merge** — arms GitHub native auto-merge
114
- (`gh pr merge --auto --squash --delete-branch`) once the
115
- required checks have passed so the PR lands without a second
116
- operator visit. The operator can disarm auto-merge in the
117
- GitHub UI if they want to gate the merge manually.
118
- 10. **Phase 9 — cleanup** — reaps local Story/Epic branch refs and
119
- worktrees after the PR merges so the workspace returns to a
120
- clean state for the next Epic.
121
-
122
- For a single Epic-attached Story (re-driving a hotfix, resuming after
123
- a halt), re-run `/deliver <epicId>` — the wave loop picks up
124
- incomplete Stories from the dispatch manifest automatically. Standalone
125
- Stories (no `Epic: #N` reference) use `/deliver <storyId>` instead.
126
- Mixed input — several Epics, or Epics plus standalone Stories — is
127
- accepted in one invocation: `/deliver` composes a **sequential segment
128
- plan** (the standalone-Story set as one segment, delivered first, then
129
- each Epic as its own segment in input order) and executes the segments
130
- one at a time through the same two path helpers, never interleaved.
29
+ 1. **Plan the work.** Run [`/plan`](../workflows/plan.md) in your agentic
30
+ IDE. The framework authors **one Story by default** (folded Tech Spec
31
+ in `## Spec`), with N>1 only under the default-single split policy.
32
+
33
+ Three operator modes (the **only** accepted entries):
34
+ - `/plan --seed "<text>"` ideate from chat text.
35
+ - `/plan --seed-file <path>` — author from on-disk notes / a plan seed
36
+ (this is the [`/audit-to-stories`](../workflows/audit-to-stories.md)
37
+ handoff seam via `--emit-plan-seed`).
38
+ - `/plan --tickets 123[,456…]` analyze existing issue(s) into proper
39
+ Stories (prefer an N=1 rewrite).
40
+
41
+ `/plan` is a **single path** — there is no Epic/Story router, no
42
+ scope-triage `epic|story` verdict, and no `deliveryShape`. All GitHub
43
+ reads happen in `plan-context.js`, all writes in `plan-persist.js`, and
44
+ two HITL gates bracket the authoring middle. Duplicate search targets
45
+ open **Stories** (`type::story`), never Epics.
46
+
47
+ 1. **Interrogate** — `plan-context.js` emits the single authoring
48
+ envelope (open-Story duplicate candidates, codebase snapshot, BDD
49
+ probe, risk heuristics, `systemPrompts.story`). Duplicate review
50
+ folds into **gate #1**.
51
+ 2. **Author** write `stories.json` (**one Story by default**) with a
52
+ folded Tech Spec in `## Spec` / `## Slicing`. There is no risk artifact
53
+ to author (Story #4542).
54
+ Binding criteria live in top-level `acceptance[]` / `verify[]`;
55
+ changes/references are `{ path, assumption }` objects. Split into
56
+ N>1 only under the default-single split policy.
57
+ 3. **Persist** **gate #2** (raised only by an explicit `--force-review`)
58
+ then `plan-persist.js` runs every deterministic gate and
59
+ creates Story issue(s) with `type::story` + `agent::ready`, writing
60
+ each authored `depends_on` edge into the sibling body as a
61
+ `blocked by #<id>` footer when N>1.
62
+
63
+ 2. **Deliver the Story.** Run [`/deliver <storyId>`](../workflows/deliver.md)
64
+ (or `/deliver <a> <b> …` for several) in your IDE. `/deliver` takes
65
+ only Story ids and resolves their dependency graph from live state —
66
+ body edges union native GitHub `blocked_by` edges, with every blocker
67
+ checked against its real issue state, so a Story whose blocker landed in
68
+ an earlier plan run is simply ready. `/deliver` owns input resolution and
69
+ `depends_on` sequencing only every Story runs through
70
+ [`helpers/deliver-story`](../workflows/helpers/deliver-story.md), the
71
+ single v2 delivery engine. Per-Story it:
72
+
73
+ 1. **Init** (`single-story-init.js`) — acquires the Story lease, cuts
74
+ `story-<id>` from `main`, materializes a worktree, flips to
75
+ `agent::executing`.
76
+ 2. **Implement** the agent delivers the Story in one guarded session
77
+ against its inline `acceptance[]` / `verify[]` contract (optional
78
+ `## Slicing` intra-session checkpoints).
79
+ 3. **Acceptance self-eval** a bounded critic loop scores the working
80
+ diff against each acceptance item before close (see
81
+ [`helpers/acceptance-self-eval`](../workflows/helpers/acceptance-self-eval.md)).
82
+ 4. **Ceremony** acceptance critic mode and review depth, both routed off
83
+ the change level derived from the Story's own diff
84
+ (`review-depth.js#deriveChangeLevel` `ceremony-routing.js`).
85
+ 5. **Close** (`single-story-close.js`) runs close-validation gates,
86
+ the maker-blind Story-scope code review, pushes `story-<id>`, opens
87
+ a PR to `main`, and (under the default `delivery.ci.autoMerge:
88
+ "trust-ci"`) arms GitHub native auto-merge. The Story flips to
89
+ `agent::closing` (issue stays OPEN).
90
+ 6. **CI watch + fix** watches required checks to green, fixing and
91
+ re-pushing on red.
92
+ 7. **Confirm merge** (`single-story-confirm-merge.js`)on a confirmed
93
+ `MERGED` PR the Story flips to `agent::done`; local branch cleanup
94
+ and Projects-v2 Status re-assert run out-of-band.
95
+
96
+ For a multi-Story run, `/deliver` sequences ready Stories by
97
+ `depends_on` and runs the per-run epilogue (audit roster · follow-up
98
+ roll-up · sibling coherence) once after the last Story lands.
131
99
 
132
100
  That is the whole happy path. Everything below is **detail** — branching
133
- conventions, HITL escalation, audit gates — that you only need when the
134
- default flow requires adjustment.
101
+ conventions, HITL escalation, audit lenses — that you only need when the
102
+ default flow requires adjustment. It intentionally **links** to
103
+ [`plan.md`](../workflows/plan.md) and [`deliver.md`](../workflows/deliver.md)
104
+ rather than re-documenting the ceremony they own.
135
105
 
136
106
  ---
137
107
 
138
108
  ## Core Principles
139
109
 
140
- - **Layered state stores with explicit precedence.** Project logic, work
141
- breakdown, and ticket status live in GitHub Issues and Labels; the
142
- lifecycle bus (`temp/epic-<id>/lifecycle.ndjson`) is the canonical
143
- resume target for in-flight runs; structured comments
144
- (`epic-run-state`, `story-run-progress`) are the operator-visible
145
- rollup. The seven stores, their owners, and their conflict-resolution
146
- rules are listed in [§ State stores](#state-stores) — that matrix is
147
- the single source of truth for "who owns which write" and supersedes
148
- the earlier "GitHub as SSOT" / "lifecycle ledger canonical" prose.
149
- - **Provider Abstraction.** Orchestration flows through `ITicketingProvider`, an
150
- abstract interface with a shipped GitHub implementation.
151
- - **Story-Level Branching.** All work for a Story lands on the shared
152
- `story-<id>` branch. Stories merge into `epic/<epicId>`; the Epic
153
- branch reaches `main` only via a pull request the operator merges
154
- through the GitHub UI.
155
- - **Hierarchy-aligned skills.** Execution is split along the ticket
156
- hierarchy: `/plan` builds the backlog (with optional ideation
157
- entry), `/deliver` owns the merged wave-loop + close-tail, and
158
- `/deliver` delivers one or more standalone Stories end-to-end.
159
- `helpers/epic-deliver-story` and `helpers/single-story-deliver` are the
160
- per-Story workers called by those two commands respectively. All share
161
- the same primitives (`Graph.computeWaves`, `cascadeCompletion`,
162
- `ticketing.js`, `WorktreeManager`).
163
- - **Single-session fan-out.** `/deliver` launches Story sub-agents via
164
- the Agent tool — every Story runs inside the operator's Claude session,
165
- with no subprocess boundary. Worktree filesystem isolation is preserved;
166
- only the process boundary is gone.
167
- - **PR is the sole promotion gate.** `/deliver` ends with a PR open
168
- against `main` and (by default) GitHub native auto-merge armed; the
169
- workflow itself never executes `git merge` against `main`. Branch
170
- protection on `main` enforces required-checks before the merge button
171
- (auto or manual) fires. The operator can disarm auto-merge in the
172
- GitHub UI to make the merge an explicit human action.
173
- - **HITL-minimal by default.** Exactly one mandatory operator touchpoint on
174
- the happy path — blocker resolution mid-run. PR merge is autonomous via
175
- the armed auto-merge; the operator becomes a second touchpoint only when
176
- they disarm auto-merge or when required checks fail and need
177
- remediation.
110
+ - **Layered state stores with explicit precedence.** Ticket status lives
111
+ in GitHub Issues and Labels; the lifecycle bus
112
+ (`temp/run-<id>/lifecycle.ndjson`) is the canonical resume target for
113
+ in-flight runs; structured comments (`verification-results`, retro) are
114
+ the operator-visible rollup. The
115
+ stores, their owners, and their conflict-resolution rules are listed in
116
+ [§ State stores](#state-stores) — that matrix is the single source of
117
+ truth for "who owns which write."
118
+ - **Provider Abstraction.** Orchestration flows through
119
+ `ITicketingProvider`, an abstract interface with a shipped GitHub
120
+ implementation.
121
+ - **Story-level branching.** All work for a Story lands on the shared
122
+ `story-<id>` branch. Each Story reaches `main` through its own PR
123
+ (squash + required checks); there is **no** `epic/<id>` integration
124
+ branch and **no** `--no-ff` wave merge.
125
+ - **One delivery engine.** `/deliver` resolves and sequences a Story set;
126
+ `helpers/deliver-story` executes each Story identically (trivial or
127
+ large). Story sub-agents run inside the operator's Claude session via
128
+ the Agent tool worktree filesystem isolation is preserved; only the
129
+ subprocess boundary is gone.
130
+ - **PR is the sole promotion gate.** Delivery ends with a PR open against
131
+ `main` and (by default) GitHub native auto-merge armed; the workflow
132
+ itself never executes `git merge` against `main`. Branch protection on
133
+ `main` enforces required checks before the merge button (auto or
134
+ manual) fires.
135
+ - **HITL-minimal by default.** Exactly one mandatory operator touchpoint
136
+ on the happy path — blocker resolution mid-run. PR merge is autonomous
137
+ via the armed auto-merge; the operator becomes a second touchpoint only
138
+ when they disarm auto-merge (`--no-auto-merge` / `delivery.ci.autoMerge:
139
+ "strict"`) or when required checks fail and need remediation.
178
140
 
179
141
  ---
180
142
 
181
143
  ## State stores
182
144
 
183
- Mandrel writes orchestration state across seven distinct stores. Each
184
- store has exactly one canonical writer and one well-defined idempotency
185
- key; conflicts between stores are resolved in the **Conflict
186
- resolution** column. When the same fact appears in more than one store
187
- (common during a run labels mirror lifecycle events, comments mirror
188
- ledger entries) the entry below names the authoritative reader for that
189
- fact so downstream code does not have to guess.
190
-
191
- | State Store | Owner (canonical writer) | Mutation API | Idempotency key | Conflict resolution |
192
- | --------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
193
- | GitHub labels | `transitionTicketState` via `ticketing.js` | `gh issue edit --add-label / --remove-label`, wrapped in `update-ticket-state.js` | `(ticketId, label-set)` — set-equality check before write | Authoritative for current ticket lifecycle state; if the label disagrees with the lifecycle ledger, the **ledger wins on resume** and the label is re-derived. |
194
- | `epic-run-state` comment | `checkpointer` submodule in the Epic Deliver Runner | `post-structured-comment.js` (upsert by `kind`) | `(epicId, kind='epic-run-state')` | Operator-visible rollup of phase progress; on conflict with the lifecycle ledger, the ledger is authoritative and the comment is re-rendered. |
195
- | `story-run-progress` comment | `story-phase.js` (per Story, per phase transition) | `post-structured-comment.js` (upsert by `kind`) | `(storyId, kind='story-run-progress')` | Authoritative for Story-level phase progress; the wave aggregator reads this comment, not labels. |
196
- | Lifecycle ledger NDJSON | `lifecycle-emit.js` (single append-only writer per Epic run) | Append-only line write to `temp/epic-<id>/lifecycle.ndjson` | `(epicId, eventId)` — `eventId` is a content hash of `{type, ts, payload}` | **Canonical resume target.** When labels / comments disagree with the ledger, the ledger wins and the others are re-derived from it. |
197
- | Validation evidence cache | `evidence-gate.js` | JSON cache file under `temp/epic-<id>/evidence/<gate>/<sha>.json` | `(gate, git rev-parse HEAD)` | Pure cache: a missing entry triggers a re-run; presence is a fast-path skip. Cache eviction is safe. |
198
- | PR / auto-merge state | `AutomergeArmer` listener (sole authorized caller of `gh pr merge`) | `gh pr merge --auto --squash --delete-branch`; PR open via `openOrLocatePr` in `Finalizer` | `(prNumber, head-branch SHA)` — `gh pr view` probes existing PR before create | GitHub is authoritative for PR + auto-merge arming state; the lifecycle ledger records the *intent* to arm, GitHub records the outcome. |
199
- | Worktree cleanup state | `WorktreeManager.reap` (via `story-close.js` / cleanup state) | `git worktree remove` + on-disk pending-cleanup JSON under `temp/epic-<id>/worktree-cleanup.json` | `(storyId, worktree-path)` | Filesystem is authoritative for "is the worktree gone?"; the pending-cleanup JSON only tracks Windows stale-registry entries that need a follow-up sweep. |
145
+ Mandrel writes orchestration state across several distinct stores. Each
146
+ store has one canonical writer and one well-defined idempotency key;
147
+ conflicts are resolved in the **Conflict resolution** column. Run-scoped
148
+ artifacts live under `temp/run-<id>/` (standalone Stories under
149
+ `temp/standalone/stories/story-<id>/`); the `run-<id>` directory naming is
150
+ historical (it predates the Story-centric cutover) but remains the live
151
+ on-disk layout resolved by
152
+ [`lib/config/temp-paths.js`](../scripts/lib/config/temp-paths.js).
153
+
154
+ | State Store | Owner (canonical writer) | Mutation API | Idempotency key | Conflict resolution |
155
+ | --- | --- | --- | --- | --- |
156
+ | GitHub labels | `transitionTicketState` via `ticketing.js` | `gh issue edit --add-label / --remove-label`, wrapped in `update-ticket-state.js` | `(ticketId, label-set)` — set-equality before write | Authoritative for current ticket lifecycle state; if a label disagrees with the lifecycle ledger, the **ledger wins on resume** and the label is re-derived. |
157
+ | `verification-results` comment | `lib/orchestration/code-review.js` | `post-structured-comment.js` (upsert by `kind`) | `(storyId, kind='verification-results')` | Authoritative for the Story-scope review + lens findings; critical findings block close. |
158
+ | Lifecycle ledger NDJSON | `LedgerWriter` (`lib/orchestration/lifecycle/ledger-writer.js`, registered as the first listener on every bus event — single append-only writer per run) | Append-only line write to `temp/run-<id>/lifecycle.ndjson` | `(runId, eventId)` — `eventId` is a content hash of `{type, ts, payload}` | **Canonical resume target.** When labels / comments disagree with the ledger, the ledger wins and the others are re-derived. |
159
+ | Validation evidence cache | `evidence-gate.js` | JSON cache file under the run temp tree, keyed by HEAD SHA | `(gate, git rev-parse HEAD)` | Pure cache: a missing entry triggers a re-run; presence is a fast-path skip. Cache eviction is safe. |
160
+ | PR / auto-merge state | `single-story-close.js` (sole authorized caller of `gh pr merge`) | `gh pr merge --auto --squash --delete-branch`; PR open via the close pipeline's `gh pr create` | `(prNumber, head-branch SHA)` — `gh pr list --head` probes before create | GitHub is authoritative for PR + auto-merge arming state; the ledger records the *intent* to arm, GitHub records the outcome. |
161
+ | Worktree cleanup state | `WorktreeManager.reap` (via `single-story-close.js` / `git-cleanup.js`) | `git worktree remove` + on-disk pending-cleanup JSON under the run temp tree | `(storyId, worktree-path)` | Filesystem is authoritative for "is the worktree gone?"; the pending-cleanup JSON only tracks stale-registry entries needing a follow-up sweep. |
162
+
163
+ > The `gh pr merge` merge-lockout lint rule keeps the merge command
164
+ > confined to the sanctioned close path; no other production caller may
165
+ > shell it.
200
166
 
201
167
  ---
202
168
 
@@ -210,48 +176,42 @@ graph LR
210
176
 
211
177
  subgraph Phase0 ["Phase 0: Bootstrap"]
212
178
  direction TB
213
- Z["👤 npx mandrel init<br/>(install → sync → prompt → bootstrap.js → onboarding tail → /plan handoff)"]:::manual
179
+ Z["👤 npx mandrel init<br/>(install → sync → bootstrap.js → onboarding tail → /plan handoff)"]:::manual
214
180
  end
215
181
 
216
- subgraph Phase1 ["Phase 1: Initiation"]
182
+ subgraph Phase1 ["Phase 1: Plan"]
217
183
  direction TB
218
- A["👤 /plan (ideation)<br/>or 👤 Create GitHub Epic + /plan &lt;id&gt;"]:::manual
184
+ A["👤 /plan --seed | --seed-file | --tickets"]:::manual
185
+ B["🤖 interrogate → author → persist"]:::agentic
186
+ A --> B
187
+ B -.-> B_Art["📄 type::story issue(s)<br/>(+ depends_on edges)"]:::artifact
219
188
  end
220
189
 
221
- subgraph Phase2 ["Phase 2: Planning"]
190
+ subgraph Phase2 ["Phase 2: Deliver"]
222
191
  direction TB
223
- C["🤖 Tech Spec authoring"]:::agentic
224
- D["🤖 Ticket Decomposer"]:::agentic
225
- C --> D
226
- D -.-> D_Art["📄 GitHub Issue Hierarchy"]:::artifact
227
- end
228
-
229
- subgraph Phase3 ["Phase 3: Delivery"]
230
- direction TB
231
- E["👤 /deliver &lt;epicId&gt;"]:::manual
232
- F["🤖 wave loop (one /deliver sub-agent per Story per wave)"]:::agentic
233
- G["🤖 close-validation → code-review → retro → open PR"]:::agentic
192
+ E["👤 /deliver &lt;storyId&gt; [&lt;storyId&gt;…]"]:::manual
193
+ F["🤖 deliver-story: story-&lt;id&gt; from main<br/>implement → self-eval → ceremony → close"]:::agentic
194
+ G["🤖 close-validation → code-review → open PR"]:::agentic
234
195
  E --> F --> G
235
196
  G -.-> G_Art["📄 PR open against main"]:::artifact
236
197
  end
237
198
 
238
- subgraph Phase4 ["Phase 4: PR merge (auto by default)"]
199
+ subgraph Phase3 ["Phase 3: PR merge (auto by default)"]
239
200
  direction TB
240
201
  H["🤖 Auto-merge armed → PR lands when checks pass<br/>(👤 operator may disarm to merge manually)"]:::agentic
241
202
  end
242
203
 
243
204
  Z --> A
244
- A --> C
245
- D --> E
205
+ B --> E
246
206
  G --> H
247
207
  ```
248
208
 
249
209
  ---
250
210
 
251
- ## Phase 0: Bootstrap (One-Time Setup)
211
+ ## Phase 0: Bootstrap (one-time setup)
252
212
 
253
- Before any Epic workflow, bootstrap your project to seed `.agentrc.json`,
254
- wire the framework system prompt, and create the GitHub labels, Projects V2
213
+ Before any workflow, bootstrap your project to seed `.agentrc.json`, wire
214
+ the framework system prompt, and create the GitHub labels, Projects V2
255
215
  fields, and (when enabled) main-branch protection the orchestration engine
256
216
  depends on.
257
217
 
@@ -261,916 +221,256 @@ The canonical cold-start path is a single command:
261
221
  npx mandrel init
262
222
  ```
263
223
 
264
- `mandrel init` installs `mandrel` (when `./.agents/` is absent), materializes
265
- `./.agents/` via `mandrel sync`, then presents a two-option prompt: **configure
266
- now** (option 1 → runs `node .agents/scripts/bootstrap.js`, forwarding any flags
267
- you pass) or **just the files** (option 2 → re-run `mandrel init` any time to
268
- configure later). `--assume-yes` skips the prompt and proceeds straight to
269
- configure (and is forwarded to bootstrap); a non-TTY run without it defaults to
270
- files-only so the GitHub provisioning never runs unattended. `bootstrap.js`:
271
-
272
- 1. **Provisions a cold start.** Initializes the local git repo (with a first
273
- commit) when absent, creates the GitHub repo (`gh repo create --source=.
274
- --push`; choose visibility with `--visibility private|public|internal`,
275
- default `private`), and creates the Projects V2 board (`gh project
276
- create`) when it doesn't exist. No pre-created repo or remote is required.
277
- 2. **Seeds `.agentrc.json`** from `.agents/starter-agentrc.json` (the `github`
278
- section carries owner, repo, base branch, operator handle, and project
279
- number inferred from your local `git` config where possible). See
280
- `.agents/docs/agentrc-reference.json` for the exhaustive reference of every
281
- available key.
224
+ `mandrel init` installs `mandrel` (when `./.agents/` is absent),
225
+ materializes `./.agents/` via `mandrel sync`, then presents a two-option
226
+ prompt: **configure now** (option 1 → runs `node
227
+ .agents/scripts/bootstrap.js`, forwarding any flags you pass) or **just
228
+ the files** (option 2 re-run `mandrel init` any time to configure
229
+ later). `--assume-yes` skips the prompt and proceeds straight to configure;
230
+ a non-TTY run without it defaults to files-only so GitHub provisioning
231
+ never runs unattended. `bootstrap.js`:
232
+
233
+ 1. **Provisions a cold start.** Initializes the local git repo (with a
234
+ first commit) when absent, creates the GitHub repo (`gh repo create
235
+ --source=. --push`; choose visibility with `--visibility
236
+ private|public|internal`, default `private`), and creates the Projects
237
+ V2 board (`gh project create`) when it doesn't exist. No pre-created
238
+ repo or remote is required.
239
+ 2. **Seeds `.agentrc.json`** from `.agents/starter-agentrc.json` (the
240
+ `github` section carries owner, repo, base branch, operator handle, and
241
+ project number — inferred from your local `git` config where possible).
242
+ See `.agents/docs/agentrc-reference.json` for the exhaustive key
243
+ reference.
282
244
  3. **Creates the label taxonomy and Projects V2 fields**, and — when
283
- `github.branchProtection.enforce` is `true` (default) — creates or merges
284
- branch protection on `main` with the project's
285
- `github.branchProtection.requiredChecks` as required status checks. This
286
- step is load-bearing for the SDL because PR merges to `main` are the sole
245
+ `github.branchProtection.enforce` is `true` (default) — creates or
246
+ merges branch protection on `main` with the project's
247
+ `github.branchProtection.requiredChecks` as required status checks.
248
+ This step is load-bearing because PR merges to `main` are the sole
287
249
  promotion gate.
288
250
 
289
251
  When `.agents/` is already materialized you can run the bootstrap directly
290
252
  (`node .agents/scripts/bootstrap.js`). The guided first-run steps (stack
291
253
  detection, docs scaffolding, `mandrel doctor` readiness gate, and `/plan`
292
- handoff) are now part of `mandrel init`'s configure path — run
293
- `mandrel init` again to pick them up if you bootstrapped via the script
294
- directly.
254
+ handoff) are part of `mandrel init`'s configure path.
295
255
 
296
- > [!NOTE] Bootstrap runs once per repository. It is safe to re-run — existing
297
- > labels, fields, and branch-protection entries are preserved; missing ones
298
- > are added.
256
+ > [!NOTE] Bootstrap runs once per repository. It is safe to re-run —
257
+ > existing labels, fields, and branch-protection entries are preserved;
258
+ > missing ones are added.
299
259
 
300
260
  ---
301
261
 
302
- ## Phase 1: Initiation
303
-
304
- The product lead defines the objective and triggers planning.
305
-
306
- ### 1a. Ideation entry (optional)
307
-
308
- Run `/plan` with no arguments (or `--idea "<seed>"`) to enter ideation
309
- mode:
310
-
311
- 1. **Sharpen the idea.** The `idea-refinement` skill drives a divergent
312
- convergent sharpen loop and emits a markdown one-pager with the
313
- five canonical Epic sections (Context, Goal, Non-Goals, Scope,
314
- Acceptance Criteria).
315
- 2. **Scope triage.** Before the ceremony is paid for, the
316
- one-pager is judged against the story-vs-epic rubric (see the
317
- subsection below). On a `story` / `borderline` verdict the operator may
318
- route the work to `/plan` instead of opening an Epic.
319
- 3. **Cross-Epic duplicate search.** `lib/duplicate-search.js` queries the
320
- open Epics in the repo, scores by title + body keyword overlap, and
321
- surfaces matches above a threshold. The operator either confirms the
322
- new idea is genuinely distinct or folds it into an existing Epic
323
- (`/plan` exits and the operator resumes work on the existing
324
- id).
325
- 4. **Confirm at gate #1.** The one-pager, the triage verdict, and the
326
- duplicate review fold into one operator confirmation at the exit of
327
- the interrogate step.
328
- 5. **Open the Epic (persist step).** `plan-persist.js` renders the
329
- one-pager into the canonical Epic-from-idea template and opens the
330
- Issue with **only** the `type::epic` label — no `state::*` label at
331
- creation; the same persist pass folds in the authored sections and
332
- flips the Epic straight to `agent::ready`.
333
-
334
- #### Scope triage
335
-
336
- The `/plan` Epic path's interrogate step runs the
337
- [`core/scope-triage`](../skills/core/scope-triage/SKILL.md) rubric over the
338
- sharpened one-pager so a story-sized scope is not pushed through the full Epic
339
- ceremony (Tech Spec + Acceptance Spec + Story backlog +
340
- `epic/<id>` integration branch) only to land as a degenerate one-Story
341
- output. The rubric anchors its sizing judgment **by reference** to
342
- the existing sizing SSOT (`DELIVERABLE_GRANULARITY_GUIDANCE` /
343
- `DEFAULT_TASK_SIZING` in `ticket-validator-sizing.js`) and emits one of three
344
- verdicts — `epic` | `story` | `borderline`.
345
-
346
- The verdict is **host-LLM judgment** (no scorer, no schema, no label
347
- transition) and **advisory** — the operator always decides. It folds into the
348
- existing gate #1 confirmation rather than adding a second stop: an `epic`
349
- verdict proceeds with a plain confirm, while a `story` / `borderline` verdict
350
- offers a three-way choice (single Story / plan as Epic anyway / abort). On an
351
- accepted `story`, `/plan` hands the one-pager off to
352
- `/plan --from-notes` as a scope-triage handoff and exits. The ideation triage is
353
- skipped when `/plan` is itself entered via a scope-triage handoff, so the
354
- two workflows never ping-pong a settled decision.
355
-
356
- The same rubric also guards the **existing-Epic entry** (1b) as the
357
- **story-sized advisory**, which catches a story-sized scope that was
358
- hand-opened directly as a `type::epic` issue (the Epic Clarity Gate rubric
359
- scores section *presence*, not scope *size*, so a clear-but-thin Epic would
360
- otherwise sail through). The advisory fires **only** when the envelope's
361
- re-plan signal found no folded Tech Spec sections **and** the Epic has no
362
- open Story children, so
363
- it never re-triages an Epic that is being re-planned. An `epic` verdict
364
- proceeds silently; a `story` / `borderline` verdict STOPs with the same
365
- three-way choice (convert to a standalone Story / proceed as Epic anyway /
366
- abort). Converting is **close-and-recreate** — a `type::epic` body cannot
367
- satisfy `validateStoryBody`, and editing the issue in place would violate the
368
- "do not modify existing issues without explicit permission" rule — so, only
369
- after the operator confirms, the Epic body seeds a notes file,
370
- `/plan --from-notes` opens a replacement Story (identified as a
371
- scope-triage handoff so it skips its own gate, with a `## Notes` back-link to
372
- the Epic), and the Epic is closed with `gh issue close --comment` cross-linking
373
- the replacement. No deterministic scorer, no schema, and no label transition
374
- sit behind either gate.
375
-
376
- The rubric also runs in the **escalation direction** — the symmetric
377
- counterpart in [`/plan`](../workflows/helpers/plan-story.md). After `/plan`
378
- Phase 2 drafts a standalone Story body (the draft, not the seed, is the honest
379
- basis for the judgment), the same `core/scope-triage` rubric judges whether the
380
- scope is actually Epic-sized. The verdict folds into the existing Phase 2
381
- draft-confirmation HITL stop with no extra stop on a `story` verdict; an `epic`
382
- verdict offers a three-way choice (escalate to `/plan --idea` as a
383
- scope-triage handoff / persist as a standalone Story anyway / abort). On an
384
- accepted escalation, `/plan` abandons the draft and hands the notes off to
385
- `/plan --idea`, marked as a handoff so `/plan` skips its own ideation
386
- triage gate. This gate is itself skipped when `/plan` was entered via a
387
- scope-triage handoff (from the Epic path's ideation triage or its
388
- story-sized conversion path), so the two workflows never ping-pong a settled decision. As with the
389
- inbound gates, the verdict is advisory and host-LLM judgment — no auto-routing,
390
- no scorer, no schema, and no label transition.
391
-
392
- ### 1b. Existing-Epic entry
393
-
394
- Run `/plan <epicId>` directly when the Epic Issue already exists. The
395
- ideation phases (1a) are skipped.
396
-
397
- In both modes the planning flow continues into Phase 2 with the captured
398
- Epic id.
262
+ ## Phase 1: Planning
263
+
264
+ Planning is owned end-to-end by [`/plan`](../workflows/plan.md). Rather than
265
+ re-document the ceremony here, this section states the contract the rest of
266
+ the SDLC depends on:
267
+
268
+ - **Entry is text or tickets, never Epic.** The only accepted invocations
269
+ are `--seed`, `--seed-file`, and `--tickets`. There is no `--idea`, no
270
+ `--one-pager`, no `--from-notes`, and no positional `/plan <epicId>`.
271
+ - **One Story by default.** `/plan` authors a single `type::story` issue
272
+ whose body carries a folded `## Spec` (inline only never spilled to
273
+ `docs/`) plus top-level `acceptance[]` / `verify[]`. It splits into N>1
274
+ siblings (ordered by `depends_on` edges) **only**
275
+ under the default-single split policy: near-zero overlap or a genuine
276
+ architectural seam. Coupled work stays one Story and is decomposed inside
277
+ `## Slicing` as intra-session checkpoints, not sibling tickets.
278
+ - **No Epic-scale ceremony on the default path.** N=1 skips the
279
+ Epic-era Tech Spec / Acceptance Table / clarity-gate / decompose /
280
+ reconciler machinery. `plan-persist.js` runs the deterministic gates
281
+ (ticket validator, split policy, reachability, budget) and for N>1 —
282
+ `assertAcceptancePartition` so every acceptance criterion belongs to
283
+ exactly one Story.
284
+ - **Handoff.** Persist creates the Story issue(s) at `agent::ready` and
285
+ names the delivery command: `/deliver <storyId> [<storyId> ...]`.
286
+
287
+ Optional split advisory notes come from
288
+ [`core/scope-triage`](../skills/core/scope-triage/SKILL.md); there is no
289
+ `epic|story` routing verdict, scorer, schema field, or label transition
290
+ behind them.
291
+
292
+ Audit findings enter planning through
293
+ [`/audit-to-stories`](../workflows/audit-to-stories.md), which groups and
294
+ deduplicates findings and hands off via `--emit-plan-seed` →
295
+ `/plan --seed-file <path>`.
399
296
 
400
297
  ---
401
298
 
402
- ## Phase 2: Planning (Autonomous)
403
-
404
- The framework reads the Epic and autonomously builds the entire work breakdown.
405
-
406
- > **Epic Clarity Gate (`/plan` `planning.clarity-gate` state).** Before Tech Spec /
407
- > Acceptance Spec authoring kicks off, `/plan` scores the Epic body
408
- > against the five canonical sections from
409
- > [`templates/epic-from-idea.md`](../templates/epic-from-idea.md) (Context,
410
- > Goal, Non-Goals, Scope, Acceptance Criteria). Common legacy heading
411
- > variants (`Problem`, `Direction`, `MVP Scope`, `Not Doing`,
412
- > `Out of Scope`) are accepted by the scorer's regex for back-compat.
413
- > The rubric is deterministic (section-presence): `clear` requires ≥ 4 of 5
414
- > sections present **and** the Acceptance Criteria section present (AC is
415
- > required). A
416
- > `clear` verdict
417
- > skips fast with no prompt; a `needs-refinement` verdict drops into the
418
- > `idea-refinement` skill seeded from the current Epic body, surfaces a
419
- > HITL diff, and on approval persists the sharpened body via
420
- > `gh issue edit` before the `planning.spec-authoring` state begins.
421
- > The gate honours the
422
- > "do not modify existing issues without permission" Constraint — every
423
- > body rewrite is operator-confirmed.
424
-
425
- 1. **Epic Planner** (the spec half of `plan-persist.js`):
426
- - Synthesizes the Epic body with project documentation.
427
- - Folds the authored **Tech Spec** (opening with `## Delivery
428
- Slicing`) and the **Acceptance Table** (the AC-ID table) into
429
- marker-delimited managed sections of the Epic body. The Epic body
430
- carries its `## User Stories` section inline — the PRD artifact
431
- class was retired (Story #4314), and the `context::tech-spec` /
432
- `context::acceptance-spec` ticket classes were retired the same way
433
- (Story #4324): a `/plan` Epic run creates exactly **one** GitHub
434
- issue.
435
-
436
- > [!TIP] **Acceptance criteria phrasing.** Write the Epic's acceptance
437
- > criteria in Gherkin-compatible `Given / When / Then` form so the QA
438
- > acceptance suite can lift them directly into executable `.feature` files. See
439
- > [`rules/gherkin-standards.md`](../rules/gherkin-standards.md) for the canonical
440
- > clause grammar, tag taxonomy, and forbidden patterns.
441
-
442
- ### Acceptance Table — the second folded planning section
443
-
444
- Every planned Epic body carries **two** managed planning sections
445
- (Story #4324 — no separate context tickets):
446
-
447
- | Section | Artifact | Authored by | Drives |
448
- | ---------------------- | ---------------- | --------------------------------------------------- | ----------------------------------------------------- |
449
- | `## Delivery Slicing`… | Tech Spec | `epic-plan-spec-author` skill (Architect persona) | How we're shipping it. |
450
- | `## Acceptance Table` | Acceptance Table | `epic-plan-spec-author` skill (Acceptance Engineer) | The AC ID table that gates close-time reconciliation. |
451
-
452
- The Acceptance Table section is a single Markdown table —
453
- `| AC ID | Outcome | Feature File | Scenario | Disposition |` — with
454
- stable `AC-<n>` IDs assigned in document order. IDs are reused across
455
- re-plans when an Outcome is materially unchanged so scenario tags
456
- (`@ac-N`) stay aligned with the spec. Each row's `Disposition` is one
457
- of `new | updated | unchanged`. The skill also renders a **Runner
458
- Verification** line directly under the table that records the verified
459
- BDD runner + pending-tag (e.g. `playwright-bdd supports @skip`) for the
460
- features-first Story to consume.
461
-
462
- The spec is persisted by
463
- `plan-persist.js --epic [Epic_ID] --tech-spec ... --acceptance-table ...`
464
- — the persist step folds both artifacts into the Epic body's managed
465
- sections in one atomic, section-scoped write (everything outside the
466
- managed regions is byte-preserved) and fails loudly if any input is
467
- missing or empty. At delivery time, hydration strips the
468
- `## Acceptance Table` section from story prompts — it is
469
- authoring/close-time machinery, not delivery context.
470
-
471
- #### Adaptive planning risk routing
472
-
473
- `/plan`'s `planning.spec-authoring` state derives a deterministic
474
- **`planningRisk`** envelope from a **planner-authored risk verdict**
475
- (`risk-verdict.json`, the third planning artifact the
476
- `epic-plan-spec-author` Skill writes from the Epic body / Tech Spec it just
477
- authored). `plan-persist.js` validates the verdict
478
- against `risk-verdict.schema.json` — a malformed verdict fails closed —
479
- then derives the envelope via `deriveRiskEnvelope`
480
- (`lib/orchestration/planning-risk.js`). The verdict is recorded as a
481
- `risk-verdict` structured comment on the Epic, and both the verdict and
482
- the envelope land in the `epic-plan-state` checkpoint, consumed by two
483
- downstream decisions:
484
-
485
- - **Acceptance disposition** — `acceptanceDisposition` is one of
486
- `required`, `recommended`, or `not-applicable`. The `not-applicable`
487
- case is the planner-selected route to the `acceptance::n-a` waiver
488
- (see the section below); the other two cause the Acceptance Spec to
489
- be authored normally.
490
- - **Gate routing** — `gateDecision` is either `review-required`
491
- (paired with `requiresReview: true`) or `auto-proceed`. High-risk
492
- Epics (visible behavior, public API, security, billing, data
493
- migration, destructive mutation, critical workflow) trigger a HITL
494
- stop after `planning.spec-authoring` so the operator can read the
495
- Epic body's Tech Spec / Acceptance Table sections on GitHub before
496
- decomposition starts. Low-risk Epics (docs-only, internal refactor, pure test
497
- harness, cleanup) print the auto-proceed message from
498
- `reviewRouting.operatorMessage` and chain directly into the
499
- `planning.decompose` state. The operator can force the review
500
- stop on low-risk work by passing `--force-review` to `/plan`.
501
-
502
- The risk envelope is also threaded into the `planning.decompose` state's decomposer context
503
- so the ticket array can cite the relevant axes when assigning
504
- `risk::high` labels to Stories. The split is "judgment proposes, harness
505
- gates": the planner supplies the axes (with per-axis rationale), and the
506
- envelope derivation — overall level, review requirement, acceptance
507
- disposition, gate decision — is local and deterministic.
508
-
509
- #### Opting out — the `acceptance::n-a` waiver
510
-
511
- Not every Epic warrants a formal Acceptance Spec (pure refactors,
512
- framework maintenance, docs-only churn). The **`acceptance::n-a`** label
513
- on the Epic ticket records the waiver. There are two routes to the label:
514
-
515
- - **Operator-applied** — the operator labels the Epic before or during
516
- `/plan`'s `planning.spec-authoring` state when they already
517
- know the work does not need a spec.
518
- - **Planner-selected** — `/plan`'s `planning.spec-authoring` state derives a
519
- `planningRisk` envelope from the planner-authored risk verdict
520
- (see § Adaptive planning risk routing) and,
521
- when `acceptanceDisposition === 'not-applicable'`, the persist step
522
- of `plan-persist.js` applies `acceptance::n-a` on the Epic and skips
523
- the Acceptance Table section for that run (stripping a stale one on a
524
- re-plan). The disposition is also
525
- recorded in the `epic-plan-state` checkpoint so the decision is
526
- auditable.
527
-
528
- Either route produces the same runtime behavior. The waiver is respected
529
- by both runtime gates:
530
-
531
- - The `/deliver` **start gate** (`delivery.snapshot` state) skips
532
- the acceptance-spec presence check when the label is set.
533
- - The finalize-time **acceptance reconciler** returns
534
- `status: 'waived'` without scanning `tests/features/**` and the
535
- finalize step proceeds. (The waiver now waives the Epic body's
536
- `## Acceptance Table` section — Story #4324 — with unchanged
537
- meaning.)
538
-
539
- The waiver is binary — there is no partial opt-out. If an Epic later
540
- warrants spec coverage, remove the label and run `/plan`'s
541
- `planning.spec-authoring` state to author the spec.
542
-
543
- 1. **Ticket Decomposer** (the fan-out half of `plan-persist.js`):
544
- - Decomposes specs into the **2-tier hierarchy**
545
- (Epic → Story):
546
-
547
- ```text
548
- Epic (type::epic) ← body carries the folded Tech Spec
549
- │ sections + ## Acceptance Table
550
- ├── Story (type::story)
551
- │ ├── acceptance[] ← inline on Story body
552
- │ └── verify[] ← inline on Story body
553
- └── Story (type::story)
554
- ```
555
-
556
- - **Wiring.** Each ticket is linked using `blocked by #NNN` syntax and
557
- GitHub's native sub-issues API.
558
- - **Metadata.** Each Story is stamped with persona, estimated files,
559
- and agent prompts, plus the inline `acceptance[]` / `verify[]`
560
- arrays the executing sub-agent reads.
561
-
562
- `/deliver` runs a **single** Story-implementation phase per
563
- Story. The wave-loop fan-out in `/deliver` and the
564
- Story-branch → Epic-branch merge model are unchanged; the Feature and
565
- Task layers are gone, and thematic grouping lives as prose in the Epic
566
- body (which also carries the folded Tech Spec sections).
567
-
568
- When the persist step completes the Epic flips to `agent::ready` and the
569
- `plan-summary` structured comment (with the dry-run wave table) lands on
570
- the Epic. The live wave manifest is written at deliver time by the
571
- prepare phase — planning posts no separate dispatch-manifest comment
572
- (Epic #4474).
573
-
574
- ### `agent::ready` exit conditions
575
-
576
- The planning → delivery handoff is governed by an explicit checklist.
577
- `plan-persist.js` refuses to flip the Epic to `agent::ready`
578
- unless **every** condition below is true. The
579
- contract is enforced at the planner boundary so `/deliver` can
580
- treat `agent::ready` as a load-bearing precondition rather than a
581
- hopeful signal.
582
-
583
- - **Planning sections present or waived.** The Epic body carries the
584
- folded Tech Spec sections (`## Delivery Slicing` onward), and either
585
- the `## Acceptance Table` managed section **or** the
586
- `acceptance::n-a` waiver label. Missing-without-waiver fails the
587
- handoff.
588
- - **Decomposition persisted.** The structural reconciler has applied
589
- the Epic's child-Story backlog and written the spec to
590
- `.agents/epics/<epicId>.yaml`. The `epic-plan-state` checkpoint
591
- comment records `phase: ready`.
592
- - **Healthcheck green.** The inline `epic-plan-healthcheck` pass run
593
- by the persist CLI returned `ok: true`. A failing healthcheck
594
- blocks the handoff — there is no advisory degrade-mode for
595
- `agent::ready`.
596
- - **Summary posted.** The persist CLI has posted the `plan-summary`
597
- structured comment (backlog stats + the dry-run wave table) so the
598
- operator knows the Epic is ready to fan out.
599
-
600
- **Operator override.** The `planning::healthcheck-waived` label, applied
601
- to the Epic by the operator, is the documented escape hatch for cases
602
- where the healthcheck reports `ok: false` for an environmental reason
603
- the operator has triaged and accepted (for example: a transient
604
- `origin` outage during a known maintenance window). When the label is
605
- present, the persist half allows the `agent::ready` flip even though
606
- the healthcheck failed. Every other exit condition above still
607
- applies — the waiver scopes to the healthcheck check alone. Remove
608
- the label to re-arm the gate.
609
-
610
- ---
299
+ ## Phase 2: Delivery
611
300
 
612
- ## Phase 3: Delivery (Agentic)
613
-
614
- Delivery is driven by the **`/deliver`** slash command for whole-Epic
615
- flows and the **Story Init/Close** scripts for individual Stories. All entry
616
- points share the same primitives DAG computation, context hydration,
617
- worktree isolation, and cascade closure. The lifecycle bus listener
618
- chain inside the session is the single runtime; it owns wave fan-out,
619
- finalize, automerge, and cleanup. The `delivery.finalize`,
620
- `delivery.automerge`, and `delivery.complete` states each fire one
621
- typed event via `lifecycle-emit.js` (`epic.close.end`,
622
- `epic.automerge.start`, `epic.merge.armed`); the matching listeners run
623
- the side effects. See
624
- [`docs/LIFECYCLE.md`](../../docs/LIFECYCLE.md) for the bus contract,
625
- event taxonomy, ledger format, and listener model — every phase
626
- transition, ticket-state flip, and webhook fan-out now flows through
627
- that bus, and the on-disk ledger at `temp/epic-<id>/lifecycle.ndjson`
628
- is the canonical resume target. Safety gates (auto-merge arming,
629
- acceptance-spec reconciliation, blocker handling) are listener
630
- side-effects rather than inline calls at phase boundaries; the
631
- "merge-lockout" lint rule keeps `gh pr merge` confined to the
632
- `AutomergeArmer` listener.
633
-
634
- > **Acceptance start gate.** Before a single wave fans out,
635
- > `/deliver`'s `delivery.snapshot` state
636
- > ([`lib/orchestration/epic-runner/phases/snapshot.js`](../scripts/lib/orchestration/epic-runner/phases/snapshot.js))
637
- > asserts that the Epic either (a) carries the `acceptance::n-a`
638
- > waiver label, or (b) carries the `## Acceptance Table` managed
639
- > section in its body (Story #4324). Presence is sufficient — the
640
- > reviewer's OK during `/plan`'s `planning.spec-authoring` state is
641
- > the approval signal. Neither
642
- > condition met → the snapshot throws a clear error naming the
643
- > missing precondition and `runAsCli` maps it to `process.exit(1)`.
644
- > This refuses to launch Epics that skipped acceptance-spec
645
- > authoring, surfacing the gap at delivery time rather than letting
646
- > Story dispatch race ahead.
301
+ Delivery is owned end-to-end by [`/deliver`](../workflows/deliver.md), which
302
+ delegates every Story to
303
+ [`helpers/deliver-story`](../workflows/helpers/deliver-story.md). This
304
+ section states the contract; the per-Story step detail (init, implement,
305
+ self-eval, ceremony, close, CI watch, confirm-merge, cleanup) lives in the
306
+ `deliver-story` workflow and its
307
+ [reference](../workflows/helpers/deliver-story-reference.md).
647
308
 
648
309
  ### Invocation modes
649
310
 
650
- | Mode | Entry point | When to use |
651
- | -------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
652
- | **Whole Epic** | `/deliver <epicId>` | Drive an Epic end-to-end. Owns the wave loop and the close-tail; ends with a PR open to main. |
653
- | **Epic-attached Story (worker)** | *helper* `helpers/epic-deliver-story <storyId>` | Per-Story sub-agent called internally by `/deliver`'s wave fan-out; not an operator slash command. |
654
- | **Standalone Story plan** | `/plan` | Plan a one-off Story that does not belong to an Epic backlog. |
655
- | **Standalone Story — deliver** | `/deliver <storyId> [<storyId>...]` | Deliver one or more standalone Stories authored by `/plan`. |
656
- | **Standalone Story (worker)** | *helper* `helpers/single-story-deliver <storyId>` | Per-Story sub-agent called internally by `/deliver`; not an operator slash command. |
657
- | **Mixed set** | `/deliver <ids...>` | Any mix of ≥1 Epics and standalone Stories. The router composes a sequential segment plan — standalone segment first, then Epic segments in input order — delegating each segment to the path helpers above. |
658
-
659
- The single operator-facing entry point is `/deliver` — it routes a lone
660
- Epic, a standalone-Story set, or a mixed set (via the sequential segment
661
- plan) to the right path helper(s). The `helpers/` layer sits below it and
662
- is never invoked directly by the operator.
663
-
664
- ### Story-centric branching
665
-
666
- - **Format**: `story-<storyId>` (merges into `epic/<epicId>`).
667
- - **Goal**: minimize merge conflicts and consolidation waves by grouping
668
- related work on one context slice.
669
-
670
- ### Story execution lifecycle
671
-
672
- Whether the Story is launched directly by the operator or fanned out by
673
- `/deliver`'s wave loop, the same three phases run:
674
-
675
- 1. **Initialization** (`story-init.js`):
676
- - Verifies all upstream dependencies are satisfied.
677
- - Syncs the Epic base branch with `main`.
678
- - Creates or seeds the Story branch (in a worktree when
679
- `delivery.worktreeIsolation.enabled: true`).
680
- - Transitions the Story to `agent::executing`. `story-phase.js`
681
- upserts the initial `story-run-progress` snapshot at the `init`
682
- phase.
683
- 2. **Story implementation.** The agent executes the Story's inline
684
- `acceptance[]` / `verify[]` contract on the shared Story branch,
685
- authoring one or more commits referencing the parent Story via
686
- `(refs #<storyId>)`. Each phase transition (`implementing`,
687
- `closing`, `done`, `blocked`) is recorded via `story-phase.js`.
688
-
689
- After the implementation commits land and **before** the phase flips
690
- to `closing`, a **bounded acceptance self-eval loop** runs (Story
691
- #3819). An independent, fresh-context critic pass scores the working
692
- diff against **each** `acceptance[]` item — `met | partial | unmet`
693
- plus a short evidence string, consuming the Story's `verify[]`
694
- commands as **required evidence** (the `verify[]` commands are no
695
- longer optional advisory pre-flight). The critic writes its verdict to
696
- a verdict file
697
- (`.agents/schemas/acceptance-eval-verdict.schema.json`); the
698
- `acceptance-eval.js` gate validates it, enforces the bounded round
699
- cap, and decides the next action:
700
- - **all `met`** → the phase flips to `closing`.
701
- - **any `partial`/`unmet`, rounds remaining** → the agent redrafts
702
- the flagged criteria and re-runs the critic pass for the next round.
703
- - **round cap reached, criteria still unmet** → the Story transitions
704
- to `agent::blocked` (not `closing`), posts a `friction` comment
705
- naming the unmet criteria and their evidence, and exits non-zero. It
706
- never silently proceeds to close.
707
-
708
- The loop is **always on** (a hard cutover — there is no flag toggling
709
- it off) and **bounded**: the redraft ceiling is
710
- `delivery.acceptanceEval.maxRounds` (default 2), clamped by the
711
- resolver into `[1, hard ceiling]` so no configuration can disable the
712
- cap or let the loop spin unbounded. Each terminus emits a
713
- per-criterion `acceptance-eval` signal into the retro / feedback
714
- substrate so the retro and `/plan` Phase 0 feedback fetch see
715
- which acceptance items needed rework and the round count. The loop is
716
- **additive** and sits below the Epic-level acceptance-spec
717
- reconciliation (`/deliver` Phase 7.1) — it evaluates the actual
718
- work product per Story mid-delivery, not test-tag presence at
719
- finalize.
720
- 3. **Closure** (`story-close.js`):
721
- - Runs shift-left validation (lint, format, test).
722
- - Merges the Story branch into `epic/<epicId>`.
723
- - Transitions the Story → `agent::done`. There is no upward
724
- auto-cascade — the Epic flips only when the operator merges the
725
- `epic/<id>` PR to `main`.
726
- - Reaps the Story worktree and cleans up the merged Story branch.
727
-
728
- ### Context hydration
729
-
730
- When a sub-agent runs `helpers/epic-deliver-story <storyId>` (for
731
- Epic-attached Stories) or `helpers/single-story-deliver <storyId>` (for
732
- standalone Stories), the Context Hydrator assembles a self-contained prompt:
733
-
734
- 1. `agent-protocol.md` (universal rules).
735
- 2. Persona and skill directives (from Task labels).
736
- 3. Hierarchy context (Story → Epic — the Epic body carries the folded
737
- Tech Spec sections; the `## Acceptance Table` section is stripped
738
- from delivery prompts).
739
- 4. **Story branch context.** Automatic checkouts to the Story branch. Under
740
- worktree isolation, each Story runs in its own `.worktrees/story-<id>/` so
741
- branch swaps, staging, and reflog activity are isolated per-story. See
742
- [`workflows/helpers/worktree-lifecycle.md`](../workflows/helpers/worktree-lifecycle.md).
743
- 5. Task-specific instructions and subtask checklist.
744
-
745
- ### State sync
746
-
747
- Agents update their state in real-time on GitHub:
748
-
749
- - **Labels**: `agent::ready` → `agent::executing` → `agent::done`. The
750
- intermediate review label is not part of the label taxonomy; the
751
- PR opened by `/deliver`'s `delivery.finalize` state is the equivalent "ready to merge"
752
- signal at the Epic level. The `WaveObserver` submodule additionally
753
- syncs a GitHub Projects v2 Status column on each transition when a
754
- `projectNumber` is configured.
755
- - **Tasklists**: subtasks are checked off in the ticket body (`- [ ]` →
756
- `- [x]`).
757
- - **Friction**: friction logs are posted as structured comments on the Task.
758
- - **Wave transitions**: the Epic Deliver Runner emits `wave-N-start` and
759
- `wave-N-end` structured comments on the Epic, each carrying the wave
760
- manifest, story outcomes, and timing.
761
-
762
- ### Dependency unblocking
311
+ | Mode | Entry point | When to use |
312
+ | --- | --- | --- |
313
+ | **Single Story** | `/deliver <storyId>` | Deliver one Story end-to-end; ends with a PR open to `main`. |
314
+ | **Story set** | `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order (default concurrency **3**), resolved from live state so edges may point at Stories from earlier plan runs; each lands through its own PR, and the per-run epilogue runs after the set lands. |
315
+ | **Story worker (internal)** | *helper* `helpers/deliver-story <storyId>` | Per-Story engine invoked internally by `/deliver`; not an operator slash command. |
763
316
 
764
- When a Task reaches `agent::done`, the runner re-evaluates the DAG and
765
- dispatches any newly-unblocked Tasks. This continues until all waves complete.
317
+ The single operator-facing entry point is `/deliver`. It performs no
318
+ git/label mutations itself `deliver-story` owns every script invocation
319
+ per Story. Any ticket that is not `type::story`, or that still carries an
320
+ `Epic: #N` reference, is a hard error naming the ID and the fix (close or
321
+ re-plan as a v2 Story).
766
322
 
767
- ### Story assignment (deterministic)
323
+ ### Branch model (authoritative)
768
324
 
769
- `helpers/epic-deliver-story` requires an explicit Story id. The parent
770
- `/deliver` wave loop picks Story ids off the frozen dispatch manifest
771
- deterministically and launches one Agent-tool sub-agent (calling
772
- `helpers/epic-deliver-story`) per id per wave; sibling sub-agents never
773
- race on the same Story.
774
-
775
- `runtime.sessionId` survives as a stable per-process identity surfaced in
776
- the startup `[ENV]` log line for operator correlation. It is a 12-char
777
- short-id derived from hostname+pid+random.
778
-
779
- ### Launch-time dependency guard
780
-
781
- Before any branch operation, `story-init.js` reads the Epic's
782
- dispatch manifest and verifies the target story's blockers are all merged.
783
- Unmerged blockers print each blocker's id, state, and URL; the session exits
784
- 0 (operator-error, not a system error) without touching any branches. A
785
- missing or stale-format manifest emits a warning and proceeds — the guard is
786
- a footgun-prevention layer, not a strict gate.
325
+ ```text
326
+ story-<id> → PR → main (squash + required checks)
327
+ ```
787
328
 
788
- The guard runs identically on web and local.
329
+ There is no `epic/<id>` integration branch and no `--no-ff` wave merge.
330
+ Dependent Stories land sequentially so each builds on the previous merge to
331
+ `main`.
789
332
 
790
- ### Concurrent close — push retry
333
+ ### Ceremony
791
334
 
792
- `story-close.js` merges the Story branch into `epic/<epicId>` locally
793
- and pushes. With multiple sessions closing into the same Epic branch from
794
- separate clones, a non-fast-forward rejection is expected. The push step is
795
- wrapped in a bounded retry: on rejection the script fetches
796
- `origin/epic/<id>`, replays the Story merge on top of the new remote tip,
797
- and pushes again. Bounds:
335
+ Ceremony depth is selected by `delivery.routing.ceremonyProfile`
336
+ (`minimal` | `standard` | `strict`, default `standard`) and the Story's
337
+ own planning risk. Hard gates (lint / test / format / coverage / CRAP /
338
+ maintainability) always run at close risk never disables them; it only
339
+ tunes acceptance-critic mode, review depth, and audit-lens selection. The
340
+ full profile × scope matrix lives in
341
+ [`deliver.md` § Ceremony](../workflows/deliver.md).
798
342
 
799
- - `DEFAULT_STORY_MERGE_RETRY.maxAttempts` (framework-internal constant in
800
- `.agents/scripts/lib/config/runners.js`) — 3.
801
- - `DEFAULT_STORY_MERGE_RETRY.backoffMs` (same module) — `[250, 500, 1000]`.
343
+ ### State sync
802
344
 
803
- A real content conflict (both stories touched the same lines) aborts the
804
- loop with a clear error, leaves the local tree clean, and exits non-zero for
805
- manual resolution. The retry path is a wrapper around the existing happy path.
345
+ Agents update their state in real time on GitHub, always through
346
+ `update-ticket-state.js`:
347
+
348
+ - **Labels**: `agent::ready` → `agent::executing` → `agent::closing` →
349
+ `agent::done`. The `agent::done` flip happens only after
350
+ `single-story-confirm-merge.js` confirms the PR merged. When a
351
+ `projectNumber` is configured, the Projects v2 Status column is synced on
352
+ each transition (and re-asserted after merge to beat the board's late
353
+ built-in write).
354
+ - **Acceptance/verify**: the agent works the Story's inline `acceptance[]`
355
+ / `verify[]` arrays; `verify[]` commands are consumed as required
356
+ evidence by the acceptance self-eval loop.
357
+ - **Friction**: friction is posted as a structured comment on the **Story**
358
+ (`diagnose-friction.js`), and rolls up into the retro and, for N>1, the
359
+ per-run follow-up roll-up.
806
360
 
807
361
  ### Cross-clone coordination
808
362
 
809
- Concurrent runs are serialised by **two distinct layers**, and the
810
- distinction matters: getting it wrong leaves two clones racing on the same
811
- Epic with no guard between them.
812
-
813
- **Filesystem locks are same-machine-only.** The Epic merge lock
814
- (`.agents/scripts/lib/epic-merge-lock.js`) lives at
815
- `<gitCommonDir>/epic-<epicId>.merge.lock` inside `.git/`, and the
816
- single-story sweep lock (`.agents/scripts/lib/single-story-sweep/sweep-lock.js`)
817
- is a single-file rendezvous on the local filesystem. Both decide staleness
818
- by probing a recorded **process PID** with `process.kill(pid, 0)` and by
819
- comparing a local-filesystem mtime against a TTL. Because a PID is only
820
- meaningful on the machine that owns it and `.git/` is never committed,
821
- **these locks coordinate only the worktrees and sessions on a single
822
- machine/clone. They do NOT coordinate across clones.** Two operators on
823
- two separate clones (or two CI runners) will each acquire their *own*
824
- merge lock and never see the other's — the locks are invisible to each
825
- other. The only cross-clone safety the merge step itself has is the
826
- bounded push-retry described above, which recovers from the
827
- non-fast-forward rejection *after* the race has already happened.
828
-
829
- **The assignee-as-lease is the cross-clone layer.** To stop two clones
830
- from both *starting* to drive the same Epic or Story, the framework takes
831
- an exclusive, time-bounded claim on the ticket via
832
- [`ticket-lease.js`](../scripts/lib/orchestration/ticket-lease.js). The lease
833
- rides the ticket's GitHub `assignees` field — a substrate every clone can
834
- read — so a live foreign claim is visible to, and refuses, a second
835
- operator regardless of which machine they are on. All three delivery and
836
- planning entry points take the claim: `/plan`'s persist CLI acquires the
837
- Epic lease before its first mutation and releases it on every exit path,
838
- `/deliver` acquires the Epic lease in its prepare guard, and
839
- `/single-story-deliver` acquires the Story lease at init. For
840
- `/deliver` and `/single-story-deliver`, liveness is decided by the
841
- owner's most-recent `story.heartbeat` against `delivery.lease.ttlMs`;
842
- because planning emits no `story.heartbeat`, `/plan` has no
843
- live-heartbeat source and treats **any** foreign assignee as a live claim.
844
- A live foreign claim fails the preflight closed (refuse-and-exit, naming
845
- the owner); `--steal` is the only override. See
846
- [`README.md` § Multi-developer coordination](../README.md#multi-developer-coordination)
847
- for the full lease behaviour table.
848
-
849
- The two layers are complementary, not redundant: the lease prevents two
850
- clones from racing in the first place, while the same-machine merge lock
851
- serialises the parallel-wave story closures *within* the one clone that
852
- holds the lease.
853
-
854
- ### Close-tail (`delivery.close-validation` through `delivery.complete` of `/deliver`)
855
-
856
- After the wave loop returns `complete`, `/deliver` runs the
857
- remaining phases against the Epic branch — close-validation, audit,
858
- code-review, retro, and finalize — before handing off to the
859
- watch / auto-merge / cleanup tail that drives the PR to merge:
860
-
861
- 1. **Close-validation (Phase 3).** Lint + test + project-extended ratchets
862
- (maintainability, CRAP, lint baseline) run via `evidence-gate.js` keyed
863
- on `git rev-parse HEAD`. A clean tree on a re-run short-circuits in
864
- milliseconds. A failing gate halts the workflow until the regression is
865
- fixed on a hotfix branch and re-merged into the Epic.
866
- 2. **Epic-close lens roster (Phase 4).** Resolves — does not walk — the slim
867
- Epic-close lens roster (`epicCloseLenses`): the `gate3` change-set selection
868
- plus the risk-routed lenses, restricted to the **cumulative + global +
869
- risk-routed** tiers via `selectEpicCloseLenses`. Every **local-tier**
870
- change-set lens is excluded because its concern is already verified
871
- shift-left — the write-time distilled checklist threaded into the Story
872
- prompt (Story #4410) and the maker-blind Story-scope local-lens pass in
873
- `story-close` (Story #4409). The roster is handed to Phase 5; there is no
874
- standalone Phase 4 walk and no separate `audit-results` comment (Story
875
- #4412).
876
- 3. **Code-review (Phase 5) — cumulative diff walked once.**
877
- `lib/orchestration/code-review.js` walks `main..epic/<id>` a single time,
878
- executing the Phase 4 lens roster as review **dimensions** alongside the
879
- review pillars, and posts the unified `verification-results` structured
880
- comment on the Epic (Story #4411 unified the former `code-review` and
881
- `audit-results` contracts; Story #4412 folded the lens walk into this pass).
882
- Remediation is **tier-aware and split by finding class** (Story #4412): the
883
- review-pillar findings route off `delivery.codeReview.autoFixSeverity`
884
- (default `medium` — 🔴/🟠/🟡 on-branch, 🟢 stays on the comment), while the
885
- Epic-close lens findings route off `delivery.epicAudit.autoFixSeverity`
886
- (default **`high`** — only 🔴/🟠 on-branch; 🟡/🟢 graduate, because 🟡
887
- Medium concerns were already remediated shift-left). Fixed findings land
888
- under the comment's `## Fixed on-branch` section so the graduator skips
889
- them. The severity gate is unchanged: surviving 🔴 Critical findings halt
890
- the run; surviving 🟠/🟡/🟢 flow through as non-blocking.
891
- 4. **Retro (Phase 6).** `lib/orchestration/retro-runner.js` (extracted from the old
892
- retro helper) aggregates perf signals, friction counts, hotfix counts,
893
- recut counts, parked counts, and HITL count using
894
- `retro-heuristics.js`. The structured retro comment is posted on the
895
- Epic. The retro fires **before** the PR opens — this keeps it inside
896
- the operator's local session with full env access (env vars,
897
- credentials, MCP servers); pushing it after PR-open would deny it
898
- that access. After the GitHub upsert succeeds, the retro body is
899
- also **mirrored locally** to the per-Epic temp tree at
900
- `temp/epic-<id>/retro.md` (path resolved via
901
- [`lib/config/temp-paths.js`](../scripts/lib/config/temp-paths.js)'s
902
- `epicRetroMirrorPath`) so operators can read the retro without
903
- re-fetching from GitHub. GitHub remains the source of truth; the
904
- mirror write is best-effort and a failure only logs a warn.
905
- 5. **Finalize (Phase 7).** `/deliver` fires `epic.close.end` via
906
- `lifecycle-emit.js`; the `AcceptanceReconciler` → `Finalizer`
907
- listener chain owns every close-time side effect end to end
908
- (Story #2894 — bus-owned finalize). The chain runs three
909
- responsibilities in order:
910
- 1. **Acceptance reconciliation.** Invokes
911
- `acceptance-spec-reconciler.js` to diff the AC IDs declared in
912
- the Epic body's `## Acceptance Table` managed section
913
- (Story #4324) against `@epic-<id>-ac-*` / `@pending` tags in
914
- `tests/features/**`. A non-OK reconciliation throws (per
915
- `.agents/rules/orchestration-error-handling.md`), aborting
916
- finalize **before** the PR opens — the Epic blocks until the AC
917
- coverage gap is fixed. On a clean run the reconciler records
918
- each row's verification outcome (`satisfied | pending |
919
- missing`) into the table's Disposition column — a
920
- section-scoped write that touches only the managed region.
921
- Skipped (`status: 'waived'`) when the Epic carries
922
- `acceptance::n-a`.
923
- 2. **PR open (bus-owned, Story #2894).** On
924
- `acceptance.reconcile.ok`, the `Finalizer` listener invokes
925
- `openOrLocatePr({ epicId, headBranch: 'epic/<id>', baseBranch:
926
- 'main' })`. The helper probes for an existing open PR on the
927
- head branch first (idempotent locate path) and only runs
928
- `gh pr create` when the head branch has no open PR. The
929
- Finalizer does **not** arm auto-merge — it emits
930
- `epic.merge.ready` carrying `{ prNumber, epicId, prUrl }` and
931
- hands off to the auto-merge gate. The sole production caller
932
- authorised to shell `gh pr merge` in the entire codebase is
933
- the `AutomergeArmer` listener at the `delivery.automerge` state
934
- (enforced by the merge-lockout rule in
935
- `.agents/scripts/check-lifecycle-lint.js`); the
936
- `delivery.finalize` state never shells the merge command.
937
- 3. **Hand-off (bus-owned, Story #2894).** The `Finalizer` runs
938
- `postHandoffComment({ epicId, prNumber, prUrl, provider })` to
939
- upsert the canonical `epic-handoff` structured comment naming
940
- the PR. (Story #4324 retired the `closePlanningTickets` sweep
941
- with the context-ticket classes — there are no planning tickets
942
- to close.) The helper is idempotent — re-running finalize after
943
- a crash edits the existing handoff comment in place rather than
944
- appending a duplicate. The Epic stays at `agent::executing`
945
- until the PR merges.
946
- 6. **Watch-and-iterate (Phase 8).** `/deliver` watches the open PR's
947
- required checks until they turn green. Transient failures trigger an
948
- automated re-run loop; durable failures surface for human remediation
949
- on the Epic branch.
950
- 7. **Auto-merge (Phase 8.5).** Once the watch loop reports all required
951
- checks passing, the auto-merge gate arms GitHub native auto-merge via
952
- `gh pr merge --auto --squash --delete-branch` so the PR lands without
953
- a second operator visit.
954
- 8. **Cleanup (Phase 9).** After the PR merges, the cleanup phase reaps
955
- local Story/Epic branch refs and any lingering worktrees so the
956
- workspace returns to a clean state for the next Epic.
957
-
958
- `/deliver` exits cleanly once auto-merge is armed (or sooner if the
959
- operator declines auto-merge). The operator can merge through the GitHub UI
960
- at any time; the `delivery.complete` state handles the post-merge branch reap.
363
+ Concurrent runs are serialised by **two distinct layers**:
364
+
365
+ - **Filesystem locks are same-machine-only.** The single-story sweep lock
366
+ (`sweep-lock.js`) is a single-file rendezvous keyed on a local process
367
+ PID + mtime TTL. Because a PID is only meaningful on its own machine and
368
+ `.git/` is never committed, these locks coordinate only the worktrees and
369
+ sessions on **one** clone.
370
+ - **The assignee-as-lease is the cross-clone layer.** To stop two clones
371
+ from both *starting* the same Story, `deliver-story` takes an exclusive,
372
+ time-bounded claim on the ticket via
373
+ [`ticket-lease.js`](../scripts/lib/orchestration/ticket-lease.js), riding
374
+ the ticket's GitHub `assignees` field so a live foreign claim is visible
375
+ to every clone. The standalone lease **fails closed** on a foreign
376
+ assignee; `--steal` is the only override. See
377
+ [`README.md` § Multi-developer coordination](../README.md#multi-developer-coordination).
378
+
379
+ ### Concurrent close
380
+
381
+ `single-story-close.js` syncs the Story branch from `origin/main` before
382
+ pushing and opening/locating the PR, so concurrent closes serialize through
383
+ their own worktrees rather than racing one shared branch. The push does not
384
+ retry: a rejected push, or a real content conflict at base-sync, aborts with
385
+ a clear error, leaves the tree clean, and exits non-zero for manual
386
+ resolution.
961
387
 
962
388
  ---
963
389
 
964
390
  ## HITL (Human-in-the-Loop) model
965
391
 
966
392
  On the happy path there is exactly **one** mandatory operator touchpoint
967
- after `/deliver` fires (blocker resolution). PR merge is autonomous
968
- via armed auto-merge; the operator can opt in as a second touchpoint by
969
- disarming auto-merge in the GitHub UI, or is pulled in by exception when
970
- required checks fail.
971
-
972
- 1. **Blocker resolution (mandatory when triggered).** If the orchestrator
973
- hits an unresolvable condition, `BlockerHandler` flips the Epic to
974
- `agent::blocked`, posts a structured friction comment, fires the
975
- notification webhook (fire-and-forget), and halts wave N+1 (letting
976
- wave N's in-flight stories finish naturally). The operator resolves
977
- the underlying issue (e.g. a hand-fix commit on the Story branch or a
978
- scope edit on the blocking ticket), then flips the Epic back to
393
+ after `/deliver` fires (blocker resolution). PR merge is autonomous via
394
+ armed auto-merge; the operator becomes a second touchpoint only by
395
+ exception.
396
+
397
+ 1. **Blocker resolution (mandatory when triggered).** If a Story hits an
398
+ unresolvable condition, it flips to `agent::blocked`, posts a structured
399
+ friction comment, and fires the notification webhook (fire-and-forget).
400
+ The operator resolves the underlying issue (a hand-fix commit on the
401
+ Story branch, or a scope edit on the ticket) and flips the Story back to
979
402
  `agent::executing` to resume.
980
403
  2. **PR merge (autonomous by default; operator-gated by exception).** At
981
- the end of `/deliver`, the workflow opens a PR to `main` and
982
- arms GitHub native auto-merge (the `delivery.automerge` state). When required checks pass,
983
- the PR lands without a second operator visit; the standard
984
- label-transition pathway flips the Epic to `agent::done` on merge.
985
- The operator becomes a touchpoint here only when they (a) disarm
986
- auto-merge in the GitHub UI to inspect required-checks, the
987
- `verification-results` comment, and the retro before merging by hand, or
988
- (b) checks fail and need remediation on the Epic branch. There is
989
- no separate close command — the close-out side effects (PR open,
990
- handoff comment) are owned by `/deliver`'s
991
- `delivery.finalize` state (the lifecycle Finalizer listener), whose
992
- replay is idempotent.
404
+ close, `deliver-story` opens a PR to `main` and arms GitHub native
405
+ auto-merge. When required checks pass, the PR lands without a second
406
+ operator visit and the standard label transition flips the Story to
407
+ `agent::done`. The operator becomes a touchpoint only when they (a)
408
+ disarm auto-merge (`--no-auto-merge` per run, or
409
+ `delivery.ci.autoMerge: "strict"`) to inspect checks / the
410
+ `verification-results` comment / the retro before merging by hand, or
411
+ (b) checks fail and need remediation.
993
412
 
994
413
  ### What triggers `agent::blocked`
995
414
 
996
- - Unresolvable merge conflict that automated strategies cannot reconcile.
997
- - Test failures that persist after one automated remediation attempt.
998
- - Ambiguity in a ticket requiring a product/scope decision the orchestrator
999
- cannot make from ticket context alone.
1000
- - A destructive action not pre-authorized by the ticket body (e.g. dropping a
1001
- table, deleting user data, force-pushing to a protected branch).
1002
- - External service failure preventing progress (GitHub API 5xx loop, npm
415
+ - Unresolvable merge conflict automated strategies cannot reconcile.
416
+ - Test failures that persist after automated remediation.
417
+ - Ambiguity requiring a product/scope decision the agent cannot make from
418
+ ticket context alone.
419
+ - A destructive action not pre-authorized by the ticket body.
420
+ - External-service failure preventing progress (GitHub API 5xx loop, npm
1003
421
  registry down).
1004
- - Wave concurrency exhausted for an unbounded time (possible deadlock).
422
+ - Acceptance self-eval exhausting its bounded round cap with criteria still
423
+ unmet.
1005
424
 
1006
425
  ### What is *not* gated at runtime
1007
426
 
1008
- - `risk::high` tasks **run without pause.** The label remains as planning
1009
- metadata and retro telemetry, but it does **not** halt the dispatcher
1010
- or `/deliver`. Branch protection on `main` and
1011
- `BlockerHandler`-driven escalation are the runtime defenses for
1012
- destructive actions.
1013
- - Wave boundaries — the runner advances as soon as wave N completes.
1014
- - Individual story completion — no per-story approval prompt.
1015
-
1016
- > [!NOTE] Legacy `risk::high` runtime gating has been retired. `risk::high`
1017
- > remains planning/audit metadata only; the sole runtime pause point is
1018
- > `agent::blocked`.
1019
-
1020
- ---
1021
-
1022
- ## Epic Deliver Runner internals
1023
-
1024
- `/deliver` drives the long-running coordinator inside the operator's
1025
- Claude session. The slash command composes the submodules listed below;
1026
- `helpers/epic-deliver-story` is launched as an Agent-tool sub-agent of
1027
- `/deliver`'s wave loop — no subprocess worker sessions for Story
1028
- execution, no GitHub Actions runner. Deterministic Node CLIs remain the
1029
- state-mutation contract.
1030
-
1031
- | Submodule | Role |
1032
- | ------------------- | ----------------------------------------------------------------------------------------------------------------------- |
1033
- | `wave-scheduler` | Iterates waves from `Graph.computeWaves()`. |
1034
- | `story-launcher` | Fans out up to `concurrencyCap` Agent-tool Story sub-agents per wave. |
1035
- | `checkpointer` | Upserts the `epic-run-state` structured comment; handles phase-granular resume across all six phases. |
1036
- | `blocker-handler` | The sole runtime pause point — halts on `agent::blocked`. |
1037
- | `notification-hook` | Fire-and-forget webhook for blocker / wave-transition events. |
1038
- | `wave-observer` | Emits `wave-N-start` / `wave-N-end` comments and reads each Story's `story-run-progress` snapshot. |
1039
- | `column-sync` | Syncs the Projects v2 Status column from `agent::` labels. |
1040
- | `code-review` | `lib/orchestration/code-review.js` — `delivery.code-review` inline audit; halts on critical findings. |
1041
- | `retro-runner` | `lib/orchestration/retro-runner.js` — `delivery.retro` authoring; posts structured retro comment. |
1042
-
1043
- ### Claude Max quota
1044
-
1045
- `/deliver` consumes Max subscription quota (5-hour rolling window with
1046
- overage disabled at the org level by default). If a long Epic exceeds the
1047
- 5-hour window, `BlockerHandler` surfaces the rate-limit error as
1048
- `agent::blocked` so you can resume after the quota rolls.
1049
-
1050
- ### Skipping CI/CD on orchestrator commits
1051
-
1052
- The orchestrator pushes many commits during a run, each potentially triggering
1053
- the project's `CI / CD` workflow. Two mitigations:
1054
-
1055
- - Add `[skip ci]` to orchestrator commit messages (requires a small tweak in
1056
- `story-close.js`), OR
1057
- - Add a `paths-ignore` or branch filter to `ci.yml` that excludes `epic/*` and
1058
- `story-*` branches. Only `main` pushes trigger CI.
1059
-
1060
- ---
1061
-
1062
- ## Phase 4: PR merge (auto by default)
1063
-
1064
- Once the wave loop, close-validation, code-review, and retro have all
1065
- completed, `/deliver` opens a pull request from `epic/<epicId>` to
1066
- `main` and arms GitHub native auto-merge. When the required checks pass
1067
- the PR lands without further intervention; the operator can disarm
1068
- auto-merge in the GitHub UI to make the final merge an explicit human
1069
- action.
1070
-
1071
- 1. **Story merging.** Stories merge into `epic/<epicId>` automatically
1072
- during Story closure (`story-close.js`). The Epic branch is the rolling
1073
- integration target.
1074
- 2. **Completion.** Each Story flips to `agent::done` at its own closure
1075
- (`story-close.js`); the wave loop tracks Epic-level progress as
1076
- Stories complete. There is no upward auto-cascade — Epics and
1077
- Tech Specs are never flipped by Story closure; the Epic only flips to
1078
- `agent::done` when the operator merges the PR to `main`.
1079
-
1080
- 3. **PR merge — the sole promotion gate.** When the PR merges (auto or
1081
- manual):
1082
- - the Epic-to-`main` merge lands as a real PR merge with a real
1083
- reviewer-trail and required-checks history;
1084
- - the standard label-transition pathway flips the Epic to
1085
- `agent::done`;
1086
- - branch cleanup runs out-of-band: the `delivery.complete` state of
1087
- `/deliver` reaps local refs after the merge; the rare "scrap
1088
- and reset" case for an unmerged Epic is handled manually.
1089
-
1090
- If the operator chooses not to merge (rolling back, deferring, re-scoping),
1091
- `/deliver` has not poisoned `main`. The Epic branch can be amended
1092
- in place; re-running `/deliver <epicId>` re-runs the
1093
- `delivery.close-validation` / `delivery.audit` / `delivery.code-review`
1094
- states against the new HEAD (the evidence wrapper picks up the new SHA) and
1095
- updates the same PR — no duplicate PRs are opened against the same Epic
1096
- branch.
427
+ - `risk::high` Stories **run without pause.** The label is planning/audit
428
+ metadata and retro telemetry only; the sole runtime pause point is
429
+ `agent::blocked`. Branch protection on `main` and blocker escalation are
430
+ the runtime defenses for destructive actions.
431
+ - Individual Story completion — no per-Story approval prompt beyond the PR
432
+ merge gate.
1097
433
 
1098
434
  ---
1099
435
 
1100
436
  ## Testing strategy
1101
437
 
1102
438
  Tests are **pyramid-aware**. Every test written during Story delivery
1103
- belongs to exactly one tier — **unit**, **contract**, or **e2e / acceptance** —
1104
- and each tier has distinct scope, dependency, and assertion rules. The canonical
1105
- tier definitions, assertion-placement rules, and coverage thresholds live in
1106
- [`rules/testing-standards.md`](../rules/testing-standards.md); Gherkin authoring
1107
- for the acceptance tier is governed by
439
+ belongs to exactly one tier — **unit**, **contract**, or **e2e /
440
+ acceptance**. The canonical tier definitions, assertion-placement rules,
441
+ and coverage thresholds live in
442
+ [`rules/testing-standards.md`](../rules/testing-standards.md); Gherkin
443
+ authoring for the acceptance tier is governed by
1108
444
  [`rules/gherkin-standards.md`](../rules/gherkin-standards.md).
1109
445
 
1110
- The acceptance tier is executed and reported via
1111
- [`workflows/qa-run.md`](../workflows/qa-run.md) and consumed as
1112
- epic evidence by
1113
- [`workflows/helpers/epic-testing.md`](../workflows/helpers/epic-testing.md).
446
+ Write a Story's acceptance criteria in Gherkin-compatible `Given / When /
447
+ Then` form so the acceptance suite can lift them into executable `.feature`
448
+ files.
1114
449
 
1115
450
  ### QA workflows: explore, assist, and run-harness
1116
451
 
1117
- Three complementary QA workflows sit alongside the automated test pyramid, all
1118
- adopting the `qa-engineer` persona and all reading the consumer's `qa.*`
1119
- project contract from `.agentrc.json`. The first two are exploratory siblings
1120
- that differ on **who drives** the session; the third steps a known scenario
1121
- set:
1122
-
1123
- - **[`workflows/qa-explore.md`](../workflows/qa-explore.md)** (`/qa-explore`) an
1124
- **agent-led**, open-ended **Plan Capture Triage** exploratory sweep.
1125
- The operator names a surface; the **agent drives** it (through the browser
1126
- MCP by default, or statically as a documented interim), probing for product
1127
- bugs, environment-setup friction, tooling/DX gaps, missing tests, and
1128
- enhancement ideas. Each observation is recorded as a `QaLedgerItem` against
1129
- [`schemas/qa-ledger.schema.json`](../schemas/qa-ledger.schema.json) and appended
1130
- to a **session ledger** at `temp/qa/<sessionId>.ndjson` (one item per ndjson
1131
- line, under `project.paths.tempRoot`, gitignored, never committed). Capture
1132
- is strictly **read-only** the ledger append is its only write — so every
1133
- state-changing action (filing a follow-up ticket, mutating a label) lands in
1134
- Triage, and only after explicit operator confirmation. The session is
1135
- HITL-gated: every phase transition is operator-gated. Deterministic Node
1136
- helpers under `scripts/lib/qa/` (session resolution, evidence redaction,
1137
- coverage verdict, missing-test proposal) and `scripts/lib/findings/`
1138
- (classification, dedup/route — the same dedup implementation shared with
1139
- `audit-to-stories`) make the decisions; the agent never re-derives them in
1140
- prose. A resumed session appends and carries its un-triaged backlog forward
1141
- as a rolling backlog.
1142
- - **[`workflows/qa-assist.md`](../workflows/qa-assist.md)** (`/qa-assist`) — the
1143
- **human-led** sibling of `/qa-explore`: a single-observation
1144
- **Intake → Enrich → Record** loop. Here the **human drives** — the operator
1145
- reports one observation they hit (a bug, a flaky behavior, a "this feels
1146
- off") and the agent enriches it into a triage-ready `QaLedgerItem` (a clean
1147
- repro, a `file:line` root-cause locus, a coverage verdict), asking clarifying
1148
- questions when the observation is ambiguous, then appends it — after explicit
1149
- operator confirmation — to a persistent, resumable rolling session under
1150
- `temp/qa/`. It produces the **same** ledger contract `/qa-explore` writes
1151
- (`qa-ledger.schema.json`) and reuses the same `scripts/lib/qa/` and
1152
- `scripts/lib/findings/` decision seams, so a `/qa-assist` item flows through
1153
- the identical dedup, classification, and promotion machinery later.
1154
- - **[`workflows/qa-run.md`](../workflows/qa-run.md)**
1155
- (`/qa-run`) — the **automated complement**: it steps a *known* set of
1156
- Gherkin `.feature` scenarios through a real browser, asserting `Then`
1157
- outcomes semantically against the accessibility snapshot and bundling
1158
- console/network problems into structured `F#` findings for operator sign-off.
1159
-
1160
- All three workflows resolve the `qa.*` contract through the single seam
1161
- [`scripts/lib/qa/resolve-qa-contract.js`](../scripts/lib/qa/resolve-qa-contract.js).
1162
- The block is **optional in the schema** (so config validation never breaks a
1163
- non-QA consumer) but enforced at run time: the resolver fails **loudly** with
1164
- "this project has not bound the QA harness" when no `qa` block is present —
1165
- there is no silent fallback. The contract's four required keys are
1166
- `qa.featureRoot` (the `.feature` discovery root), `qa.fixturesManifest`
1167
- (persona → seed-data binding), `qa.environments` (the environment-keyed map —
1168
- each entry `{ baseUrl, signInSeam, allowWrites? }`, selected per invocation by
1169
- `resolveQaEnvironment`; the per-environment `signInSeam` is either
1170
- `{ urlTemplate }` or `{ skill }`), and `qa.personas` (the persona set,
1171
- authored as a name-only array under a url-template seam or as a per-persona
1172
- credential/skill map under a skill seam); the two optional keys
1173
- `qa.consoleAllowlist` and `qa.designTokens` default to `[]` and `null`.
452
+ Three complementary QA workflows sit alongside the automated pyramid, all
453
+ reading the consumer's `qa.*` contract from `.agentrc.json` through
454
+ [`scripts/lib/qa/resolve-qa-contract.js`](../scripts/lib/qa/resolve-qa-contract.js)
455
+ (which fails loudly when no `qa` block is bound):
456
+
457
+ - **[`/qa-explore`](../workflows/qa-explore.md)** — an **agent-led**,
458
+ open-ended **Plan → Capture → Triage** exploratory sweep. The operator
459
+ names a surface; the agent drives it (browser MCP by default), recording
460
+ each observation as a `QaLedgerItem`
461
+ ([`schemas/qa-ledger.schema.json`](../schemas/qa-ledger.schema.json)) in a
462
+ session ledger under `temp/qa/`. Capture is strictly **read-only**; every
463
+ state-changing action lands in Triage after explicit operator
464
+ confirmation.
465
+ - **[`/qa-assist`](../workflows/qa-assist.md)** the **human-led** sibling:
466
+ a single-observation **Intake Enrich → Record** loop. The operator
467
+ reports one observation; the agent enriches it into a triage-ready
468
+ `QaLedgerItem`. Same ledger contract and decision seams as `/qa-explore`.
469
+ - **[`/qa-run`](../workflows/qa-run.md)** the **automated complement**:
470
+ steps a *known* set of Gherkin `.feature` scenarios through a real
471
+ browser, asserting `Then` outcomes against the accessibility snapshot and
472
+ bundling console/network problems into structured `F#` findings.
473
+
1174
474
  Consumer adoption steps are in
1175
475
  [`README.md` § Adopting the QA harness](../README.md#adopting-the-qa-harness).
1176
476
 
@@ -1178,170 +478,120 @@ Consumer adoption steps are in
1178
478
 
1179
479
  ## Static analysis & audit orchestration
1180
480
 
1181
- An automated, gate-based static-analysis and audit orchestration pipeline
1182
- replaces manual auditing with a CLI-driven system.
1183
-
1184
- ### Audit triggering
1185
-
1186
- Audits are selectively invoked by the orchestrator at four Epic lifecycle
1187
- gates (`gate1` through `gate4`). The audit orchestrator
1188
- (`lib/dynamic-workflow/audit-orchestrator.js`) evaluates rules
1189
- defined in `.agents/schemas/audit-rules.json` (schema:
1190
- `.agents/schemas/audit-rules.schema.json`) based on:
1191
-
1192
- 1. **Gate configuration** — which gate is currently firing.
1193
- 2. **Contextual keywords** the Epic or Task body contents (e.g., `auth` or
1194
- `encrypt` triggers security audits).
1195
- 3. **File patterns** which files changed compared to the base branch (e.g.,
1196
- `user-profile` files trigger privacy audits).
1197
-
1198
- ### Epic lifecycle gates
1199
-
1200
- The matrix lists every quality gate the framework fires during an Epic
1201
- run. **Blocking?** is `blocking` when a non-OK result aborts the
1202
- workflow, `advisory` when findings are surfaced but execution proceeds.
1203
- **Idempotency key** names the on-disk or in-ticket marker the gate keys
1204
- on so re-runs short-circuit when state has not changed.
1205
-
1206
- | Gate | When | What Runs | Blocking? | Idempotency key |
1207
- | ------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------ |
1208
- | Tier 1 — write-time | During Story implementation | Local-tier lens checklists threaded into the Story prompt (clean-code, etc.) | advisory | Distilled checklist in the hydrated Story prompt (Story #4410) |
1209
- | Tier 2 — Story-scope | `story-close` (maker-blind subprocess) | Local-tier lens roster over the Story diff (`selectLocalLenses`) | advisory | `verification-results` structured comment on the Story |
1210
- | Tier 3 — Epic-close | `/deliver` Phase 4→5 (code-review pass) | Slim roster: cumulative + global + risk-routed lenses (`selectEpicCloseLenses`) + pillars | blocking | `verification-results` structured comment on Epic, keyed by Epic HEAD SHA |
1211
- | Gate 4 | `/deliver` `delivery.finalize` state (pre-PR) | `audit-sre` production readiness gate | blocking | `audit-sre` structured comment on Epic, keyed by Epic HEAD SHA |
1212
- | Close-validation | `/deliver` `delivery.close-validation` state | lint + test + maintainability + CRAP + coverage ratchets via `evidence-gate.js` | blocking | `evidence-gate` cache entry keyed by `git rev-parse HEAD` |
1213
- | Pre-push | Local `.husky/pre-push` hook on every push | Diff-scoped quality preview + coverage/CRAP ratchet | blocking | Working-tree SHA + staged-diff hash (per push) |
1214
- | Acceptance reconciliation | `/deliver` `delivery.finalize` state | `acceptance-spec-reconciler.js` diffs AC IDs against `@ac-*` / `@pending` feature tags | blocking | `acceptance-reconcile` structured comment on Epic, keyed by spec-body SHA |
1215
- | Spec freshness | `/plan` `planning.spec-authoring` state | Re-derives Tech Spec / Acceptance Spec staleness against Epic body checksum | advisory | `epic-plan-state` checkpoint entry per spec artifact body SHA |
1216
-
1217
- #### Three-tier lens model (Epic #4405)
1218
-
1219
- The three audit-lens tiers above (write-time → Story-scope → Epic-close) form a
1220
- **shift-left** verification model in which **each lens concern is verified at
1221
- exactly one tier**, chosen by the lens's `scope` field in `audit-rules.json`
1222
- (resolved by `resolveLensTier`):
1223
-
1224
- - **`local`** lenses (decidable from a single Story's diff) are verified at the
1225
- two innermost tiers — the write-time distilled checklist (Tier 1) and the
1226
- maker-blind Story-scope pass (Tier 2). They are **not** re-run at Epic close.
1227
- - **`cumulative`** lenses (only decidable across the Epic's combined diff) and
1228
- **`global`** lenses (whole-product properties) are verified at Epic close
1229
- (Tier 3) only.
1230
- - **Risk-routed** lenses run at Epic close regardless of tier, because a
1231
- high-risk axis (or a route-adding change set) explicitly demands them.
1232
-
1233
- The Epic-close roster (`selectEpicCloseLenses`) is deliberately **slim**: it
1234
- excludes every local-tier change-set lens so the outermost tier — where a fix
1235
- is most expensive — does not re-verify a concern already covered shift-left.
1236
- The Epic-close remediation threshold reflects this (`delivery.epicAudit.autoFixSeverity`
1237
- defaults to `high`): 🟡 Medium code-quality findings are remediated at the
1238
- innermost tiers, not re-remediated at close.
1239
-
1240
- ### Review & feedback loop
1241
-
1242
- When audits produce findings, the orchestrator compiles a structured Markdown
1243
- report and posts it as a ticket comment via the `ITicketingProvider`.
1244
-
1245
- - **Maintainability ratchet.** The orchestrator enforces code quality by relying
1246
- on maintainability checks (`check-maintainability.js`), which fail if the
481
+ Audit lenses are woven into delivery as a **shift-left, three-tier**
482
+ verification model in which each lens concern is verified at exactly one
483
+ tier, chosen by the lens's `scope` field in `audit-rules.json` (resolved by
484
+ `resolveLensTier`). There is **no** separate Epic-lifecycle-gate delivery
485
+ pass — the tiers below *are* the audit machinery.
486
+
487
+ | Tier | When | What runs | Blocking? |
488
+ | --- | --- | --- | --- |
489
+ | Tier 1 — write-time | During Story implementation | Footprint-matched **local**-lens authoring checklists threaded into the Story prompt (`checklistPath`) | advisory |
490
+ | Tier 2 — Story-scope | `single-story-close.js` (maker-blind subprocess) | Local-tier lens roster over the Story diff (`selectLocalLenses`) + review pillars, posted as `verification-results` | blocking on 🔴 |
491
+ | Tier 3 — run closeout | `/deliver` per-run epilogue (`plan-run-epilogue.js`, N>1 only) | Cumulative + global lenses (`selectAudits`) over the combined landed tip | blocking |
492
+
493
+ - **`local`** lenses (decidable from a single Story's diff) are verified at
494
+ Tiers 1–2 and are **not** re-run at run closeout.
495
+ - **`cumulative`** lenses (only decidable across a run's combined diff)
496
+ and **`global`** lenses (whole-product properties) are verified at Tier 3.
497
+
498
+ There is no risk-routed lens tier. Story #4542 deleted the risk→lens router:
499
+ it had zero callers while this document claimed it ran inside close. Lens
500
+ selection is change-set-matched (`selectAudits` / `selectLocalLenses`); the
501
+ `sensitivePaths` classes in `audit-rules.json` route review **depth**, not
502
+ lenses.
503
+
504
+ The run-closeout roster is deliberately **slim**: it excludes every
505
+ local-tier change-set lens so the outermost tier — where a fix is most
506
+ expensive does not re-verify a concern already covered shift-left.
507
+
508
+ ### Code review
509
+
510
+ The Story-scope code review runs **outside the maker's context**, inside
511
+ the `single-story-close.js` close subprocess, over `main...story-<id>`
512
+ (see [`helpers/code-review.md`](../workflows/helpers/code-review.md)). It
513
+ walks the Story diff once, executing the change-set-matched local lens roster
514
+ as review dimensions alongside the review pillars, and posts the unified
515
+ `verification-results` comment. Remediation is tier-aware and split by
516
+ finding class off `delivery.codeReview.autoFixSeverity` (default `medium`);
517
+ surviving 🔴 Critical findings halt the run. The legacy `scope: epic`
518
+ Epic-branch review path was removed with the v2 cutover.
519
+
520
+ ### Quality ratchets
521
+
522
+ - **Maintainability ratchet** (`check-baselines.js` via
523
+ `lib/baselines/kinds/maintainability.js`) — fails if the
1247
524
  composite score drops below the established baseline.
1248
- - **CRAP gate.** Sibling per-method gate (`check-crap.js`) wired
1249
- into `close-validation` after `check-maintainability`, the `ci.yml` step
1250
- after `test:coverage`, and `.husky/pre-push`. Tracks complexity × coverage
1251
- risk per method against `baselines/crap.json`. Self-skips when
1252
- `delivery.quality.gates.crap.enabled` is `false`. The
1253
- `baseline-refresh:`-tagged commit convention for baseline edits is the
1254
- project standard; the operator is the gate during `/deliver`
1255
- `delivery.finalize` state (the prior CI guardrail that enforced the tag was removed).
1256
- - **Human review on High/Critical.** If High or Critical findings are detected,
1257
- the workflow halts for human review at the corresponding `/deliver`
1258
- phase. Approval is given by the operator advancing the phase (the
1259
- auto-approve webhook listener was never wired into CI and was removed during the rebrand).
1260
- - **Implementation.** Once the operator approves the fixes, the ticket
1261
- transitions to `agent::executing` and `/deliver` dispatches an agent to
1262
- implement and verify them.
525
+ - **CRAP gate** (`check-baselines.js` via `lib/baselines/kinds/crap.js`)
526
+ per-method complexity × coverage risk
527
+ against `baselines/crap.json`, wired into close-validation, `ci.yml`, and
528
+ `.husky/pre-push`. The `baseline-refresh: true` commit-trailer convention
529
+ is the project standard for baseline edits (see
530
+ [`core/gates-and-baselines`](../skills/core/gates-and-baselines/SKILL.md)).
531
+
532
+ ### Audits Stories
533
+
534
+ The standalone `/audit-<dimension>` workflows are read-only emitters that
535
+ write `audit-<dimension>-results.md` under `temp/audits/`.
536
+ [`/audit-to-stories`](../workflows/audit-to-stories.md) parses those
537
+ reports, groups and deduplicates findings, and hands off to
538
+ `/plan --seed-file` (or opens standalone Stories) closing the loop back
539
+ into planning.
1263
540
 
1264
541
  ---
1265
542
 
1266
543
  ## Notification system
1267
544
 
1268
- Two independent notification surfaces, both living in `.agents/` so they ship to
1269
- consuming projects:
545
+ Two independent notification surfaces, both living in `.agents/` so they
546
+ ship to consuming projects.
1270
547
 
1271
548
  ### 1. Unified `notify()` dispatcher
1272
549
 
1273
- Every notification — whether a manual orchestration milestone (story merged,
1274
- HITL gate triggered) or an auto-fired ticket-state transition — routes through
1275
- [`notify.js`](../scripts/notify.js). Two delivery channels:
550
+ Every notification — whether a manual orchestration milestone (Story
551
+ merged, HITL gate triggered) or an auto-fired ticket-state transition —
552
+ routes through [`notify.js`](../scripts/notify.js). Two delivery channels:
1276
553
 
1277
- | Channel | What it does |
1278
- | ----------------- | ------------------------------------------------------------------------- |
1279
- | GitHub comment | Posts to the targeted ticket; @mentions operator for `medium`/`high`. |
1280
- | Webhook | Fire-and-forget POST to the configured URL (Make.com / Slack / Discord). |
554
+ | Channel | What it does |
555
+ | --- | --- |
556
+ | GitHub comment | Posts to the targeted ticket; @mentions the operator for `medium`/`high`. |
557
+ | Webhook | Fire-and-forget POST to the configured URL (Make.com / Slack / Discord). |
1281
558
 
1282
- Severity vocabulary (assigned by callers; `eventSeverity()` in
1283
- `lib/notifications/notifier.js` derives it for state transitions):
559
+ Severity vocabulary (`eventSeverity()` derives it for state transitions):
1284
560
 
1285
- | Severity | Used for | Webhook prefix |
1286
- | -------- | --------------------------------------------------------------------------------------------------------------------- | -------------------- |
1287
- | `low` | Task transitions, `story-run-progress` upserts, intermediate state transitions, audit reports. | `[low]` |
1288
- | `medium` | Operator-visible milestones: Story state transitions, `wave-run-progress`, `epic-run-progress`, story merged, epic complete. | `[medium]` |
1289
- | `high` | Operator must act (HITL gates, epic blockers, autonomous-chain failures). Message body should also lead with `🚨 Action Required:`. | `[Action Required]` |
561
+ | Severity | Used for | Webhook prefix |
562
+ | --- | --- | --- |
563
+ | `low` | Intermediate transitions, audit reports. | `[low]` |
564
+ | `medium` | Operator-visible milestones: Story state transitions, story merged, run complete. | `[medium]` |
565
+ | `high` | Operator must act (HITL gates, Story blockers, autonomous-chain failures); body leads with `🚨 Action Required:`. | `[Action Required]` |
1290
566
 
1291
- Two independent event-allowlist knobs in `github.notifications`
1292
- (both mandatory):
567
+ Two independent event-allowlist knobs in `github.notifications` (both
568
+ mandatory) filter each channel independently — there is no fallback chain:
1293
569
 
1294
- - `commentEvents` — event-name allowlist for GitHub-ticket comment
1295
- posting. Default:
570
+ - `commentEvents` — allowlist for GitHub-ticket comment posting. Default:
1296
571
  `["state-transition", "story-merged", "operator-message"]`.
1297
- - `webhookEvents` — event-name allowlist for `NOTIFICATION_WEBHOOK_URL`
1298
- deliveries. Default:
1299
- `["epic-started", "epic-progress", "epic-blocked", "epic-unblocked", "epic-complete"]`.
1300
-
1301
- Each channel filters independently; there is no fallback chain.
1302
- Severity is carried as envelope metadata (and still drives `@mention`
1303
- behavior on the comment channel — high always @mentions, medium
1304
- @mentions when `mentionOperator: true`) but is no longer a routing
1305
- factor for either channel. `transitionTicketState` suppresses the
1306
- `notify()` dispatch entirely for low-severity transitions (task-level,
1307
- non-terminal story / epic flips) so the comment channel sees only the
1308
- medium-severity story-level events operators expect on the ticket
1309
- timeline. To suppress either channel entirely, set its array to `[]`.
1310
- The schema enums pin closed vocabularies — adding custom event names
1311
- requires loosening the enum in `.agents/scripts/lib/config-schema.js`.
1312
-
1313
- Webhook URL resolution:
1314
-
1315
- - `NOTIFICATION_WEBHOOK_URL` process env var only — loaded from `.env` at the
1316
- project root. The webhook URL is **not** sourced from `.agentrc.json`, and
1317
- (as of Epic #702) is no longer sourced from `.mcp.json`.
1318
-
1319
- Because `notify()` is called in-band from the orchestration SDK, it captures
1320
- changes from:
1321
-
1322
- - The Epic Deliver Runner (coordinator-driven state flips).
1323
- - Per-story scripts (`story-init.js`, `story-close.js`).
1324
- - Any script that routes state changes through `transitionTicketState`.
1325
-
1326
- It does **not** capture manual label clicks in the GitHub UI (no webhook
1327
- receiver). For programmatic orchestration workflows this covers >95% of
1328
- lifecycle transitions.
1329
-
1330
- ### 2. Deliver-runner blocker / HITL notifications
1331
-
1332
- The `NotificationHook` inside the Epic Deliver Runner fires on
1333
- blocker-escalation events (`agent::blocked`) and operator-attention events
1334
- (PR-open hand-off, run cancellation). Fire-and-forget by design; webhook
1335
- failures never block execution.
1336
-
1337
- | Event | Type | Channel | Operator Action |
1338
- | ------------------ | ---------- | ------------------ | ---------------------- |
1339
- | `task-complete` | **INFO** | @mention | Review when convenient |
1340
- | `feature-complete` | **INFO** | @mention | Informational only |
1341
- | `epic-complete` | **INFO** | @mention + webhook | Final review |
1342
- | `pr-opened` | **ACTION** | @mention + webhook | Inspect checks + merge |
1343
- | `epic-blocked` | **ACTION** | webhook | Resolve and re-flip |
1344
- | `wave-transition` | **INFO** | webhook | Informational only |
572
+ - `webhookEvents` — allowlist for `NOTIFICATION_WEBHOOK_URL` deliveries.
573
+
574
+ `transitionTicketState` suppresses the `notify()` dispatch for low-severity
575
+ transitions so the comment channel sees only the medium-severity
576
+ Story-level events operators expect. To suppress a channel entirely, set
577
+ its array to `[]`.
578
+
579
+ **Webhook URL resolution.** `NOTIFICATION_WEBHOOK_URL` process env var only
580
+ loaded from `.env` at the project root. It is **not** sourced from
581
+ `.agentrc.json` or `.mcp.json`.
582
+
583
+ Because `notify()` is called in-band from the orchestration SDK, it
584
+ captures state changes from `deliver-story`, the per-Story scripts
585
+ (`single-story-init.js`, `single-story-close.js`,
586
+ `single-story-confirm-merge.js`), and any script that routes through
587
+ `transitionTicketState`. It does **not** capture manual label clicks in the
588
+ GitHub UI.
589
+
590
+ ### 2. Blocker / HITL notifications
591
+
592
+ Fire-and-forget webhooks fire on blocker-escalation events
593
+ (`agent::blocked`) and operator-attention events (PR-open hand-off, run
594
+ cancellation). Webhook failures never block execution.
1345
595
 
1346
596
  ---
1347
597
 
@@ -1349,106 +599,67 @@ failures never block execution.
1349
599
 
1350
600
  ### Sub-agent CI workflow editing
1351
601
 
1352
- Sub-agents (any agent operating under the framework's default
1353
- `GITHUB_TOKEN`) **cannot edit files under `.github/workflows/**`**. The
1354
- framework's token does not carry the `workflows` permission scope by
1355
- default, so a push that touches a workflow file is rejected by GitHub
1356
- with an error of the shape:
602
+ Sub-agents operating under the framework's default `GITHUB_TOKEN` **cannot
603
+ edit files under `.github/workflows/**`** — the token does not carry the
604
+ `workflows` permission scope, so a push touching a workflow file is
605
+ rejected with:
1357
606
 
1358
607
  > refusing to allow a GitHub App to create or update workflow
1359
608
  > `.github/workflows/<file>.yml` without `workflows` permission
1360
609
 
1361
- This is a hard constraint, not a transient failure. Re-running the same
1362
- push will not succeed.
1363
-
1364
- **When a Story plans a new CI gate**, route the check through a
1365
- `package.json` script rather than adding it directly to the workflow
1366
- YAML. Examples:
1367
-
1368
- - Add the new check to `npm run lint`, `npm run docs:check`, or
1369
- `npm test` so an existing CI job picks it up by transitivity.
1370
- - Wire a new `package.json` script and chain it from one of the
1371
- existing scripts the workflow already invokes.
1372
- - For a check that genuinely cannot be expressed as an npm script,
1373
- surface it as a script anyway (e.g. `npm run check:<name>` →
1374
- `node .agents/scripts/<name>.js`) and call the script from the
1375
- existing `Validate and Test` job's `run:` block — but the YAML edit
1376
- itself must be made by an operator.
1377
-
1378
- Precedent: Epic #2880 Story #2895 Task #2916 intended to add
1379
- `check-lifecycle-doc-drift.js` directly to `.github/workflows/ci.yml`,
1380
- hit this constraint, and worked around it by chaining the check into
1381
- `npm run docs:check`. The functional outcome is identical; the
1382
- workaround is the canonical pattern.
1383
-
1384
- **When a workflow file genuinely must change** (a new top-level job, a
1385
- trigger change, a runner-image bump, etc.), the edit must be made by an
1386
- operator with `Workflows: Read and write` PAT permissions. See
1387
- [§ One-time PAT setup](../../AGENTS.md#one-time-pat-setup) in the root
1388
- `AGENTS.md` for how to provision a PAT with the required scope. The same
1389
- operator surface that release-please relies on is the one that authorizes
1390
- workflow edits.
610
+ This is a hard constraint, not a transient failure. **When a Story plans a
611
+ new CI gate**, route the check through a `package.json` script (add it to
612
+ `npm run lint` / `npm run docs:check` / `npm test`, or wire a new
613
+ `npm run check:<name>` script) so an existing CI job picks it up by
614
+ transitivity. **When a workflow file genuinely must change** (a new job, a
615
+ trigger change, a runner bump), the edit must be made by an operator with
616
+ `Workflows: Read and write` PAT permissions — see
617
+ [`docs/release-operations.md` § One-time PAT setup](../../docs/release-operations.md#one-time-pat-setup).
1391
618
 
1392
619
  ### Worktree config shadow
1393
620
 
1394
- `helpers/epic-deliver-story` and `helpers/single-story-deliver` run inside
1395
- per-Story worktrees under `.worktrees/story-<id>/`. A git worktree checks out the
1396
- **Story branch's own copy** of every repo-tracked file — including
1397
- `.agentrc.json`, `package.json`, `release-please-config.json`, and any
1398
- other config under version control. **Operator edits made in the main
1399
- checkout do NOT propagate to an already-active worktree.** The worktree
1400
- sees the branch's committed contents until the operator either re-edits
1401
- inside the worktree or merges the change into the Story branch.
1402
-
1403
- Symptom: you bump a runtime knob in `<main-repo>/.agentrc.json` (e.g.
1404
- raise `delivery.quality.gates.coverage.timeoutMs`), re-run
1405
- `story-close.js --cwd <worktree>`, and the script still uses the old
1406
- value. The script resolved config from the worktree's stale
1407
- `.agentrc.json`, not the main checkout's edited one. Precedent: Epic
1408
- \#2880 friction note F-W0-6 (Story \#2896 recovery debugging time).
1409
-
1410
- When tuning runtime knobs **mid-Story**:
1411
-
1412
- 1. **Prefer an env-var override** when the knob exposes one (e.g.
1413
- timeouts, log level via `AGENT_LOG_LEVEL`, concurrency caps). Env
1414
- vars are read from the operator's actual shell, not from the
1415
- checked-out config, so they bypass worktree shadow entirely.
1416
- 2. **Edit the file inside the worktree** —
1417
- `.worktrees/<story-id>/.agentrc.json` — so the script sees the bump
1418
- on its next read. Either commit the change on the Story branch (if
1419
- the bump is project-wide and should land with the Story) or leave
1420
- it uncommitted as a scratch tweak that gets discarded when the
1421
- worktree is reaped.
1422
- 3. **Use `.agentrc.local.json`** for per-machine tuning you never want
1423
- to commit. The file is gitignored and layered on top of
1424
- `.agentrc.json` by the config resolver
1425
- (see [`.agents/docs/configuration.md`](configuration.md#per-machine-local-overrides)).
1426
- Note: the local override is still read relative to the script's
1427
- cwd, so for worktree-bound scripts you must place
1428
- `.agentrc.local.json` inside the worktree directory — or invoke the
1429
- script with `--cwd <main-repo>` so the resolver reads from the main
1430
- checkout's local override.
621
+ `helpers/deliver-story` runs inside per-Story worktrees under
622
+ `.worktrees/story-<id>/`. A worktree checks out the **Story branch's own
623
+ copy** of every repo-tracked file — including `.agentrc.json`. **Operator
624
+ edits made in the main checkout do NOT propagate to an already-active
625
+ worktree.** Symptom: you bump a runtime knob in `<main-repo>/.agentrc.json`,
626
+ re-run `single-story-close.js --cwd <worktree>`, and the script still uses
627
+ the old value. When tuning knobs mid-Story:
628
+
629
+ 1. **Prefer an env-var override** when the knob exposes one (timeouts,
630
+ `AGENT_LOG_LEVEL`, concurrency caps) env vars are read from your shell,
631
+ bypassing worktree shadow entirely.
632
+ 2. **Edit the file inside the worktree** (`.worktrees/story-<id>/.agentrc.json`)
633
+ so the script sees the bump on its next read.
634
+ 3. **Use `.agentrc.local.json`** for per-machine tuning you never commit
635
+ (see
636
+ [`configuration.md`](configuration.md#per-machine-local-overrides)) —
637
+ place it inside the worktree, or invoke the script with
638
+ `--cwd <main-repo>` so the resolver reads the main checkout's override.
1431
639
 
1432
640
  Editing the main checkout's `.agentrc.json` only affects **the next**
1433
- `story-init.js` invocation, because new Story branches fork from
1434
- `main`'s current tip and therefore see the new config from the start.
1435
- For Stories already in flight, use one of the three options above.
641
+ `single-story-init.js` invocation, because new Story branches fork from
642
+ `main`'s current tip.
643
+
644
+ ### `Epic: #N` refusal
645
+
646
+ `/deliver` refuses any ticket that still carries an `Epic: #N` footer or is
647
+ not `type::story`. This is expected — v2 has no Epic delivery path. Close
648
+ the ticket or re-plan the work as a v2 Story via `/plan --tickets <id>`.
1436
649
 
1437
650
  ---
1438
651
 
1439
652
  ## Quick reference
1440
653
 
1441
- | Command | Purpose |
1442
- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1443
- | `npx mandrel init` | Cold-start command — install `mandrel` (if absent), `mandrel sync`, bootstrap.js (provisions repo + Projects V2 board, labels, branch protection), then onboarding tail (stack detection, docs scaffolding, doctor gate, `/plan` handoff). |
1444
- | `/plan` | Ideation entrysharpen idea, search duplicates, open Epic, then Tech Spec + decomposition. |
1445
- | `/plan --idea "<seed>"` | Same ideation entry with pre-supplied seed. |
1446
- | `/plan <epicId>` | Existing-Epic mode Tech Spec + decomposition for an Epic Issue already opened. |
1447
- | `/deliver <epicId>` | Drive an Epic end-to-end. Wave loop → close-validation code-reviewretroopens PR to `main` with auto-merge armed. |
1448
- | `/deliver <storyId> [<storyId>...]` | Deliver one or more standalone Stories (no `Epic: #N` reference). Builds a dependency-aware wave plan and fans out one worker per Story per wave. |
1449
- | `/plan` | Plan a one-off Story outside an Epic backlog. |
1450
- | *helper* `workflows/helpers/epic-deliver-story` | Per-Story worker called by `/deliver`'s wave loop; not an operator slash command. See [`helpers/epic-deliver-story.md`](../workflows/helpers/epic-deliver-story.md). |
1451
- | *helper* `workflows/helpers/single-story-deliver` | Per-Story worker called by `/deliver`; not an operator slash command. See [`helpers/single-story-deliver.md`](../workflows/helpers/single-story-deliver.md). |
1452
- | *helper* `workflows/helpers/code-review.md` | Auto-invoked by `/deliver`'s `delivery.code-review` state (scope: epic); not a slash command. |
1453
- | `/git-deliver` | Ad-hoc delivery of working-tree changes — detects the git setup and escalates to commit, commit + push, or commit + push + PR (auto-merge armed). |
1454
- | `epic-reconcile.js --explicit-delete` | Hard reset — close orphaned Epic-scoped issues per `.agents/epics/<id>.yaml` |
654
+ | Command | Purpose |
655
+ | --- | --- |
656
+ | `npx mandrel init` | Cold-start — install `mandrel` (if absent), `mandrel sync`, `bootstrap.js` (provisions repo + Projects V2 board, labels, branch protection), then the onboarding tail (stack detection, docs scaffolding, doctor gate, `/plan` handoff). |
657
+ | `/plan --seed "<text>"` | Plan from chat text interrogate author **one Story by default** persist `type::story`. |
658
+ | `/plan --seed-file <path>` | Plan from on-disk notes / a plan seed (the `/audit-to-stories` handoff). |
659
+ | `/plan --tickets <ids>` | Analyze existing issue(s) into proper Stories (prefer an N=1 rewrite). |
660
+ | `/deliver <storyId>` | Deliver one Story via `helpers/deliver-story` `story-<id>`PR → `main`. |
661
+ | `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order (resolved from live state), then run the per-run epilogue. |
662
+ | *helper* `helpers/deliver-story` | Per-Story engine invoked by `/deliver`; not an operator slash command. See [`deliver-story.md`](../workflows/helpers/deliver-story.md). |
663
+ | `/audit-to-stories` | Convert audit findings into a plan seed / Stories `/plan --seed-file`. |
664
+ | `/qa-explore` · `/qa-assist` · `/qa-run` | Agent-led / human-led exploratory QA and the automated Gherkin harness. |
665
+ | `/git-deliver` | Ad-hoc delivery of working-tree changes detects the git setup and escalates to commit, commit + push, or commit + push + PR (auto-merge armed). |