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,310 +0,0 @@
1
- /**
2
- * epic-deliver-lease-guard.js — preflight guards for `/deliver`
3
- * (Story #3482, Epic #3457).
4
- *
5
- * `/deliver`'s prepare phase used to checkout `epic/<id>` over whatever
6
- * the operator had checked out, yanking HEAD and resetting baselines under a
7
- * live working session (the documented "epic-deliver shares the main checkout"
8
- * footgun). This module supplies the two preflight guards the Tech Spec
9
- * (#3476) wires into `epic-deliver-prepare.js` *before* any mutating git work:
10
- *
11
- * 1. **Epic-lease preflight.** Acquire the assignee-as-lease on the Epic
12
- * ticket via `ticket-lease.acquireLease`. On a live foreign claim the
13
- * guard **fails closed** — it throws a clear, operator-facing error that
14
- * names the current owner (the `critical-workflow` risk axis demands
15
- * refuse-and-exit, never warn-and-continue). `--steal` is the only
16
- * override.
17
- * 2. **Checkout-safety preflight.** Refuse to start when the working tree is
18
- * dirty or HEAD is on a branch other than the expected one, instead of
19
- * checking `epic/<id>` out over the operator's work.
20
- *
21
- * Both guards are pure over injected seams (a `git` shim with
22
- * `statusPorcelain` / `currentBranch`, and the lease provider) so the unit
23
- * suite exercises them without standing up a real repo or hitting GitHub.
24
- * Liveness is threaded in via `heartbeatAt` exactly as `ticket-lease.js`
25
- * documents — this module does not read the lifecycle ledger itself.
26
- *
27
- * Per `.agents/rules/orchestration-error-handling.md`, the composed guard
28
- * surfaces unrecoverable failures with `throw new Error(...)` (mapped to
29
- * `process.exit(1)` by the `runAsCli` boundary), never `Logger.fatal`.
30
- */
31
-
32
- import {
33
- acquireLeaseFailClosed,
34
- resolveOperatorFromCandidates,
35
- } from './lease-guard-shared.js';
36
-
37
- /**
38
- * Resolve the acquiring operator's identity. Precedence (Tech Spec #3476):
39
- * 1. An explicit `--as <handle>` flag.
40
- * 2. `github.operatorHandle` in `.agentrc.json`.
41
- * 3. The local `git config user.email`.
42
- *
43
- * The `@`-prefix some operators carry on `operatorHandle` is stripped (via
44
- * the shared lease-guard kernel) so the value matches the bare login written
45
- * to a ticket's `assignees`. This surface's missing-handle policy is `'null'`
46
- * — `runPrepareGuards` fails closed on a null operator with deliver-specific
47
- * wording after the (cheap, local) checkout-safety guard has run.
48
- *
49
- * @param {object} args
50
- * @param {string} [args.asFlag] Explicit `--as` value.
51
- * @param {object} [args.config] Resolved config (reads github.operatorHandle).
52
- * @param {string} [args.gitUserEmail] Fallback `git config user.email`.
53
- * @returns {string|null} The resolved operator handle, or null when none could
54
- * be determined.
55
- */
56
- export function resolveOperator({ asFlag, config, gitUserEmail } = {}) {
57
- return resolveOperatorFromCandidates({
58
- candidates: [asFlag, config?.github?.operatorHandle, gitUserEmail],
59
- });
60
- }
61
-
62
- /**
63
- * Normalise the expected-branch argument into a non-empty list. `/deliver`
64
- * may legitimately start from either the Epic integration branch (`epic/<id>`,
65
- * on a resume) or the project base branch (`main`, on a fresh run), so the
66
- * guard accepts a set rather than a single name.
67
- *
68
- * @param {string|string[]} expectedBranch
69
- * @returns {string[]}
70
- */
71
- function normaliseExpectedBranches(expectedBranch) {
72
- const list = (
73
- Array.isArray(expectedBranch) ? expectedBranch : [expectedBranch]
74
- ).filter((b) => typeof b === 'string' && b.length > 0);
75
- if (list.length === 0) {
76
- throw new Error('checkCheckoutSafety: expectedBranch is required');
77
- }
78
- return list;
79
- }
80
-
81
- /**
82
- * Pure checkout-safety classifier. The working tree must be clean and HEAD
83
- * must sit on one of `expectedBranch` before prepare moves anything.
84
- *
85
- * @param {object} args
86
- * @param {object} args.git Injected git shim.
87
- * @param {() => { dirty: boolean, entries?: string }} args.git.statusPorcelain
88
- * Returns whether the tree has uncommitted/untracked changes.
89
- * @param {() => string|null} args.git.currentBranch Returns HEAD's branch (or
90
- * null in detached-HEAD state).
91
- * @param {string|string[]} args.expectedBranch The branch (or branches)
92
- * prepare expects HEAD on (typically `epic/<id>` or the project base branch).
93
- * @returns {{
94
- * safe: boolean,
95
- * reason: 'clean'|'dirty'|'wrong-branch'|'detached-head',
96
- * currentBranch: string|null,
97
- * expectedBranches: string[],
98
- * dirtyEntries?: string,
99
- * }}
100
- */
101
- export function checkCheckoutSafety({ git, expectedBranch }) {
102
- if (!git || typeof git.statusPorcelain !== 'function') {
103
- throw new Error(
104
- 'checkCheckoutSafety: git shim with statusPorcelain/currentBranch is required',
105
- );
106
- }
107
- const expectedBranches = normaliseExpectedBranches(expectedBranch);
108
-
109
- const status = git.statusPorcelain();
110
- if (status?.dirty) {
111
- return {
112
- safe: false,
113
- reason: 'dirty',
114
- currentBranch: git.currentBranch(),
115
- expectedBranches,
116
- dirtyEntries: status.entries,
117
- };
118
- }
119
-
120
- const branch = git.currentBranch();
121
- if (branch === null || branch === undefined || branch === '') {
122
- return {
123
- safe: false,
124
- reason: 'detached-head',
125
- currentBranch: null,
126
- expectedBranches,
127
- };
128
- }
129
- if (!expectedBranches.includes(branch)) {
130
- return {
131
- safe: false,
132
- reason: 'wrong-branch',
133
- currentBranch: branch,
134
- expectedBranches,
135
- };
136
- }
137
- return {
138
- safe: true,
139
- reason: 'clean',
140
- currentBranch: branch,
141
- expectedBranches,
142
- };
143
- }
144
-
145
- /**
146
- * Render the operator-facing message for a failed checkout-safety guard.
147
- *
148
- * @param {ReturnType<typeof checkCheckoutSafety>} result
149
- * @returns {string}
150
- */
151
- export function renderCheckoutRefusal(result) {
152
- const expected = result.expectedBranches.map((b) => `'${b}'`).join(' or ');
153
- if (result.reason === 'dirty') {
154
- return (
155
- `[epic-deliver] Refusing to start: the working tree is dirty. ` +
156
- `/deliver will not check out ${expected} over uncommitted ` +
157
- `or untracked changes — commit, stash, or clean them, then re-run.\n` +
158
- `--- dirty entries ---\n${result.dirtyEntries ?? '(unavailable)'}`
159
- );
160
- }
161
- if (result.reason === 'detached-head') {
162
- return (
163
- `[epic-deliver] Refusing to start: HEAD is detached. /deliver ` +
164
- `expects HEAD on ${expected}. Check one out before re-running.`
165
- );
166
- }
167
- return (
168
- `[epic-deliver] Refusing to start: HEAD is on '${result.currentBranch}', ` +
169
- `not the expected ${expected}. /deliver will not yank HEAD ` +
170
- `away from your branch — switch to ${expected} yourself before re-running.`
171
- );
172
- }
173
-
174
- /**
175
- * Render the operator-facing message for a refused (live foreign) lease.
176
- *
177
- * @param {{ owner: string|null }} lease
178
- * @param {number} epicId
179
- * @returns {string}
180
- */
181
- export function renderLeaseRefusal(lease, epicId) {
182
- return (
183
- `[epic-deliver] Refusing to start: Epic #${epicId} is already claimed by ` +
184
- `'${lease.owner}' with a live lease (recent heartbeat within the TTL). ` +
185
- `Another /deliver run is driving this Epic. Wait for it to finish, ` +
186
- `or pass --steal to forcibly transfer the claim.`
187
- );
188
- }
189
-
190
- /**
191
- * Acquire the Epic lease for `operator`, failing closed on a live foreign
192
- * claim. Returns the `acquireLease` result on success.
193
- *
194
- * @param {object} args
195
- * @param {object} args.provider Ticketing provider.
196
- * @param {number} args.epicId Epic ticket id.
197
- * @param {string} args.operator Resolved operator handle.
198
- * @param {number|null} [args.heartbeatAt] Current owner's last heartbeat (epoch ms).
199
- * @param {boolean} [args.steal=false] Transfer a live foreign claim.
200
- * @param {object} [args.config] Resolved config (TTL default).
201
- * @param {number} [args.now] Injectable clock.
202
- * @returns {Promise<{ acquired: boolean, owner: string, previousOwner: string|null, reason: string }>}
203
- * @throws {Error} when the Epic carries a live foreign claim and `steal` is false.
204
- */
205
- export async function acquireEpicLease({
206
- provider,
207
- epicId,
208
- operator,
209
- heartbeatAt = null,
210
- steal = false,
211
- config,
212
- now,
213
- }) {
214
- return acquireLeaseFailClosed({
215
- provider,
216
- ticketId: epicId,
217
- operator,
218
- heartbeatAt,
219
- steal,
220
- config,
221
- now,
222
- renderRefusal: renderLeaseRefusal,
223
- });
224
- }
225
-
226
- /**
227
- * Compose the two preflight guards in the order prepare needs them: the
228
- * checkout-safety check first (cheap, local, no network), then the Epic-lease
229
- * acquisition (a GitHub round-trip). Both fail closed by throwing.
230
- *
231
- * The checkout-safety guard runs first (cheap, local) and always executes. The
232
- * Epic-lease step then **fails closed**: when `operator` cannot be resolved
233
- * (null — `--as`, `github.operatorHandle`, and `git user.email` all empty, with
234
- * the shipped `@[USERNAME]` placeholder normalised to null), this throws rather
235
- * than running an ownerless, unguarded delivery. The lease is the cross-clone
236
- * coordination layer; without an identity it cannot serialise concurrent runs,
237
- * so silently skipping it would defeat the guard.
238
- *
239
- * @param {object} args
240
- * @param {number} args.epicId
241
- * @param {string|string[]} args.expectedBranch Branch(es) prepare expects HEAD on.
242
- * @param {object} args.git Injected git shim (statusPorcelain/currentBranch).
243
- * @param {object} args.provider Ticketing provider for the lease.
244
- * @param {string|null} args.operator Resolved operator handle (null fails closed → throw).
245
- * @param {number|null} [args.heartbeatAt] Current owner's last heartbeat (epoch ms).
246
- * @param {boolean} [args.steal=false] Transfer a live foreign claim.
247
- * @param {object} [args.config] Resolved config.
248
- * @param {number} [args.now] Injectable clock.
249
- * @param {object} [args.logger] Logger with info/warn.
250
- * @returns {Promise<{
251
- * checkout: ReturnType<typeof checkCheckoutSafety>,
252
- * lease: { acquired: boolean, owner: string, reason: string },
253
- * }>}
254
- * @throws {Error} on a dirty/wrong-branch tree, an unresolvable operator
255
- * identity, or a refused live foreign lease.
256
- */
257
- export async function runPrepareGuards({
258
- epicId,
259
- expectedBranch,
260
- git,
261
- provider,
262
- operator,
263
- heartbeatAt = null,
264
- steal = false,
265
- config,
266
- now,
267
- logger,
268
- }) {
269
- const log = logger ?? { info: () => {}, warn: () => {} };
270
-
271
- const checkout = checkCheckoutSafety({ git, expectedBranch });
272
- if (!checkout.safe) {
273
- throw new Error(renderCheckoutRefusal(checkout));
274
- }
275
- log.info?.(
276
- `[epic-deliver] ✅ Checkout-safety guard passed (clean tree on '${checkout.currentBranch}').`,
277
- );
278
-
279
- if (!operator) {
280
- throw new Error(
281
- '[epic-deliver] Refusing to start: no operator identity could be ' +
282
- 'resolved. --as, github.operatorHandle (unset or still the shipped ' +
283
- '`@[USERNAME]` placeholder), and git user.email are all empty, so the ' +
284
- 'Epic-lease has no owner and concurrent /deliver runs cannot be ' +
285
- 'serialised. Set your own handle in .agentrc.local.json (e.g. ' +
286
- '{ "github": { "operatorHandle": "@your-login" } }), pass --as <handle>, ' +
287
- 'or configure git user.email, then re-run.',
288
- );
289
- }
290
-
291
- const lease = await acquireEpicLease({
292
- provider,
293
- epicId,
294
- operator,
295
- heartbeatAt,
296
- steal,
297
- config,
298
- now,
299
- });
300
- if (lease.reason === 'stolen') {
301
- log.warn?.(
302
- `[epic-deliver] ⚠️ Stole the Epic #${epicId} lease from '${lease.previousOwner}' via --steal (live claim forcibly transferred).`,
303
- );
304
- } else {
305
- log.info?.(
306
- `[epic-deliver] ✅ Acquired Epic #${epicId} lease for '${operator}' (reason=${lease.reason}).`,
307
- );
308
- }
309
- return { checkout, lease };
310
- }
@@ -1,163 +0,0 @@
1
- /**
2
- * context.js — Phase 3 of the epic-plan-decompose pipeline (Story #2466).
3
- *
4
- * Builds the authoring context (the Epic body — which carries the folded
5
- * Tech Spec sections and Acceptance Table per Story #4324 — plus
6
- * heuristics, system prompt, ticket cap) the host LLM /
7
- * `epic-plan-decompose-author` Skill consumes when producing the ticket
8
- * JSON array.
9
- *
10
- * Extracted verbatim from `epic-plan-decompose.js`; both
11
- * `buildDecomposerSystemPrompt` and `buildDecompositionContext` retain
12
- * their public-export contract for the existing unit tests.
13
- *
14
- * @module lib/orchestration/epic-plan-decompose/phases/context
15
- */
16
-
17
- import {
18
- getLimits,
19
- resolvePreflightCeilings,
20
- } from '../../../config-resolver.js';
21
- import { hasTechSpecContent } from '../../../epic-body-sections.js';
22
- import { renderDecomposerSystemPrompt } from '../../../templates/decomposer-prompts.js';
23
- import { read as readPlanState } from '../../epic-plan-state-store.js';
24
- import { applyBudget } from '../../planning-context-budget.js';
25
-
26
- export function buildDecomposerSystemPrompt(
27
- heuristics = [],
28
- { maxTickets, maxTokenBudget, epicId } = {},
29
- ) {
30
- const base = renderDecomposerSystemPrompt({
31
- maxTickets,
32
- maxTokenBudget,
33
- epicId,
34
- });
35
- const heuristicsStr =
36
- heuristics.length > 0
37
- ? `### RISK HEURISTICS (planning metadata if any apply):\n- ${heuristics.join('\n- ')}`
38
- : '';
39
- return `${base}${heuristicsStr ? `\n\n${heuristicsStr}` : ''}`;
40
- }
41
-
42
- function resolveHeuristics(config) {
43
- if (Array.isArray(config.planning?.riskHeuristics)) {
44
- return config.planning.riskHeuristics;
45
- }
46
- return config.agentSettings?.planning?.riskHeuristics || [];
47
- }
48
-
49
- function projectBudgetedEntry(item, ticket, mode) {
50
- if (mode === 'full') return { id: ticket.id, body: ticket.body };
51
- return { id: ticket.id, body: null, bodySummary: item };
52
- }
53
-
54
- /**
55
- * Read the persisted planning decision for the Epic so the decomposition
56
- * authoring step (Phase 8) can cite the same risk classification the
57
- * gate routing used in Phase 7. The decision lives in the `epic-plan-state`
58
- * structured comment written by `epic-plan-spec.js`.
59
- *
60
- * Returns `{ planningRisk: null, reviewRouting: null }` when the comment
61
- * is missing (older plans planned before Story #2801 landed) or when the
62
- * payload predates the fields. The null sentinels are part of the
63
- * decomposition context contract — callers MUST be able to JSON.stringify
64
- * the result without losing the keys.
65
- *
66
- * @param {import('../../../ITicketingProvider.js').ITicketingProvider} provider
67
- * @param {number} epicId
68
- * @returns {Promise<{ planningRisk: object | null, reviewRouting: object | null }>}
69
- */
70
- async function readPlanningDecision(provider, epicId) {
71
- let state = null;
72
- try {
73
- state = await readPlanState({ provider, epicId });
74
- } catch (_err) {
75
- // `read` already swallows JSON parse errors; a thrown error here
76
- // means the provider couldn't fetch comments. Treat as null state —
77
- // the decomposer can still author tickets without the risk envelope.
78
- state = null;
79
- }
80
- if (!state || typeof state !== 'object') {
81
- return { planningRisk: null, reviewRouting: null };
82
- }
83
- return {
84
- planningRisk: state.planningRisk ?? null,
85
- reviewRouting: state.reviewRouting ?? null,
86
- };
87
- }
88
-
89
- async function fetchPlanningEpic(provider, epicId) {
90
- const epic = await provider.getEpic(epicId);
91
- if (!epic || !hasTechSpecContent(epic.body ?? '')) {
92
- throw new Error(
93
- `[Decomposer] Epic #${epicId} body carries no Tech Spec sections (no ## Delivery Slicing). Run the Epic Planner (Phase 7) first.`,
94
- );
95
- }
96
- return { epic };
97
- }
98
-
99
- /**
100
- * Build the authoring context the host LLM (or the
101
- * `epic-plan-decompose-author` Skill) needs to produce the ticket JSON.
102
- *
103
- * The Epic body (ideation sections + folded Tech Spec sections +
104
- * Acceptance Table — the AC-ID source for wave-0 BDD scaffold tags) is
105
- * bounded by the planning-context budget (Epic #817 Story 9). Pass
106
- * `{ fullContext: true }` (CLI: `--full-context`) to restore the
107
- * unbounded full body.
108
- */
109
- export async function buildDecompositionContext(
110
- epicId,
111
- provider,
112
- config = {},
113
- opts = {},
114
- ) {
115
- const { epic } = await fetchPlanningEpic(provider, epicId);
116
- const { planningRisk, reviewRouting } = await readPlanningDecision(
117
- provider,
118
- epicId,
119
- );
120
- const heuristics = resolveHeuristics(config);
121
- const limits = getLimits(config);
122
- const maxTickets = limits.maxTickets;
123
- const maxTokenBudget = limits.maxTokenBudget;
124
- const planningLimits = limits.planningContext;
125
- const { fullContext = false } = opts;
126
- const systemPrompt = buildDecomposerSystemPrompt(heuristics, {
127
- maxTickets,
128
- maxTokenBudget,
129
- epicId,
130
- });
131
-
132
- const budgeted = applyBudget(
133
- [{ path: `epic-${epic.id}.md`, content: epic.body ?? '' }],
134
- planningLimits,
135
- { fullContext },
136
- );
137
- const [epicItem] = budgeted.items;
138
- return {
139
- epic: { id: epic.id, title: epic.title },
140
- // Story #4324 — the Epic body is the single planning document: it
141
- // carries the ideation sections, the folded Tech Spec sections
142
- // (## Delivery Slicing first), and the ## Acceptance Table the wave-0
143
- // BDD scaffold reads its AC IDs from.
144
- epicBody: projectBudgetedEntry(epicItem, epic, budgeted.mode),
145
- heuristics,
146
- systemPrompt,
147
- maxTickets,
148
- // Story #3875 — surface the real delivery envelope to the decomposer
149
- // so Stories are sized against the hydration budget and the
150
- // configured preflight ceilings rather than guessed. Story #4162 also
151
- // threads this value into the rendered systemPrompt above as a sizing
152
- // input so the prompt itself names the budget.
153
- maxTokenBudget,
154
- preflightCeilings: resolvePreflightCeilings(config),
155
- contextMode: budgeted.mode,
156
- // Story #2801 — surface the Phase 7 planning decision so the
157
- // decomposition authoring step can cite the same risk
158
- // classification used by gate routing. Both fields are `null`
159
- // when the Epic was planned before the decision contract existed.
160
- planningRisk,
161
- reviewRouting,
162
- };
163
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * creation.js — sub-issue link reconciliation, Epic label transitions,
3
- * the advisory ticket-cap warning, and the blocked-by dependency wiring
4
- * used by the reconciler-based persist flow (`persist.js`).
5
- *
6
- * Exports: `reconcileSubIssueLinks`, `setBlockedByDependencies`,
7
- * `setEpicLabel`, `warnTicketCapNearLimit`.
8
- *
9
- * @module lib/orchestration/epic-plan-decompose/phases/creation
10
- */
11
-
12
- import { applyBlockedByDependencies } from '../../../../providers/github/blocked-by-add.js';
13
- import { Logger } from '../../../Logger.js';
14
- import { AGENT_LABELS } from '../../../label-constants.js';
15
-
16
- export async function reconcileSubIssueLinks(epicId, provider) {
17
- if (typeof provider.reconcileSubIssueLinks !== 'function') return;
18
- Logger.info(
19
- `[Decomposer] Reconciling sub-issue API links for Epic #${epicId}...`,
20
- );
21
- const result = await provider.reconcileSubIssueLinks(epicId);
22
- const { totalExpected, alreadyLinked, reconciled, failed, failures } = result;
23
- if (failed === 0) {
24
- const reconciledNote = reconciled > 0 ? ` (${reconciled} reconciled)` : '';
25
- Logger.info(
26
- `[Decomposer] linked ${alreadyLinked + reconciled}/${totalExpected} sub-issues${reconciledNote}`,
27
- );
28
- return;
29
- }
30
- for (const failure of failures) {
31
- Logger.error(
32
- `[Decomposer] sub-issue link gap: parent #${failure.parentId} ← child #${failure.childId}: ${failure.reason}`,
33
- );
34
- }
35
- throw new Error(
36
- `[Decomposer] Sub-issue reconciliation incomplete: ${failed}/${totalExpected} links could not be established (linked=${alreadyLinked}, reconciled=${reconciled}). See log for per-child reasons.`,
37
- );
38
- }
39
-
40
- /**
41
- * Translate each Story's `dependsOn` slug list into native GitHub "blocked
42
- * by" dependency edges. Best-effort and non-fatal: individual edge failures
43
- * are logged as warnings and do not abort the decompose phase.
44
- *
45
- * Requires the provider to expose `owner`, `repo`, `_gh`, and `getTicket`.
46
- * No-ops silently when any required surface is absent so callers remain
47
- * safe across provider stubs.
48
- *
49
- * @param {number} epicId
50
- * @param {import('../../../ITicketingProvider.js').ITicketingProvider} provider
51
- * @param {object} spec — the parsed YAML spec (has `.stories[*].dependsOn`).
52
- * @param {object} stateMapping — slug → `{ issueNumber }` from the reconciler state.
53
- */
54
- export async function setBlockedByDependencies(
55
- epicId,
56
- provider,
57
- spec,
58
- stateMapping,
59
- ) {
60
- const stories = Array.isArray(spec?.stories) ? spec.stories : [];
61
- const hasDeps = stories.some(
62
- (s) => Array.isArray(s.dependsOn) && s.dependsOn.length > 0,
63
- );
64
- if (!hasDeps) return;
65
-
66
- if (
67
- !provider?.owner ||
68
- !provider?.repo ||
69
- !provider?._gh ||
70
- typeof provider.getTicket !== 'function'
71
- ) {
72
- Logger.warn(
73
- `[Decomposer] setBlockedByDependencies: provider missing required surface (owner/repo/_gh/getTicket); skipping.`,
74
- );
75
- return;
76
- }
77
-
78
- Logger.info(
79
- `[Decomposer] Setting native blocked-by dependency edges for Epic #${epicId}...`,
80
- );
81
-
82
- // Build a slug → issueNumber map from the reconciler state mapping.
83
- const slugToIssueNumber = {};
84
- for (const [slug, entry] of Object.entries(stateMapping ?? {})) {
85
- if (typeof entry?.issueNumber === 'number') {
86
- slugToIssueNumber[slug] = entry.issueNumber;
87
- }
88
- }
89
-
90
- const result = await applyBlockedByDependencies({
91
- stories,
92
- slugToIssueNumber,
93
- getTicket: (n) => provider.getTicket(n),
94
- owner: provider.owner,
95
- repo: provider.repo,
96
- gh: provider._gh,
97
- });
98
-
99
- const { edgesAdded, edgesSkipped, edgesFailed, storiesProcessed } = result;
100
- Logger.info(
101
- `[Decomposer] blocked-by edges: ${edgesAdded} added, ${edgesSkipped} already present, ${edgesFailed} failed (${storiesProcessed} stories with deps processed).`,
102
- );
103
- }
104
-
105
- export async function setEpicLabel(provider, epicId, targetLabel) {
106
- const planningLabels = [AGENT_LABELS.REVIEW_SPEC, AGENT_LABELS.READY];
107
- await provider.updateTicket(epicId, {
108
- labels: {
109
- add: [targetLabel],
110
- remove: planningLabels.filter((l) => l !== targetLabel),
111
- },
112
- });
113
- }
114
-
115
- /**
116
- * Advisory-only ticket-count check (Story #2798).
117
- *
118
- * `maxTickets` is a **reviewability budget**, not a hard authoring cap.
119
- * This helper emits a non-destructive warning when a decomposition meets
120
- * or exceeds the budget so the operator can spot over-budget plans early
121
- * in the persist flow. It never blocks — the hard gate lives in the
122
- * `runDecomposePhase` over-budget check, which requires an explicit
123
- * `allowOverBudget` (CLI: `--allow-over-budget`) override.
124
- *
125
- * @param {Array} tickets
126
- * @param {number} maxTickets — the reviewability budget
127
- * @param {string} [tag] — log prefix
128
- * @param {{ logger?: Pick<typeof Logger, 'warn'> }} [opts]
129
- */
130
- export function warnTicketCapNearLimit(
131
- tickets,
132
- maxTickets,
133
- tag = 'epic-plan-decompose',
134
- { logger = Logger } = {},
135
- ) {
136
- if (tickets.length < maxTickets) return;
137
- logger.warn(
138
- `[${tag}] ⚠️ Received ${tickets.length} tickets against a reviewability budget of ${maxTickets}. Review the Story decomposition before persisting; over-budget persistence requires --allow-over-budget.`,
139
- );
140
- }
@@ -1,64 +0,0 @@
1
- /**
2
- * dag.js — Phase 2 of the epic-plan-decompose pipeline (Story #2466).
3
- *
4
- * Owns the deterministic DAG helpers used by the reconciler pipeline:
5
- * - `resolveDependencies(ticket, slugMap)`
6
- * - `orderTicketsForCreation(validated)` (topological sort of the
7
- * Story set so dependency producers are created before consumers)
8
- *
9
- * Extracted verbatim from `epic-plan-decompose.js` so the named exports
10
- * (`resolveDependencies`, `orderTicketsForCreation`) that the
11
- * `tests/ticket-decomposer.test.js` suite imports keep their contract.
12
- *
13
- * @module lib/orchestration/epic-plan-decompose/phases/dag
14
- */
15
-
16
- export function resolveDependencies(ticket, slugMap) {
17
- const resolved = [];
18
- for (const dep of ticket.depends_on || []) {
19
- const depId = slugMap.get(dep);
20
- if (depId === undefined) {
21
- // Unreachable through normal flow: validateAndNormalizeTickets
22
- // already rejects unknown slugs and the topological sort guarantees
23
- // creation order. A throw here turns a future regression (e.g.
24
- // someone bypassing the validator) into a loud failure instead of a
25
- // silently-dropped DAG edge.
26
- throw new Error(
27
- `[Decomposer] ${ticket.type.toUpperCase()} "${ticket.title}" (${ticket.slug}) depends on unresolved slug "${dep}". This indicates a planner bug or out-of-order ticket creation.`,
28
- );
29
- }
30
- resolved.push(depId);
31
- }
32
- return resolved;
33
- }
34
-
35
- function topoSortGroup(group) {
36
- const slugToTicket = new Map(group.map((t) => [t.slug, t]));
37
- const visited = new Set();
38
- const sorted = [];
39
-
40
- function visit(t) {
41
- if (visited.has(t.slug)) return;
42
- visited.add(t.slug);
43
- for (const dep of t.depends_on ?? []) {
44
- const depTicket = slugToTicket.get(dep);
45
- if (depTicket) visit(depTicket);
46
- }
47
- sorted.push(t);
48
- }
49
-
50
- for (const t of group) visit(t);
51
- return sorted;
52
- }
53
-
54
- /**
55
- * Topologically sort the Story set so intra-set dep chains resolve
56
- * before their dependents are created. The 2-tier hierarchy has a
57
- * single ticket type (story) attached directly to the Epic, so there
58
- * is no parent-type ordering to interleave.
59
- */
60
- export function orderTicketsForCreation(validated) {
61
- const result = [];
62
- for (const t of topoSortGroup([...validated])) result.push(t);
63
- return result;
64
- }