mandrel 1.93.0 → 2.0.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 (463) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/acceptance-critic.md +129 -0
  3. package/.agents/agents/story-worker.md +161 -0
  4. package/.agents/docs/SDLC.md +489 -1285
  5. package/.agents/docs/agentrc-reference.json +177 -67
  6. package/.agents/docs/configuration.md +108 -136
  7. package/.agents/docs/execution-reference.md +44 -22
  8. package/.agents/docs/quality-gates.md +13 -19
  9. package/.agents/docs/workflows.md +3 -3
  10. package/.agents/instructions.md +107 -108
  11. package/.agents/rules/ci-remediation.md +8 -12
  12. package/.agents/rules/git-conventions-reference.md +224 -0
  13. package/.agents/rules/git-conventions.md +42 -223
  14. package/.agents/rules/security-baseline.md +5 -0
  15. package/.agents/rules/testing-standards.md +106 -13
  16. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  17. package/.agents/schemas/agentrc.schema.json +71 -201
  18. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  20. package/.agents/schemas/risk-verdict.schema.json +0 -13
  21. package/.agents/scripts/acceptance-eval.js +62 -18
  22. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  23. package/.agents/scripts/analyze-execution.js +1 -1
  24. package/.agents/scripts/audit-to-stories.js +7 -7
  25. package/.agents/scripts/boot-sweep.js +1 -1
  26. package/.agents/scripts/check-context-budget.js +62 -5
  27. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  28. package/.agents/scripts/check-prepush-recovery.js +1 -1
  29. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  30. package/.agents/scripts/diagnose-friction.js +0 -6
  31. package/.agents/scripts/lib/Logger.js +6 -10
  32. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  33. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  34. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  35. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  36. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  37. package/.agents/scripts/lib/baselines/reader.js +1 -6
  38. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  39. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  40. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
  41. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  42. package/.agents/scripts/lib/checks/index.js +2 -1
  43. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  44. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  45. package/.agents/scripts/lib/cli-args.js +16 -7
  46. package/.agents/scripts/lib/close-validation/gates.js +160 -22
  47. package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
  48. package/.agents/scripts/lib/config/ci.js +6 -31
  49. package/.agents/scripts/lib/config/delivery-routing.js +103 -0
  50. package/.agents/scripts/lib/config/explain.js +57 -36
  51. package/.agents/scripts/lib/config/limits.js +17 -58
  52. package/.agents/scripts/lib/config/paths.js +0 -2
  53. package/.agents/scripts/lib/config/quality.js +1 -1
  54. package/.agents/scripts/lib/config/runners.js +17 -50
  55. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  56. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  57. package/.agents/scripts/lib/config-resolver.js +3 -8
  58. package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
  59. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -14
  60. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  61. package/.agents/scripts/lib/dependency-parser.js +3 -2
  62. package/.agents/scripts/lib/doc-tiers.js +39 -4
  63. package/.agents/scripts/lib/duplicate-search.js +211 -41
  64. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  65. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  66. package/.agents/scripts/lib/framework-version.js +2 -3
  67. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  68. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  69. package/.agents/scripts/lib/git-utils.js +32 -6
  70. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  71. package/.agents/scripts/lib/label-constants.js +10 -23
  72. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  73. package/.agents/scripts/lib/observability/active-story-env.js +112 -3
  74. package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
  75. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  76. package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
  77. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  78. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
  79. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
  80. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  81. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
  82. package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
  83. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  84. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  85. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  86. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  87. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  88. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  89. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  90. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  91. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  92. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  93. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  94. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  95. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  96. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  97. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  98. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
  99. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  100. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  101. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  102. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  103. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  104. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  105. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  106. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  107. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  108. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +14 -14
  109. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  110. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  111. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  112. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  113. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  114. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  115. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  116. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  117. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  118. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  119. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  120. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  121. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  122. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  123. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  124. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  130. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  131. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  132. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  133. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  134. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  135. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  136. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  137. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  138. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  139. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  140. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  141. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  142. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  143. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  144. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  145. package/.agents/scripts/lib/orchestration/ticket-validator.js +3 -8
  146. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  147. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  148. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  149. package/.agents/scripts/lib/planning-corpus.js +16 -11
  150. package/.agents/scripts/lib/preflight-runner.js +2 -2
  151. package/.agents/scripts/lib/provider-factory.js +1 -1
  152. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  153. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  154. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  155. package/.agents/scripts/lib/story-adjacency.js +11 -14
  156. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  157. package/.agents/scripts/lib/story-plan.js +2 -4
  158. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  159. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  160. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  161. package/.agents/scripts/lib/validation-evidence.js +1 -1
  162. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  163. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  164. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  165. package/.agents/scripts/lint-issue-body.js +71 -40
  166. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  167. package/.agents/scripts/notify.js +4 -3
  168. package/.agents/scripts/plan-context.js +64 -74
  169. package/.agents/scripts/plan-persist.js +121 -280
  170. package/.agents/scripts/plan-run-epilogue.js +97 -0
  171. package/.agents/scripts/post-structured-comment.js +38 -0
  172. package/.agents/scripts/providers/github/issues.js +17 -33
  173. package/.agents/scripts/providers/github/mappers.js +0 -12
  174. package/.agents/scripts/providers/github/tickets.js +2 -5
  175. package/.agents/scripts/resolve-plan-run.js +117 -0
  176. package/.agents/scripts/signals-view.js +24 -19
  177. package/.agents/scripts/single-story-close.js +11 -14
  178. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  179. package/.agents/scripts/single-story-init.js +29 -20
  180. package/.agents/scripts/stories-wave-tick.js +6 -6
  181. package/.agents/scripts/story-plan.js +26 -47
  182. package/.agents/scripts/sync-claude-agents.js +165 -0
  183. package/.agents/scripts/update-ticket-state.js +37 -15
  184. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  185. package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
  186. package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
  187. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
  188. package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
  189. package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
  190. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
  191. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  192. package/.agents/skills/skills.index.json +8 -418
  193. package/.agents/starter-agentrc.json +0 -5
  194. package/.agents/templates/agent-protocol.md +9 -10
  195. package/.agents/workflows/audit-architecture.md +3 -3
  196. package/.agents/workflows/audit-clean-code.md +3 -3
  197. package/.agents/workflows/audit-dependencies.md +3 -3
  198. package/.agents/workflows/audit-devops.md +3 -3
  199. package/.agents/workflows/audit-documentation.md +5 -5
  200. package/.agents/workflows/audit-lighthouse.md +3 -3
  201. package/.agents/workflows/audit-navigability.md +3 -2
  202. package/.agents/workflows/audit-performance.md +3 -3
  203. package/.agents/workflows/audit-privacy.md +3 -3
  204. package/.agents/workflows/audit-quality.md +3 -3
  205. package/.agents/workflows/audit-security.md +3 -3
  206. package/.agents/workflows/audit-seo.md +3 -3
  207. package/.agents/workflows/audit-sre.md +3 -3
  208. package/.agents/workflows/audit-to-stories.md +20 -20
  209. package/.agents/workflows/audit-ux-ui.md +3 -3
  210. package/.agents/workflows/deliver.md +122 -131
  211. package/.agents/workflows/git-cleanup.md +3 -4
  212. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  213. package/.agents/workflows/helpers/acceptance-self-eval.md +52 -40
  214. package/.agents/workflows/helpers/code-review.md +70 -193
  215. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  216. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  217. package/.agents/workflows/helpers/diagnose.md +10 -10
  218. package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
  219. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  220. package/.agents/workflows/helpers/signals.md +16 -16
  221. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  222. package/.agents/workflows/mandrel-update.md +3 -2
  223. package/.agents/workflows/plan.md +112 -145
  224. package/.agents/workflows/qa-assist.md +24 -30
  225. package/.agents/workflows/qa-explore.md +29 -38
  226. package/.agents/workflows/qa-run.md +2 -2
  227. package/README.md +9 -8
  228. package/docs/CHANGELOG.md +46 -0
  229. package/lib/cli/registry.js +95 -0
  230. package/lib/migrations/index.js +6 -5
  231. package/package.json +5 -3
  232. package/.agents/personas/architect.md +0 -113
  233. package/.agents/personas/devops-engineer.md +0 -38
  234. package/.agents/personas/engineer-mobile.md +0 -120
  235. package/.agents/personas/engineer-web.md +0 -111
  236. package/.agents/personas/engineer.md +0 -119
  237. package/.agents/personas/product.md +0 -94
  238. package/.agents/personas/project-manager.md +0 -114
  239. package/.agents/personas/qa-engineer.md +0 -95
  240. package/.agents/personas/refactorer.md +0 -113
  241. package/.agents/personas/security-engineer.md +0 -112
  242. package/.agents/personas/sre.md +0 -86
  243. package/.agents/personas/technical-writer.md +0 -101
  244. package/.agents/personas/ux-designer.md +0 -95
  245. package/.agents/schemas/dispatch-manifest.json +0 -232
  246. package/.agents/schemas/epic-spec.schema.json +0 -153
  247. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  248. package/.agents/scripts/dispatcher.js +0 -295
  249. package/.agents/scripts/epic-audit-prepare.js +0 -497
  250. package/.agents/scripts/epic-audit-recheck.js +0 -274
  251. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  252. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  253. package/.agents/scripts/epic-deliver-prepare.js +0 -590
  254. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  255. package/.agents/scripts/epic-plan-clarity.js +0 -211
  256. package/.agents/scripts/epic-plan-decompose.js +0 -54
  257. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  258. package/.agents/scripts/epic-plan-spec.js +0 -64
  259. package/.agents/scripts/epic-reconcile.js +0 -625
  260. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  261. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  262. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  263. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  264. package/.agents/scripts/lib/config/preflight.js +0 -58
  265. package/.agents/scripts/lib/config/retro.js +0 -77
  266. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  267. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  268. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  269. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -660
  270. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  271. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  272. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  273. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  274. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  275. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  276. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  277. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  278. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  279. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  280. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  281. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  282. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  283. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  284. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  285. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  286. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  287. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  288. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -388
  289. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  290. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  291. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  292. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  293. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  294. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  295. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  296. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  297. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  298. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  299. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  300. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  301. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  302. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  303. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  304. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  305. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  306. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  307. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  308. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  309. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  310. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  311. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  312. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  313. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
  314. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  315. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  316. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  317. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -278
  318. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  319. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  320. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  321. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  322. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  323. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  324. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  325. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  326. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  327. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  328. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  329. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  330. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  331. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  332. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  333. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  334. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  335. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
  336. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  337. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  338. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  339. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  340. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  341. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  342. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  343. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  344. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  345. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  346. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  347. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  348. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  349. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  350. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  351. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  352. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  353. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  354. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  355. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  356. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  357. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  358. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  359. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  360. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  361. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -301
  362. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  363. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  364. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  365. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  366. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  367. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  368. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  369. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  370. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  371. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  372. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  373. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  374. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  375. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  376. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  377. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  378. package/.agents/scripts/lib/spec/index.js +0 -36
  379. package/.agents/scripts/lib/spec/loader.js +0 -425
  380. package/.agents/scripts/lib/spec/state.js +0 -208
  381. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  382. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  383. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  384. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  385. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  386. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  387. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  388. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  389. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  390. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  391. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  392. package/.agents/scripts/lifecycle-emit.js +0 -510
  393. package/.agents/scripts/plan-critics.js +0 -199
  394. package/.agents/scripts/retro-run.js +0 -218
  395. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  396. package/.agents/scripts/story-close.js +0 -294
  397. package/.agents/scripts/story-init.js +0 -599
  398. package/.agents/scripts/story-phase.js +0 -369
  399. package/.agents/scripts/wave-tick.js +0 -335
  400. package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
  401. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
  402. package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
  403. package/.agents/skills/core/code-simplification/SKILL.md +0 -389
  404. package/.agents/skills/core/context-engineering/SKILL.md +0 -309
  405. package/.agents/skills/core/context-engineering/examples.md +0 -58
  406. package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
  407. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  408. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  409. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  410. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  411. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  412. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  413. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -413
  414. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  415. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
  416. package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
  417. package/.agents/skills/core/idea-refinement/examples.md +0 -437
  418. package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
  419. package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
  420. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
  421. package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
  422. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
  423. package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
  424. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
  425. package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
  426. package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
  427. package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
  428. package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
  429. package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
  430. package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
  431. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
  432. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
  433. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
  434. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
  435. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
  436. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
  437. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
  438. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
  439. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
  440. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
  441. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
  442. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
  443. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
  444. package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
  445. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
  446. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
  447. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
  448. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
  449. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
  450. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
  451. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
  452. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
  453. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
  454. package/.agents/workflows/helpers/deliver-epic.md +0 -955
  455. package/.agents/workflows/helpers/deliver-stories.md +0 -440
  456. package/.agents/workflows/helpers/epic-audit.md +0 -189
  457. package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
  458. package/.agents/workflows/helpers/epic-testing.md +0 -125
  459. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  460. package/.agents/workflows/helpers/plan-epic.md +0 -351
  461. package/.agents/workflows/helpers/plan-story.md +0 -251
  462. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  463. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -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 (optionally grouped by a `plan-run::<id>` label);
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,139 @@ 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`, plus
53
+ `risk-verdict.json` (axes + summary only no `deliveryShape`).
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** (risk-routed; typically skipped for N=1
58
+ low-risk) then `plan-persist.js` runs every deterministic gate and
59
+ creates Story issue(s) with `type::story` + `agent::ready` (plus a
60
+ shared `plan-run::<id>` label when N>1).
61
+
62
+ 2. **Deliver the Story.** Run [`/deliver <storyId>`](../workflows/deliver.md)
63
+ (or `/deliver <a> <b> …`, or `/deliver --run <planRunId>` for a
64
+ multi-Story plan-run) in your IDE. `/deliver` owns input resolution and
65
+ `depends_on` sequencing only every Story runs through
66
+ [`helpers/deliver-story`](../workflows/helpers/deliver-story.md), the
67
+ single v2 delivery engine. Per-Story it:
68
+
69
+ 1. **Init** (`single-story-init.js`) — acquires the Story lease, cuts
70
+ `story-<id>` from `main`, materializes a worktree, flips to
71
+ `agent::executing`.
72
+ 2. **Implement** the agent delivers the Story in one guarded session
73
+ against its inline `acceptance[]` / `verify[]` contract (optional
74
+ `## Slicing` intra-session checkpoints).
75
+ 3. **Acceptance self-eval** a bounded, risk-routed critic loop scores
76
+ the working diff against each acceptance item before close (see
77
+ [`helpers/acceptance-self-eval`](../workflows/helpers/acceptance-self-eval.md)).
78
+ 4. **Ceremony** risk-routed acceptance critics, review depth, and
79
+ audit lenses (`ceremony-routing.js`).
80
+ 5. **Close** (`single-story-close.js`) — runs close-validation gates,
81
+ the maker-blind Story-scope code review, pushes `story-<id>`, opens
82
+ a PR to `main`, and (under the default `delivery.ci.autoMerge:
83
+ "trust-ci"`) arms GitHub native auto-merge. The Story flips to
84
+ `agent::closing` (issue stays OPEN).
85
+ 6. **CI watch + fix** watches required checks to green, fixing and
86
+ re-pushing on red.
87
+ 7. **Confirm merge** (`single-story-confirm-merge.js`) on a confirmed
88
+ `MERGED` PR the Story flips to `agent::done`; local branch cleanup
89
+ and Projects-v2 Status re-assert run out-of-band.
90
+
91
+ For a multi-Story plan-run, `/deliver` sequences ready Stories by
92
+ `depends_on` and runs the per-run epilogue (audit roster · follow-up
93
+ roll-up · sibling coherence) once after the last Story lands.
131
94
 
