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,302 +0,0 @@
1
- /**
2
- * pre-merge-validation.js — shift-left close-validation gate runner +
3
- * maintainability projection advisory.
4
- *
5
- * Extracted from story-close.js (Story #956, Theme A finishing touch) so the
6
- * close orchestrator becomes a thin CLI shell. Two responsibilities:
7
- *
8
- * - runPreMergeGates — drives `runCloseValidation` over the
9
- * canonical gate list (typecheck, lint, test,
10
- * format, maintainability, crap), routes
11
- * `lint`/`test` start events into the supplied
12
- * phase-timer, and throws on the first failed
13
- * gate with the gate-specific hint embedded in
14
- * the error message.
15
- * - emitMaintainabilityProjection — runs the per-file MI ceiling projection
16
- * and emits the `baseline-refresh:` advisory
17
- * before the merge so the operator can ship the
18
- * refresh atomically with the Story PR.
19
- *
20
- * Both helpers take their dependencies as injectable seams so unit tests
21
- * pin behaviour without spawning the close script.
22
- */
23
-
24
- // Story #1973 / Task #1985 — direct import from the maintainability per-kind
25
- // module under `.agents/scripts/lib/baselines/kinds/`. Replaces the historical
26
- // `child_process.spawn(node check-maintainability.js)` arm of this helper:
27
- // the kernel-version label that used to come from the CLI's stdout is now
28
- // resolved in-process from the per-kind module, and the test suite's
29
- // no-spawn spy proves the projection path never reaches a per-kind CLI
30
- // subprocess.
31
- import * as maintainabilityKind from '../../baselines/kinds/maintainability.js';
32
- import { buildDefaultGates as defaultBuildDefaultGates } from '../../close-validation/gates.js';
33
- import {
34
- formatMaintainabilityProjection as defaultFormatMaintainabilityProjection,
35
- projectMaintainabilityRegressions as defaultProjectMaintainabilityRegressions,
36
- } from '../../close-validation/projections/maintainability.js';
37
- import { runCloseValidation as defaultRunCloseValidation } from '../../close-validation/runner.js';
38
- import { getBaselines as defaultGetBaselines } from '../../config-resolver.js';
39
- import { Logger as DefaultLogger } from '../../Logger.js';
40
-
41
- /**
42
- * Story #2250 — lifecycle emits fire only when both a positive epicId and a
43
- * positive storyId are present (the schema requires both) and a bus exists.
44
- * Legacy resume fixtures pass `storyId: null` and must run without emits.
45
- * Story #4075 — extracted from `runPreMergeGates`.
46
- */
47
- export function lifecycleEmitsActive({ epicId, storyId, bus }) {
48
- return (
49
- Number.isInteger(epicId) &&
50
- epicId > 0 &&
51
- Number.isInteger(storyId) &&
52
- storyId > 0 &&
53
- !!bus
54
- );
55
- }
56
-
57
- /**
58
- * Build the typed `PRE_MERGE_GATE_FAILED` Error from the first failed gate.
59
- * Story #2136 / Task #2143 — failure metadata is surfaced as typed Error
60
- * properties so callers (notably `runPreMergeGatesWithAttribution`)
61
- * pattern-match exit codes without parsing the human message; the message
62
- * format is preserved byte-for-byte so existing regex consumers keep
63
- * matching. Story #4075 — extracted from `runPreMergeGates`.
64
- */
65
- export function buildGateFailureError({ gate, status, gateCwd }) {
66
- const err = new Error(
67
- `Pre-merge validation failed at "${gate.name}" (exit ${status})${gateCwd ? ` in ${gateCwd}` : ''}.` +
68
- (gate.hint ? ` ${gate.hint}` : ''),
69
- );
70
- err.code = 'PRE_MERGE_GATE_FAILED';
71
- err.gateName = gate.name;
72
- err.exitCode = status;
73
- err.gateCwd = gateCwd ?? null;
74
- return err;
75
- }
76
-
77
- /**
78
- * Run the pre-merge validation gate chain. On failure throws an `Error`
79
- * whose message embeds the first failed gate's name, exit code, hint, and
80
- * the working directory the gate ran in — the `runAsCli` boundary in
81
- * `story-close.js` maps the throw to `process.exit(1)`. (See Story #959 —
82
- * close-tail scripts must throw rather than route through the logger's
83
- * fatal sink, so a mocked `process.exit` cannot swallow the failure
84
- * silently.)
85
- *
86
- * Story #1120: pass `worktreePath` (`.worktrees/story-<id>/`) so every
87
- * gate runs against the Story branch's post-rebase tree, not the main
88
- * checkout. Without it, gate spawn falls back to `cwd` (the main
89
- * checkout) — the legacy single-tree path remains intact.
90
- *
91
- * `phaseTimer` may be omitted; when present, lint/test starts are timed.
92
- *
93
- * Story #2250 — the gate chain is the canonical close-validate sub-phase
94
- * of the close-tail; this helper emits `close-validate.start` /
95
- * `close-validate.end` events on the supplied lifecycle bus so the
96
- * lifecycle ledger captures the sub-phase boundary with non-zero
97
- * `durationMs`. On gate failure the helper also emits `story.blocked`
98
- * with a typed `close-validate-failed:<gateName>` reason BEFORE throwing
99
- * — the existing BlockerHandler listener (Story #2241 / Task #2246)
100
- * cascades that to `epic.blocked`, so a failed validator routes through
101
- * the lifecycle cascade rather than being silently swallowed by the
102
- * caller's try/catch. The throw shape is preserved byte-for-byte so the
103
- * regex consumers in `runPreMergeGatesWithAttribution` keep matching.
104
- */
105
- export async function runPreMergeGates({
106
- cwd,
107
- worktreePath,
108
- epicBranch,
109
- config,
110
- storyId,
111
- epicId,
112
- useEvidence = true,
113
- phaseTimer,
114
- bus,
115
- now = Date.now,
116
- logger = DefaultLogger,
117
- buildDefaultGates = defaultBuildDefaultGates,
118
- runCloseValidation = defaultRunCloseValidation,
119
- }) {
120
- // Epic #2646 Story C (Task #2700) — `bus` is a hard input. The
121
- // previous guarded `emitLifecycleSafe` helper that tolerated a null
122
- // bus is gone. The `emitsActive` outer guard below still skips emits
123
- // for legacy resume fixtures that pass `storyId: null` (those rows
124
- // have no Story-scoped lifecycle), but bus must be present.
125
- if (!bus || typeof bus.emit !== 'function') {
126
- throw new TypeError(
127
- 'runPreMergeGates: bus is required (object with emit()).',
128
- );
129
- }
130
- logger.info?.(
131
- `[close-validation] Running pre-merge gates (typecheck, lint, test, format, maintainability, crap, baselines)${worktreePath ? ` in ${worktreePath}` : ''}${epicBranch ? ` against baseline ref ${epicBranch}` : ''}...`,
132
- );
133
- // `buildDefaultGates` reads the canonical resolved config directly:
134
- // gate commands resolve from `project.commands` and the CRAP toggle
135
- // from `delivery.quality.gates.crap.enabled`.
136
- // Probe the coverage script from the gate execution directory (the Story
137
- // worktree when present) so coverage-capture is only registered when the
138
- // consumer ships `test:coverage` (#4473).
139
- const gates = buildDefaultGates({
140
- config,
141
- epicBranch,
142
- cwd: worktreePath || cwd,
143
- log: (m) => logger.info?.(m),
144
- });
145
- const gateCount = Array.isArray(gates) ? gates.length : 0;
146
- // Story #2250 — emit `close-validate.start` only when both an epicId
147
- // and a storyId are present; the schema requires both, and unit
148
- // fixtures that drive the helper with `storyId: null` (legacy resume
149
- // tests) must continue to operate without lifecycle observability.
150
- const emitsActive = lifecycleEmitsActive({ epicId, storyId, bus });
151
- const startedAt = typeof now === 'function' ? now() : Date.now();
152
-
153
- // Emit the `close-validate.end` boundary, plus the `story.blocked`
154
- // cascade trigger on failure (Story #2250). No-op when emits are
155
- // inactive (legacy resume fixtures with `storyId: null`).
156
- const emitEnd = async ({ ok, extra = {}, blockedReason } = {}) => {
157
- if (!emitsActive) return;
158
- const endedAt = typeof now === 'function' ? now() : Date.now();
159
- await bus.emit('close-validate.end', {
160
- epicId,
161
- storyId,
162
- ok,
163
- gateCount,
164
- ...extra,
165
- durationMs: Math.max(0, endedAt - startedAt),
166
- });
167
- if (blockedReason) {
168
- await bus.emit('story.blocked', { storyId, reason: blockedReason });
169
- }
170
- };
171
-
172
- if (emitsActive) {
173
- await bus.emit('close-validate.start', { epicId, storyId });
174
- }
175
- let validation;
176
- try {
177
- validation = await runCloseValidation({
178
- cwd,
179
- worktreePath,
180
- gates,
181
- log: (m) => logger.info(m),
182
- onGateStart: (gate) => {
183
- // Only the canonical phase-enum gates drive `mark()`. Non-enum gates
184
- // (`typecheck`, `format`, `check-maintainability`) share the
185
- // currently-open phase's wall clock — a deliberate choice so the
186
- // `phase-timings` schema stays stable against future gate churn.
187
- if (phaseTimer && (gate.name === 'lint' || gate.name === 'test')) {
188
- phaseTimer.mark(gate.name);
189
- }
190
- },
191
- storyId,
192
- epicId,
193
- useEvidence,
194
- });
195
- } catch (err) {
196
- // Reach this branch only when `runCloseValidation` itself throws
197
- // (spawn-level catastrophe — the per-gate failures route through
198
- // `validation.failed` below). Emit the matching `close-validate.end`
199
- // with `ok:false` so the ledger always carries the boundary, then
200
- // re-throw.
201
- await emitEnd({
202
- ok: false,
203
- extra: { failedGate: 'runner-error' },
204
- blockedReason: 'close-validate-failed:runner-error',
205
- });
206
- throw err;
207
- }
208
- if (!validation.ok) {
209
- const { gate, status, cwd: gateCwd } = validation.failed[0];
210
- // Story #2250 — emit the boundary + `story.blocked` cascade BEFORE
211
- // throwing so the lifecycle ledger captures the boundary even when the
212
- // caller's try/catch swallows the throw, and the BlockerHandler
213
- // listener cascades to `epic.blocked`.
214
- await emitEnd({
215
- ok: false,
216
- extra: { failedGate: gate.name, exitCode: status },
217
- blockedReason: `close-validate-failed:${gate.name}`,
218
- });
219
- throw buildGateFailureError({ gate, status, gateCwd });
220
- }
221
- await emitEnd({ ok: true });
222
- return validation;
223
- }
224
-
225
- /**
226
- * Resolve the maintainability kernel version from the per-kind module so
227
- * the projection log header can name the kernel currently in scope. Reads
228
- * are best-effort — a sentinel `'0.0.0'` from the kernel-version
229
- * resolver (e.g. when typhonjs-escomplex is missing under a partial
230
- * install) collapses to `null` so the helper never injects a misleading
231
- * label into the log.
232
- *
233
- * Story #1973 / Task #1985 — this is the only call site outside the
234
- * `baselines/` tree that touches `kinds/maintainability.js` directly; the
235
- * import is the load-bearing acceptance hook for "no per-kind CLI spawn"
236
- * because referencing `kindModule.kernelVersion` proves the helper does
237
- * not need to fork a subprocess to learn what kernel it is running under.
238
- *
239
- * @param {object} kindModule - Per-kind maintainability module.
240
- * @returns {string | null}
241
- */
242
- function resolveKernelLabel(kindModule) {
243
- try {
244
- const v = kindModule?.kernelVersion?.();
245
- if (typeof v !== 'string' || v === '0.0.0') return null;
246
- return v;
247
- } catch {
248
- return null;
249
- }
250
- }
251
-
252
- /**
253
- * Emit the per-file MI ceiling projection advisory. Failure is non-fatal
254
- * (logged through `logger.warn`) — the projection is informational only,
255
- * and a missing baseline path skips the helper entirely.
256
- *
257
- * Story #1973 / Task #1985 — the projection no longer fans out a
258
- * per-kind `child_process.spawn(node check-maintainability.js)` to learn
259
- * the kernel context: the per-kind module under `baselines/kinds/` is
260
- * imported directly. The `kindModule` collaborator is injectable so unit
261
- * tests can pin the kernel label without touching the on-disk module.
262
- */
263
- export function emitMaintainabilityProjection({
264
- cwd,
265
- epicBranch,
266
- storyBranch,
267
- config,
268
- logger = DefaultLogger,
269
- getBaselines = defaultGetBaselines,
270
- projectMaintainabilityRegressions = defaultProjectMaintainabilityRegressions,
271
- formatMaintainabilityProjection = defaultFormatMaintainabilityProjection,
272
- kindModule = maintainabilityKind,
273
- }) {
274
- try {
275
- const baselinePath = getBaselines(config)?.maintainability?.path;
276
- if (!baselinePath) return;
277
- const projection = projectMaintainabilityRegressions({
278
- cwd,
279
- epicBranch,
280
- storyBranch,
281
- baselinePath,
282
- });
283
- const advisory = formatMaintainabilityProjection(projection);
284
- if (advisory) {
285
- const kernel = resolveKernelLabel(kindModule);
286
- if (kernel) {
287
- logger.info(
288
- `[close-validation] Pre-merge MI projection (kernel=${kernel}):`,
289
- );
290
- }
291
- for (const line of advisory.split('\n')) logger.info(line);
292
- } else if (projection.skipped) {
293
- logger.info(
294
- `[close-validation] Pre-merge MI projection skipped (${projection.skipped}).`,
295
- );
296
- }
297
- } catch (err) {
298
- logger.warn?.(
299
- `[close-validation] Pre-merge MI projection failed: ${err?.message ?? err}`,
300
- );
301
- }
302
- }
@@ -1,163 +0,0 @@
1
- /**
2
- * shared-checkout-guard.js — cross-epic contention guard for the merge
3
- * phase's `git checkout <epicBranch>` in the shared main checkout
4
- * (Story #4460).
5
- *
6
- * `story-close.js`'s merge phase (`runFinalizeMerge` in `merge-runner.js`)
7
- * runs `git checkout <epicBranch>` directly in the shared main repo
8
- * checkout — `close-inputs.js` resolves `cwd` to `PROJECT_ROOT`, not an
9
- * isolated worktree. The only exclusivity guard around that shared
10
- * checkout is the per-Epic `epic-merge-lock.js` mutex, which only
11
- * serializes concurrent runs for the SAME epic. Nothing stops a
12
- * DIFFERENT epic's concurrently-running `story-close.js` from treating
13
- * the same shared checkout as scratch space at the same time.
14
- *
15
- * This was observed live: while delivering Epic #4425 Stories #4427/#4428,
16
- * the shared checkout repeatedly carried uncommitted stray changes
17
- * belonging to a concurrently-running Epic #4405 delivery (parked on
18
- * `epic/4405` with dirty edits), which blocked the `git checkout epic/4425`
19
- * merge step with a raw `error: Your local changes ... would be
20
- * overwritten by checkout`.
21
- *
22
- * `assertSharedCheckoutAvailable` runs immediately before that checkout
23
- * and fails fast with an actionable, story-close-specific diagnostic
24
- * instead of letting the raw git error surface. It COMPOSES with (does
25
- * not replace) the per-Epic lock: by the time this guard runs, the
26
- * caller's own epic lock is already held (acquired around the whole
27
- * close flow in `story-close.js`), so this guard only inspects OTHER
28
- * epics' lock files plus the tree's overall dirty state — it never
29
- * contends with same-epic concurrent runs, which continue to serialize
30
- * solely via `withEpicMergeLock` before this guard ever executes.
31
- *
32
- * Breadth trade-off (deliberate): the foreign-lock probe keys on the
33
- * OTHER epic's per-epic merge lock, which that run holds for its WHOLE
34
- * close flow — so any overlapping story-close of another epic trips this
35
- * guard even when that run never actually touches the shared checkout
36
- * during the overlap. The refusal is deterministic and loud (throws with
37
- * a diagnostic naming the holder pid), never a deadlock (no waiting),
38
- * and stale/dead-pid foreign locks are ignored via the pid-liveness
39
- * probe. Narrowing the window (a dedicated checkout-phase lock) was
40
- * considered and rejected: the coarse refusal is rare, cheap to retry,
41
- * and far simpler than a second lock tier.
42
- */
43
-
44
- import { findForeignActiveEpicLock as defaultFindForeignActiveEpicLock } from '../../epic-merge-lock.js';
45
- import { gitSpawn as defaultGitSpawn } from '../../git-utils.js';
46
-
47
- const MAX_LISTED_DIRTY_FILES = 20;
48
-
49
- function describeForeignLock(foreign) {
50
- const acquired = Number.isFinite(foreign.acquiredAt)
51
- ? new Date(foreign.acquiredAt).toISOString()
52
- : 'an unknown time';
53
- return (
54
- `[story-close] shared-checkout guard: refusing to touch the shared main checkout — ` +
55
- `it is currently held by epic #${foreign.epicId}'s story-close merge phase ` +
56
- `(lock ${foreign.filePath}, pid ${foreign.pid}, acquired ${acquired}). ` +
57
- `Wait for that epic's story-close run to finish before retrying this merge. ` +
58
- `If you have independently confirmed that process is no longer running, remove ` +
59
- `the lock file by hand — never force a checkout past a live foreign lock. ` +
60
- `See .agents/rules/git-conventions.md § Shared-checkout contention (Story #4460).`
61
- );
62
- }
63
-
64
- function listDirtyFiles(porcelainOutput) {
65
- // `git status --porcelain` lines are `XY <path>` — a fixed 2-char status
66
- // column, a space, then the path. Strip only a trailing `\r` (Windows)
67
- // before slicing off that 3-char prefix; trimming the whole line first
68
- // would shift the slice offset whenever the status column starts with a
69
- // space (the common "unstaged modification" case), truncating the path.
70
- const lines = porcelainOutput
71
- .split('\n')
72
- .map((line) => line.replace(/\r$/, ''))
73
- .filter((line) => line.length > 0);
74
- const shown = lines
75
- .slice(0, MAX_LISTED_DIRTY_FILES)
76
- .map((line) => line.slice(3).trim() || line);
77
- const overflow = lines.length - shown.length;
78
- return overflow > 0
79
- ? `${shown.join(', ')}, … (+${overflow} more)`
80
- : shown.join(', ');
81
- }
82
-
83
- function describeDirtyCheckout({ cwd, epicId, currentBranch, dirtyFiles }) {
84
- return (
85
- `[story-close] shared-checkout guard: refusing to check out the epic branch for ` +
86
- `epic #${epicId} — the shared main checkout at ${cwd} is dirty (currently on ` +
87
- `\`${currentBranch}\`). Dirty files: ${dirtyFiles}. This usually means another ` +
88
- `epic's story-close run left uncommitted work in the shared checkout, or a prior ` +
89
- `run crashed mid-merge. Resolve manually (stash/commit/reset in ${cwd}) before ` +
90
- `retrying. See .agents/rules/git-conventions.md § Shared-checkout contention ` +
91
- `(Story #4460).`
92
- );
93
- }
94
-
95
- /**
96
- * Fail fast when the shared main checkout is not safely available for this
97
- * epic's merge-phase `git checkout <epicBranch>` — either because another
98
- * epic's story-close merge phase currently holds it (a live foreign lock),
99
- * or because it is simply dirty (regardless of whose branch is checked
100
- * out). Silent no-op when the checkout is clean and uncontended.
101
- *
102
- * @param {{
103
- * cwd: string,
104
- * epicId: number|string,
105
- * gitSpawn?: typeof defaultGitSpawn,
106
- * findForeignActiveEpicLock?: typeof defaultFindForeignActiveEpicLock,
107
- * }} opts
108
- * @throws {Error} with an actionable, story-close-specific diagnostic.
109
- */
110
- /**
111
- * Foreign-lock-only variant for the RESUME merge path (Story #4460
112
- * follow-up): a resume legitimately re-enters a shared checkout that is
113
- * dirty with THIS story's own in-progress merge, so the dirty-tree half
114
- * of `assertSharedCheckoutAvailable` would false-positive there. The
115
- * cross-epic hazard — another epic's live merge phase holding the
116
- * checkout — still applies and is the only probe this variant runs.
117
- */
118
- export function assertNoForeignEpicLock({
119
- cwd,
120
- epicId,
121
- findForeignActiveEpicLock = defaultFindForeignActiveEpicLock,
122
- }) {
123
- const foreign = findForeignActiveEpicLock(epicId, { repoRoot: cwd });
124
- if (foreign) {
125
- throw new Error(describeForeignLock(foreign));
126
- }
127
- }
128
-
129
- export function assertSharedCheckoutAvailable({
130
- cwd,
131
- epicId,
132
- gitSpawn = defaultGitSpawn,
133
- findForeignActiveEpicLock = defaultFindForeignActiveEpicLock,
134
- }) {
135
- const foreign = findForeignActiveEpicLock(epicId, { repoRoot: cwd });
136
- if (foreign) {
137
- throw new Error(describeForeignLock(foreign));
138
- }
139
-
140
- const statusRes = gitSpawn(cwd, 'status', '--porcelain');
141
- if (statusRes.status !== 0) {
142
- // Can't determine dirtiness from here — let the downstream checkout
143
- // surface whatever git itself reports rather than guessing.
144
- return;
145
- }
146
- const porcelain = statusRes.stdout || '';
147
- if (porcelain.trim().length === 0) return;
148
-
149
- const branchRes = gitSpawn(cwd, 'rev-parse', '--abbrev-ref', 'HEAD');
150
- const currentBranch =
151
- branchRes.status === 0
152
- ? (branchRes.stdout || '').trim() || 'unknown'
153
- : 'unknown';
154
-
155
- throw new Error(
156
- describeDirtyCheckout({
157
- cwd,
158
- epicId,
159
- currentBranch,
160
- dirtyFiles: listDirtyFiles(porcelain),
161
- }),
162
- );
163
- }