mandrel 2.0.0 → 2.2.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 (323) hide show
  1. package/.agents/README.md +59 -28
  2. package/.agents/agents/acceptance-critic.md +20 -9
  3. package/.agents/agents/story-worker.md +45 -48
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +60 -46
  6. package/.agents/docs/agentrc-reference.json +8 -13
  7. package/.agents/docs/configuration.md +33 -57
  8. package/.agents/docs/execution-reference.md +39 -10
  9. package/.agents/docs/quality-gates.md +17 -19
  10. package/.agents/docs/workflows.md +6 -6
  11. package/.agents/instructions.md +64 -79
  12. package/.agents/rules/ci-remediation.md +3 -3
  13. package/.agents/rules/gherkin-standards.md +10 -0
  14. package/.agents/rules/git-conventions-reference.md +42 -51
  15. package/.agents/schemas/acceptance-eval-verdict.schema.json +2 -2
  16. package/.agents/schemas/agentrc.schema.json +35 -46
  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/story.merged.schema.json +1 -1
  24. package/.agents/schemas/signal-event.schema.json +3 -3
  25. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  26. package/.agents/schemas/validation-evidence.schema.json +1 -1
  27. package/.agents/scripts/acceptance-eval.js +24 -68
  28. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  29. package/.agents/scripts/bootstrap.js +3 -3
  30. package/.agents/scripts/check-dead-exports.js +43 -104
  31. package/.agents/scripts/check-doc-links.js +2 -2
  32. package/.agents/scripts/check-lifecycle-lint.js +1 -1
  33. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  34. package/.agents/scripts/deliver-recover.js +122 -0
  35. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  36. package/.agents/scripts/evidence-gate.js +20 -50
  37. package/.agents/scripts/generate-skills-index.js +17 -1
  38. package/.agents/scripts/generate-workflows-doc.js +4 -4
  39. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  40. package/.agents/scripts/lib/audit-suite/selector.js +323 -23
  41. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
  42. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  43. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  44. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  45. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  46. package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
  47. package/.agents/scripts/lib/checks/index.js +1 -1
  48. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  49. package/.agents/scripts/lib/checks/state.js +17 -248
  50. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
  51. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  52. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  53. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  54. package/.agents/scripts/lib/cli-args.js +23 -2
  55. package/.agents/scripts/lib/close-validation/gates.js +13 -13
  56. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  57. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  58. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  59. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  60. package/.agents/scripts/lib/config/acceptance-eval.js +2 -2
  61. package/.agents/scripts/lib/config/delivery-routing.js +7 -6
  62. package/.agents/scripts/lib/config/explain.js +10 -16
  63. package/.agents/scripts/lib/config/github.js +7 -5
  64. package/.agents/scripts/lib/config/limits.js +15 -25
  65. package/.agents/scripts/lib/config/quality.js +11 -14
  66. package/.agents/scripts/lib/config/runners.js +8 -21
  67. package/.agents/scripts/lib/config/temp-paths.js +18 -56
  68. package/.agents/scripts/lib/config-settings-schema-delivery.js +34 -16
  69. package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
  70. package/.agents/scripts/lib/config-settings-schema.js +48 -22
  71. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  72. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  73. package/.agents/scripts/lib/duplicate-search.js +38 -7
  74. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  75. package/.agents/scripts/lib/format-generated-json.js +97 -0
  76. package/.agents/scripts/lib/framework-version.js +19 -189
  77. package/.agents/scripts/lib/gh-exec.js +8 -0
  78. package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
  79. package/.agents/scripts/lib/git-utils.js +0 -14
  80. package/.agents/scripts/lib/json-utils.js +1 -2
  81. package/.agents/scripts/lib/label-constants.js +0 -15
  82. package/.agents/scripts/lib/label-taxonomy.js +1 -12
  83. package/.agents/scripts/lib/observability/active-story-env.js +42 -163
  84. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  85. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  86. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  87. package/.agents/scripts/lib/observability/source-classifier.js +2 -2
  88. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  89. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  90. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  91. package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
  92. package/.agents/scripts/lib/orchestration/change-set.js +103 -0
  93. package/.agents/scripts/lib/orchestration/code-review.js +70 -191
  94. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
  95. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  96. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  97. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  98. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  99. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  100. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
  101. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  102. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  103. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
  104. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  105. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  106. package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
  107. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  108. package/.agents/scripts/lib/orchestration/plan-context.js +116 -33
  109. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +26 -36
  110. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +31 -22
  111. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  112. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
  113. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
  114. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  115. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -100
  116. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
  117. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
  118. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  119. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +230 -0
  120. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
  121. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -2
  122. package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
  123. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  124. package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
  125. package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
  126. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  127. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  128. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  129. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  130. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  131. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  132. package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
  133. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
  134. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  135. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  136. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +4 -13
  137. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  138. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
  139. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  140. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  141. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
  142. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  143. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  144. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
  145. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  146. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  147. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +104 -279
  148. package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +191 -0
  149. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +120 -0
  150. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  151. package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
  152. package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
  153. package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
  154. package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
  155. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  156. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
  157. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
  158. package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
  159. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
  160. package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
  161. package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
  162. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  163. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  164. package/.agents/scripts/lib/planning-corpus.js +12 -286
  165. package/.agents/scripts/lib/preflight-runner.js +2 -2
  166. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  167. package/.agents/scripts/lib/signals/index.js +4 -17
  168. package/.agents/scripts/lib/signals/read.js +35 -35
  169. package/.agents/scripts/lib/signals/schema.js +8 -11
  170. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  171. package/.agents/scripts/lib/signals/write.js +0 -1
  172. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  173. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  174. package/.agents/scripts/lib/story-adjacency.js +8 -7
  175. package/.agents/scripts/lib/story-body/story-body.js +81 -13
  176. package/.agents/scripts/lib/templates/decomposer-prompts.js +15 -16
  177. package/.agents/scripts/lib/test-env.js +14 -1
  178. package/.agents/scripts/lib/test-tiers.js +0 -3
  179. package/.agents/scripts/lib/ticket-body-sections.js +0 -14
  180. package/.agents/scripts/lib/validation-evidence.js +31 -59
  181. package/.agents/scripts/lib/wave-runner/live-probe.js +315 -0
  182. package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
  183. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  184. package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
  185. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  186. package/.agents/scripts/plan-context.js +38 -7
  187. package/.agents/scripts/plan-critics.js +203 -0
  188. package/.agents/scripts/plan-persist.js +145 -35
  189. package/.agents/scripts/plan-run-epilogue.js +83 -38
  190. package/.agents/scripts/post-structured-comment.js +0 -38
  191. package/.agents/scripts/pr-watch-with-update.js +43 -22
  192. package/.agents/scripts/providers/github/compose.js +0 -1
  193. package/.agents/scripts/providers/github/errors.js +0 -19
  194. package/.agents/scripts/providers/github/issues.js +1 -11
  195. package/.agents/scripts/providers/github/mappers.js +5 -0
  196. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  197. package/.agents/scripts/providers/github/tickets.js +33 -153
  198. package/.agents/scripts/providers/github.js +17 -6
  199. package/.agents/scripts/quality-preview.js +13 -6
  200. package/.agents/scripts/resolve-stories.js +236 -0
  201. package/.agents/scripts/run-coverage.js +4 -1
  202. package/.agents/scripts/run-lint.js +2 -2
  203. package/.agents/scripts/run-verify.js +31 -2
  204. package/.agents/scripts/signals-view.js +9 -10
  205. package/.agents/scripts/single-story-close.js +173 -18
  206. package/.agents/scripts/single-story-confirm-merge.js +288 -15
  207. package/.agents/scripts/single-story-init.js +6 -10
  208. package/.agents/scripts/stories-wave-tick.js +380 -53
  209. package/.agents/scripts/story-plan.js +3 -3
  210. package/.agents/scripts/update-ticket-state.js +8 -50
  211. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  212. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  213. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  214. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  215. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  216. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  217. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  218. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  219. package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
  220. package/.agents/skills/core/scope-triage/SKILL.md +3 -0
  221. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  222. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  223. package/.agents/skills/skills.index.json +2 -12
  224. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  225. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  226. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  227. package/.agents/workflows/audit-architecture.md +3 -4
  228. package/.agents/workflows/audit-clean-code.md +4 -4
  229. package/.agents/workflows/audit-documentation.md +4 -5
  230. package/.agents/workflows/audit-lighthouse.md +8 -0
  231. package/.agents/workflows/audit-navigability.md +10 -0
  232. package/.agents/workflows/audit-performance.md +2 -3
  233. package/.agents/workflows/audit-quality.md +8 -9
  234. package/.agents/workflows/audit-security.md +1 -2
  235. package/.agents/workflows/audit-seo.md +10 -0
  236. package/.agents/workflows/audit-ux-ui.md +7 -0
  237. package/.agents/workflows/deliver.md +133 -45
  238. package/.agents/workflows/git-cleanup.md +2 -2
  239. package/.agents/workflows/git-deliver.md +1 -1
  240. package/.agents/workflows/helpers/acceptance-self-eval.md +34 -17
  241. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -12
  242. package/.agents/workflows/helpers/code-review.md +14 -12
  243. package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
  244. package/.agents/workflows/helpers/deliver-story.md +209 -118
  245. package/.agents/workflows/helpers/parallel-tooling.md +2 -2
  246. package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
  247. package/.agents/workflows/plan.md +239 -19
  248. package/.agents/workflows/qa-assist.md +6 -6
  249. package/.agents/workflows/qa-explore.md +3 -3
  250. package/.agents/workflows/qa-run.md +1 -5
  251. package/bin/mandrel.js +12 -1
  252. package/docs/CHANGELOG.md +62 -0
  253. package/lib/cli/registry.js +262 -19
  254. package/lib/cli/sync-agents.js +157 -0
  255. package/lib/cli/sync-commands.js +115 -6
  256. package/lib/cli/sync.js +168 -6
  257. package/lib/cli/update.js +105 -8
  258. package/lib/cli/version-helpers.js +131 -0
  259. package/lib/migrations/README.md +7 -5
  260. package/lib/migrations/index.js +17 -9
  261. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  262. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  263. package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +154 -0
  264. package/package.json +2 -2
  265. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  266. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  267. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  268. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  269. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  270. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  271. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  272. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  273. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  274. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  275. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  276. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  277. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  278. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  279. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  280. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  281. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  282. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  283. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  284. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  285. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  286. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  287. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  288. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  289. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  290. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  291. package/.agents/schemas/risk-verdict.schema.json +0 -53
  292. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  293. package/.agents/scripts/analyze-execution.js +0 -444
  294. package/.agents/scripts/check-prepush-recovery.js +0 -90
  295. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  296. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  297. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
  298. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  299. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  300. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  301. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
  302. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
  303. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  304. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  305. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  306. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  307. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  308. package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
  309. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  310. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  311. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  312. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
  313. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  314. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  315. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  316. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
  317. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  318. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
  319. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  320. package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
  321. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
  322. package/.agents/scripts/resolve-plan-run.js +0 -117
  323. package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