132
95
  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.
96
+ conventions, HITL escalation, audit lenses — that you only need when the
97
+ default flow requires adjustment. It intentionally **links** to
98
+ [`plan.md`](../workflows/plan.md) and [`deliver.md`](../workflows/deliver.md)
99
+ rather than re-documenting the ceremony they own.
135
100
 
136
101
  ---
137
102
 
138
103
  ## Core Principles
139
104
 
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.
105
+ - **Layered state stores with explicit precedence.** Ticket status lives
106
+ in GitHub Issues and Labels; the lifecycle bus
107
+ (`temp/run-<id>/lifecycle.ndjson`) is the canonical resume target for
108
+ in-flight runs; structured comments (`story-run-progress`,
109
+ `verification-results`, retro) are the operator-visible rollup. The
110
+ stores, their owners, and their conflict-resolution rules are listed in
111
+ [§ State stores](#state-stores) — that matrix is the single source of
112
+ truth for "who owns which write."
113
+ - **Provider Abstraction.** Orchestration flows through
114
+ `ITicketingProvider`, an abstract interface with a shipped GitHub
115
+ implementation.
116
+ - **Story-level branching.** All work for a Story lands on the shared
117
+ `story-<id>` branch. Each Story reaches `main` through its own PR
118
+ (squash + required checks); there is **no** `epic/<id>` integration
119
+ branch and **no** `--no-ff` wave merge.
120
+ - **One delivery engine.** `/deliver` resolves and sequences a Story set;
121
+ `helpers/deliver-story` executes each Story identically (trivial or
122
+ large). Story sub-agents run inside the operator's Claude session via
123
+ the Agent tool worktree filesystem isolation is preserved; only the
124
+ subprocess boundary is gone.
125
+ - **PR is the sole promotion gate.** Delivery ends with a PR open against
126
+ `main` and (by default) GitHub native auto-merge armed; the workflow
127
+ itself never executes `git merge` against `main`. Branch protection on
128
+ `main` enforces required checks before the merge button (auto or
129
+ manual) fires.
130
+ - **HITL-minimal by default.** Exactly one mandatory operator touchpoint
131
+ on the happy path — blocker resolution mid-run. PR merge is autonomous
132
+ via the armed auto-merge; the operator becomes a second touchpoint only
133
+ when they disarm auto-merge (`--no-auto-merge` / `delivery.ci.autoMerge:
134
+ "strict"`) or when required checks fail and need remediation.
178
135
 
179
136
  ---
180
137
 
181
138
  ## State stores
182
139
 
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. |
140
+ Mandrel writes orchestration state across several distinct stores. Each
141
+ store has one canonical writer and one well-defined idempotency key;
142
+ conflicts are resolved in the **Conflict resolution** column. Run-scoped
143
+ artifacts live under `temp/run-<id>/` (standalone Stories under
144
+ `temp/standalone/stories/story-<id>/`); the `run-<id>` directory naming is
145
+ historical (it predates the Story-centric cutover) but remains the live
146
+ on-disk layout resolved by
147
+ [`lib/config/temp-paths.js`](../scripts/lib/config/temp-paths.js).
148
+
149
+ | State Store | Owner (canonical writer) | Mutation API | Idempotency key | Conflict resolution |
150
+ | --- | --- | --- | --- | --- |
151
+ | 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. |
152
+ | `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. |
153
+ | `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. |
154
+ | Lifecycle ledger NDJSON | `lifecycle-emit.js` (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. |
155
+ | 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. |
156
+ | 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. |
157
+ | 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. |
158
+
159
+ > The `gh pr merge` merge-lockout lint rule keeps the merge command
160
+ > confined to the sanctioned close path; no other production caller may
161
+ > shell it.
200
162
 
201
163
  ---
202
164
 
@@ -210,48 +172,42 @@ graph LR
210
172
 
211
173
  subgraph Phase0 ["Phase 0: Bootstrap"]
212
174
  direction TB
213
- Z["👤 npx mandrel init<br/>(install → sync → prompt → bootstrap.js → onboarding tail → /plan handoff)"]:::manual
175
+ Z["👤 npx mandrel init<br/>(install → sync → bootstrap.js → onboarding tail → /plan handoff)"]:::manual
214
176
  end
215
177
 
216
- subgraph Phase1 ["Phase 1: Initiation"]
178
+ subgraph Phase1 ["Phase 1: Plan"]
217
179
  direction TB
218
- A["👤 /plan (ideation)<br/>or 👤 Create GitHub Epic + /plan &lt;id&gt;"]:::manual
180
+ A["👤 /plan --seed | --seed-file | --tickets"]:::manual
181
+ B["🤖 interrogate → author → persist"]:::agentic
182
+ A --> B
183
+ B -.-> B_Art["📄 type::story issue(s)<br/>(+ optional plan-run::&lt;id&gt;)"]:::artifact
219
184
  end
220
185
 
221
- subgraph Phase2 ["Phase 2: Planning"]
186
+ subgraph Phase2 ["Phase 2: Deliver"]
222
187
  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
188
+ E["👤 /deliver &lt;storyId&gt;<br/>(or --run &lt;planRunId&gt;)"]:::manual
189
+ F["🤖 deliver-story: story-&lt;id&gt; from main<br/>implement → self-eval → ceremony → close"]:::agentic
190
+ G["🤖 close-validation → code-review → open PR"]:::agentic
234
191
  E --> F --> G
235
192
  G -.-> G_Art["📄 PR open against main"]:::artifact
236
193
  end
237
194
 
238
- subgraph Phase4 ["Phase 4: PR merge (auto by default)"]
195
+ subgraph Phase3 ["Phase 3: PR merge (auto by default)"]
239
196
  direction TB
240
197
  H["🤖 Auto-merge armed → PR lands when checks pass<br/>(👤 operator may disarm to merge manually)"]:::agentic
241
198
  end
242
199
 
243
200
  Z --> A
244
- A --> C
245
- D --> E
201
+ B --> E
246
202
  G --> H
247
203
  ```
248
204
 
249
205
  ---
250
206
 
251
- ## Phase 0: Bootstrap (One-Time Setup)
207
+ ## Phase 0: Bootstrap (one-time setup)
252
208
 
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
209
+ Before any workflow, bootstrap your project to seed `.agentrc.json`, wire
210
+ the framework system prompt, and create the GitHub labels, Projects V2
255
211
  fields, and (when enabled) main-branch protection the orchestration engine
256
212
  depends on.
257
213
 
@@ -261,916 +217,258 @@ The canonical cold-start path is a single command:
261
217
  npx mandrel init
262
218
  ```
263
219
 
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.
220
+ `mandrel init` installs `mandrel` (when `./.agents/` is absent),
221
+ materializes `./.agents/` via `mandrel sync`, then presents a two-option
222
+ prompt: **configure now** (option 1 → runs `node
223
+ .agents/scripts/bootstrap.js`, forwarding any flags you pass) or **just
224
+ the files** (option 2 re-run `mandrel init` any time to configure
225
+ later). `--assume-yes` skips the prompt and proceeds straight to configure;
226
+ a non-TTY run without it defaults to files-only so GitHub provisioning
227
+ never runs unattended. `bootstrap.js`:
228
+
229
+ 1. **Provisions a cold start.** Initializes the local git repo (with a
230
+ first commit) when absent, creates the GitHub repo (`gh repo create
231
+ --source=. --push`; choose visibility with `--visibility
232
+ private|public|internal`, default `private`), and creates the Projects
233
+ V2 board (`gh project create`) when it doesn't exist. No pre-created
234
+ repo or remote is required.
235
+ 2. **Seeds `.agentrc.json`** from `.agents/starter-agentrc.json` (the
236
+ `github` section carries owner, repo, base branch, operator handle, and
237
+ project number — inferred from your local `git` config where possible).
238
+ See `.agents/docs/agentrc-reference.json` for the exhaustive key
239
+ reference.
282
240
  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
241
+ `github.branchProtection.enforce` is `true` (default) — creates or
242
+ merges branch protection on `main` with the project's
243
+ `github.branchProtection.requiredChecks` as required status checks.
244
+ This step is load-bearing because PR merges to `main` are the sole
287
245
  promotion gate.
288
246
 
289
247
  When `.agents/` is already materialized you can run the bootstrap directly
290
248
  (`node .agents/scripts/bootstrap.js`). The guided first-run steps (stack
291
249
  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.
250
+ handoff) are part of `mandrel init`'s configure path.
295
251
 
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.
252
+ > [!NOTE] Bootstrap runs once per repository. It is safe to re-run —
253
+ > existing labels, fields, and branch-protection entries are preserved;
254
+ > missing ones are added.
299
255
 
300
256
  ---
301
257
 
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.
258
+ ## Phase 1: Planning
259
+
260
+ Planning is owned end-to-end by [`/plan`](../workflows/plan.md). Rather than
261
+ re-document the ceremony here, this section states the contract the rest of
262
+ the SDLC depends on:
263
+
264
+ - **Entry is text or tickets, never Epic.** The only accepted invocations
265
+ are `--seed`, `--seed-file`, and `--tickets`. There is no `--idea`, no
266
+ `--one-pager`, no `--from-notes`, and no positional `/plan <epicId>`.
267
+ - **One Story by default.** `/plan` authors a single `type::story` issue
268
+ whose body carries a folded `## Spec` (inline only never spilled to
269
+ `docs/`) plus top-level `acceptance[]` / `verify[]`. It splits into N>1
270
+ siblings (sharing a `plan-run::<id>` label + `depends_on` edges) **only**
271
+ under the default-single split policy: near-zero overlap or a genuine
272
+ architectural seam. Coupled work stays one Story and is decomposed inside
273
+ `## Slicing` as intra-session checkpoints, not sibling tickets.
274
+ - **No Epic-scale ceremony on the default path.** N=1 skips the
275
+ Epic-era Tech Spec / Acceptance Table / clarity-gate / decompose /
276
+ reconciler machinery. `plan-persist.js` runs the deterministic gates
277
+ (ticket validator, split policy, reachability, budget) and for N>1 —
278
+ `assertAcceptancePartition` so every acceptance criterion belongs to
279
+ exactly one Story.
280
+ - **Handoff.** Persist creates the Story issue(s) at `agent::ready` and
281
+ names the delivery command: `/deliver <storyId>` (or `/deliver --run
282
+ <planRunId>`).
283
+
284
+ Optional split advisory notes come from
285
+ [`core/scope-triage`](../skills/core/scope-triage/SKILL.md); there is no
286
+ `epic|story` routing verdict, scorer, schema field, or label transition
287
+ behind them.
288
+
289
+ Audit findings enter planning through
290
+ [`/audit-to-stories`](../workflows/audit-to-stories.md), which groups and
291
+ deduplicates findings and hands off via `--emit-plan-seed` →
292
+ `/plan --seed-file <path>`.
399
293
 
400
294
  ---
401
295
 
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
- ---
296
+ ## Phase 2: Delivery
611
297
 
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.
298
+ Delivery is owned end-to-end by [`/deliver`](../workflows/deliver.md), which
299
+ delegates every Story to
300
+ [`helpers/deliver-story`](../workflows/helpers/deliver-story.md). This
301
+ section states the contract; the per-Story step detail (init, implement,
302
+ self-eval, ceremony, close, CI watch, confirm-merge, cleanup) lives in the
303
+ `deliver-story` workflow and its
304
+ [reference](../workflows/helpers/deliver-story-reference.md).
647
305
 
648
306
  ### Invocation modes
649
307
 
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.
308
+ | Mode | Entry point | When to use |
309
+ | --- | --- | --- |
310
+ | **Single Story** | `/deliver <storyId>` | Deliver one Story end-to-end; ends with a PR open to `main`. |
311
+ | **Story set** | `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order (default concurrency **3**); each lands through its own PR. |
312
+ | **Plan-run** | `/deliver --run <planRunId>` | Resolve Stories labeled `plan-run::<id>`, sequence them, and run the per-run epilogue after the set lands. |
313
+ | **Story worker (internal)** | *helper* `helpers/deliver-story <storyId>` | Per-Story engine invoked internally by `/deliver`; not an operator slash command. |
744
314
 
745
- ### State sync
746
-
747
- Agents update their state in real-time on GitHub:
315
+ The single operator-facing entry point is `/deliver`. It performs no
316
+ git/label mutations itself — `deliver-story` owns every script invocation
317
+ per Story. Any ticket that is not `type::story`, or that still carries an
318
+ `Epic: #N` reference, is a hard error naming the ID and the fix (close or
319
+ re-plan as a v2 Story).
748
320
 
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.
321
+ ### Branch model (authoritative)
761
322
 
762
- ### Dependency unblocking
323
+ ```text
324
+ story-<id> → PR → main (squash + required checks)
325
+ ```
763
326
 
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.
327
+ There is no `epic/<id>` integration branch and no `--no-ff` wave merge.
328
+ Dependent Stories land sequentially so each builds on the previous merge to
329
+ `main`.
766
330
 
767
- ### Story assignment (deterministic)
331
+ ### Ceremony
768
332
 
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.
333
+ Ceremony depth is selected by `delivery.routing.ceremonyProfile`
334
+ (`minimal` | `standard` | `strict`, default `standard`) and the Story's
335
+ own planning risk. Hard gates (lint / test / format / coverage / CRAP /
336
+ maintainability) always run at close risk never disables them; it only
337
+ tunes acceptance-critic mode, review depth, and audit-lens selection. The
338
+ full profile × scope matrix lives in
339
+ [`deliver.md` § Ceremony](../workflows/deliver.md).
774
340
 
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.
341
+ ### State sync
778
342
 
779
- ### Launch-time dependency guard
343
+ Agents update their state in real time on GitHub, always through
344
+ `update-ticket-state.js`:
345
+
346
+ - **Labels**: `agent::ready` → `agent::executing` → `agent::closing` →
347
+ `agent::done`. The `agent::done` flip happens only after
348
+ `single-story-confirm-merge.js` confirms the PR merged. When a
349
+ `projectNumber` is configured, the Projects v2 Status column is synced on
350
+ each transition (and re-asserted after merge to beat the board's late
351
+ built-in write).
352
+ - **Acceptance/verify**: the agent works the Story's inline `acceptance[]`
353
+ / `verify[]` arrays; `verify[]` commands are consumed as required
354
+ evidence by the acceptance self-eval loop.
355
+ - **Friction**: friction is posted as a structured comment on the **Story**
356
+ (`diagnose-friction.js`), and rolls up into the retro and, for N>1, the
357
+ per-run follow-up roll-up.
780
358
 
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.
359
+ ### Cross-clone coordination
787
360
 
788
- The guard runs identically on web and local.
361
+ Concurrent runs are serialised by **two distinct layers**:
362
+
363
+ - **Filesystem locks are same-machine-only.** The single-story sweep lock
364
+ (`sweep-lock.js`) is a single-file rendezvous keyed on a local process
365
+ PID + mtime TTL. Because a PID is only meaningful on its own machine and
366
+ `.git/` is never committed, these locks coordinate only the worktrees and
367
+ sessions on **one** clone.
368
+ - **The assignee-as-lease is the cross-clone layer.** To stop two clones
369
+ from both *starting* the same Story, `deliver-story` takes an exclusive,
370
+ time-bounded claim on the ticket via
371
+ [`ticket-lease.js`](../scripts/lib/orchestration/ticket-lease.js), riding
372
+ the ticket's GitHub `assignees` field so a live foreign claim is visible
373
+ to every clone. The standalone lease **fails closed** on a foreign
374
+ assignee; `--steal` is the only override. See
375
+ [`README.md` § Multi-developer coordination](../README.md#multi-developer-coordination).
789
376
 
790
377
  ### Concurrent close — push retry
791
378
 
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:
798
-
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]`.
802
-
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.
806
-
807
- ### Cross-clone coordination
808
-
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.
379
+ `single-story-close.js` syncs the Story branch from `origin/main` before
380
+ pushing and opening/locating the PR. Bounded retry constants live in
381
+ `.agents/scripts/lib/config/runners.js` (`DEFAULT_STORY_MERGE_RETRY`:
382
+ 3 attempts, `[250, 500, 1000]` ms backoff). A real content conflict aborts
383
+ the loop with a clear error, leaves the tree clean, and exits non-zero for
384
+ manual resolution.
961
385
 
962
386
  ---
963
387
 
964
388
  ## HITL (Human-in-the-Loop) model
965
389
 
966
390
  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
391
+ after `/deliver` fires (blocker resolution). PR merge is autonomous via
392
+ armed auto-merge; the operator becomes a second touchpoint only by
393
+ exception.
394
+
395
+ 1. **Blocker resolution (mandatory when triggered).** If a Story hits an
396
+ unresolvable condition, it flips to `agent::blocked`, posts a structured
397
+ friction comment, and fires the notification webhook (fire-and-forget).
398
+ The operator resolves the underlying issue (a hand-fix commit on the
399
+ Story branch, or a scope edit on the ticket) and flips the Story back to
979
400
  `agent::executing` to resume.
980
401
  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.
402
+ close, `deliver-story` opens a PR to `main` and arms GitHub native
403
+ auto-merge. When required checks pass, the PR lands without a second
404
+ operator visit and the standard label transition flips the Story to
405
+ `agent::done`. The operator becomes a touchpoint only when they (a)
406
+ disarm auto-merge (`--no-auto-merge` per run, or
407
+ `delivery.ci.autoMerge: "strict"`) to inspect checks / the
408
+ `verification-results` comment / the retro before merging by hand, or
409
+ (b) checks fail and need remediation.
993
410
 
994
411
  ### What triggers `agent::blocked`
995
412
 
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
413
+ - Unresolvable merge conflict automated strategies cannot reconcile.
414
+ - Test failures that persist after automated remediation.
415
+ - Ambiguity requiring a product/scope decision the agent cannot make from
416
+ ticket context alone.
417
+ - A destructive action not pre-authorized by the ticket body.
418
+ - External-service failure preventing progress (GitHub API 5xx loop, npm
1003
419
  registry down).
1004
- - Wave concurrency exhausted for an unbounded time (possible deadlock).
420
+ - Acceptance self-eval exhausting its bounded round cap with criteria still
421
+ unmet.
1005
422
 
1006
423
  ### What is *not* gated at runtime
1007
424
 
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.
425
+ - `risk::high` Stories **run without pause.** The label is planning/audit
426
+ metadata and retro telemetry only; the sole runtime pause point is
427
+ `agent::blocked`. Branch protection on `main` and blocker escalation are
428
+ the runtime defenses for destructive actions.
429
+ - Individual Story completion — no per-Story approval prompt beyond the PR
430
+ merge gate.
1097
431
 
1098
432
  ---
1099
433
 
1100
434
  ## Testing strategy
1101
435
 
1102
436
  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
437
+ belongs to exactly one tier — **unit**, **contract**, or **e2e /
438
+ acceptance**. The canonical tier definitions, assertion-placement rules,
439
+ and coverage thresholds live in
440
+ [`rules/testing-standards.md`](../rules/testing-standards.md); Gherkin
441
+ authoring for the acceptance tier is governed by
1108
442
  [`rules/gherkin-standards.md`](../rules/gherkin-standards.md).
1109
443
 
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).
444
+ Write a Story's acceptance criteria in Gherkin-compatible `Given / When /
445
+ Then` form so the acceptance suite can lift them into executable `.feature`
446
+ files.
1114
447
 
1115
448
  ### QA workflows: explore, assist, and run-harness
1116
449
 
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`.
450
+ Three complementary QA workflows sit alongside the automated pyramid, all
451
+ reading the consumer's `qa.*` contract from `.agentrc.json` through
452
+ [`scripts/lib/qa/resolve-qa-contract.js`](../scripts/lib/qa/resolve-qa-contract.js)
453
+ (which fails loudly when no `qa` block is bound):
454
+
455
+ - **[`/qa-explore`](../workflows/qa-explore.md)** — an **agent-led**,
456
+ open-ended **Plan → Capture → Triage** exploratory sweep. The operator
457
+ names a surface; the agent drives it (browser MCP by default), recording
458
+ each observation as a `QaLedgerItem`
459
+ ([`schemas/qa-ledger.schema.json`](../schemas/qa-ledger.schema.json)) in a
460
+ session ledger under `temp/qa/`. Capture is strictly **read-only**; every
461
+ state-changing action lands in Triage after explicit operator
462
+ confirmation.
463
+ - **[`/qa-assist`](../workflows/qa-assist.md)** the **human-led** sibling:
464
+ a single-observation **Intake Enrich → Record** loop. The operator
465
+ reports one observation; the agent enriches it into a triage-ready
466
+ `QaLedgerItem`. Same ledger contract and decision seams as `/qa-explore`.
467
+ - **[`/qa-run`](../workflows/qa-run.md)** the **automated complement**:
468
+ steps a *known* set of Gherkin `.feature` scenarios through a real
469
+ browser, asserting `Then` outcomes against the accessibility snapshot and
470
+ bundling console/network problems into structured `F#` findings.
471
+
1174
472
  Consumer adoption steps are in
1175
473
  [`README.md` § Adopting the QA harness](../README.md#adopting-the-qa-harness).
1176
474
 
@@ -1178,170 +476,114 @@ Consumer adoption steps are in
1178
476
 
1179
477
  ## Static analysis & audit orchestration
1180
478
 
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
479
+ Audit lenses are woven into delivery as a **shift-left, three-tier**
480
+ verification model in which each lens concern is verified at exactly one
481
+ tier, chosen by the lens's `scope` field in `audit-rules.json` (resolved by
482
+ `resolveLensTier`). There is **no** separate Epic-lifecycle-gate delivery
483
+ pass — the tiers below *are* the audit machinery.
484
+
485
+ | Tier | When | What runs | Blocking? |
486
+ | --- | --- | --- | --- |
487
+ | Tier 1 — write-time | During Story implementation | Footprint-matched **local**-lens authoring checklists threaded into the Story prompt (`checklistPath`) | advisory |
488
+ | 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 🔴 |
489
+ | Tier 3 — run closeout | `/deliver` per-run epilogue (`plan-run-epilogue.js`, N>1 only) | Cumulative + global + risk-routed lenses (`selectAudits` / `resolveAuditLenses`) over the combined landed tip | blocking |
490
+
491
+ - **`local`** lenses (decidable from a single Story's diff) are verified at
492
+ Tiers 1–2 and are **not** re-run at run closeout.
493
+ - **`cumulative`** lenses (only decidable across a plan-run's combined diff)
494
+ and **`global`** lenses (whole-product properties) are verified at Tier 3.
495
+ - **Risk-routed** lenses run regardless of tier when a high-risk axis (or a
496
+ route-adding change set) demands them.
497
+
498
+ The run-closeout roster is deliberately **slim**: it excludes every
499
+ local-tier change-set lens so the outermost tier — where a fix is most
500
+ expensive does not re-verify a concern already covered shift-left.
501
+
502
+ ### Code review
503
+
504
+ The Story-scope code review runs **outside the maker's context**, inside
505
+ the `single-story-close.js` close subprocess, over `main...story-<id>`
506
+ (see [`helpers/code-review.md`](../workflows/helpers/code-review.md)). It
507
+ walks the Story diff once, executing the risk-routed lens roster as review
508
+ dimensions alongside the review pillars, and posts the unified
509
+ `verification-results` comment. Remediation is tier-aware and split by
510
+ finding class off `delivery.codeReview.autoFixSeverity` (default `medium`);
511
+ surviving 🔴 Critical findings halt the run. The legacy `scope: epic`
512
+ Epic-branch review path was removed with the v2 cutover.
513
+
514
+ ### Quality ratchets
515
+
516
+ - **Maintainability ratchet** (`check-maintainability.js`) — fails if the
1247
517
  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.
518
+ - **CRAP gate** (`check-crap.js`) — per-method complexity × coverage risk
519
+ against `baselines/crap.json`, wired into close-validation, `ci.yml`, and
520
+ `.husky/pre-push`. The `baseline-refresh:`-tagged commit convention is the
521
+ project standard for baseline edits (see
522
+ [`core/gates-and-baselines`](../skills/core/gates-and-baselines/SKILL.md)).
523
+
524
+ ### Audits Stories
525
+
526
+ The standalone `/audit-<dimension>` workflows are read-only emitters that
527
+ write `audit-<dimension>-results.md` under `temp/audits/`.
528
+ [`/audit-to-stories`](../workflows/audit-to-stories.md) parses those
529
+ reports, groups and deduplicates findings, and hands off to
530
+ `/plan --seed-file` (or opens standalone Stories) closing the loop back
531
+ into planning.
1263
532
 
1264
533
  ---
1265
534
 
1266
535
  ## Notification system
1267
536
 
1268
- Two independent notification surfaces, both living in `.agents/` so they ship to
1269
- consuming projects:
537
+ Two independent notification surfaces, both living in `.agents/` so they
538
+ ship to consuming projects.
1270
539
 
1271
540
  ### 1. Unified `notify()` dispatcher
1272
541
 
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:
542
+ Every notification — whether a manual orchestration milestone (Story
543
+ merged, HITL gate triggered) or an auto-fired ticket-state transition —
544
+ routes through [`notify.js`](../scripts/notify.js). Two delivery channels:
1276
545
 
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). |
546
+ | Channel | What it does |
547
+ | --- | --- |
548
+ | GitHub comment | Posts to the targeted ticket; @mentions the operator for `medium`/`high`. |
549
+ | Webhook | Fire-and-forget POST to the configured URL (Make.com / Slack / Discord). |
1281
550
 
1282
- Severity vocabulary (assigned by callers; `eventSeverity()` in
1283
- `lib/notifications/notifier.js` derives it for state transitions):
551
+ Severity vocabulary (`eventSeverity()` derives it for state transitions):
1284
552
 
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]` |
553
+ | Severity | Used for | Webhook prefix |
554
+ | --- | --- | --- |
555
+ | `low` | `story-run-progress` upserts, intermediate transitions, audit reports. | `[low]` |
556
+ | `medium` | Operator-visible milestones: Story state transitions, story merged, run complete. | `[medium]` |
557
+ | `high` | Operator must act (HITL gates, Story blockers, autonomous-chain failures); body leads with `🚨 Action Required:`. | `[Action Required]` |
1290
558
 
1291
- Two independent event-allowlist knobs in `github.notifications`
1292
- (both mandatory):
559
+ Two independent event-allowlist knobs in `github.notifications` (both
560
+ mandatory) filter each channel independently — there is no fallback chain:
1293
561
 
1294
- - `commentEvents` — event-name allowlist for GitHub-ticket comment
1295
- posting. Default:
562
+ - `commentEvents` — allowlist for GitHub-ticket comment posting. Default:
1296
563
  `["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 |
564
+ - `webhookEvents` — allowlist for `NOTIFICATION_WEBHOOK_URL` deliveries.
565
+
566
+ `transitionTicketState` suppresses the `notify()` dispatch for low-severity
567
+ transitions so the comment channel sees only the medium-severity
568
+ Story-level events operators expect. To suppress a channel entirely, set
569
+ its array to `[]`.
570
+
571
+ **Webhook URL resolution.** `NOTIFICATION_WEBHOOK_URL` process env var only
572
+ loaded from `.env` at the project root. It is **not** sourced from
573
+ `.agentrc.json` or `.mcp.json`.
574
+
575
+ Because `notify()` is called in-band from the orchestration SDK, it
576
+ captures state changes from `deliver-story`, the per-Story scripts
577
+ (`single-story-init.js`, `single-story-close.js`,
578
+ `single-story-confirm-merge.js`), and any script that routes through
579
+ `transitionTicketState`. It does **not** capture manual label clicks in the
580
+ GitHub UI.
581
+
582
+ ### 2. Blocker / HITL notifications
583
+
584
+ Fire-and-forget webhooks fire on blocker-escalation events
585
+ (`agent::blocked`) and operator-attention events (PR-open hand-off, run
586
+ cancellation). Webhook failures never block execution.
1345
587
 
1346
588
  ---
1347
589
 
@@ -1349,106 +591,68 @@ failures never block execution.
1349
591
 
1350
592
  ### Sub-agent CI workflow editing
1351
593
 
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:
594
+ Sub-agents operating under the framework's default `GITHUB_TOKEN` **cannot
595
+ edit files under `.github/workflows/**`** — the token does not carry the
596
+ `workflows` permission scope, so a push touching a workflow file is
597
+ rejected with:
1357
598
 
1358
599
  > refusing to allow a GitHub App to create or update workflow
1359
600
  > `.github/workflows/<file>.yml` without `workflows` permission
1360
601
 
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.
602
+ This is a hard constraint, not a transient failure. **When a Story plans a
603
+ new CI gate**, route the check through a `package.json` script (add it to
604
+ `npm run lint` / `npm run docs:check` / `npm test`, or wire a new
605
+ `npm run check:<name>` script) so an existing CI job picks it up by
606
+ transitivity. **When a workflow file genuinely must change** (a new job, a
607
+ trigger change, a runner bump), the edit must be made by an operator with
608
+ `Workflows: Read and write` PAT permissions — see
609
+ [`docs/release-operations.md` § One-time PAT setup](../../docs/release-operations.md#one-time-pat-setup).
1391
610
 
1392
611
  ### Worktree config shadow
1393
612
 
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.
613
+ `helpers/deliver-story` runs inside per-Story worktrees under
614
+ `.worktrees/story-<id>/`. A worktree checks out the **Story branch's own
615
+ copy** of every repo-tracked file — including `.agentrc.json`. **Operator
616
+ edits made in the main checkout do NOT propagate to an already-active
617
+ worktree.** Symptom: you bump a runtime knob in `<main-repo>/.agentrc.json`,
618
+ re-run `single-story-close.js --cwd <worktree>`, and the script still uses
619
+ the old value. When tuning knobs mid-Story:
620
+
621
+ 1. **Prefer an env-var override** when the knob exposes one (timeouts,
622
+ `AGENT_LOG_LEVEL`, concurrency caps) env vars are read from your shell,
623
+ bypassing worktree shadow entirely.
624
+ 2. **Edit the file inside the worktree** (`.worktrees/story-<id>/.agentrc.json`)
625
+ so the script sees the bump on its next read.
626
+ 3. **Use `.agentrc.local.json`** for per-machine tuning you never commit
627
+ (see
628
+ [`configuration.md`](configuration.md#per-machine-local-overrides)) —
629
+ place it inside the worktree, or invoke the script with
630
+ `--cwd <main-repo>` so the resolver reads the main checkout's override.
1431
631
 
1432
632
  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.
633
+ `single-story-init.js` invocation, because new Story branches fork from
634
+ `main`'s current tip.
635
+
636
+ ### `Epic: #N` refusal
637
+
638
+ `/deliver` refuses any ticket that still carries an `Epic: #N` footer or is
639
+ not `type::story`. This is expected — v2 has no Epic delivery path. Close
640
+ the ticket or re-plan the work as a v2 Story via `/plan --tickets <id>`.
1436
641
 
1437
642
  ---
1438
643
 
1439
644
  ## Quick reference
1440
645
 
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` |
646
+ | Command | Purpose |
647
+ | --- | --- |
648
+ | `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). |
649
+ | `/plan --seed "<text>"` | Plan from chat text interrogate author **one Story by default** persist `type::story`. |
650
+ | `/plan --seed-file <path>` | Plan from on-disk notes / a plan seed (the `/audit-to-stories` handoff). |
651
+ | `/plan --tickets <ids>` | Analyze existing issue(s) into proper Stories (prefer an N=1 rewrite). |
652
+ | `/deliver <storyId>` | Deliver one Story via `helpers/deliver-story` `story-<id>`PR → `main`. |
653
+ | `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order; each lands through its own PR. |
654
+ | `/deliver --run <planRunId>` | Resolve Stories labeled `plan-run::<id>`, sequence them, and run the per-run epilogue. |
655
+ | *helper* `helpers/deliver-story` | Per-Story engine invoked by `/deliver`; not an operator slash command. See [`deliver-story.md`](../workflows/helpers/deliver-story.md). |
656
+ | `/audit-to-stories` | Convert audit findings into a plan seed / Stories `/plan --seed-file`. |
657
+ | `/qa-explore` · `/qa-assist` · `/qa-run` | Agent-led / human-led exploratory QA and the automated Gherkin harness. |
658
+ | `/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). |