mandrel 1.94.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/.agents/README.md +116 -99
  2. package/.agents/agents/acceptance-critic.md +9 -7
  3. package/.agents/agents/story-worker.md +45 -51
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +498 -1287
  6. package/.agents/docs/agentrc-reference.json +185 -80
  7. package/.agents/docs/configuration.md +132 -190
  8. package/.agents/docs/execution-reference.md +51 -25
  9. package/.agents/docs/quality-gates.md +25 -33
  10. package/.agents/docs/workflows.md +8 -8
  11. package/.agents/instructions.md +113 -139
  12. package/.agents/rules/ci-remediation.md +11 -15
  13. package/.agents/rules/git-conventions-reference.md +48 -58
  14. package/.agents/rules/git-conventions.md +16 -22
  15. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  16. package/.agents/schemas/agentrc.schema.json +83 -254
  17. package/.agents/schemas/audit-rules.json +59 -1
  18. package/.agents/schemas/audit-rules.schema.json +33 -1
  19. package/.agents/schemas/lifecycle/README.md +1 -2
  20. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  21. package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
  22. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
  23. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  24. package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
  25. package/.agents/schemas/signal-event.schema.json +3 -3
  26. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  27. package/.agents/schemas/validation-evidence.schema.json +1 -1
  28. package/.agents/scripts/acceptance-eval.js +22 -66
  29. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  30. package/.agents/scripts/audit-to-stories.js +7 -7
  31. package/.agents/scripts/boot-sweep.js +1 -1
  32. package/.agents/scripts/bootstrap.js +3 -3
  33. package/.agents/scripts/check-dead-exports.js +43 -104
  34. package/.agents/scripts/check-doc-links.js +2 -2
  35. package/.agents/scripts/check-lifecycle-lint.js +7 -10
  36. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  37. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  38. package/.agents/scripts/deliver-recover.js +122 -0
  39. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  40. package/.agents/scripts/evidence-gate.js +20 -50
  41. package/.agents/scripts/generate-skills-index.js +17 -1
  42. package/.agents/scripts/generate-workflows-doc.js +4 -4
  43. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  44. package/.agents/scripts/lib/Logger.js +6 -10
  45. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  46. package/.agents/scripts/lib/audit-suite/selector.js +328 -28
  47. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  48. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  49. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
  50. package/.agents/scripts/lib/baselines/reader.js +1 -6
  51. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  52. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  53. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  54. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  55. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  56. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  57. package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
  58. package/.agents/scripts/lib/checks/index.js +3 -2
  59. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  60. package/.agents/scripts/lib/checks/state.js +17 -248
  61. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
  62. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  63. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  64. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  65. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  66. package/.agents/scripts/lib/cli-args.js +39 -9
  67. package/.agents/scripts/lib/close-validation/gates.js +15 -15
  68. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  69. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  70. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  71. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  72. package/.agents/scripts/lib/config/ci.js +6 -31
  73. package/.agents/scripts/lib/config/delivery-routing.js +52 -35
  74. package/.agents/scripts/lib/config/explain.js +61 -48
  75. package/.agents/scripts/lib/config/github.js +7 -5
  76. package/.agents/scripts/lib/config/limits.js +29 -80
  77. package/.agents/scripts/lib/config/paths.js +0 -2
  78. package/.agents/scripts/lib/config/quality.js +12 -15
  79. package/.agents/scripts/lib/config/runners.js +20 -66
  80. package/.agents/scripts/lib/config/temp-paths.js +30 -63
  81. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  82. package/.agents/scripts/lib/config-resolver.js +2 -7
  83. package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
  84. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
  85. package/.agents/scripts/lib/config-settings-schema.js +100 -60
  86. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  87. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  88. package/.agents/scripts/lib/dependency-parser.js +3 -2
  89. package/.agents/scripts/lib/doc-tiers.js +2 -2
  90. package/.agents/scripts/lib/duplicate-search.js +242 -41
  91. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  92. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  93. package/.agents/scripts/lib/format-generated-json.js +97 -0
  94. package/.agents/scripts/lib/framework-version.js +19 -190
  95. package/.agents/scripts/lib/gh-exec.js +8 -0
  96. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  97. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
  98. package/.agents/scripts/lib/git-utils.js +32 -20
  99. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  100. package/.agents/scripts/lib/json-utils.js +1 -2
  101. package/.agents/scripts/lib/label-constants.js +10 -38
  102. package/.agents/scripts/lib/label-taxonomy.js +10 -55
  103. package/.agents/scripts/lib/observability/active-story-env.js +44 -165
  104. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  105. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  106. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  107. package/.agents/scripts/lib/observability/source-classifier.js +5 -5
  108. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  109. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  110. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  111. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  112. package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
  113. package/.agents/scripts/lib/orchestration/code-review.js +78 -436
  114. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  115. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
  116. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  117. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  118. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  119. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  120. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  121. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  122. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  123. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  124. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  125. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
  126. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  127. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  128. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  129. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
  130. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  131. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  132. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  133. package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
  134. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  135. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  136. package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
  137. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
  138. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
  139. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  140. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  141. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
  142. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
  143. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  144. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
  145. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
  146. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
  147. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  148. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  149. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  150. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
  151. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  152. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
  153. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  154. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  155. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  156. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  157. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  158. package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
  159. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  160. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  161. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  162. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  163. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  164. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  165. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  166. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  167. package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
  168. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
  169. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  170. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  171. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
  172. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  173. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
  174. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  175. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  176. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
  177. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  178. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  179. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
  180. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  181. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  182. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  183. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  184. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  185. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  186. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  187. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
  188. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  189. package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
  190. package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
  191. package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
  192. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  193. package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
  194. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  195. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
  196. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
  197. package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
  198. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
  199. package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
  200. package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
  201. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  202. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  203. package/.agents/scripts/lib/planning-corpus.js +12 -281
  204. package/.agents/scripts/lib/preflight-runner.js +4 -4
  205. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  206. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  207. package/.agents/scripts/lib/signals/index.js +4 -17
  208. package/.agents/scripts/lib/signals/read.js +35 -35
  209. package/.agents/scripts/lib/signals/schema.js +8 -11
  210. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  211. package/.agents/scripts/lib/signals/write.js +0 -1
  212. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  213. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  214. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  215. package/.agents/scripts/lib/story-adjacency.js +17 -19
  216. package/.agents/scripts/lib/story-body/story-body.js +130 -75
  217. package/.agents/scripts/lib/story-plan.js +2 -4
  218. package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
  219. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  220. package/.agents/scripts/lib/test-env.js +14 -1
  221. package/.agents/scripts/lib/test-tiers.js +0 -3
  222. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
  223. package/.agents/scripts/lib/validation-evidence.js +32 -60
  224. package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
  225. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  226. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  227. package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
  228. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  229. package/.agents/scripts/lint-issue-body.js +71 -40
  230. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  231. package/.agents/scripts/notify.js +4 -3
  232. package/.agents/scripts/plan-context.js +102 -80
  233. package/.agents/scripts/plan-persist.js +230 -279
  234. package/.agents/scripts/plan-run-epilogue.js +142 -0
  235. package/.agents/scripts/post-structured-comment.js +0 -38
  236. package/.agents/scripts/pr-watch-with-update.js +43 -22
  237. package/.agents/scripts/providers/github/compose.js +0 -1
  238. package/.agents/scripts/providers/github/errors.js +0 -19
  239. package/.agents/scripts/providers/github/issues.js +13 -39
  240. package/.agents/scripts/providers/github/mappers.js +5 -12
  241. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  242. package/.agents/scripts/providers/github/tickets.js +33 -156
  243. package/.agents/scripts/providers/github.js +17 -6
  244. package/.agents/scripts/resolve-stories.js +236 -0
  245. package/.agents/scripts/run-coverage.js +4 -1
  246. package/.agents/scripts/run-lint.js +2 -2
  247. package/.agents/scripts/run-verify.js +31 -2
  248. package/.agents/scripts/signals-view.js +25 -21
  249. package/.agents/scripts/single-story-close.js +178 -26
  250. package/.agents/scripts/single-story-confirm-merge.js +313 -24
  251. package/.agents/scripts/single-story-init.js +35 -30
  252. package/.agents/scripts/stories-wave-tick.js +85 -10
  253. package/.agents/scripts/story-plan.js +28 -49
  254. package/.agents/scripts/update-ticket-state.js +14 -65
  255. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  256. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  257. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  258. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  259. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  260. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  261. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  262. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  263. package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
  264. package/.agents/skills/core/scope-triage/SKILL.md +31 -172
  265. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  266. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  267. package/.agents/skills/skills.index.json +3 -53
  268. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  269. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  270. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  271. package/.agents/starter-agentrc.json +0 -5
  272. package/.agents/templates/agent-protocol.md +9 -10
  273. package/.agents/workflows/audit-architecture.md +6 -7
  274. package/.agents/workflows/audit-clean-code.md +7 -7
  275. package/.agents/workflows/audit-dependencies.md +3 -3
  276. package/.agents/workflows/audit-devops.md +3 -3
  277. package/.agents/workflows/audit-documentation.md +9 -10
  278. package/.agents/workflows/audit-lighthouse.md +11 -3
  279. package/.agents/workflows/audit-navigability.md +13 -2
  280. package/.agents/workflows/audit-performance.md +5 -6
  281. package/.agents/workflows/audit-privacy.md +3 -3
  282. package/.agents/workflows/audit-quality.md +11 -12
  283. package/.agents/workflows/audit-security.md +4 -5
  284. package/.agents/workflows/audit-seo.md +13 -3
  285. package/.agents/workflows/audit-sre.md +3 -3
  286. package/.agents/workflows/audit-to-stories.md +20 -20
  287. package/.agents/workflows/audit-ux-ui.md +10 -3
  288. package/.agents/workflows/deliver.md +177 -176
  289. package/.agents/workflows/git-cleanup.md +5 -6
  290. package/.agents/workflows/git-deliver.md +1 -1
  291. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  292. package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
  293. package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
  294. package/.agents/workflows/helpers/code-review.md +75 -196
  295. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
  296. package/.agents/workflows/helpers/deliver-story.md +606 -0
  297. package/.agents/workflows/helpers/diagnose.md +10 -10
  298. package/.agents/workflows/helpers/parallel-tooling.md +3 -3
  299. package/.agents/workflows/helpers/signals.md +16 -16
  300. package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
  301. package/.agents/workflows/mandrel-update.md +2 -1
  302. package/.agents/workflows/plan.md +277 -145
  303. package/.agents/workflows/qa-assist.md +27 -33
  304. package/.agents/workflows/qa-explore.md +29 -38
  305. package/.agents/workflows/qa-run.md +2 -6
  306. package/README.md +9 -8
  307. package/bin/mandrel.js +12 -1
  308. package/docs/CHANGELOG.md +70 -0
  309. package/lib/cli/registry.js +262 -19
  310. package/lib/cli/sync-agents.js +157 -0
  311. package/lib/cli/sync-commands.js +115 -6
  312. package/lib/cli/sync.js +168 -6
  313. package/lib/cli/update.js +105 -8
  314. package/lib/cli/version-helpers.js +131 -0
  315. package/lib/migrations/README.md +7 -5
  316. package/lib/migrations/index.js +12 -8
  317. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  318. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  319. package/package.json +2 -2
  320. package/.agents/agents/retro.md +0 -42
  321. package/.agents/personas/architect.md +0 -113
  322. package/.agents/personas/devops-engineer.md +0 -38
  323. package/.agents/personas/engineer.md +0 -33
  324. package/.agents/personas/project-manager.md +0 -114
  325. package/.agents/personas/qa-engineer.md +0 -95
  326. package/.agents/personas/security-engineer.md +0 -111
  327. package/.agents/personas/technical-writer.md +0 -101
  328. package/.agents/schemas/dispatch-manifest.json +0 -232
  329. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  330. package/.agents/schemas/epic-spec.schema.json +0 -153
  331. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  332. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  333. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  334. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  335. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  336. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  337. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  338. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  339. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  340. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  341. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  342. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  343. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  344. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  345. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  346. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  347. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  348. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  349. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  350. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  351. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  352. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  353. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  354. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  355. package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
  356. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
  357. package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
  358. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  359. package/.agents/schemas/risk-verdict.schema.json +0 -66
  360. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  361. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  362. package/.agents/scripts/analyze-execution.js +0 -444
  363. package/.agents/scripts/bookkeeping-reconcile.js +0 -117
  364. package/.agents/scripts/check-prepush-recovery.js +0 -90
  365. package/.agents/scripts/dispatcher.js +0 -295
  366. package/.agents/scripts/epic-audit-prepare.js +0 -497
  367. package/.agents/scripts/epic-audit-recheck.js +0 -274
  368. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  369. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  370. package/.agents/scripts/epic-deliver-prepare.js +0 -852
  371. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  372. package/.agents/scripts/epic-plan-clarity.js +0 -211
  373. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  374. package/.agents/scripts/epic-reconcile.js +0 -625
  375. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  376. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  377. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  378. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  379. package/.agents/scripts/lib/config/preflight.js +0 -58
  380. package/.agents/scripts/lib/config/retro.js +0 -77
  381. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  382. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  383. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  384. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  385. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  386. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
  387. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  388. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  389. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  390. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
  391. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
  392. package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
  393. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  394. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  395. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  396. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  397. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  398. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  399. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  400. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  401. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  402. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  403. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  404. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  405. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  406. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  407. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  408. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
  409. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  410. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  411. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  412. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
  413. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  414. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  415. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  416. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  417. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  418. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  419. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  420. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  421. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  422. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  423. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  424. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  425. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  426. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
  427. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  428. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  429. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  430. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  431. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  432. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  433. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  434. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  435. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  436. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  437. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  438. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
  439. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  440. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  441. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  442. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
  443. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  444. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  445. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  446. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
  447. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  448. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  449. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  450. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  451. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  452. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  453. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  454. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  455. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  456. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  457. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  458. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  459. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  460. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  461. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
  462. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  463. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  464. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  465. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
  466. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  467. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  468. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  469. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  470. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  471. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  472. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  473. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  474. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  475. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  476. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  477. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  478. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  479. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  480. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  481. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  482. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  483. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  484. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  485. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  486. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  487. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  488. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  489. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  490. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  491. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  492. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  493. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  494. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  495. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  496. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  497. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  498. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  499. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  500. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  501. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
  502. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  503. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  504. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  505. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  506. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  507. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  508. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  509. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  510. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  511. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  512. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  513. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  514. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  515. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  516. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  517. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  518. package/.agents/scripts/lib/spec/index.js +0 -36
  519. package/.agents/scripts/lib/spec/loader.js +0 -425
  520. package/.agents/scripts/lib/spec/state.js +0 -208
  521. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  522. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  523. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  524. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  525. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  526. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  527. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  528. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  529. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  530. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  531. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  532. package/.agents/scripts/lifecycle-emit.js +0 -510
  533. package/.agents/scripts/retro-run.js +0 -218
  534. package/.agents/scripts/slice-phase.js +0 -361
  535. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  536. package/.agents/scripts/story-close.js +0 -294
  537. package/.agents/scripts/story-init.js +0 -599
  538. package/.agents/scripts/story-phase.js +0 -369
  539. package/.agents/scripts/wave-tick.js +0 -464
  540. package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
  541. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  542. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  543. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  544. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  545. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  546. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  547. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
  548. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  549. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
  550. package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
  551. package/.agents/workflows/helpers/deliver-epic.md +0 -998
  552. package/.agents/workflows/helpers/deliver-stories.md +0 -450
  553. package/.agents/workflows/helpers/epic-audit.md +0 -189
  554. package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
  555. package/.agents/workflows/helpers/epic-testing.md +0 -125
  556. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  557. package/.agents/workflows/helpers/plan-epic.md +0 -353
  558. package/.agents/workflows/helpers/plan-story.md +0 -251
  559. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  560. package/.agents/workflows/helpers/single-story-deliver.md +0 -557
@@ -1,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' });