@@ -6,17 +6,24 @@ import { getCiDelivery } from '../../config/ci.js';
6
6
  import { resolveConfig } from '../../config-resolver.js';
7
7
  import { getStoryBranch, gitSync } from '../../git-utils.js';
8
8
  import { Logger } from '../../Logger.js';
9
+ import { emitTerminalFriction } from '../../observability/runtime-friction.js';
9
10
  import { createProvider } from '../../provider-factory.js';
10
11
  import { flipLabelAndNotify } from '../../single-story/story-merged-notify.js';
11
12
  import { WorktreeManager } from '../../worktree-manager.js';
12
13
  import { runCodeReview as runCodeReviewDefault } from '../code-review.js';
13
14
  import { releaseStoryLease } from '../single-story-lease-guard.js';
15
+ import {
16
+ buildTerminalEnvelope,
17
+ emitTerminalEnvelope,
18
+ NEXT_COMMANDS,
19
+ terminalFromWaitOutcome,
20
+ } from '../story-deliver-terminal.js';
14
21
  import { runAutoMergePhase } from './phases/auto-merge.js';
15
22
  import { runBaseSyncPhase } from './phases/base-sync.js';
16
23
  import { runCloseValidationPhase } from './phases/close-validation.js';
17
24
  import { parsePrNumber, runStoryScopeReview } from './phases/code-review.js';
