mandrel 1.94.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 (393) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/story-worker.md +12 -13
  3. package/.agents/docs/SDLC.md +489 -1285
  4. package/.agents/docs/agentrc-reference.json +177 -67
  5. package/.agents/docs/configuration.md +104 -138
  6. package/.agents/docs/execution-reference.md +17 -20
  7. package/.agents/docs/quality-gates.md +13 -19
  8. package/.agents/docs/workflows.md +3 -3
  9. package/.agents/instructions.md +70 -81
  10. package/.agents/rules/ci-remediation.md +8 -12
  11. package/.agents/rules/git-conventions-reference.md +6 -7
  12. package/.agents/rules/git-conventions.md +16 -22
  13. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  14. package/.agents/schemas/agentrc.schema.json +54 -214
  15. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  16. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  17. package/.agents/schemas/risk-verdict.schema.json +0 -13
  18. package/.agents/scripts/analyze-execution.js +1 -1
  19. package/.agents/scripts/audit-to-stories.js +7 -7
  20. package/.agents/scripts/boot-sweep.js +1 -1
  21. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  22. package/.agents/scripts/check-prepush-recovery.js +1 -1
  23. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  24. package/.agents/scripts/lib/Logger.js +6 -10
  25. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  26. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  27. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  28. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  29. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  30. package/.agents/scripts/lib/baselines/reader.js +1 -6
  31. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  32. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  33. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  34. package/.agents/scripts/lib/checks/index.js +2 -1
  35. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  36. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  37. package/.agents/scripts/lib/cli-args.js +16 -7
  38. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  39. package/.agents/scripts/lib/config/ci.js +6 -31
  40. package/.agents/scripts/lib/config/delivery-routing.js +45 -29
  41. package/.agents/scripts/lib/config/explain.js +55 -36
  42. package/.agents/scripts/lib/config/limits.js +17 -58
  43. package/.agents/scripts/lib/config/paths.js +0 -2
  44. package/.agents/scripts/lib/config/quality.js +1 -1
  45. package/.agents/scripts/lib/config/runners.js +17 -50
  46. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  47. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  48. package/.agents/scripts/lib/config-resolver.js +2 -7
  49. package/.agents/scripts/lib/config-settings-schema-delivery.js +24 -148
  50. package/.agents/scripts/lib/config-settings-schema-quality.js +8 -14
  51. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  52. package/.agents/scripts/lib/dependency-parser.js +3 -2
  53. package/.agents/scripts/lib/doc-tiers.js +2 -2
  54. package/.agents/scripts/lib/duplicate-search.js +211 -41
  55. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  56. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  57. package/.agents/scripts/lib/framework-version.js +2 -3
  58. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  59. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  60. package/.agents/scripts/lib/git-utils.js +32 -6
  61. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  62. package/.agents/scripts/lib/label-constants.js +10 -23
  63. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  64. package/.agents/scripts/lib/observability/active-story-env.js +1 -1
  65. package/.agents/scripts/lib/observability/hook-heartbeat.js +20 -52
  66. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  67. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  68. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  69. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +8 -5
  70. package/.agents/scripts/lib/orchestration/ceremony-routing.js +83 -20
  71. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  72. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  73. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  74. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  75. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  76. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  77. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  78. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  79. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  80. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  81. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  82. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  83. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  84. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  85. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  86. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  87. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  88. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  89. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  90. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  91. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  92. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  93. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  94. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  95. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +13 -13
  96. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  97. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  98. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  99. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  100. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  101. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  102. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  103. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  104. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  105. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  106. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  107. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  108. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  109. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  110. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  111. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  112. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  113. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  116. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  117. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  118. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  119. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  120. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  121. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  122. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  123. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  124. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  125. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  126. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  127. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  128. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  129. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  130. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  131. package/.agents/scripts/lib/orchestration/ticket-validator.js +2 -7
  132. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  133. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  134. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  135. package/.agents/scripts/lib/planning-corpus.js +16 -11
  136. package/.agents/scripts/lib/preflight-runner.js +2 -2
  137. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  138. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  139. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  140. package/.agents/scripts/lib/story-adjacency.js +11 -14
  141. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  142. package/.agents/scripts/lib/story-plan.js +2 -4
  143. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  144. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  145. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  146. package/.agents/scripts/lib/validation-evidence.js +1 -1
  147. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  148. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  149. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  150. package/.agents/scripts/lint-issue-body.js +71 -40
  151. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  152. package/.agents/scripts/notify.js +4 -3
  153. package/.agents/scripts/plan-context.js +64 -74
  154. package/.agents/scripts/plan-persist.js +121 -280
  155. package/.agents/scripts/plan-run-epilogue.js +97 -0
  156. package/.agents/scripts/providers/github/issues.js +17 -33
  157. package/.agents/scripts/providers/github/mappers.js +0 -12
  158. package/.agents/scripts/providers/github/tickets.js +2 -5
  159. package/.agents/scripts/resolve-plan-run.js +117 -0
  160. package/.agents/scripts/signals-view.js +24 -19
  161. package/.agents/scripts/single-story-close.js +11 -14
  162. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  163. package/.agents/scripts/single-story-init.js +29 -20
  164. package/.agents/scripts/stories-wave-tick.js +6 -6
  165. package/.agents/scripts/story-plan.js +26 -47
  166. package/.agents/scripts/update-ticket-state.js +6 -15
  167. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  168. package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
  169. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  170. package/.agents/skills/skills.index.json +3 -43
  171. package/.agents/starter-agentrc.json +0 -5
  172. package/.agents/templates/agent-protocol.md +9 -10
  173. package/.agents/workflows/audit-architecture.md +3 -3
  174. package/.agents/workflows/audit-clean-code.md +3 -3
  175. package/.agents/workflows/audit-dependencies.md +3 -3
  176. package/.agents/workflows/audit-devops.md +3 -3
  177. package/.agents/workflows/audit-documentation.md +5 -5
  178. package/.agents/workflows/audit-lighthouse.md +3 -3
  179. package/.agents/workflows/audit-navigability.md +3 -2
  180. package/.agents/workflows/audit-performance.md +3 -3
  181. package/.agents/workflows/audit-privacy.md +3 -3
  182. package/.agents/workflows/audit-quality.md +3 -3
  183. package/.agents/workflows/audit-security.md +3 -3
  184. package/.agents/workflows/audit-seo.md +3 -3
  185. package/.agents/workflows/audit-sre.md +3 -3
  186. package/.agents/workflows/audit-to-stories.md +20 -20
  187. package/.agents/workflows/audit-ux-ui.md +3 -3
  188. package/.agents/workflows/deliver.md +122 -174
  189. package/.agents/workflows/git-cleanup.md +3 -4
  190. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  191. package/.agents/workflows/helpers/acceptance-self-eval.md +16 -29
  192. package/.agents/workflows/helpers/code-review.md +70 -193
  193. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  194. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  195. package/.agents/workflows/helpers/diagnose.md +10 -10
  196. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  197. package/.agents/workflows/helpers/signals.md +16 -16
  198. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  199. package/.agents/workflows/mandrel-update.md +2 -1
  200. package/.agents/workflows/plan.md +112 -145
  201. package/.agents/workflows/qa-assist.md +24 -30
  202. package/.agents/workflows/qa-explore.md +29 -38
  203. package/.agents/workflows/qa-run.md +2 -2
  204. package/README.md +9 -8
  205. package/docs/CHANGELOG.md +30 -0
  206. package/lib/migrations/index.js +6 -5
  207. package/package.json +2 -2
  208. package/.agents/agents/retro.md +0 -42
  209. package/.agents/personas/architect.md +0 -113
  210. package/.agents/personas/devops-engineer.md +0 -38
  211. package/.agents/personas/engineer.md +0 -33
  212. package/.agents/personas/project-manager.md +0 -114
  213. package/.agents/personas/qa-engineer.md +0 -95
  214. package/.agents/personas/security-engineer.md +0 -111
  215. package/.agents/personas/technical-writer.md +0 -101
  216. package/.agents/schemas/dispatch-manifest.json +0 -232
  217. package/.agents/schemas/epic-spec.schema.json +0 -153
  218. package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
  219. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
  220. package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
  221. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  222. package/.agents/scripts/bookkeeping-reconcile.js +0 -117
  223. package/.agents/scripts/dispatcher.js +0 -295
  224. package/.agents/scripts/epic-audit-prepare.js +0 -497
  225. package/.agents/scripts/epic-audit-recheck.js +0 -274
  226. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  227. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  228. package/.agents/scripts/epic-deliver-prepare.js +0 -852
  229. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  230. package/.agents/scripts/epic-plan-clarity.js +0 -211
  231. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  232. package/.agents/scripts/epic-reconcile.js +0 -625
  233. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  234. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  235. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  236. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  237. package/.agents/scripts/lib/config/preflight.js +0 -58
  238. package/.agents/scripts/lib/config/retro.js +0 -77
  239. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  240. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  241. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  242. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
  243. package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
  244. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  245. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  246. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  247. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  248. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  249. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  250. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  251. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  252. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  253. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  254. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  255. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  256. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  257. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  258. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  259. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  260. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  261. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  262. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
  263. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  264. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  265. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  266. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  267. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  268. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  269. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  270. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  271. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  272. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  273. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  274. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  275. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  276. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  277. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  278. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  279. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  280. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  281. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  282. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  283. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  284. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  285. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  286. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
  287. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  288. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
  289. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  290. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  291. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  292. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
  293. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  294. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  295. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  296. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  297. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  298. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  299. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  300. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  301. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  302. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  303. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  304. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  305. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  306. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  307. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  308. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  309. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  310. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  311. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  312. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  313. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  314. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  315. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  316. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  317. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  318. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  319. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  320. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  321. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  322. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  323. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  324. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  325. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  326. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  327. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  328. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  329. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  330. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  331. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  332. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  333. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  334. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  335. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
  336. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  337. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  338. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  339. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  340. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  341. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  342. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  343. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  344. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  345. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  346. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  347. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  348. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  349. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  350. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  351. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  352. package/.agents/scripts/lib/spec/index.js +0 -36
  353. package/.agents/scripts/lib/spec/loader.js +0 -425
  354. package/.agents/scripts/lib/spec/state.js +0 -208
  355. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  356. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  357. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  358. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  359. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  360. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  361. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  362. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  363. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  364. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  365. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  366. package/.agents/scripts/lifecycle-emit.js +0 -510
  367. package/.agents/scripts/retro-run.js +0 -218
  368. package/.agents/scripts/slice-phase.js +0 -361
  369. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  370. package/.agents/scripts/story-close.js +0 -294
  371. package/.agents/scripts/story-init.js +0 -599
  372. package/.agents/scripts/story-phase.js +0 -369
  373. package/.agents/scripts/wave-tick.js +0 -464
  374. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  375. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  376. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  377. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  378. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  379. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  380. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
  381. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  382. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
  383. package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
  384. package/.agents/workflows/helpers/deliver-epic.md +0 -998
  385. package/.agents/workflows/helpers/deliver-stories.md +0 -450
  386. package/.agents/workflows/helpers/epic-audit.md +0 -189
  387. package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
  388. package/.agents/workflows/helpers/epic-testing.md +0 -125
  389. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  390. package/.agents/workflows/helpers/plan-epic.md +0 -353
  391. package/.agents/workflows/helpers/plan-story.md +0 -251
  392. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  393. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -1,852 +0,0 @@
