mandrel 1.58.0 → 1.60.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 (319) hide show
  1. package/.agents/README.md +100 -98
  2. package/.agents/docs/SDLC.md +140 -141
  3. package/.agents/docs/configuration.md +16 -16
  4. package/.agents/docs/workflows.md +7 -8
  5. package/.agents/instructions.md +12 -11
  6. package/.agents/personas/architect.md +1 -1
  7. package/.agents/personas/product.md +1 -1
  8. package/.agents/personas/project-manager.md +14 -14
  9. package/.agents/personas/technical-writer.md +1 -1
  10. package/.agents/rules/changelog-style.md +5 -5
  11. package/.agents/rules/git-conventions.md +3 -3
  12. package/.agents/schemas/agentrc.schema.json +3 -3
  13. package/.agents/schemas/audit-rules.json +20 -0
  14. package/.agents/schemas/dispatch-manifest.json +4 -4
  15. package/.agents/schemas/epic-spec.schema.json +15 -45
  16. package/.agents/schemas/lifecycle/README.md +1 -1
  17. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
  18. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
  20. package/.agents/schemas/validation-evidence.schema.json +1 -1
  21. package/.agents/scripts/README.md +1 -1
  22. package/.agents/scripts/acceptance-eval.js +21 -4
  23. package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
  24. package/.agents/scripts/analyze-execution.js +2 -2
  25. package/.agents/scripts/assert-branch.js +1 -3
  26. package/.agents/scripts/audit-to-stories.js +1 -1
  27. package/.agents/scripts/bootstrap.js +1 -1
  28. package/.agents/scripts/check-arch-cycles.js +360 -0
  29. package/.agents/scripts/check-doc-links.js +2 -3
  30. package/.agents/scripts/coverage-capture.js +24 -3
  31. package/.agents/scripts/diagnose-friction.js +1 -1
  32. package/.agents/scripts/dispatcher.js +2 -2
  33. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  34. package/.agents/scripts/epic-audit-prepare.js +3 -3
  35. package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
  36. package/.agents/scripts/epic-deliver-preflight.js +11 -9
  37. package/.agents/scripts/epic-deliver-prepare.js +13 -5
  38. package/.agents/scripts/epic-execute-record-wave.js +5 -5
  39. package/.agents/scripts/epic-plan-healthcheck.js +6 -10
  40. package/.agents/scripts/epic-plan-spec-validate.js +1 -1
  41. package/.agents/scripts/epic-reconcile.js +11 -29
  42. package/.agents/scripts/evidence-gate.js +2 -2
  43. package/.agents/scripts/generate-workflows-doc.js +1 -1
  44. package/.agents/scripts/git-rebase-and-resolve.js +1 -1
  45. package/.agents/scripts/hierarchy-gate.js +40 -24
  46. package/.agents/scripts/lib/ITicketingProvider.js +1 -1
  47. package/.agents/scripts/lib/audit-suite/selector.js +1 -1
  48. package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
  49. package/.agents/scripts/lib/baseline-snapshot.js +7 -7
  50. package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
  51. package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
  52. package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
  53. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
  54. package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
  55. package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
  56. package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
  57. package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
  58. package/.agents/scripts/lib/baselines/writer.js +1 -1
  59. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  60. package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
  61. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
  62. package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
  63. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +1 -1
  64. package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
  65. package/.agents/scripts/lib/close-validation/commands.js +188 -0
  66. package/.agents/scripts/lib/close-validation/gates.js +235 -0
  67. package/.agents/scripts/lib/close-validation/process.js +101 -0
  68. package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
  69. package/.agents/scripts/lib/close-validation/runner.js +325 -0
  70. package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
  71. package/.agents/scripts/lib/codebase-snapshot.js +1 -1
  72. package/.agents/scripts/lib/config/explain.js +1 -1
  73. package/.agents/scripts/lib/config/quality.js +6 -6
  74. package/.agents/scripts/lib/config/runners.js +2 -2
  75. package/.agents/scripts/lib/config/runtime.js +1 -1
  76. package/.agents/scripts/lib/config/temp-paths.js +2 -2
  77. package/.agents/scripts/lib/config-resolver.js +2 -5
  78. package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
  79. package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
  80. package/.agents/scripts/lib/config-settings-schema.js +3 -3
  81. package/.agents/scripts/lib/coverage-capture.js +147 -4
  82. package/.agents/scripts/lib/cpu-pool.js +14 -0
  83. package/.agents/scripts/lib/crap-utils.js +6 -11
  84. package/.agents/scripts/lib/duplicate-search.js +1 -1
  85. package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
  86. package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
  87. package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
  88. package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
  89. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
  90. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
  91. package/.agents/scripts/lib/findings/classify-finding.js +1 -1
  92. package/.agents/scripts/lib/findings/promote-finding.js +10 -10
  93. package/.agents/scripts/lib/git-utils.js +24 -22
  94. package/.agents/scripts/lib/label-constants.js +3 -4
  95. package/.agents/scripts/lib/label-taxonomy.js +3 -8
  96. package/.agents/scripts/lib/maintainability-engine.js +1 -1
  97. package/.agents/scripts/lib/maintainability-utils.js +4 -187
  98. package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
  99. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
  100. package/.agents/scripts/lib/orchestration/code-review.js +95 -82
  101. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
  102. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
  103. package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
  104. package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
  105. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +14 -37
  106. package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
  107. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
  108. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
  109. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
  110. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
  111. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
  112. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
  113. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
  114. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
  115. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
  116. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +28 -8
  117. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
  118. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
  119. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
  120. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
  121. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +13 -41
  122. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
  123. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
  124. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
  125. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
  126. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
  127. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
  128. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
  129. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
  130. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
  131. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
  132. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
  133. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
  134. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
  135. package/.agents/scripts/lib/orchestration/file-assumptions.js +6 -5
  136. package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
  137. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
  138. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
  139. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
  140. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
  141. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
  142. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
  143. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
  144. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
  145. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
  146. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
  147. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
  148. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
  149. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
  150. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +8 -8
  151. package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
  152. package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
  153. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
  154. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
  155. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
  156. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
  157. package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
  158. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
  159. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
  160. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
  161. package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
  162. package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
  163. package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
  164. package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
  165. package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
  166. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
  167. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
  168. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
  169. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
  170. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
  171. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
  172. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
  173. package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
  174. package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
  175. package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
  176. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
  177. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
  178. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
  179. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
  180. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
  181. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
  182. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
  183. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
  184. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
  185. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
  186. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
  187. package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
  188. package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
  189. package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
  190. package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
  191. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +4 -35
  192. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
  193. package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
  194. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +44 -73
  195. package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
  196. package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
  197. package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
  198. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  199. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
  200. package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
  201. package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
  202. package/.agents/scripts/lib/preflight-runner.js +1 -1
  203. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
  204. package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
  205. package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
  206. package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
  207. package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
  208. package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
  209. package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
  210. package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
  211. package/.agents/scripts/lib/project-root.js +17 -0
  212. package/.agents/scripts/lib/signals/schema.js +1 -1
  213. package/.agents/scripts/lib/spec/index.js +1 -1
  214. package/.agents/scripts/lib/spec/loader.js +2 -2
  215. package/.agents/scripts/lib/spec/state.js +7 -16
  216. package/.agents/scripts/lib/story-adjacency.js +76 -0
  217. package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
  218. package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
  219. package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
  220. package/.agents/scripts/lib/story-lifecycle.js +9 -9
  221. package/.agents/scripts/lib/story-plan.js +1 -1
  222. package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
  223. package/.agents/scripts/lib/transpile.js +93 -0
  224. package/.agents/scripts/lib/wave-runner/tick.js +4 -153
  225. package/.agents/scripts/lib/workers/crap-worker.js +1 -1
  226. package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
  227. package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
  228. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
  229. package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
  230. package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
  231. package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
  232. package/.agents/scripts/lifecycle-emit.js +1 -1
  233. package/.agents/scripts/providers/github/board-add.js +1 -1
  234. package/.agents/scripts/providers/github/errors.js +1 -1
  235. package/.agents/scripts/providers/github/mappers.js +2 -2
  236. package/.agents/scripts/providers/github/tickets.js +114 -10
  237. package/.agents/scripts/resync-status-column.js +1 -1
  238. package/.agents/scripts/retro-run.js +2 -2
  239. package/.agents/scripts/run-lint.js +10 -1
  240. package/.agents/scripts/run-tests.js +24 -4
  241. package/.agents/scripts/single-story-init.js +1 -1
  242. package/.agents/scripts/stories-wave-tick.js +13 -10
  243. package/.agents/scripts/story-close.js +1 -1
  244. package/.agents/scripts/story-init.js +162 -26
  245. package/.agents/scripts/story-phase.js +5 -5
  246. package/.agents/scripts/story-plan.js +3 -3
  247. package/.agents/scripts/sync-branch-from-base.js +2 -2
  248. package/.agents/scripts/validate-docs-freshness.js +1 -1
  249. package/.agents/scripts/wave-tick.js +1 -1
  250. package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
  251. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
  252. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
  253. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
  254. package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
  255. package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
  256. package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
  257. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
  258. package/.agents/skills/core/scope-triage/SKILL.md +9 -10
  259. package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
  260. package/.agents/skills/skills.index.json +7 -7
  261. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
  262. package/.agents/templates/agent-protocol.md +2 -2
  263. package/.agents/workflows/agents-update.md +2 -2
  264. package/.agents/workflows/audit-architecture.md +2 -2
  265. package/.agents/workflows/audit-clean-code.md +2 -2
  266. package/.agents/workflows/audit-dependencies.md +1 -1
  267. package/.agents/workflows/audit-devops.md +1 -1
  268. package/.agents/workflows/audit-documentation.md +226 -0
  269. package/.agents/workflows/audit-lighthouse.md +1 -1
  270. package/.agents/workflows/audit-performance.md +2 -2
  271. package/.agents/workflows/audit-privacy.md +1 -1
  272. package/.agents/workflows/audit-quality.md +2 -2
  273. package/.agents/workflows/audit-security.md +2 -2
  274. package/.agents/workflows/audit-seo.md +1 -1
  275. package/.agents/workflows/audit-sre.md +1 -1
  276. package/.agents/workflows/audit-to-stories.md +10 -10
  277. package/.agents/workflows/audit-ux-ui.md +1 -1
  278. package/.agents/workflows/deliver.md +85 -0
  279. package/.agents/workflows/explain.md +3 -3
  280. package/.agents/workflows/git-merge-pr.md +1 -1
  281. package/.agents/workflows/git-pr-all.md +13 -10
  282. package/.agents/workflows/git-push.md +6 -3
  283. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  284. package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
  285. package/.agents/workflows/helpers/code-review.md +5 -5
  286. package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
  287. package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
  288. package/.agents/workflows/helpers/diagnose.md +1 -1
  289. package/.agents/workflows/helpers/epic-audit.md +6 -6
  290. package/.agents/workflows/helpers/epic-deliver-story.md +28 -39
  291. package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
  292. package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
  293. package/.agents/workflows/helpers/epic-testing.md +3 -3
  294. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  295. package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
  296. package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
  297. package/.agents/workflows/helpers/signals.md +1 -1
  298. package/.agents/workflows/helpers/single-story-deliver.md +12 -11
  299. package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
  300. package/.agents/workflows/onboard.md +21 -20
  301. package/.agents/workflows/plan.md +89 -0
  302. package/.agents/workflows/qa-explore.md +1 -1
  303. package/.agents/workflows/qa-run-harness.md +1 -1
  304. package/README.md +17 -20
  305. package/docs/CHANGELOG.md +1149 -0
  306. package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
  307. package/lib/cli/__tests__/update-reexec.test.js +513 -0
  308. package/lib/cli/init.js +338 -0
  309. package/lib/cli/update.js +413 -52
  310. package/package.json +3 -1
  311. package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
  312. package/.agents/scripts/lib/close-validation.js +0 -897
  313. package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
  314. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
  315. package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
  316. package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
  317. package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
  318. package/.agents/scripts/lib/task-utils.js +0 -26
  319. package/.agents/scripts/story-deliver-prepare.js +0 -267