18
25
  import { runConfirmMergePhase } from './phases/confirm-merge.js';
19
- import { parseCloseOptions } from './phases/options.js';
26
+ import { parseCloseOptions, resolveWaitForMerge } from './phases/options.js';
20
27
  import { ensurePullRequestWith } from './phases/pull-request.js';
21
28
  import { pushStoryBranch } from './phases/push.js';
22
29
  import { handleCriticalReviewBlock } from './phases/review-block.js';
@@ -25,16 +32,94 @@ import { runWrongTreeGuardPhase } from './phases/wrong-tree-guard.js';
25
32
 
26
33
  const progress = Logger.createProgress('single-story-close', { stderr: true });
27
34
 
28
- function alreadyClosedResult(storyId) {
29
- return {
30
- success: true,
31
- result: {
35
+ /**
36
+ * Emit the terminal envelope on stdout alongside the legacy close result.
37
+ *
38
+ * Both are printed: the envelope is the contract callers parse (Story
39
+ * #4543), while `STORY CLOSE RESULT` stays byte-compatible for the existing
40
+ * surfaces that grep it. One writer, one place — so the envelope can never
41
+ * be emitted from a path that forgot it.
42
+ *
43
+ * Story #4578 — the same one-place property is why the runtime-derived
44
+ * friction emit hangs here: a close that ends `failed`, or parks `pending`
45
+ * on an exhausted merge-wait budget, is friction the retro must see, and
46
+ * routing it through the single emitter means no terminal path can forget
47
+ * it. `emitTerminalFriction` decides what (if anything) is worth a record —
48
+ * see `frictionForTerminal` for why `blocked` and a `--no-wait-merge`
49
+ * `pending` are deliberately NOT flagged here.
50
+ *
51
+ * Async, and awaited by every caller: the CLI exits via `process.exit` the
52
+ * moment `main` resolves, so a fire-and-forget append would be discarded.
53
+ * The emit is best-effort internally and cannot throw.
54
+ */
55
+ async function emitTerminal({ terminal, result, config }) {
56
+ // The human-facing result dump stays level-gated; the terminal envelope is
57
+ // the machine contract and must survive AGENT_LOG_LEVEL=silent.
58
+ if (result) {
59
+ Logger.info(
60
+ `\n--- STORY CLOSE RESULT ---\n${JSON.stringify(result, null, 2)}\n--- END RESULT ---\n`,
61
+ );
62
+ }
63
+ emitTerminalEnvelope(terminal);
64
+ await emitTerminalFriction({ envelope: terminal, config });
65
+ }
66
+
67
+ /**
68
+ * Terminal for a Story that was already closed before this run started.
69
+ *
70
+ * `state: 'closed'` alone does NOT mean the work landed. GitHub closes an
71
+ * issue as `completed` (the `Closes #<id>` footer firing on merge — the work
72
+ * IS on the base branch) or as `not_planned` (superseded by a re-plan,
73
+ * abandoned — nothing ever merged). Reporting the second as `landed` told
74
+ * `/deliver` that work had reached `main` when no PR ever merged, which would
75
+ * also satisfy any dependent Story waiting on it. Fail loudly instead: being
76
+ * handed an abandoned Story is an input error only the operator can resolve.
77
+ *
78
+ * A null `stateReason` keeps the `completed` reading — GitHub defaults to it,
79
+ * and issues closed before the field existed carry null.
80
+ */
81
+ async function alreadyClosedResult(storyId, stateReason = null, config) {
82
+ if (stateReason === 'not_planned') {
83
+ const result = {
32
84
  storyId,
33
85
  standalone: true,
34
86
  action: 'noop',
35
- reason: 'already-closed',
36
- },
87
+ reason: 'closed-not-planned',
88
+ };
89
+ const terminal = buildTerminalEnvelope({
90
+ storyId,
91
+ status: 'failed',
92
+ phase: 'init',
93
+ failure: {
94
+ reason:
95
+ `Story #${storyId} is closed as not planned (superseded or abandoned) — ` +
96
+ 'there is nothing to land. Re-plan it as a new Story, or pass a Story that is still open.',
97
+ },
98
+ nextCommand: null,
99
+ elapsedSeconds: 0,
100
+ });
101
+ await emitTerminal({ terminal, result, config });
102
+ return { success: false, result, terminal };
103
+ }
104
+
105
+ const result = {
106
+ storyId,
107
+ standalone: true,
108
+ action: 'noop',
109
+ reason: 'already-closed',
37
110
  };
111
+ // Idempotent re-run against a Story closed as completed. `landed` is the
112
+ // honest status — the issue closed on merge, so the work is on the base
113
+ // branch — and there is nothing left to command.
114
+ const terminal = buildTerminalEnvelope({
115
+ storyId,
116
+ status: 'landed',
117
+ phase: 'done',
118
+ nextCommand: null,
119
+ elapsedSeconds: 0,
120
+ });
121
+ await emitTerminal({ terminal, result, config });
122
+ return { success: true, result, terminal };
38
123
  }
39
124
 
40
125
  function resolveWorktreePath({ cwd, config, storyId }) {
@@ -55,7 +140,9 @@ async function runPrePushPhases({
55
140
  skipSync,
56
141
  injectedSync,
57
142
  injectedGitSpawn,
143
+ setPhase = () => {},
58
144
  }) {
145
+ setPhase('wrong-tree-guard');
59
146
  await runWrongTreeGuardPhase({
60
147
  cwd,
61
148
  worktreePath,
@@ -66,6 +153,7 @@ async function runPrePushPhases({
66
153
  gitSpawn: injectedGitSpawn,
67
154
  });
68
155
  if (!skipValidation) {
156
+ setPhase('close-validation');
69
157
  await runCloseValidationPhase({
70
158
  cwd,
71
159
  worktreePath,
@@ -81,6 +169,7 @@ async function runPrePushPhases({
81
169
  progress('VALIDATE', '⏭ Skipped (--skip-validation).');
82
170
  }
83
171
  if (!skipSync) {
172
+ setPhase('base-sync');
84
173
  await runBaseSyncPhase({
85
174
  cwd,
86
175
  worktreePath,
@@ -105,8 +194,11 @@ async function openAndReviewPr({
105
194
  provider,
106
195
  injectedGh,
107
196
  injectedRunCodeReview,
197
+ setPhase = () => {},
108
198
  }) {
199
+ setPhase('push');
109
200
  pushStoryBranch({ cwd, storyBranch, gitSync, progress });
201
+ setPhase('pull-request');
110
202
  const prUrl = await ensurePullRequestWith({
111
203
  cwd,
112
204
  storyId,
@@ -117,6 +209,7 @@ async function openAndReviewPr({
117
209
  progress,
118
210
  });
119
211
  const prNumber = parsePrNumber(prUrl);
212
+ setPhase('code-review');
120
213
  const reviewOutcome = await runStoryScopeReview({
121
214
  cwd,
122
215
  storyId,
@@ -176,8 +269,12 @@ async function releaseLease({
176
269
  * The two recoverable-blocked close exits (base-sync conflict in
177
270
  * `runBaseSyncPhase`, and a critical-blocker review halt in
178
271
  * `openAndReviewPr`) throw before the clean-close lease release at the
179
- * tail of `runSingleStoryClose`, stranding the operator's lease until its
180
- * TTL expires. That fail-closed-refuses a different operator who picks up
272
+ * tail of `runSingleStoryClose`, stranding the operator's lease
273
+ * indefinitely. The standalone lease does **not** expire by TTL: it is
274
+ * fail-closed by design (`lease-guard-shared.js` anchors `heartbeatAt` to
275
+ * now, so `isClaimLive` is true for any foreign assignee regardless of the
276
+ * configured TTL), so a stranded claim is cleared only by `--steal` or
277
+ * de-assignment. That fail-closed-refuses a different operator who picks up
181
278
  * the blocked Story — exactly the hand-off case. Releasing here closes
182
279
  * that gap.
183
280
  *
@@ -232,7 +329,7 @@ function closeResult({
232
329
  waitedForMerge,
233
330
  merged,
234
331
  note: waitedForMerge
235
- ? 'Headless must-land: PR merge confirmed. Story flipped agent::closing → agent::done and the issue closed (confirmStoryMerged).'
332
+ ? 'Close-and-land: PR merge confirmed. Story flipped agent::closing → agent::done, the issue closed (confirmStoryMerged), and the post-land tail ran.'
236
333
  : autoMergeEnabled
237
334
  ? 'PR open against baseBranch with auto-merge enabled. Story rests at agent::closing (issue stays OPEN). GitHub will squash-merge when required checks pass; run single-story-confirm-merge.js after the merge confirms to flip agent::done and close the issue (the Closes #<id> footer also auto-closes it).'
238
335
  : 'PR open against baseBranch. Story rests at agent::closing (issue stays OPEN). Operator merges via GitHub UI; run single-story-confirm-merge.js after the merge confirms to flip agent::done (the Closes #<id> footer also auto-closes the issue).',
@@ -245,9 +342,9 @@ export async function runSingleStoryClose({
245
342
  skipValidation: skipValidationParam,
246
343
  skipSync: skipSyncParam,
247
344
  noAutoMerge: noAutoMergeParam,
248
- noFullScopeCrap: noFullScopeCrapParam,
249
345
  waitForMerge: waitForMergeParam,
250
346
  noWaitForMerge: noWaitForMergeParam,
347
+ maxWaitSeconds: maxWaitSecondsParam,
251
348
  injectedProvider,
252
349
  injectedConfig,
253
350
  injectedNotify,
@@ -263,16 +360,65 @@ export async function runSingleStoryClose({
263
360
  skipValidationParam,
264
361
  skipSyncParam,
265
362
  noAutoMergeParam,
266
- noFullScopeCrapParam,
267
363
  waitForMergeParam,
268
364
  noWaitForMergeParam,
365
+ maxWaitSecondsParam,
269
366
  });
270
367
  if (!options.storyId) {
271
368
  throw new Error(
272
- 'Usage: node single-story-close.js --story <STORY_ID> [--cwd <main-repo>] [--skip-validation] [--skip-sync] [--no-auto-merge] [--no-full-scope-crap] [--wait-merge|--no-wait-merge]',
369
+ 'Usage: node single-story-close.js --story <STORY_ID> [--cwd <main-repo>] [--skip-validation] [--skip-sync] [--no-auto-merge] [--wait-merge|--no-wait-merge] [--max-wait-seconds <n>]',
273
370
  );
274
371
  }
275
372
 
373
+ // Story #4543 — track the phase so a crash is attributable. The runner
374
+ // deliberately keeps THROWING (the library contract every caller and test
375
+ // relies on: a failed gate must not look like a return value); it only tags
376
+ // the error with the phase it died in, and the CLI boundary turns that into
377
+ // the `failed` terminal envelope. Tagging rather than swallowing is what
378
+ // lets `failed` name its phase without inventing a second success path.
379
+ let phase = 'init';
380
+ const setPhase = (next) => {
381
+ phase = next;
382
+ };
383
+ try {
384
+ return await runClosePipeline({
385
+ options,
386
+ setPhase,
387
+ injectedProvider,
388
+ injectedConfig,
389
+ injectedNotify,
390
+ injectedSync,
391
+ injectedRunCodeReview,
392
+ injectedGh,
393
+ injectedGitSpawn,
394
+ injectedReleaseLease,
395
+ });
396
+ } catch (err) {
397
+ if (err && typeof err === 'object' && !err.closePhase) {
398
+ err.closePhase = phase;
399
+ }
400
+ throw err;
401
+ }
402
+ }
403
+
404
+ /**
405
+ * The close pipeline proper. Split out of `runSingleStoryClose` so the
406
+ * phase-tagging wrapper above stays a thin, obviously-correct boundary rather
407
+ * than a try block wrapped around a hundred lines of pipeline.
408
+ */
409
+ async function runClosePipeline({
410
+ options,
411
+ setPhase,
412
+ injectedProvider,
413
+ injectedConfig,
414
+ injectedNotify,
415
+ injectedSync,
416
+ injectedRunCodeReview,
417
+ injectedGh,
418
+ injectedGitSpawn,
419
+ injectedReleaseLease,
420
+ }) {
421
+ const startedAtMs = Date.now();
276
422
  const config = injectedConfig || resolveConfig({ cwd: options.cwd });
277
423
  const provider = injectedProvider || createProvider(config);
278
424
  const baseBranch = config.project?.baseBranch ?? 'main';
@@ -283,9 +429,15 @@ export async function runSingleStoryClose({
283
429
  if (story.state === 'closed') {
284
430
  progress(
285
431
  'NOOP',
286
- `Story #${options.storyId} is already closed. Nothing to do.`,
432
+ story.stateReason === 'not_planned'
433
+ ? `Story #${options.storyId} is closed as not planned — nothing to land.`
434
+ : `Story #${options.storyId} is already closed. Nothing to do.`,
435
+ );
436
+ return await alreadyClosedResult(
437
+ options.storyId,
438
+ story.stateReason,
439
+ config,
287
440
  );
288
- return alreadyClosedResult(options.storyId);
289
441
  }
290
442
 
291
443
  const worktreePath = resolveWorktreePath({
@@ -314,6 +466,7 @@ export async function runSingleStoryClose({
314
466
  worktreePath,
315
467
  injectedSync,
316
468
  injectedGitSpawn,
469
+ setPhase,
317
470
  }),
318
471
  leaseArgs,
319
472
  );
@@ -329,9 +482,11 @@ export async function runSingleStoryClose({
329
482
  provider,
330
483
  injectedGh,
331
484
  injectedRunCodeReview,
485
+ setPhase,
332
486
  }),
333
487
  leaseArgs,
334
488
  );
489
+ setPhase('auto-merge');
335
490
  const { autoMergeEnabled, autoMergeReason } = await runAutoMergePhase({
336
491
  cwd: options.cwd,
337
492
  prNumber,
@@ -362,32 +517,68 @@ export async function runSingleStoryClose({
362
517
  });
363
518
  const leaseReleased = await releaseLease(leaseArgs);
364
519
 
365
- // Story #4428 headless must-land: `--wait-merge` polls the just-armed
366
- // PR to merge confirmation (or an explicit `agent::blocked` +
367
- // `merge.unlanded`) instead of resting at `agent::closing`. Attended
368
- // (non-headless) runs never set this flag, so the exit shape below is
369
- // unreachable and the pre-existing early-return is byte-identical.
370
- if (options.waitForMerge) {
520
+ // Close-and-land (Story #4428; default since `delivery.routing.closeAndLand`
521
+ // — Story #4539): poll the just-armed PR to merge confirmation, or block
522
+ // explicitly with `merge.unlanded`, instead of resting at `agent::closing`.
523
+ // This is the DEFAULT path for attended and headless runs alike.
524
+ //
525
+ // Resolved here rather than at parse time because two inputs do not exist
526
+ // until now: the resolved config (whose cwd the parse produces) and the
527
+ // actual arm outcome. A PR the operator deliberately left un-armed
528
+ // (`--no-auto-merge` / `autoMerge: "strict"`) has nothing to land, so it
529
+ // rests at `agent::closing` for the human instead of burning the poll
530
+ // budget and then blocking a healthy Story.
531
+ const { waitForMerge, reason: waitForMergeReason } = resolveWaitForMerge({
532
+ waitForMergeExplicit: options.waitForMergeExplicit,
533
+ noWaitForMerge: options.noWaitForMerge,
534
+ config,
535
+ autoMergeReason,
536
+ });
537
+ if (waitForMergeReason === 'operator-merge') {
538
+ progress(
539
+ 'MERGE',
540
+ `⏭ Not waiting for merge (${autoMergeReason}) — the operator owns this merge; ` +
541
+ `Story #${options.storyId} rests at agent::closing.` +
542
+ (options.waitForMergeExplicit === true
543
+ ? ' --wait-merge cannot land a PR that was deliberately left un-armed.'
544
+ : ''),
545
+ );
546
+ }
547
+ const gates = {
548
+ validation: options.skipValidation ? 'skipped' : 'passed',
549
+ baseSync: options.skipSync ? 'skipped' : 'passed',
550
+ codeReview: 'passed',
551
+ };
552
+
553
+ if (waitForMerge) {
554
+ setPhase('confirm-merge');
371
555
  const waitOutcome = await runConfirmMergePhase({
372
556
  cwd: options.cwd,
373
557
  storyId: options.storyId,
558
+ storyBranch,
559
+ baseBranch,
374
560
  prNumber,
375
561
  prUrl,
376
562
  autoMergeEnabled,
377
563
  autoMergeReason,
378
564
  provider,
379
565
  config,
566
+ maxWaitSeconds: options.maxWaitSeconds,
380
567
  progress,
381
568
  injectedGh,
382
569
  injectedNotify,
383
570
  });
384
- if (!waitOutcome.confirmed) {
385
- throw new Error(
386
- `[single-story-close] Headless must-land: PR ${prUrl} did not reach a confirmed merge ` +
387
- `(blockClass=${waitOutcome.blockClass}). Story #${options.storyId} was transitioned to ` +
388
- `agent::blocked with a merge.unlanded lifecycle event. Reason: ${waitOutcome.reason}`,
389
- );
390
- }
571
+ const terminal = terminalFromWaitOutcome({
572
+ waitOutcome,
573
+ storyId: options.storyId,
574
+ storyBranch,
575
+ baseBranch,
576
+ prNumber,
577
+ prUrl,
578
+ autoMergeEnabled,
579
+ gates,
580
+ elapsedSeconds: Math.round((Date.now() - startedAtMs) / 1000),
581
+ });
391
582
  const result = closeResult({
392
583
  storyId: options.storyId,
393
584
  storyBranch,
@@ -399,16 +590,29 @@ export async function runSingleStoryClose({
399
590
  worktreeReaped,
400
591
  leaseReleased,
401
592
  waitedForMerge: true,
402
- merged: true,
593
+ merged: waitOutcome.confirmed === true,
403
594
  });
404
- Logger.info(
405
- `\n--- STORY CLOSE RESULT ---\n${JSON.stringify(result, null, 2)}\n--- END RESULT ---\n`,
406
- );
407
- progress(
408
- 'DONE',
409
- `✅ Standalone Story #${options.storyId}: PR merged ${prUrl}`,
410
- );
411
- return { success: true, result };
595
+ await emitTerminal({ terminal, result, config });
596
+
597
+ if (terminal.status === 'landed') {
598
+ progress('DONE', `✅ Story #${options.storyId}: PR merged → ${prUrl}`);
599
+ } else if (terminal.status === 'pending') {
600
+ // NOT a failure and NOT a block — the wait reached the edge of its
601
+ // host slot with the PR healthy and in flight. The CLI maps this to
602
+ // its own exit code so a caller can resume without classifying.
603
+ progress(
604
+ 'PENDING',
605
+ `⏸ Story #${options.storyId}: PR ${prUrl} still in flight — resume with: ${terminal.nextCommand}`,
606
+ );
607
+ } else {
608
+ progress(
609
+ 'BLOCKED',
610
+ `🛑 Story #${options.storyId}: PR ${prUrl} did not land ` +
611
+ `(blockClass=${terminal.blocked?.blockClass}). Story is at agent::blocked. ` +
612
+ `Next: ${terminal.nextCommand}`,
613
+ );
614
+ }
615
+ return { success: terminal.status === 'landed', result, terminal };
412
616
  }
413
617
 
414
618
  const result = closeResult({
@@ -422,13 +626,30 @@ export async function runSingleStoryClose({
422
626
  worktreeReaped,
423
627
  leaseReleased,
424
628
  });
425
-
426
- Logger.info(
427
- `\n--- STORY CLOSE RESULT ---\n${JSON.stringify(result, null, 2)}\n--- END RESULT ---\n`,
428
- );
629
+ // `--no-wait-merge` / operator-merge: the PR is open and the human owns
630
+ // the land. That is a `pending` terminal by definition — the work is not
631
+ // done, nothing is broken, and one named command finishes it — rather
632
+ // than a fourth status invented for this one case.
633
+ const terminal = buildTerminalEnvelope({
634
+ storyId: options.storyId,
635
+ status: 'pending',
636
+ phase: 'auto-merge',
637
+ storyBranch,
638
+ baseBranch,
639
+ pr: {
640
+ number: prNumber,
641
+ url: prUrl ?? null,
642
+ state: 'OPEN',
643
+ autoMergeEnabled: Boolean(autoMergeEnabled),
644
+ },
645
+ gates,
646
+ nextCommand: NEXT_COMMANDS.confirmMerge(options.storyId),
647
+ elapsedSeconds: Math.round((Date.now() - startedAtMs) / 1000),
648
+ });
649
+ await emitTerminal({ terminal, result, config });
429
650
  progress(
430
651
  'DONE',
431
- `✅ Standalone Story #${options.storyId}: PR ready → ${prUrl}`,
652
+ `✅ Story #${options.storyId}: PR ready → ${prUrl} (${waitForMergeReason})`,
432
653
  );
433
- return { success: true, result };
654
+ return { success: true, result, terminal };
434
655
  }
@@ -116,7 +116,7 @@ export async function acquireStoryLease({
116
116
  anchorHeartbeatToNow: true,
117
117
  renderRefusal: (result) =>
118
118
  `single-story lease: Story #${storyId} is currently held by @${result.owner}. ` +
119
- 'Another /single-story-deliver run owns this Story. Coordinate with that ' +
119
+ 'Another /deliver run owns this Story. Coordinate with that ' +
120
120
  'operator, or re-run with --steal to forcibly transfer the claim once you ' +
121
121
  'have confirmed the other run is dead. (The standalone path has no Epic ' +
122
122
  'heartbeat ledger, so a foreign assignee always blocks unless stolen.)',
@@ -328,7 +328,7 @@ export function runFormatAutofix({
328
328
  }
329
329
 
330
330
  /**
331
- * List the files changed between `epicBranch` and `storyBranch` using the
331
+ * List the files changed between `baseBranch` and `storyBranch` using the
332
332
  * three-dot merge-base diff. Delegates parsing to `diffNameOnly` from
333
333
  * `changed-files.js` so the stdout → path-list conversion lives in one place.
334
334
  *
@@ -336,10 +336,10 @@ export function runFormatAutofix({
336
336
  * `(args: string[], opts: object) => string`. A bridge adapter wraps it into
337
337
  * the `gitSpawn(cwd, ...args)` shape that `diffNameOnly` expects.
338
338
  *
339
- * @param {{ cwd: string, epicBranch: string, storyBranch: string, git: Function }} opts
339
+ * @param {{ cwd: string, baseBranch: string, storyBranch: string, git: Function }} opts
340
340
  * @returns {string[]}
341
341
  */
342
- function listChangedFiles({ cwd, epicBranch, storyBranch, git }) {
342
+ function listChangedFiles({ cwd, baseBranch, storyBranch, git }) {
343
343
  // Bridge the (args, opts) → string interface into gitSpawn(cwd, ...args).
344
344
  const gitSpawn = (_cwd, ...args) => {
345
345
  try {
@@ -358,7 +358,7 @@ function listChangedFiles({ cwd, epicBranch, storyBranch, git }) {
358
358
  }
359
359
  };
360
360
  return diffNameOnly({
361
- range: `${epicBranch}...${storyBranch}`,
361
+ range: `${baseBranch}...${storyBranch}`,
362
362
  cwd,
363
363
  gitSpawn,
364
364
  });
@@ -401,7 +401,7 @@ function listChangedFiles({ cwd, epicBranch, storyBranch, git }) {
401
401
  * cwd: string,
402
402
  * worktreePath?: string,
403
403
  * storyId: number|string,
404
- * epicBranch: string,
404
+ * baseBranch: string,
405
405
  * storyBranch: string,
406
406
  * config?: object,
407
407
  * logger?: object,
@@ -420,7 +420,7 @@ export function runScopedFormatAutofix({
420
420
  cwd,
421
421
  worktreePath,
422
422
  storyId,
423
- epicBranch,
423
+ baseBranch,
424
424
  storyBranch,
425
425
  config,
426
426
  logger = DefaultLogger,
@@ -428,8 +428,8 @@ export function runScopedFormatAutofix({
428
428
  gitSync,
429
429
  } = {}) {
430
430
  if (!cwd) throw new Error('runScopedFormatAutofix: cwd is required');
431
- if (!epicBranch)
432
- throw new Error('runScopedFormatAutofix: epicBranch is required');
431
+ if (!baseBranch)
432
+ throw new Error('runScopedFormatAutofix: baseBranch is required');
433
433
  if (!storyBranch)
434
434
  throw new Error('runScopedFormatAutofix: storyBranch is required');
435
435
 
@@ -449,13 +449,13 @@ export function runScopedFormatAutofix({
449
449
 
450
450
  const changed = listChangedFiles({
451
451
  cwd: workTree,
452
- epicBranch,
452
+ baseBranch,
453
453
  storyBranch,
454
454
  git,
455
455
  });
456
456
  if (changed.length === 0) {
457
457
  logger.info?.(
458
- `${SCOPED_TAG} skipped — no changed files between ${epicBranch} and ${storyBranch}.`,
458
+ `${SCOPED_TAG} skipped — no changed files between ${baseBranch} and ${storyBranch}.`,
459
459
  );
460
460
  return { ran: false, committed: false, reason: 'no-changed-files' };
461
461
  }