1
- #!/usr/bin/env node
2
- /* node:coverage ignore file */
3
-
4
- /**
5
- * epic-deliver-prepare.js — Step 0/1 of the operator-driven `/deliver`.
6
- *
7
- * Composes the existing engine phases that the in-process epic-runner used to
8
- * call sequentially, but does NOT dispatch any Stories. The CLI is the single
9
- * point at which the slash-command captures:
10
- *
11
- * 1. The Epic ticket snapshot (`runSnapshotPhase`).
12
- * 2. The story DAG (`runBuildWaveDagPhase`) computed from every child Story —
13
- * used here only to enumerate the open Story set and run the
14
- * concurrency-hazard gate; the ready-set runtime re-derives readiness
15
- * from live labels on every `tick`, so the prepare no longer persists a
16
- * wave grouping.
17
- * 3. The seeded `epic-run-state` checkpoint (`epic-run-state-store.initialize`)
18
- * in the per-Story-status shape (Story #4155): a flat
19
- * `stories: { [storyId]: { status: 'pending' } }` map plus the GLOBAL
20
- * in-flight `concurrencyCap`. Idempotent — re-running prepare against a
21
- * partially-driven Epic preserves the original `startedAt` and every
22
- * already-recorded Story status (it never resets recorded progress).
23
- * 4. The dispatch hint (`StoryLauncher.planWave`) — a deterministic list of
24
- * `{ storyId, worktree }` entries the slash command uses to resolve
25
- * per-Story worktree paths. The ready-set `tick` selects which of these
26
- * to dispatch on each beat; the prepare only enumerates the set.
27
- *
28
- * Stdout is a single JSON envelope so the slash command can parse without
29
- * re-reading any tickets.
30
- *
31
- * Usage:
32
- * node .agents/scripts/epic-deliver-prepare.js --epic <epicId>
33
- */
34
-
35
- import fs from 'node:fs';
36
- import path from 'node:path';
37
- import { parseArgs } from 'node:util';
38
-
39
- import { runBootSweep } from './boot-sweep.js';
40
- import { buildChecklistPayload } from './lib/audit-suite/index.js';
41
- import { runAsCli } from './lib/cli-utils.js';
42
- import {
43
- getPaths,
44
- getRunners,
45
- resolveConfig,
46
- resolveRuntime,
47
- } from './lib/config-resolver.js';
48
- import { cachedGitFetch } from './lib/git/cached-fetch.js';
49
- import {
50
- ensureEpicBranchRef,
51
- currentBranch as gitCurrentBranch,
52
- } from './lib/git-branch-lifecycle.js';
53
- import { getEpicBranch, gitSpawn, gitSync } from './lib/git-utils.js';
54
- import { Logger } from './lib/Logger.js';
55
- import { ACCEPTANCE_NA, TYPE_LABELS } from './lib/label-constants.js';
56
- import { parseDeliverySlicingTable } from './lib/orchestration/consolidation-precondition.js';
57
- import { ensureDocsDigest } from './lib/orchestration/docs-digest.js';
58
- import {
59
- resolveOperator,
60
- runPrepareGuards,
61
- } from './lib/orchestration/epic-deliver-lease-guard.js';
62
- import {
63
- initialize as initializeEpicRunState,
64
- initializeSingle as initializeEpicRunStateSingle,
65
- write as writeEpicRunState,
66
- } from './lib/orchestration/epic-run-state-store.js';
67
- import {
68
- collectPendingStoryKeys,
69
- evaluateConcurrencyGate,
70
- filterFindingsToPending,
71
- renderGateErrorMessage,
72
- } from './lib/orchestration/epic-runner/concurrency-gate.js';
73
- import { runBuildWaveDagPhase } from './lib/orchestration/epic-runner/phases/build-wave-dag.js';
74
- import { runSnapshotPhase } from './lib/orchestration/epic-runner/phases/snapshot.js';
75
- import { StoryLauncher } from './lib/orchestration/epic-runner/story-launcher.js';
76
- import { collectStoryAssumptionEntries } from './lib/orchestration/file-assumptions.js';
77
- import {
78
- computeBaseSha,
79
- readPreflightCache,
80
- } from './lib/orchestration/preflight-cache.js';
81
- import {
82
- latestHeartbeatForOwner,
83
- currentOwner as leaseCurrentOwner,
84
- } from './lib/orchestration/ticket-lease.js';
85
- import { createProvider } from './lib/provider-factory.js';
86
-
87
- const HELP = `Usage: node .agents/scripts/epic-deliver-prepare.js --epic <epicId> [--single] [--ignore-concurrency-hazards] [--steal] [--as <handle>]
88
-
89
- Snapshots Epic #<id>, builds the wave DAG, initializes the epic-run-state
90
- checkpoint, and prints the per-wave dispatch plan as JSON. Before any of that,
91
- runs two fail-closed preflight guards (Story #3482): a checkout-safety check
92
- (refuse on a dirty tree or an unexpected branch) and an Epic-lease acquisition
93
- (refuse on a live foreign claim).
94
-
95
- Options:
96
- --single Single-delivery prepare (Epic #4475). Short-
97
- circuits Story enumeration: seeds epic/<id>,
98
- materializes ONE worktree on it, and writes a
99
- slice-map epic-run-state (deliveryShape:
100
- "single", storyCount: 0) from the Epic body's
101
- ## Delivery Slicing table. Refuses
102
- acceptance::n-a (fail-closed). INERT until
103
- M4-B: nothing in production drives this flag
104
- yet.
105
- --ignore-concurrency-hazards Bypass the cross-Story concurrency-hazard
106
- gate (Story #2297). The flag's use is
107
- recorded on the Epic checkpoint so retro
108
- tooling can flag a run that shipped
109
- despite an outstanding hazard.
110
- --steal Forcibly transfer a live foreign Epic lease
111
- to this operator instead of refusing. The
112
- takeover is logged for auditability.
113
- --as <handle> Operator identity to claim the Epic lease as.
114
- Defaults to github.operatorHandle, then the
115
- local git config user.email.
116
- `;
117
-
118
- /**
119
- * Build the production git shim the checkout-safety guard reads through. Pure
120
- * `git` subprocess wrappers over `cwd`; injected as a seam so the unit suite
121
- * can substitute an in-memory shim.
122
- *
123
- * @param {string} cwd
124
- * @returns {{ statusPorcelain: () => { dirty: boolean, entries: string }, currentBranch: () => string|null }}
125
- */
126
- function createGitShim(cwd) {
127
- return {
128
- statusPorcelain() {
129
- const res = gitSpawn(cwd, 'status', '--porcelain');
130
- if (res.status !== 0) {
131
- throw new Error(
132
- `[epic-deliver] Failed to read git status: ${res.stderr || '(no stderr)'}`,
133
- );
134
- }
135
- const entries = res.stdout ?? '';
136
- return { dirty: entries.length > 0, entries };
137
- },
138
- currentBranch() {
139
- return gitCurrentBranch(cwd);
140
- },
141
- };
142
- }
143
-
144
- /**
145
- * Resolve the local `git config user.email` as the last-resort operator
146
- * identity. Returns null when git is unavailable or the value is unset.
147
- *
148
- * @param {string} cwd
149
- * @returns {string|null}
150
- */
151
- function resolveGitUserEmail(cwd) {
152
- const res = gitSpawn(cwd, 'config', 'user.email');
153
- if (res.status !== 0) return null;
154
- const value = (res.stdout ?? '').trim();
155
- return value.length > 0 ? value : null;
156
- }
157
-
158
- /**
159
- * End-to-end prepare. DI-friendly: tests pass `injectedProvider` and skip the
160
- * real GitHub round-trips.
161
- *
162
- * @param {{
163
- * epicId: number,
164
- * cwd?: string,
165
- * injectedProvider?: object,
166
- * injectedConfig?: object,
167
- * }} args
168
- * @returns {Promise<{
169
- * epicId: number,
170
- * storyCount: number,
171
- * concurrencyCap: number,
172
- * stories: Array<{ storyId: number, title: string, worktree?: string }>,
173
- * checkpointInitializedAt: string,
174
- * }>}
175
- */
176
- /**
177
- * Run the fail-closed preflight guards (Story #3482): refuse on a
178
- * dirty/foreign-branch checkout and on a live foreign Epic lease, BEFORE any
179
- * snapshot or git mutation. No-op when guards are suppressed. The guards are
180
- * skipped when `skipPreflightGuards` is set, OR — implicitly — when a caller
181
- * injects a provider but no git seam (the signature of the prepare-runner
182
- * unit tests that drive an in-memory provider and never stand up a tree). The
183
- * real CLI path injects neither, so the guards always run for an
184
- * operator-driven invocation. Story #4075 — extracted from
185
- * `runEpicDeliverPrepare`.
186
- */
187
- async function runPreflightGuardsForPrepare({
188
- epicId,
189
- cwd,
190
- config,
191
- provider,
192
- injectedProvider,
193
- injectedGit,
194
- asOperator,
195
- steal,
196
- leaseHeartbeatAt,
197
- leaseNow,
198
- skipPreflightGuards,
199
- }) {
200
- const guardsSuppressed =
201
- skipPreflightGuards || (Boolean(injectedProvider) && !injectedGit);
202
- if (guardsSuppressed) return;
203
-
204
- const guardCwd = cwd ?? process.cwd();
205
- const git = injectedGit ?? createGitShim(guardCwd);
206
- const baseBranch = config.project?.baseBranch ?? 'main';
207
- const expectedBranch = [getEpicBranch(epicId), baseBranch];
208
- const operator =
209
- resolveOperator({
210
- asFlag: asOperator,
211
- config,
212
- gitUserEmail: injectedGit ? undefined : resolveGitUserEmail(guardCwd),
213
- }) ?? null;
214
-
215
- // Liveness seam: a foreign claim is only "live" (and so refuses) when the
216
- // claim *owner* has a recent `story.heartbeat`. Without this the lease
217
- // guard is inert — every foreign claim looks stale and gets silently
218
- // reclaimed (audit #3513). Read the Epic's current assignee (the claim
219
- // owner) and resolve that owner's latest heartbeat from the Epic lifecycle
220
- // ledger via the shared resolver. Tests may inject `leaseHeartbeatAt`
221
- // directly (any value, including null) to bypass the ledger read.
222
- let heartbeatAt = leaseHeartbeatAt;
223
- if (heartbeatAt === undefined) {
224
- const epicTicket = await provider.getTicket(epicId);
225
- const claimOwner = leaseCurrentOwner(epicTicket?.assignees);
226
- heartbeatAt = claimOwner
227
- ? latestHeartbeatForOwner({ epicId, owner: claimOwner, config })
228
- : null;
229
- }
230
-
231
- await runPrepareGuards({
232
- epicId,
233
- expectedBranch,
234
- git,
235
- provider,
236
- operator,
237
- heartbeatAt,
238
- steal,
239
- config,
240
- now: leaseNow,
241
- logger: Logger,
242
- });
243
- }
244
-
245
- /**
246
- * Route the Epic boot cleanup through the shared protected boot-sweep
247
- * engine (Story #4373). Reaps merged, done `story-*` branches left over
248
- * from prior runs — the protection partition skips any branch with
249
- * unpushed work, a dirty worktree, or a still-open parent Story, so an
250
- * in-flight Story is never touched. Fast-forward is off: the prepare may
251
- * run on the Epic branch, and the fast-forward phase would otherwise
252
- * check out the base branch.
253
- *
254
- * Best-effort — a sweep failure (lock contention, git/gh error) is
255
- * swallowed and never blocks or fails the prepare. Skipped in the same
256
- * injected-test shape the preflight guards use (a provider injected with
257
- * no git seam) so unit tests never spawn real git/gh.
258
- */
259
- async function runBootSweepForPrepare({
260
- cwd,
261
- config,
262
- provider,
263
- injectedProvider,
264
- injectedGit,
265
- injectedSweep,
266
- skipPreflightGuards,
267
- }) {
268
- const suppressed =
269
- skipPreflightGuards || (Boolean(injectedProvider) && !injectedGit);
270
- if (suppressed) return;
271
- try {
272
- await runBootSweep({
273
- cwd,
274
- include: ['story-*'],
275
- fastForward: false,
276
- injectedConfig: config,
277
- injectedProvider: provider,
278
- injectedSweep,
279
- logger: Logger,
280
- });
281
- } catch (err) {
282
- Logger.warn(
283
- `[epic-deliver-prepare] ⚠️ boot sweep threw (prepare continues): ${err?.message ?? err}`,
284
- );
285
- }
286
- }
287
-
288
- /**
289
- * Resolve the Epic state, preferring the preflight cache (Story #3027) and
290
- * falling back to a fresh snapshot + wave-DAG pass on miss or baseSha
291
- * mismatch. Returns `{ state, cacheStatus }`. Story #4075 — extracted from
292
- * `runEpicDeliverPrepare`.
293
- */
294
- async function resolvePrepareState({ epicId, cwd, provider }) {
295
- const cached = await readPreflightCache({ epicId, cwd });
296
- if (cached) {
297
- const freshEpic = await provider.getTicket(epicId);
298
- const cachedStoryIds = cached.stories
299
- .map((s) => Number(s?.id ?? s?.number))
300
- .filter((id) => Number.isInteger(id) && id > 0);
301
- const freshStories = await Promise.all(
302
- cachedStoryIds.map((id) => provider.getTicket(id)),
303
- );
304
- const freshBaseSha = computeBaseSha(freshEpic, freshStories);
305
- if (freshBaseSha === cached.baseSha) {
306
- return {
307
- state: {
308
- epic: cached.epic,
309
- stories: cached.stories,
310
- waves: cached.waves,
311
- },
312
- cacheStatus: 'hit',
313
- };
314
- }
315
- }
316
- const ctx = { epicId, provider };
317
- let state = await runSnapshotPhase(ctx, {}, {});
318
- state = await runBuildWaveDagPhase(ctx, {}, state);
319
- return { state, cacheStatus: cached ? 'stale' : 'miss' };
320
- }
321
-
322
- /**
323
- * Evaluate the cross-Story concurrency-hazard gate (Story #2297). Throws on a
324
- * tripped, non-bypassed gate; warns (and returns `gate`) on a bypassed trip.
325
- * Story #4075 — extracted from `runEpicDeliverPrepare`.
326
- */
327
- function evaluatePrepareConcurrencyGate({
328
- config,
329
- waves,
330
- injectedFindings,
331
- ignoreConcurrencyHazards,
332
- }) {
333
- const findings = Array.isArray(injectedFindings) ? injectedFindings : [];
334
- const pendingKeys = collectPendingStoryKeys(waves);
335
- const pendingFindings = filterFindingsToPending(findings, pendingKeys);
336
- const gate = evaluateConcurrencyGate({
337
- findings: pendingFindings,
338
- policy: {
339
- failOnConcurrencyHazards:
340
- config?.delivery?.failOnConcurrencyHazards === true,
341
- },
342
- ignore: ignoreConcurrencyHazards === true,
343
- });
344
- if (gate.tripped && !gate.bypassed) {
345
- const ownerRepo =
346
- config?.github?.owner && config?.github?.repo
347
- ? `${config.github.owner}/${config.github.repo}`
348
- : undefined;
349
- throw new Error(renderGateErrorMessage(gate.findings, ownerRepo));
350
- }
351
- if (gate.tripped && gate.bypassed) {
352
- Logger.warn(
353
- `[epic-deliver-prepare] ⚠️ Concurrency-hazard gate bypassed via --ignore-concurrency-hazards (reason=${gate.reason}, count=${gate.findings.length}).`,
354
- );
355
- }
356
- return gate;
357
- }
358
-
359
- /**
360
- * Build the per-Epic docs digest and write it to
361
- * `<tempRoot>/epic-<id>/docs-digest.md`, returning its repo-relative path.
362
- * Story #4338 — the parent threads this path into every child prompt so
363
- * delivery sub-agents read one compact outline instead of re-ingesting the
364
- * full `project.docsContextFiles` set per Story.
365
- *
366
- * Keyed off the **un-defaulted** config (`config.raw`): when the operator has
367
- * not configured `project.docsContextFiles`, this returns `null` (no file
368
- * written) rather than digesting the resolver's built-in default set — the
369
- * digest is an opt-in surface for projects that curate their docs context.
370
- *
371
- * @param {{ epicId: number, cwd?: string, config: object }} args
372
- * @returns {Promise<string|null>} repo-relative digest path, or null when
373
- * `project.docsContextFiles` is empty/unset (or every file is missing).
374
- */
375
- async function writeDocsDigest({ epicId, cwd, config }) {
376
- const rawFiles = config?.raw?.project?.docsContextFiles;
377
- const docsContextFiles = Array.isArray(rawFiles) ? rawFiles : [];
378
- if (docsContextFiles.length === 0) return null;
379
-
380
- const paths = getPaths(config);
381
- const root = path.resolve(cwd ?? process.cwd());
382
- const docsRoot = path.resolve(root, paths.docsRoot);
383
- const relPath = path.join(paths.tempRoot, `epic-${epicId}`, 'docs-digest.md');
384
- const absPath = path.resolve(root, relPath);
385
- const result = await ensureDocsDigest({
386
- docsContextFiles,
387
- docsRoot,
388
- outputPath: absPath,
389
- });
390
- return result ? relPath : null;
391
- }
392
-
393
- /**
394
- * Thread footprint-matched **local**-lens authoring checklists into the
395
- * per-Story dispatch entries (Epic #4405, Story #4410). For each planned Story,
396
- * derive its predicted footprint from the full ticket body's `changes[]` /
397
- * `references[]` entries, build the budget-capped checklist payload (matched by
398
- * `resolveLensTier(lens) === 'local'` + `matchesAnyFilePattern` — NOT
399
- * `selectAudits`, so no provider or git diff runs here), and write it to
400
- * `<tempRoot>/epic-<id>/checklists/story-<sid>.md`. The parent threads the
401
- * returned repo-relative `checklistPath` into that child's maker prompt, next
402
- * to `docsDigestPath`; a Story that matches no local lens gets a `null` path
403
- * and no file.
404
- *
405
- * @param {{
406
- * epicId: number,
407
- * cwd?: string,
408
- * config: object,
409
- * stories: Array<{ storyId: number, worktree?: string, title?: string }>,
410
- * storyById: Map<number, object>,
411
- * }} args
412
- * @returns {Promise<Array<{ storyId: number, worktree?: string, title?: string, checklistPath: string|null }>>}
413
- */
414
- export async function writeStoryChecklists({
415
- epicId,
416
- cwd,
417
- config,
418
- stories,
419
- storyById,
420
- buildPayload = buildChecklistPayload,
421
- }) {
422
- const paths = getPaths(config);
423
- const root = path.resolve(cwd ?? process.cwd());
424
-
425
- return Promise.all(
426
- stories.map(async (entry) => {
427
- const ticket = storyById.get(Number(entry.storyId));
428
- const footprint = ticket
429
- ? collectStoryAssumptionEntries(ticket).map((e) => e.path)
430
- : [];
431
- const { payload } = buildPayload({ footprint, logger: Logger });
432
- if (!payload) return { ...entry, checklistPath: null };
433
-
434
- const relPath = path.join(
435
- paths.tempRoot,
436
- `epic-${epicId}`,
437
- 'checklists',
438
- `story-${entry.storyId}.md`,
439
- );
440
- const absPath = path.resolve(root, relPath);
441
- await fs.promises.mkdir(path.dirname(absPath), { recursive: true });
442
- await fs.promises.writeFile(absPath, payload, 'utf-8');
443
- return { ...entry, checklistPath: relPath };
444
- }),
445
- );
446
- }
447
-
448
- /**
449
- * Seed `epic/<id>` and materialize the ONE worktree the single-delivery
450
- * executor walks — the single-delivery counterpart to `single-story-init.js`'s
451
- * single-worktree seed (Epic #4475). Fetches origin so remote-tracking refs
452
- * are authoritative, publishes the Epic integration branch via the shared
453
- * `ensureEpicBranchRef` seeder (the same helper `branch-initializer.js` uses),
454
- * then adds a worktree at `.worktrees/epic-<id>/` on `epic/<id>` — idempotent,
455
- * reused on a re-prepare. When worktree isolation is off it checks the branch
456
- * out on the main tree instead (mirroring `provisionWorktree`).
457
- *
458
- * Skipped in the same injected-test shape the preflight guards use (a provider
459
- * injected with no git seam) so unit tests never spawn real git.
460
- *
461
- * @param {{
462
- * epicId: number,
463
- * cwd: string,
464
- * baseBranch: string,
465
- * worktreeEnabled: boolean,
466
- * progress?: (stage: string, msg: string) => void,
467
- * }} args
468
- * @returns {{ epicBranch: string, workCwd: string, worktreeCreated: boolean }}
469
- */
470
- export function provisionEpicWorktree({
471
- epicId,
472
- cwd,
473
- baseBranch,
474
- worktreeEnabled,
475
- progress = () => {},
476
- }) {
477
- const epicBranch = getEpicBranch(epicId);
478
-
479
- // Fetch origin so `ensureEpicBranchRef` can read remote-tracking refs
480
- // instead of a second network round-trip (mirrors materializeBaseBranch).
481
- cachedGitFetch(cwd, 'origin');
482
- ensureEpicBranchRef(epicBranch, baseBranch, cwd, { progress });
483
-
484
- if (!worktreeEnabled) {
485
- // Single-tree mode: check out the Epic integration branch in place.
486
- gitSync(cwd, 'checkout', epicBranch);
487
- progress('WORKTREE', `Checked out ${epicBranch} on the main tree.`);
488
- return { epicBranch, workCwd: cwd, worktreeCreated: false };
489
- }
490
-
491
- const worktreeRoot = path.join(cwd, '.worktrees');
492
- const wtPath = path.join(worktreeRoot, `epic-${epicId}`);
493
- fs.mkdirSync(worktreeRoot, { recursive: true });
494
-
495
- const listed = gitSpawn(cwd, 'worktree', 'list', '--porcelain');
496
- const alreadyPresent =
497
- listed.status === 0 && (listed.stdout ?? '').includes(wtPath);
498
- if (alreadyPresent) {
499
- progress('WORKTREE', `♻️ Reusing worktree: ${wtPath}`);
500
- return { epicBranch, workCwd: wtPath, worktreeCreated: false };
501
- }
502
-
503
- const res = gitSpawn(cwd, 'worktree', 'add', wtPath, epicBranch);
504
- if (res.status !== 0) {
505
- const stderr = res.stderr || res.stdout || '';
506
- if (/already (exists|checked out)/.test(stderr)) {
507
- progress('WORKTREE', `♻️ Reusing worktree (race): ${wtPath}`);
508
- return { epicBranch, workCwd: wtPath, worktreeCreated: false };
509
- }
510
- throw new Error(
511
- `epic-deliver-prepare --single: git worktree add failed for epic-${epicId}: ${stderr}`,
512
- );
513
- }
514
- progress('WORKTREE', `✨ Created worktree: ${wtPath}`);
515
- return { epicBranch, workCwd: wtPath, worktreeCreated: true };
516
- }
517
-
518
- /**
519
- * Step 0/1 of `/deliver` for a single-delivery Epic (Epic #4475, design §S1).
520
- *
521
- * The single-delivery counterpart to `runEpicDeliverPrepare`. It short-circuits
522
- * Story enumeration entirely — a spec-only plan authored NO Story tickets, so
523
- * there is nothing to fan out. Instead it:
524
- *
525
- * 1. Refuses `acceptance::n-a` (fail-closed front gate). Under single
526
- * delivery the non-waivable epic-level acceptance reconcile is the ONLY
527
- * acceptance gate; an Epic that declares "no acceptance criteria" is
528
- * structurally incoherent with that contract.
529
- * 2. Runs the same fail-closed preflight guards (checkout-safety + Epic
530
- * lease) as the fan-out prepare.
531
- * 3. Seeds `epic/<id>` and materializes ONE worktree on it.
532
- * 4. Parses the Epic body's `## Delivery Slicing` table and writes an
533
- * `epic-run-state` **slice map** (`deliveryShape: "single"`,
534
- * `storyCount: 0`, `concurrencyCap: 1`) — idempotent + resume-preserving
535
- * (a re-run keeps every already-`done` slice).
536
- * 5. Writes the per-Epic docs digest.
537
- *
538
- * BEHAVIOR-PRESERVING (M4-A): this function is reachable only through the
539
- * `--single` flag, which nothing in production drives yet (the `deliver.md`
540
- * router's single verdict falls through to the fan-out helper until M4-B). The
541
- * slice-map checkpoint round-trips but no executor consumes it here.
542
- *
543
- * @param {object} args — same DI surface as `runEpicDeliverPrepare` minus the
544
- * concurrency-hazard knobs (single delivery fans out nothing to gate).
545
- * @returns {Promise<{
546
- * epicId: number,
547
- * deliveryShape: 'single',
548
- * storyCount: 0,
549
- * concurrencyCap: number,
550
- * sliceCount: number,
551
- * slices: Record<string, { status: string, title?: string }>,
552
- * epicBranch: string,
553
- * workCwd: string,
554
- * worktreeCreated: boolean,
555
- * checkpointInitializedAt: string,
556
- * docsDigestPath: string|null,
557
- * }>}
558
- */
559
- export async function runEpicDeliverPrepareSingle({
560
- epicId,
561
- cwd,
562
- injectedProvider,
563
- injectedConfig,
564
- asOperator,
565
- steal = false,
566
- injectedGit,
567
- leaseHeartbeatAt,
568
- leaseNow,
569
- skipPreflightGuards = false,
570
- } = {}) {
571
- if (!Number.isInteger(epicId) || epicId <= 0) {
572
- throw new TypeError(
573
- 'runEpicDeliverPrepareSingle: --epic must be a positive integer',
574
- );
575
- }
576
-
577
- const config = injectedConfig ?? resolveConfig({ cwd });
578
- if (!config.github) {
579
- throw new Error(
580
- 'runEpicDeliverPrepareSingle: no github block in .agentrc.json',
581
- );
582
- }
583
- const provider = injectedProvider ?? createProvider(config);
584
- // Single delivery collapses the whole Epic into ONE guarded in-session slice
585
- // walk — the concurrency cap is 1 by definition (nothing fans out).
586
- const concurrencyCap = 1;
587
-
588
- const epic = await provider.getTicket(epicId);
589
- const labels = Array.isArray(epic?.labels) ? epic.labels : [];
590
- if (!labels.includes(TYPE_LABELS.EPIC)) {
591
- throw new Error(
592
- `runEpicDeliverPrepareSingle: #${epicId} is not a ${TYPE_LABELS.EPIC} (labels: ${labels.join(', ') || 'none'}).`,
593
- );
594
- }
595
-
596
- // Fail-closed front gate (design §"Non-waivable epic reconcile"): under
597
- // single delivery the epic-level acceptance reconcile is the ONLY acceptance
598
- // gate that runs — there is no per-Story self-eval critic tier behind it. An
599
- // Epic labelled `acceptance::n-a` (no acceptance criteria) is therefore
600
- // structurally incoherent with single delivery: it would waive the sole
601
- // gate. Refuse loudly instead of silently shipping ungated.
602
- if (labels.includes(ACCEPTANCE_NA)) {
603
- throw new Error(
604
- `[epic-deliver-prepare] BLOCKER: Epic #${epicId} carries ${ACCEPTANCE_NA}, ` +
605
- 'but single delivery makes the non-waivable epic-level acceptance ' +
606
- 'reconcile the ONLY acceptance gate — an Epic with no acceptance ' +
607
- 'criteria would ship ungated. Remove the label (author an ' +
608
- '## Acceptance Table), or re-plan the Epic as fan-out.',
609
- );
610
- }
611
-
612
- await runPreflightGuardsForPrepare({
613
- epicId,
614
- cwd,
615
- config,
616
- provider,
617
- injectedProvider,
618
- injectedGit,
619
- asOperator,
620
- steal,
621
- leaseHeartbeatAt,
622
- leaseNow,
623
- skipPreflightGuards,
624
- });
625
-
626
- const baseBranch = config.project?.baseBranch ?? 'main';
627
- const runtime = resolveRuntime({ config });
628
-
629
- // Seed epic/<id> + materialize the one worktree. Skipped in the injected-
630
- // test shape (a provider injected with no git seam) so unit tests never
631
- // spawn real git — matching the preflight-guard suppression rule.
632
- const worktreeSuppressed =
633
- skipPreflightGuards || (Boolean(injectedProvider) && !injectedGit);
634
- let epicBranch = getEpicBranch(epicId);
635
- let workCwd = cwd ?? process.cwd();
636
- let worktreeCreated = false;
637
- if (!worktreeSuppressed) {
638
- ({ epicBranch, workCwd, worktreeCreated } = provisionEpicWorktree({
639
- epicId,
640
- cwd: cwd ?? process.cwd(),
641
- baseBranch,
642
- worktreeEnabled: runtime.worktreeEnabled,
643
- progress: (stage, msg) =>
644
- Logger.info(`[epic-deliver-prepare:single] ${stage} ${msg}`),
645
- }));
646
- }
647
-
648
- // Parse the Epic body's `## Delivery Slicing` table — the single mode's
649
- // audit trail and the source of the slice map. A missing/unparseable table
650
- // yields an empty slice set (the executor has nothing to walk); that is a
651
- // plan-quality problem surfaced downstream, not a prepare-time throw.
652
- const slices = parseDeliverySlicingTable(epic?.body ?? '') ?? [];
653
-
654
- const checkpointState = await initializeEpicRunStateSingle({
655
- provider,
656
- epicId,
657
- slices,
658
- concurrencyCap,
659
- });
660
-
661
- const docsDigestPath = await writeDocsDigest({ epicId, cwd, config });
662
-
663
- return {
664
- epicId,
665
- deliveryShape: 'single',
666
- storyCount: 0,
667
- concurrencyCap,
668
- sliceCount: Object.keys(checkpointState.slices ?? {}).length,
669
- slices: checkpointState.slices ?? {},
670
- epicBranch,
671
- workCwd,
672
- worktreeCreated,
673
- checkpointInitializedAt:
674
- checkpointState.startedAt ??
675
- checkpointState.lastUpdatedAt ??
676
- new Date().toISOString(),
677
- docsDigestPath,
678
- };
679
- }
680
-
681
- export async function runEpicDeliverPrepare({
682
- epicId,
683
- cwd,
684
- injectedProvider,
685
- injectedConfig,
686
- injectedFindings,
687
- ignoreConcurrencyHazards = false,
688
- steal = false,
689
- asOperator,
690
- injectedGit,
691
- injectedSweep,
692
- leaseHeartbeatAt,
693
- leaseNow,
694
- skipPreflightGuards = false,
695
- } = {}) {
696
- if (!Number.isInteger(epicId) || epicId <= 0) {
697
- throw new TypeError(
698
- 'runEpicDeliverPrepare: --epic must be a positive integer',
699
- );
700
- }
701
-
702
- const config = injectedConfig ?? resolveConfig({ cwd });
703
- if (!config.github) {
704
- throw new Error('runEpicDeliverPrepare: no github block in .agentrc.json');
705
- }
706
- const provider = injectedProvider ?? createProvider(config);
707
- const { deliverRunner } = getRunners(config);
708
- const concurrencyCap = deliverRunner.concurrencyCap;
709
-
710
- await runPreflightGuardsForPrepare({
711
- epicId,
712
- cwd,
713
- config,
714
- provider,
715
- injectedProvider,
716
- injectedGit,
717
- asOperator,
718
- steal,
719
- leaseHeartbeatAt,
720
- leaseNow,
721
- skipPreflightGuards,
722
- });
723
-
724
- await runBootSweepForPrepare({
725
- cwd,
726
- config,
727
- provider,
728
- injectedProvider,
729
- injectedGit,
730
- injectedSweep,
731
- skipPreflightGuards,
732
- });
733
-
734
- const { state, cacheStatus } = await resolvePrepareState({
735
- epicId,
736
- cwd,
737
- provider,
738
- });
739
-
740
- const gate = evaluatePrepareConcurrencyGate({
741
- config,
742
- waves: state.waves,
743
- injectedFindings,
744
- ignoreConcurrencyHazards,
745
- });
746
-
747
- // Flatten the wave-DAG into the open Story set. The ready-set runtime
748
- // re-derives readiness from live labels on every tick, so the checkpoint
749
- // stores only the Story set in scope (seeded at `pending`) and the global
750
- // in-flight cap — no wave grouping, no `currentWave`, no `totalWaves`.
751
- const openStories = state.waves.flat();
752
- const checkpointState = await initializeEpicRunState({
753
- provider,
754
- epicId,
755
- storyIds: openStories,
756
- concurrencyCap,
757
- });
758
-
759
- // Resolve per-Story worktree paths via the launcher so the slash command
760
- // has a deterministic `{ storyId, worktree, title }` list to seed Agent
761
- // dispatch from. This is a dispatch *hint* — the ready-set tick decides
762
- // which Stories to dispatch on each beat; the prepare only enumerates them.
763
- const launcher = new StoryLauncher({ concurrencyCap });
764
- const plannedStories = launcher.planWave(openStories).map((entry, i) => ({
765
- ...entry,
766
- title: openStories[i]?.title ?? '',
767
- }));
768
-
769
- // Thread footprint-matched local-lens authoring checklists into each Story's
770
- // dispatch entry (Story #4410). Keyed off the full discovered tickets (which
771
- // carry the `changes[]`/`references[]` bodies the footprint is derived from);
772
- // a Story that matches no local lens gets a `null` checklistPath.
773
- const storyById = new Map(
774
- (state.stories ?? []).map((t) => [Number(t.id ?? t.number), t]),
775
- );
776
- const stories = await writeStoryChecklists({
777
- epicId,
778
- cwd,
779
- config,
780
- stories: plannedStories,
781
- storyById,
782
- });
783
-
784
- // Persist the `--ignore-concurrency-hazards` flag on the checkpoint so
785
- // retro tooling can flag a run that shipped despite an outstanding hazard
786
- // (the warning above is one-shot; the checkpoint is durable).
787
- if (gate.bypassed) {
788
- await writeEpicRunState({
789
- provider,
790
- epicId,
791
- state: { ...checkpointState, ignoreConcurrencyHazards: true },
792
- });
793
- }
794
-
795
- const docsDigestPath = await writeDocsDigest({ epicId, cwd, config });
796
-
797
- return {
798
- epicId,
799
- storyCount: openStories.length,
800
- concurrencyCap,
801
- stories,
802
- checkpointInitializedAt:
803
- checkpointState.startedAt ??
804
- checkpointState.lastUpdatedAt ??
805
- new Date().toISOString(),
806
- concurrencyHazardsBypassed: gate.bypassed,
807
- preflightCache: cacheStatus,
808
- docsDigestPath,
809
- };
810
- }
811
-
812
- async function main() {
813
- const { values } = parseArgs({
814
- options: {
815
- epic: { type: 'string' },
816
- single: { type: 'boolean', default: false },
817
- help: { type: 'boolean', short: 'h' },
818
- 'ignore-concurrency-hazards': { type: 'boolean', default: false },
819
- steal: { type: 'boolean', default: false },
820
- as: { type: 'string' },
821
- },
822
- strict: false,
823
- });
824
-
825
- if (values.help) {
826
- Logger.info(HELP);
827
- return;
828
- }
829
- const epicId = Number.parseInt(values.epic ?? '', 10);
830
- if (Number.isNaN(epicId) || epicId <= 0) {
831
- Logger.error('[epic-deliver-prepare] ERROR: --epic <epicId> is required.');
832
- Logger.error(HELP);
833
- process.exit(2);
834
- }
835
-
836
- const asOperator = typeof values.as === 'string' ? values.as : undefined;
837
- const steal = values.steal === true;
838
-
839
- const result =
840
- values.single === true
841
- ? await runEpicDeliverPrepareSingle({ epicId, asOperator, steal })
842
- : await runEpicDeliverPrepare({
843
- epicId,
844
- ignoreConcurrencyHazards:
845
- values['ignore-concurrency-hazards'] === true,
846
- steal,
847
- asOperator,
848
- });
849
- Logger.info(JSON.stringify(result, null, 2));
850
- }
851
-
852
- runAsCli(import.meta.url, main, { source: 'epic-deliver-prepare' });