@@ -5,7 +5,7 @@ events and performs a single side effect. The canonical close-tail roster
5
5
  — in registration order — is wired by
6
6
  [`index.js`](./index.js) (`buildDefaultListenerChain`), the production
7
7
  entrypoint the standalone `lifecycle-emit.js` CLI shells in for
8
- `/epic-deliver`'s Phase 6 / 7.5 / 8 / 8.5 markdown invocations:
8
+ `/deliver`'s Phase 6 / 7.5 / 8 / 8.5 markdown invocations:
9
9
 
10
10
  - `ledger-writer.js` — privileged `onEmitted` hook that lands every
11
11
  `emitted` record on disk before any listener body runs (MUST be first).
@@ -41,7 +41,7 @@
41
41
  * This is the bus-level replay defence.
42
42
  * 2. The `gh pr view` probe — short-circuits across process
43
43
  * boundaries when a prior run already armed auto-merge. This is
44
- * the recovery defence: `/epic-deliver` restarted on the same PR
44
+ * the recovery defence: `/deliver` restarted on the same PR
45
45
  * will see the existing arm and emit `epic.merge.armed` exactly
46
46
  * once.
47
47
  *
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Subscribes to:
9
9
  * - `epic.automerge.start` (production path, Story #3901) → the
10
- * `/epic-deliver` Phase 8.5 boundary that the `lifecycle-emit.js`
10
+ * `/deliver` Phase 8.5 boundary that the `lifecycle-emit.js`
11
11
  * CLI actually fires. This event carries `prUrl` but NO
12
12
  * `checkOutcomes` (Phase 8's `pr-watch-with-update.js` has already
13
13
  * polled every required check to green before Phase 8.5 runs), so
@@ -1,7 +1,7 @@
1
1
  // .agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js
2
2
  /**
3
3
  * BranchCleaner — lifecycle listener that owns post-merge **branch** reap
4
- * for `/epic-deliver`. Story #2398 (companion to Cleaner, which owns the
4
+ * for `/deliver`. Story #2398 (companion to Cleaner, which owns the
5
5
  * temp-tree archival half of end-of-Epic cleanup).
6
6
  *
7
7
  * Subscribes to:
@@ -49,7 +49,7 @@
49
49
  * level replays short-circuit.
50
50
  * 2. The `gh pr list --head` probe + `openOrLocatePr`'s internal
51
51
  * locate path — both defend against cross-process re-runs
52
- * (`/epic-deliver` restarted on the same branch after a crash).
52
+ * (`/deliver` restarted on the same branch after a crash).
53
53
  *
54
54
  * Side-effect firewall: the listener emits on the bus, shells out to
55
55
  * `gh`/`git` (via the helpers), and upserts the `epic-handoff`
@@ -8,7 +8,7 @@
8
8
  * mirrored this roster for an in-session runner was deleted with the
9
9
  * dead epic-runner stratum in Story #3908; the host-LLM-drives-CLIs
10
10
  * model reaches every close-tail listener through the `lifecycle-emit.js`
11
- * CLI shells in `/epic-deliver`'s Phase 6 / 7.5 / 8 markdown invocations,
11
+ * CLI shells in `/deliver`'s Phase 6 / 7.5 / 8 markdown invocations,
12
12
  * which call this builder.)
13
13
  *
14
14
  * Canonical roster (registration order):
@@ -20,7 +20,7 @@
20
20
  * `{ epicId, prNumber, mergeCommitSha, mergedAt, pollAttempts }`.
21
21
  * 4. If the budget is exceeded without observing a merge, return
22
22
  * a `failed` classification with reason `budget-exceeded` and
23
- * do NOT emit `epic.merge.confirmed`. The /epic-deliver
23
+ * do NOT emit `epic.merge.confirmed`. The /deliver
24
24
  * blocker-handler flow surfaces this via `agent::blocked`.
25
25
  *
26
26
  * Resume contract (AC of Task #2907): the ledger is the source of
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * webhookEvents entry lifecycle event
16
16
  * -------------------- ---------------------
17
- * `epic-started` (emitted at /epic-deliver kickoff;
17
+ * `epic-started` (emitted at /deliver kickoff;
18
18
  * this listener handles `epic.snapshot.start`)
19
19
  * `epic-blocked` `epic.blocked`
20
20
  * `epic-complete` `epic.complete`
@@ -23,7 +23,7 @@
23
23
  * Idempotency contract (AC-10): per-instance `Set<string>` of
24
24
  * `${event}:${seqId}` keys. A repeat `(event, seqId)` short-circuits
25
25
  * without re-polling and emits nothing. Combined with the bus-level
26
- * replay defence, this is sufficient — re-running `/epic-deliver` after
26
+ * replay defence, this is sufficient — re-running `/deliver` after
27
27
  * a crash will produce a NEW seqId and the listener legitimately
28
28
  * re-runs the poll loop (which is itself idempotent: the outcome map
29
29
  * always reflects the live GitHub state).
@@ -109,7 +109,7 @@ export const extractPrNumber = parsePrNumberFromUrl;
109
109
  *
110
110
  * Exported so tests can stub.
111
111
  */
112
- export function ghPrChecks({ prUrl, cwd, spawnFn = spawnSync }) {
112
+ function ghPrChecks({ prUrl, cwd, spawnFn = spawnSync }) {
113
113
  const result = spawnFn(
114
114
  'gh',
115
115
  [
@@ -134,7 +134,7 @@ export function ghPrChecks({ prUrl, cwd, spawnFn = spawnSync }) {
134
134
  * can detect the BEHIND condition (PR head is behind its base branch)
135
135
  * AFTER every required check is green. Exported so tests can stub.
136
136
  */
137
- export function ghPrView({ prUrl, cwd, spawnFn = spawnSync }) {
137
+ function ghPrView({ prUrl, cwd, spawnFn = spawnSync }) {
138
138
  const result = spawnFn(
139
139
  'gh',
140
140
  ['pr', 'view', prUrl, '--json', 'mergeStateStatus'],
@@ -154,7 +154,7 @@ export function ghPrView({ prUrl, cwd, spawnFn = spawnSync }) {
154
154
  * BEHIND" (the conservative recovery branch). Pure — exported for
155
155
  * tests.
156
156
  */
157
- export function parseMergeStateStatus(stdout) {
157
+ function parseMergeStateStatus(stdout) {
158
158
  const trimmed = String(stdout ?? '').trim();
159
159
  if (trimmed.length === 0) return '';
160
160
  try {
@@ -172,7 +172,7 @@ export function parseMergeStateStatus(stdout) {
172
172
  * loop to fast-forward the PR head with its base branch. Exported so
173
173
  * tests can stub and assert call counts.
174
174
  */
175
- export function ghPrUpdateBranch({ prUrl, cwd, spawnFn = spawnSync }) {
175
+ function ghPrUpdateBranch({ prUrl, cwd, spawnFn = spawnSync }) {
176
176
  const result = spawnFn('gh', ['pr', 'update-branch', prUrl], {
177
177
  cwd,
178
178
  encoding: 'utf-8',
@@ -192,7 +192,7 @@ export function ghPrUpdateBranch({ prUrl, cwd, spawnFn = spawnSync }) {
192
192
  * the same definition as the downstream predicate. Pure — exported
193
193
  * for tests.
194
194
  */
195
- export const GREEN_CHECK_OUTCOMES = Object.freeze(
195
+ const GREEN_CHECK_OUTCOMES = Object.freeze(
196
196
  new Set(['success', 'neutral', 'skipped']),
197
197
  );
198
198
 
@@ -202,7 +202,7 @@ export const GREEN_CHECK_OUTCOMES = Object.freeze(
202
202
  * regardless of mergeStateStatus, so we never auto-recover into a
203
203
  * failing PR.
204
204
  */
205
- export function allGreen(outcomes) {
205
+ function allGreen(outcomes) {
206
206
  const values = Object.values(outcomes);
207
207
  if (values.length === 0) return false;
208
208
  for (const v of values) {
@@ -271,7 +271,7 @@ export function allTerminal(outcomes) {
271
271
  * behaviour is reviewable. Called only when the poll loop exits via
272
272
  * the iteration cap.
273
273
  */
274
- export function promotePendingToTimedOut(outcomes) {
274
+ function promotePendingToTimedOut(outcomes) {
275
275
  const out = {};
276
276
  for (const [k, v] of Object.entries(outcomes)) {
277
277
  out[k] = v === 'pending' ? 'timed_out' : v;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * lib/orchestration/manifest-builder.js — Manifest Building Logic
3
3
  *
4
- * 3-tier-only producer. Reads Story tickets from `allTickets` directly,
4
+ * 2-tier-only producer. Reads Story tickets from `allTickets` directly,
5
5
  * computes Story-scoped waves, and emits the `waves[].stories[]` /
6
6
  * Story-only `storyManifest` shape. The pre-Epic-#3163 Task-tier branch
7
7
  * has been removed; the framework no longer carries a Task-tier
@@ -48,7 +48,7 @@ function extractSectionList(body, heading) {
48
48
 
49
49
  /**
50
50
  * Project a Story ticket into the inline-acceptance/verify shape required by
51
- * the 3-tier waves[].stories[] schema. Reads `## Acceptance` /
51
+ * the 2-tier waves[].stories[] schema. Reads `## Acceptance` /
52
52
  * `## Acceptance Criteria` and `## Verify` sections from the body.
53
53
  *
54
54
  * @param {object} story
@@ -95,7 +95,7 @@ function projectStoryForWave(story, epicId) {
95
95
  const AGENT_DONE_LABEL = STATE_LABELS.DONE;
96
96
 
97
97
  /**
98
- * Build the Story-only manifest array used by the 3-tier hierarchy path.
98
+ * Build the Story-only manifest array used by the 2-tier hierarchy path.
99
99
  * Reads Story tickets directly from `allTickets`; there is no Task tier
100
100
  * to walk. Each entry exposes an empty `tasks: []` to keep downstream
101
101
  * consumers (renderers, dispatch helpers) on a single per-Story shape
@@ -145,7 +145,7 @@ function buildStoryOnlyManifest(stories, epicId) {
145
145
  }
146
146
 
147
147
  /**
148
- * Build the wave records for a 3-tier manifest. Each wave entry exposes a
148
+ * Build the wave records for a 2-tier manifest. Each wave entry exposes a
149
149
  * `stories[]` projection (instead of the legacy `tasks[]`) so dispatch
150
150
  * consumers can fan Story execution out wave-by-wave without ever seeing
151
151
  * a `type::task` ticket.
@@ -200,7 +200,7 @@ export function buildManifest({
200
200
  epicTitle: epic?.title ?? '',
201
201
  executor: 'claude-code',
202
202
  dryRun,
203
- hierarchy: '3-tier',
203
+ hierarchy: '2-tier',
204
204
  summary: {
205
205
  totalStories,
206
206
  doneStories,
@@ -9,7 +9,7 @@
9
9
  * - "parked" — the Story is genuinely outside the manifest (carved off
10
10
  * mid-sprint, no recut lineage). The operator should explicitly
11
11
  * adopt it into the current Epic or defer it. Surfaced as a
12
- * structured comment so `/epic-deliver` has a single checkpoint.
12
+ * structured comment so `/deliver` has a single checkpoint.
13
13
  *
14
14
  * Both categories are informational at the wave-completeness gate — they do
15
15
  * not fail closure by themselves. The gate continues to enforce that every
@@ -81,7 +81,7 @@ export function renderParkedFollowOnsComment(epicId, classification) {
81
81
  `## 🪝 Parked Follow-Ons & Recuts — Epic #${epicId}`,
82
82
  '',
83
83
  'Stories created under this Epic that are **not** in the frozen dispatch',
84
- 'manifest. Surfaced here so `/epic-deliver` can gate on them at the',
84
+ 'manifest. Surfaced here so `/deliver` can gate on them at the',
85
85
  'completeness check.',
86
86
  '',
87
87
  `- **Recuts** (attributable to a manifest Story): ${recuts.length}`,
@@ -2,7 +2,7 @@
2
2
  * plan-router — given an Epic's current labels, decide which plan-phase CLI
3
3
  * should run next.
4
4
  *
5
- * Used by the local `/epic-plan` wrapper (chains spec → decompose).
5
+ * Used by the local `/plan` wrapper (chains spec → decompose).
6
6
  *
7
7
  * The router is intentionally stateless. Callers feed the current label set
8
8
  * (a string array, usually from `provider.getEpic(id).labels`) and receive a
@@ -21,7 +21,7 @@ export const PLAN_PHASE_NAMES = Object.freeze({
21
21
  * path used by the local wrapper; `command` is the slash-command invocation
22
22
  * operators fire.
23
23
  *
24
- * Spec and Decompose are served by the unified `/epic-plan` wrapper with a
24
+ * Spec and Decompose are served by the unified `/plan` wrapper with a
25
25
  * `--phase` flag — the phase workflows themselves live at
26
26
  * `.agents/workflows/helpers/epic-plan-{spec,decompose}.md` and are not
27
27
  * directly invokable slash commands.
@@ -33,20 +33,20 @@ export const PLAN_PHASE_DESCRIPTORS = Object.freeze({
33
33
  [PLAN_PHASE_NAMES.SPEC]: {
34
34
  phase: PLAN_PHASE_NAMES.SPEC,
35
35
  script: '.agents/scripts/epic-plan-spec.js',
36
- command: '/epic-plan --phase spec',
36
+ command: '/plan --phase spec',
37
37
  parkingLabel: AGENT_LABELS.REVIEW_SPEC,
38
38
  },
39
39
  [PLAN_PHASE_NAMES.DECOMPOSE]: {
40
40
  phase: PLAN_PHASE_NAMES.DECOMPOSE,
41
41
  script: '.agents/scripts/epic-plan-decompose.js',
42
- command: '/epic-plan --phase decompose',
42
+ command: '/plan --phase decompose',
43
43
  parkingLabel: AGENT_LABELS.READY,
44
44
  },
45
45
  });
46
46
 
47
47
  /**
48
48
  * Given the Epic's current labels, pick the next plan phase to run in the
49
- * local `/epic-plan` wrapper.
49
+ * local `/plan` wrapper.
50
50
  *
51
51
  * Precedence:
52
52
  * 1. If the Epic already carries `agent::ready`, there is nothing left to
@@ -21,7 +21,7 @@ export async function notificationPhase(ctx, state) {
21
21
  storyId,
22
22
  story,
23
23
  epicBranch,
24
- orchestration,
24
+ config,
25
25
  progress,
26
26
  provider,
27
27
  notifyFn = notify,
@@ -39,7 +39,7 @@ export async function notificationPhase(ctx, state) {
39
39
  level: 'story',
40
40
  epicId,
41
41
  },
42
- { orchestration },
42
+ { config },
43
43
  );
44
44
  // Fire a rolled-up `epic-progress` webhook so operators see the Epic's
45
45
  // overall stories-done count tick up at each story-close, without
@@ -66,7 +66,7 @@ export async function notificationPhase(ctx, state) {
66
66
  level: 'epic',
67
67
  epicId,
68
68
  },
69
- { orchestration, skipComment: true },
69
+ { config, skipComment: true },
70
70
  );
71
71
  } catch (err) {
72
72
  logger?.warn?.(
@@ -2,14 +2,14 @@
2
2
  * phases/ticket-closure.js — post-merge ticket transition + cascade phase.
3
3
  *
4
4
  * Transitions the Story to `agent::done`, then runs cascade completion
5
- * so any parent Feature/Epic that is now fully resolved closes too.
5
+ * so any parent Epic-side rollup that is now fully resolved closes too.
6
6
  *
7
- * **3-tier closure (Story #3127).** Under the 3-tier hierarchy a Story
7
+ * **2-tier closure (Story #3127).** Under the 2-tier hierarchy a Story
8
8
  * is the leaf unit of execution and has no child tickets — `tasks`
9
9
  * arrives as an empty array. `batchTransitionTickets` handles the empty
10
10
  * input cleanly (the loop trivially completes), the Story is
11
11
  * transitioned alone, and cascade completion walks upward to
12
- * Feature/Epic. No branch on hierarchy mode is required here.
12
+ * parent. No branch on hierarchy mode is required here.
13
13
  *
14
14
  * Notifications are intentionally
15
15
  * NOT routed through the per-ticket transitions here — `notificationPhase`
@@ -54,9 +54,9 @@ function findStillRegisteredEntry(entries, storyId) {
54
54
  });
55
55
  }
56
56
 
57
- function resolveWorktreeRoot(repoRoot, orchestration) {
57
+ function resolveWorktreeRoot(repoRoot, delivery) {
58
58
  if (!repoRoot) return null;
59
- const configuredRoot = orchestration?.worktreeIsolation?.root ?? '.worktrees';
59
+ const configuredRoot = delivery?.worktreeIsolation?.root ?? '.worktrees';
60
60
  return path.join(repoRoot, configuredRoot);
61
61
  }
62
62
 
@@ -263,7 +263,7 @@ function logStaleRegistryEntry({ state, stillRegistered, logger }) {
263
263
 
264
264
  export async function worktreeReapPhase(ctx) {
265
265
  const {
266
- orchestration,
266
+ delivery,
267
267
  storyId,
268
268
  epicId,
269
269
  epicBranch,
@@ -276,7 +276,7 @@ export async function worktreeReapPhase(ctx) {
276
276
  recordPendingCleanupFn = recordPendingCleanup,
277
277
  pathExistsFn = fs.existsSync,
278
278
  } = ctx;
279
- const wtConfig = orchestration?.worktreeIsolation;
279
+ const wtConfig = delivery?.worktreeIsolation;
280
280
  const log = reapPhaseLogger(progress);
281
281
  const skipState = resolveSkipState(wtConfig, log);
282
282
  if (skipState) return skipState;
@@ -309,7 +309,7 @@ export async function worktreeReapPhase(ctx) {
309
309
  stillRegistered,
310
310
  reapResult,
311
311
  storyId,
312
- orchestration,
312
+ delivery,
313
313
  repoRoot,
314
314
  logger,
315
315
  recordPendingCleanupFn,
@@ -352,7 +352,7 @@ function applyStillRegisteredState({
352
352
  stillRegistered,
353
353
  reapResult,
354
354
  storyId,
355
- orchestration,
355
+ delivery,
356
356
  repoRoot,
357
357
  logger,
358
358
  recordPendingCleanupFn,
@@ -370,7 +370,7 @@ function applyStillRegisteredState({
370
370
  reason: 'still-registered-after-reap',
371
371
  };
372
372
  }
373
- const worktreeRoot = resolveWorktreeRoot(repoRoot, orchestration);
373
+ const worktreeRoot = resolveWorktreeRoot(repoRoot, delivery);
374
374
  let manifestEntry = null;
375
375
  if (worktreeRoot) {
376
376
  try {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Cache adapter for the snapshot/DAG envelope produced by
5
5
  * `epic-deliver-preflight.js` so `epic-deliver-prepare.js` can skip the
6
- * second walk of Epic → Feature → Story when the underlying Epic ticket
6
+ * second walk of Epic → Story when the underlying Epic ticket
7
7
  * has not drifted between the two operator invocations.
8
8
  *
9
9
  * Contract:
@@ -53,28 +53,51 @@ export function preflightCachePath({ epicId, cwd }) {
53
53
  }
54
54
 
55
55
  /**
56
- * Stable string fingerprint of an Epic snapshot. The hash is keyed on the
57
- * exact fields `runSnapshotPhase` reads (`getTicket(epicId)` return value)
58
- * so that any drift the snapshot phase would observe forces a cache miss.
56
+ * Per-ticket fingerprint fields shared by the Epic and each Story in the
57
+ * cache key: id, body, sorted labels, and updatedAt. Story bodies carry
58
+ * the dependency edges, so hashing them means a Story-dependency edit
59
+ * forces a cache miss (Story #4019 — the previous Epic-only key let
60
+ * dependency edits slip through unnoticed).
61
+ *
62
+ * @param {{ id?: number|string, number?: number|string, body?: string, labels?: string[], updatedAt?: string }} ticket
63
+ * @returns {{ id: number|string|null, body: string, labels: string[], updatedAt: string|null }}
64
+ */
65
+ function ticketFingerprint(ticket) {
66
+ const t = ticket && typeof ticket === 'object' ? ticket : {};
67
+ return {
68
+ id: t.id ?? t.number ?? null,
69
+ body: typeof t.body === 'string' ? t.body : '',
70
+ labels: Array.isArray(t.labels) ? [...t.labels].map(String).sort() : [],
71
+ updatedAt: typeof t.updatedAt === 'string' ? t.updatedAt : null,
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Stable string fingerprint of an Epic snapshot **plus its Story
77
+ * dependency state**. The hash is keyed on the exact fields
78
+ * `runSnapshotPhase` reads (`getTicket(epicId)` return value) and on each
79
+ * Story's id/body/labels/updatedAt, so that any drift the snapshot or
80
+ * wave-DAG phases would observe — including a Story-dependency edit —
81
+ * forces a cache miss (Story #4019).
59
82
  *
60
83
  * Labels are sorted to absorb GitHub's non-deterministic label order
61
- * across responses. The hash is sha256; we return the full hex digest so
84
+ * across responses; stories are sorted by id so enumeration order never
85
+ * perturbs the key. The hash is sha256; we return the full hex digest so
62
86
  * the cache key is collision-resistant for the lifetime of a delivery.
63
87
  *
64
88
  * @param {{ id?: number|string, number?: number|string, body?: string, labels?: string[], updatedAt?: string }} epic
89
+ * @param {Array<{ id?: number|string, number?: number|string, body?: string, labels?: string[], updatedAt?: string }>} [stories]
65
90
  * @returns {string}
66
91
  */
67
- export function computeBaseSha(epic) {
92
+ export function computeBaseSha(epic, stories = []) {
68
93
  if (!epic || typeof epic !== 'object') {
69
94
  throw new TypeError('computeBaseSha: epic snapshot must be an object');
70
95
  }
71
- const id = epic.id ?? epic.number ?? null;
72
- const body = typeof epic.body === 'string' ? epic.body : '';
73
- const labels = Array.isArray(epic.labels)
74
- ? [...epic.labels].map(String).sort()
75
- : [];
76
- const updatedAt = typeof epic.updatedAt === 'string' ? epic.updatedAt : null;
77
- const payload = JSON.stringify({ id, body, labels, updatedAt });
96
+ const epicPrint = ticketFingerprint(epic);
97
+ const storyPrints = (Array.isArray(stories) ? stories : [])
98
+ .map(ticketFingerprint)
99
+ .sort((a, b) => Number(a.id ?? 0) - Number(b.id ?? 0));
100
+ const payload = JSON.stringify({ ...epicPrint, stories: storyPrints });
78
101
  return createHash('sha256').update(payload).digest('hex');
79
102
  }
80
103
 
@@ -6,7 +6,7 @@
6
6
  * `close-validate.end` `emitted` records and groups them by `failedGate`.
7
7
  * Returns one finding per gate that appears in **two or more distinct
8
8
  * Stories** within the same Epic. Findings are the substrate
9
- * `/epic-deliver` consumes when upserting the cross-Story
9
+ * `/deliver` consumes when upserting the cross-Story
10
10
  * `recurring-failure-class` structured comment on the Epic ticket.
11
11
  *
12
12
  * The helper is intentionally pure: no GitHub I/O, no global state. It
@@ -328,7 +328,7 @@ function followOnBody(s) {
328
328
  ``,
329
329
  `Trigger: ${trigger}.`,
330
330
  ``,
331
- `Apply the meta::framework-gap label so /epic-plan Phase 0 surfaces it on the next planning pass.`,
331
+ `Apply the meta::framework-gap label so /plan Phase 0 surfaces it on the next planning pass.`,
332
332
  ].join('\n');
333
333
  }
334
334
 
@@ -130,8 +130,8 @@ async function warnIfEpicLooksPopulated({ epicId, provider, logger }) {
130
130
  * `logger` so the silent failure becomes visible. Probe failure degrades
131
131
  * gracefully — the function never throws on the guard alone.
132
132
  *
133
- * **3-tier ledgers (Story #3151, Story #3200).** Under the 3-tier
134
- * hierarchy (Epic → Feature → Story; no Task-tier children), `friction`
133
+ * **2-tier ledgers (Story #3151, Story #3200).** Under the 2-tier
134
+ * hierarchy (Epic → Story; no Task-tier children), `friction`
135
135
  * / `parked` / `recuts` / `storyPerfSummaries` are all Story-scoped, so
136
136
  * the function continues to produce a non-empty signals report. The
137
137
  * empty-walk guard above is **not** triggered for this shape because
@@ -3,10 +3,10 @@
3
3
  *
4
4
  * Story #1155 (Epic #1142, 5.40.0) — extracts the helper-driven
5
5
  * `epic-retro` invocation into a callable module so the
6
- * `/epic-deliver` runner can fire Phase E without a separate LLM
6
+ * `/deliver` runner can fire Phase E without a separate LLM
7
7
  * helper turn. (Story #2259, Epic #2172: the legacy deliver-runner
8
8
  * CLI was retired once delivery moved entirely into the slash
9
- * command.) The retro fires before `/epic-deliver`'s finalize step
9
+ * command.) The retro fires before `/deliver`'s finalize step
10
10
  * opens the PR — the operator's PR-merge is the final human gate, not
11
11
  * the retro itself.
12
12
  *
@@ -56,7 +56,7 @@ export { gatherRetroSignals } from './retro/phases/gather-signals.js';
56
56
  /**
57
57
  * Public: compose and post the retro structured comment on the Epic.
58
58
  *
59
- * Story #1290 (Epic #1143) — at /epic-deliver Phase 5, the runner invokes
59
+ * Story #1290 (Epic #1143) — at /deliver Phase 5, the runner invokes
60
60
  * the self-healing checks registry with `scope: 'retro'` and
61
61
  * `autoFix: false`. The retro is **read-only by construction**: the
62
62
  * registry runner enforces the invariant by throwing if any caller flips
@@ -22,7 +22,7 @@
22
22
  * is enumerated).
23
23
  * - `standard` — everything else, including absent/malformed risk envelopes
24
24
  * and a `medium` level. Fail toward the middle, never toward
25
- * `light`: an Epic that skipped `/epic-plan` has no risk
25
+ * `light`: an Epic that skipped `/plan` has no risk
26
26
  * verdict, and treating it as `light` would under-review
27
27
  * unjudged work while `standard` preserves today's behaviour.
28
28
  *
@@ -31,6 +31,7 @@ import { spawnSync } from 'node:child_process';
31
31
  import fs from 'node:fs';
32
32
  import os from 'node:os';
33
33
  import path from 'node:path';
34
+ import { parseProviderFindings } from './parse-findings.js';
34
35
  import { renderDepthDirective } from './review-depth.js';
35
36
 
36
37
  /**
@@ -173,66 +174,10 @@ export function mapCodexSeverity(raw) {
173
174
  * @throws {Error} when stdout is not parseable JSON.
174
175
  */
175
176
  export function parseCodexFindings(rawStdout) {
176
- const text = (rawStdout ?? '').trim();
177
- if (text.length === 0) return [];
178
-
179
- let parsed;
180
- try {
181
- parsed = JSON.parse(text);
182
- } catch (err) {
183
- throw new Error(
184
- `[codex-review] Failed to parse /codex:review stdout as JSON: ${
185
- err?.message ?? err
186
- }`,
187
- );
188
- }
189
-
190
- // Unwrap a single layer of envelope when present.
191
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
192
- if (Array.isArray(parsed.findings)) parsed = parsed.findings;
193
- else if (parsed.result !== undefined) parsed = parsed.result;
194
- else if (parsed.data !== undefined) parsed = parsed.data;
195
- }
196
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
197
- if (Array.isArray(parsed.findings)) parsed = parsed.findings;
198
- }
199
-
200
- if (!Array.isArray(parsed)) return [];
201
-
202
- /** @type {Finding[]} */
203
- const findings = [];
204
- for (const entry of parsed) {
205
- if (!entry || typeof entry !== 'object') continue;
206
- const title =
207
- typeof entry.title === 'string' && entry.title.trim().length > 0
208
- ? entry.title.trim()
209
- : null;
210
- const body =
211
- typeof entry.body === 'string' && entry.body.trim().length > 0
212
- ? entry.body
213
- : typeof entry.message === 'string' && entry.message.trim().length > 0
214
- ? entry.message
215
- : null;
216
- if (!title || !body) continue;
217
-
218
- /** @type {Finding} */
219
- const finding = {
220
- severity: mapCodexSeverity(entry.severity),
221
- title,
222
- body,
223
- };
224
- if (typeof entry.file === 'string' && entry.file.length > 0) {
225
- finding.file = entry.file;
226
- }
227
- if (Number.isInteger(entry.line) && entry.line > 0) {
228
- finding.line = entry.line;
229
- }
230
- if (typeof entry.category === 'string' && entry.category.length > 0) {
231
- finding.category = entry.category;
232
- }
233
- findings.push(finding);
234
- }
235
- return findings;
177
+ return parseProviderFindings(rawStdout, {
178
+ errorPrefix: '[codex-review] Failed to parse /codex:review stdout as JSON',
179
+ mapSeverity: mapCodexSeverity,
180
+ });
236
181
  }
237
182
 
238
183
  /**
@@ -42,14 +42,14 @@
42
42
 
43
43
  import { spawnSync } from 'node:child_process';
44
44
  import path from 'node:path';
45
- import { PROJECT_ROOT } from '../../config-resolver.js';
46
- import { runOnPool } from '../../cpu-pool.js';
45
+ import { POOL_SERIAL_THRESHOLD, runOnPool } from '../../cpu-pool.js';
47
46
  import { gitSpawn } from '../../git-utils.js';
48
47
  import {
49
48
  calculateReport,
50
49
  classifyReport,
51
50
  } from '../../maintainability-engine.js';
52
- import { transpileIfNeeded } from '../../maintainability-utils.js';
51
+ import { PROJECT_ROOT } from '../../project-root.js';
52
+ import { transpileIfNeeded } from '../../transpile.js';
53
53
  import {
54
54
  hashCommandConfig,
55
55
  recordPass,
@@ -67,10 +67,11 @@ const MAINTAINABILITY_REPORT_WORKER_URL = new URL(
67
67
  * `analyzeChangedFiles` scores in-process (the pre-pool serial path). At or
68
68
  * above it, per-file `calculateReportForFile` scoring is offloaded to the
69
69
  * shared worker pool so the event loop is not blocked during epic-scoped
70
- * reviews (f-performance). Tuned to match the `SERIAL_THRESHOLD` used by the
71
- * maintainability baseline scan in `maintainability-utils.js`.
70
+ * reviews (f-performance). Single-sourced in `cpu-pool.js` (see the
71
+ * `POOL_SERIAL_THRESHOLD` docstring for the tuning rationale); the
72
+ * `SERIAL_THRESHOLD` export name is preserved as this module's public API.
72
73
  */
73
- export const SERIAL_THRESHOLD = 8;
74
+ export const SERIAL_THRESHOLD = POOL_SERIAL_THRESHOLD;
74
75
 
75
76
  const JS_MAINTAINABILITY_EXTS = new Set(['.js', '.mjs', '.cjs']);
76
77