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,621 +0,0 @@
1
- /**
2
- * epic-run-state-store — stateless functions for reading and writing the
3
- * `epic-run-state` structured comment used by `/deliver`.
4
- *
5
- * Story #4155 (Epic #4151) — the Epic `/deliver` runtime cut over from
6
- * the wave-batch scheduler to the continuous ready-set core
7
- * (`lib/wave-runner/ready-set.js`). The checkpoint shrank with it: it no
8
- * longer carries `currentWave`, `plan[][]`, `totalWaves`, or the
9
- * per-wave `waves[]` aggregation. The durable run state is now a flat
10
- * **per-Story status map** (`stories: { [storyId]: { status, title?,
11
- * blockerCommentId? } }`) plus the run-level `concurrencyCap` (the
12
- * GLOBAL in-flight cap the ready-set selector honours), `phase`,
13
- * `startedAt`, and `manualInterventions[]`. There is no resume-pointer
14
- * to reconcile — the ready-set core re-derives adjacency and readiness
15
- * from live Story bodies/labels on every tick, so the checkpoint only
16
- * records terminal Story outcomes (for the auto-merge predicate, branch
17
- * cleanup, and the operator rollup) and the run-level knobs.
18
- *
19
- * This module is the function-based replacement for the legacy
20
- * `Checkpointer` class that previously lived at
21
- * `./epic-runner/checkpointer.js`. Story #2423 (Epic #2307) deleted the
22
- * class file; the class API survives as a tests-only fixture at
23
- * `tests/fixtures/epic-run-state-store.js`.
24
- *
25
- * The comment is identified by a stable HTML marker so it can be overwritten
26
- * idempotently across orchestrator restarts. The body is a fenced JSON block.
27
- */
28
-
29
- import { assertValidDeliverPhase } from './epic-runner/deliver-phases.js';
30
- import { parseFencedJsonComment } from './structured-comment-parser.js';
31
- import { findStructuredComment, upsertStructuredComment } from './ticketing.js';
32
-
33
- export const EPIC_RUN_STATE_TYPE = 'epic-run-state';
34
- export const CHECKPOINT_SCHEMA_VERSION = 2;
35
-
36
- /** Terminal / in-progress per-Story statuses persisted on the checkpoint. */
37
- export const STORY_STATUSES = Object.freeze([
38
- 'pending',
39
- 'done',
40
- 'blocked',
41
- 'failed',
42
- ]);
43
-
44
- // Re-export the phase enum + index helper so downstream importers continue
45
- // to use this module as a single import target.
46
- export {
47
- DELIVER_PHASES,
48
- phaseIndex,
49
- } from './epic-runner/deliver-phases.js';
50
-
51
- function assertProvider(provider) {
52
- if (!provider)
53
- throw new TypeError('epic-run-state-store requires a provider');
54
- }
55
-
56
- function assertEpicId(epicId) {
57
- if (!Number.isInteger(epicId)) {
58
- throw new TypeError('epic-run-state-store requires a numeric epicId');
59
- }
60
- }
61
-
62
- /**
63
- * Normalize an inbound Story id (accepts the ticket `id` shape, the raw
64
- * GitHub `number` shape, and a bare integer) to a positive integer, or
65
- * `null` when it is absent / non-positive / non-integer.
66
- *
67
- * @param {object|number|string} entry
68
- * @returns {number|null}
69
- */
70
- function storyIdOf(entry) {
71
- if (typeof entry === 'number') {
72
- return Number.isInteger(entry) && entry > 0 ? entry : null;
73
- }
74
- if (!entry || typeof entry !== 'object') {
75
- const n = Number(entry);
76
- return Number.isInteger(n) && n > 0 ? n : null;
77
- }
78
- const raw = entry.id ?? entry.storyId ?? entry.number;
79
- const id = Number(raw);
80
- return Number.isInteger(id) && id > 0 ? id : null;
81
- }
82
-
83
- /**
84
- * Build the initial per-Story status map from a list of Story records (or
85
- * ids). Every Story seeds at `status: 'pending'`; an optional `title` is
86
- * carried through when the record supplies one so the operator rollup and
87
- * branch-cleanup surfaces have a label without a second fetch. Keys are the
88
- * positive-integer Story ids as strings (JSON object keys are strings);
89
- * shapeless / non-positive entries are dropped.
90
- *
91
- * Pure helper — exported for unit tests.
92
- *
93
- * @param {Array<object|number>} stories
94
- * @returns {Record<string, { status: string, title?: string }>}
95
- */
96
- export function buildStoryStatusMap(stories) {
97
- const out = {};
98
- for (const entry of Array.isArray(stories) ? stories : []) {
99
- const id = storyIdOf(entry);
100
- if (id === null) continue;
101
- const record = { status: 'pending' };
102
- const title =
103
- entry && typeof entry === 'object' && typeof entry.title === 'string'
104
- ? entry.title
105
- : undefined;
106
- if (title) record.title = title;
107
- out[String(id)] = record;
108
- }
109
- return out;
110
- }
111
-
112
- /**
113
- * Read and parse the checkpoint. Returns null if the comment is missing or
114
- * unparseable (callers treat null as "start fresh").
115
- *
116
- * @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number }} opts
117
- * @returns {Promise<object | null>}
118
- */
119
- export async function read({ provider, epicId } = {}) {
120
- assertProvider(provider);
121
- assertEpicId(epicId);
122
- const comment = await findStructuredComment(
123
- provider,
124
- epicId,
125
- EPIC_RUN_STATE_TYPE,
126
- );
127
- return parseFencedJsonComment(comment);
128
- }
129
-
130
- /**
131
- * Overwrite the checkpoint with `state`. Idempotent — callers may invoke
132
- * freely per tick; the marker-scoped upsert deletes the prior comment.
133
- *
134
- * @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, state: object }} opts
135
- */
136
- export async function write({ provider, epicId, state } = {}) {
137
- assertProvider(provider);
138
- assertEpicId(epicId);
139
- const payload = {
140
- version: CHECKPOINT_SCHEMA_VERSION,
141
- ...state,
142
- lastUpdatedAt: new Date().toISOString(),
143
- };
144
- const body = `\`\`\`json\n${JSON.stringify(payload, null, 2)}\n\`\`\``;
145
- await upsertStructuredComment(provider, epicId, EPIC_RUN_STATE_TYPE, body);
146
- return payload;
147
- }
148
-
149
- /**
150
- * Initial checkpoint for a brand-new run. Idempotent against re-dispatch:
151
- * when an existing checkpoint is found and the persisted `concurrencyCap`
152
- * matches the incoming value, the existing state is returned verbatim (no
153
- * rewrite) so a re-prepare preserves `startedAt`, prior Story statuses, and
154
- * `manualInterventions`. When the cap differs (an operator re-tuned the
155
- * global in-flight cap) it is refreshed in place; the Story status map is
156
- * **merged** so any Story that already reached a terminal status keeps it
157
- * while newly-discovered Stories are seeded at `pending`. Prepare owns the
158
- * Story set (it overwrites it on every run) but never clobbers recorded
159
- * progress.
160
- *
161
- * @param {{
162
- * provider: import('../ITicketingProvider.js').ITicketingProvider,
163
- * epicId: number,
164
- * storyIds: Array<object|number>,
165
- * concurrencyCap: number,
166
- * }} opts
167
- * `concurrencyCap` is the GLOBAL in-flight cap the ready-set selector
168
- * honours (`selectReadySet({ globalCap })`).
169
- */
170
- export async function initialize({
171
- provider,
172
- epicId,
173
- storyIds,
174
- concurrencyCap,
175
- } = {}) {
176
- assertProvider(provider);
177
- assertEpicId(epicId);
178
- const seededStories = buildStoryStatusMap(storyIds);
179
- const existing = await read({ provider, epicId });
180
- if (existing) {
181
- const mergedStories = mergeStoryStatuses(existing.stories, seededStories);
182
- if (
183
- existing.concurrencyCap === concurrencyCap &&
184
- storyMapsEqual(existing.stories, mergedStories)
185
- ) {
186
- return existing;
187
- }
188
- return write({
189
- provider,
190
- epicId,
191
- state: { ...existing, concurrencyCap, stories: mergedStories },
192
- });
193
- }
194
- return write({
195
- provider,
196
- epicId,
197
- state: {
198
- epicId,
199
- startedAt: new Date().toISOString(),
200
- concurrencyCap,
201
- phase: 'prepare',
202
- stories: seededStories,
203
- manualInterventions: [],
204
- },
205
- });
206
- }
207
-
208
- /**
209
- * Merge a freshly-seeded Story status map onto a persisted one. Every Story
210
- * present in either map appears in the result; when a Story exists in the
211
- * prior map its recorded status / blockerCommentId win (recorded progress is
212
- * never lost), while its `title` is refreshed from the incoming seed when the
213
- * seed supplies one. Stories present only in the incoming seed are added at
214
- * their seeded (`pending`) status. Pure — exported for unit tests.
215
- *
216
- * @param {Record<string, object>|undefined} prior
217
- * @param {Record<string, object>} incoming
218
- * @returns {Record<string, object>}
219
- */
220
- export function mergeStoryStatuses(prior, incoming) {
221
- const priorMap = prior && typeof prior === 'object' ? prior : {};
222
- const seedMap = incoming && typeof incoming === 'object' ? incoming : {};
223
- const out = {};
224
- for (const key of new Set([
225
- ...Object.keys(priorMap),
226
- ...Object.keys(seedMap),
227
- ])) {
228
- const priorEntry = priorMap[key];
229
- const seedEntry = seedMap[key];
230
- if (priorEntry && typeof priorEntry === 'object') {
231
- const merged = { ...priorEntry };
232
- if (seedEntry && typeof seedEntry.title === 'string') {
233
- merged.title = seedEntry.title;
234
- }
235
- out[key] = merged;
236
- } else {
237
- out[key] = seedEntry;
238
- }
239
- }
240
- return out;
241
- }
242
-
243
- /**
244
- * Structural equality on two Story status maps — same key set and, per key,
245
- * the same `status`, `title`, and `blockerCommentId`. Used by `initialize`
246
- * to decide whether an idempotent re-prepare needs a rewrite. Pure.
247
- *
248
- * @param {Record<string, object>|undefined} a
249
- * @param {Record<string, object>|undefined} b
250
- * @returns {boolean}
251
- */
252
- function storyMapsEqual(a, b) {
253
- const left = a && typeof a === 'object' ? a : {};
254
- const right = b && typeof b === 'object' ? b : {};
255
- const keys = Object.keys(left);
256
- if (keys.length !== Object.keys(right).length) return false;
257
- for (const key of keys) {
258
- const l = left[key] ?? {};
259
- const r = right[key];
260
- if (!r) return false;
261
- if (
262
- l.status !== r.status ||
263
- l.title !== r.title ||
264
- l.blockerCommentId !== r.blockerCommentId
265
- ) {
266
- return false;
267
- }
268
- }
269
- return true;
270
- }
271
-
272
- /**
273
- * Record a per-Story terminal (or in-progress) status on the checkpoint.
274
- * Reads the current state first, splices the single Story's record into the
275
- * `stories` map, and re-writes. Other Stories and all run-level fields are
276
- * preserved verbatim. Tolerant of a legacy/absent `stories` map (treated as
277
- * empty) so a checkpoint that predates a field is upgraded in place.
278
- *
279
- * @param {{
280
- * provider: import('../ITicketingProvider.js').ITicketingProvider,
281
- * epicId: number,
282
- * storyId: number,
283
- * status: string,
284
- * title?: string,
285
- * blockerCommentId?: string|number|null,
286
- * }} opts
287
- * @returns {Promise<object>} the persisted state
288
- */
289
- export async function recordStoryStatus({
290
- provider,
291
- epicId,
292
- storyId,
293
- status,
294
- title,
295
- blockerCommentId,
296
- } = {}) {
297
- assertProvider(provider);
298
- assertEpicId(epicId);
299
- const id = storyIdOf(storyId);
300
- if (id === null) {
301
- throw new TypeError(
302
- 'recordStoryStatus: storyId must be a positive integer',
303
- );
304
- }
305
- if (!STORY_STATUSES.includes(status)) {
306
- throw new RangeError(
307
- `recordStoryStatus: status "${status}" must be one of: ${STORY_STATUSES.join(', ')}`,
308
- );
309
- }
310
- const existing = (await read({ provider, epicId })) ?? {};
311
- const stories =
312
- existing.stories && typeof existing.stories === 'object'
313
- ? { ...existing.stories }
314
- : {};
315
- const prior = stories[String(id)] ?? {};
316
- const record = { ...prior, status };
317
- if (typeof title === 'string' && title) record.title = title;
318
- if (status === 'blocked' && blockerCommentId != null) {
319
- record.blockerCommentId = String(blockerCommentId);
320
- }
321
- stories[String(id)] = record;
322
- return write({
323
- provider,
324
- epicId,
325
- state: { ...existing, stories },
326
- });
327
- }
328
-
329
- /**
330
- * Terminal / in-progress per-slice statuses persisted on a single-delivery
331
- * checkpoint. The single-delivery analogue of `STORY_STATUSES` — a spec-only
332
- * plan authors no Story tickets, so the leaf work unit is a `## Delivery
333
- * Slicing` slice walked in-session on `epic/<id>` (Epic #4475). The design's
334
- * durable contract is `{pending, done}`; `blocked`/`failed` mirror the story
335
- * map so the M4-B executor can record a stalled slice without a schema bump.
336
- */
337
- export const SLICE_STATUSES = Object.freeze([
338
- 'pending',
339
- 'done',
340
- 'blocked',
341
- 'failed',
342
- ]);
343
-
344
- /**
345
- * Build the initial per-slice status map from parsed `## Delivery Slicing`
346
- * rows (`parseDeliverySlicingTable` output — `[{ slice, independent }]`).
347
- * Every slice seeds at `status: 'pending'`, keyed by a **stable, position-
348
- * derived** id `slice-<n>` (1-based) so a re-run over the same table
349
- * round-trips its keys. The slice's human label is carried through as `title`
350
- * for the operator rollup. Pure — exported for unit tests.
351
- *
352
- * @param {Array<{ slice?: string, independent?: boolean } | string>} slices
353
- * @returns {Record<string, { status: string, title?: string }>}
354
- */
355
- export function buildSliceStatusMap(slices) {
356
- const out = {};
357
- const rows = Array.isArray(slices) ? slices : [];
358
- rows.forEach((entry, i) => {
359
- const sliceId = `slice-${i + 1}`;
360
- const record = { status: 'pending' };
361
- const title =
362
- typeof entry === 'string'
363
- ? entry
364
- : entry && typeof entry === 'object' && typeof entry.slice === 'string'
365
- ? entry.slice
366
- : undefined;
367
- if (title) record.title = title;
368
- out[sliceId] = record;
369
- });
370
- return out;
371
- }
372
-
373
- /**
374
- * Merge a freshly-seeded slice status map onto a persisted one. Every slice
375
- * present in either map appears in the result; when a slice exists in the
376
- * prior map its recorded `status` wins (recorded progress — a `done` slice
377
- * whose work already sits on `epic/<id>` — is never lost), while its `title`
378
- * is refreshed from the incoming seed. Slices present only in the incoming
379
- * seed are added at `pending`. Pure — exported for unit tests.
380
- *
381
- * @param {Record<string, object>|undefined} prior
382
- * @param {Record<string, object>} incoming
383
- * @returns {Record<string, object>}
384
- */
385
- export function mergeSliceStatuses(prior, incoming) {
386
- const priorMap = prior && typeof prior === 'object' ? prior : {};
387
- const seedMap = incoming && typeof incoming === 'object' ? incoming : {};
388
- const out = {};
389
- for (const key of new Set([
390
- ...Object.keys(priorMap),
391
- ...Object.keys(seedMap),
392
- ])) {
393
- const priorEntry = priorMap[key];
394
- const seedEntry = seedMap[key];
395
- if (priorEntry && typeof priorEntry === 'object') {
396
- const merged = { ...priorEntry };
397
- if (seedEntry && typeof seedEntry.title === 'string') {
398
- merged.title = seedEntry.title;
399
- }
400
- out[key] = merged;
401
- } else {
402
- out[key] = seedEntry;
403
- }
404
- }
405
- return out;
406
- }
407
-
408
- /**
409
- * Initialize (or resume) the single-delivery checkpoint for an Epic. The
410
- * single-delivery analogue of `initialize` (Story #4155's per-Story map),
411
- * writing the slice-map shape the M4-B executor walks:
412
- *
413
- * ```json
414
- * {
415
- * "epicId": 4475,
416
- * "deliveryShape": "single",
417
- * "slices": { "slice-1": { "status": "pending", "title": "…" } },
418
- * "concurrencyCap": 1,
419
- * "storyCount": 0
420
- * }
421
- * ```
422
- *
423
- * Idempotent + resume-preserving: on a re-run an existing single checkpoint
424
- * has its slice map **merged** (every already-`done` slice keeps its status
425
- * so the executor skips it — the work already sits on `epic/<id>`), while
426
- * newly-authored slices seed at `pending`. `startedAt` is preserved. A
427
- * checkpoint written for the fan-out shape is NOT reinterpreted here — the
428
- * caller (`epic-deliver-prepare.js --single`) owns route selection.
429
- *
430
- * @param {{
431
- * provider: import('../ITicketingProvider.js').ITicketingProvider,
432
- * epicId: number,
433
- * slices: Array<{ slice?: string, independent?: boolean } | string>,
434
- * concurrencyCap?: number,
435
- * }} opts
436
- * @returns {Promise<object>} the persisted (or preserved) state
437
- */
438
- export async function initializeSingle({
439
- provider,
440
- epicId,
441
- slices,
442
- concurrencyCap = 1,
443
- } = {}) {
444
- assertProvider(provider);
445
- assertEpicId(epicId);
446
- const seededSlices = buildSliceStatusMap(slices);
447
- const storyCount = 0;
448
- const existing = await read({ provider, epicId });
449
- if (existing && existing.deliveryShape === 'single') {
450
- const mergedSlices = mergeSliceStatuses(existing.slices, seededSlices);
451
- if (
452
- existing.concurrencyCap === concurrencyCap &&
453
- sliceMapsEqual(existing.slices, mergedSlices)
454
- ) {
455
- return existing;
456
- }
457
- return write({
458
- provider,
459
- epicId,
460
- state: {
461
- ...existing,
462
- deliveryShape: 'single',
463
- concurrencyCap,
464
- storyCount,
465
- slices: mergedSlices,
466
- },
467
- });
468
- }
469
- return write({
470
- provider,
471
- epicId,
472
- state: {
473
- epicId,
474
- startedAt: new Date().toISOString(),
475
- deliveryShape: 'single',
476
- concurrencyCap,
477
- storyCount,
478
- phase: 'prepare',
479
- slices: seededSlices,
480
- manualInterventions: [],
481
- },
482
- });
483
- }
484
-
485
- /**
486
- * Record a per-slice terminal (or in-progress) status on a single-delivery
487
- * checkpoint. The single-delivery analogue of `recordStoryStatus`: the M4-B
488
- * executor calls this after each Delivery-Slicing slice commits to
489
- * `epic/<id>`, flipping `slices[sliceId].status` `pending → done` so a resumed
490
- * run skips the already-landed slice. Reads the current state first, splices
491
- * the single slice's record into the `slices` map, and re-writes; other slices
492
- * and all run-level fields are preserved verbatim. Tolerant of a
493
- * legacy/absent `slices` map (treated as empty).
494
- *
495
- * @param {{
496
- * provider: import('../ITicketingProvider.js').ITicketingProvider,
497
- * epicId: number,
498
- * sliceId: string,
499
- * status: string,
500
- * title?: string,
501
- * }} opts
502
- * @returns {Promise<object>} the persisted state
503
- */
504
- export async function recordSliceStatus({
505
- provider,
506
- epicId,
507
- sliceId,
508
- status,
509
- title,
510
- } = {}) {
511
- assertProvider(provider);
512
- assertEpicId(epicId);
513
- if (typeof sliceId !== 'string' || sliceId.length === 0) {
514
- throw new TypeError(
515
- 'recordSliceStatus: sliceId must be a non-empty string',
516
- );
517
- }
518
- if (!SLICE_STATUSES.includes(status)) {
519
- throw new RangeError(
520
- `recordSliceStatus: status "${status}" must be one of: ${SLICE_STATUSES.join(', ')}`,
521
- );
522
- }
523
- const existing = (await read({ provider, epicId })) ?? {};
524
- const slices =
525
- existing.slices && typeof existing.slices === 'object'
526
- ? { ...existing.slices }
527
- : {};
528
- const prior = slices[sliceId] ?? {};
529
- const record = { ...prior, status };
530
- if (typeof title === 'string' && title) record.title = title;
531
- slices[sliceId] = record;
532
- return write({
533
- provider,
534
- epicId,
535
- state: { ...existing, slices },
536
- });
537
- }
538
-
539
- /**
540
- * Structural equality on two slice status maps — same key set and, per key,
541
- * the same `status` and `title`. Used by `initializeSingle` to decide whether
542
- * an idempotent re-prepare needs a rewrite. Pure.
543
- *
544
- * @param {Record<string, object>|undefined} a
545
- * @param {Record<string, object>|undefined} b
546
- * @returns {boolean}
547
- */
548
- function sliceMapsEqual(a, b) {
549
- const left = a && typeof a === 'object' ? a : {};
550
- const right = b && typeof b === 'object' ? b : {};
551
- const keys = Object.keys(left);
552
- if (keys.length !== Object.keys(right).length) return false;
553
- for (const key of keys) {
554
- const l = left[key] ?? {};
555
- const r = right[key];
556
- if (!r) return false;
557
- if (l.status !== r.status || l.title !== r.title) return false;
558
- }
559
- return true;
560
- }
561
-
562
- /**
563
- * Append a manual-intervention record to the checkpoint. Out-of-band
564
- * recovery steps the host LLM performs during a delivery — `AskUserQuestion`
565
- * calls, `git restore`/`git reset` against the working tree, manual `--no-ff`
566
- * recovery merges, story-close `--skipValidation` overrides — disqualify the
567
- * Epic from auto-merge. The auto-merge predicate reads this array and only
568
- * fires when it is empty.
569
- *
570
- * @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, entry: { reason: string, source?: string, ts?: string } }} opts
571
- * @returns {Promise<object>} the persisted state
572
- */
573
- export async function appendIntervention({ provider, epicId, entry } = {}) {
574
- assertProvider(provider);
575
- assertEpicId(epicId);
576
- if (!entry || typeof entry.reason !== 'string' || entry.reason.length === 0) {
577
- throw new TypeError('appendIntervention: { reason: string } is required.');
578
- }
579
- const existing = (await read({ provider, epicId })) ?? {};
580
- const list = Array.isArray(existing.manualInterventions)
581
- ? existing.manualInterventions
582
- : [];
583
- const record = {
584
- reason: entry.reason,
585
- source: typeof entry.source === 'string' ? entry.source : 'host-llm',
586
- ts: typeof entry.ts === 'string' ? entry.ts : new Date().toISOString(),
587
- };
588
- return write({
589
- provider,
590
- epicId,
591
- state: {
592
- ...existing,
593
- manualInterventions: [...list, record],
594
- },
595
- });
596
- }
597
-
598
- /**
599
- * Advance the checkpoint's `phase` field to the next `/deliver`
600
- * phase. Reads the current state first so the caller does not need to
601
- * keep an in-memory copy. Other state fields are preserved verbatim.
602
- *
603
- * Story #1155 / Epic #1142 — phase-granular resume. The runner writes
604
- * the **next phase to run** here, not the phase that just finished, so
605
- * a resume can match `phase === 'code-review'` to mean "Phase D is the
606
- * next thing to do."
607
- *
608
- * @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, nextPhase: string }} opts
609
- * nextPhase - One of `DELIVER_PHASES` or `'done'`.
610
- */
611
- export async function setPhase({ provider, epicId, nextPhase } = {}) {
612
- assertProvider(provider);
613
- assertEpicId(epicId);
614
- assertValidDeliverPhase(nextPhase);
615
- const existing = (await read({ provider, epicId })) ?? {};
616
- return write({
617
- provider,
618
- epicId,
619
- state: { ...existing, phase: nextPhase },
620
- });
621
- }