mandrel 1.94.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/.agents/README.md +116 -99
  2. package/.agents/agents/acceptance-critic.md +9 -7
  3. package/.agents/agents/story-worker.md +45 -51
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +498 -1287
  6. package/.agents/docs/agentrc-reference.json +185 -80
  7. package/.agents/docs/configuration.md +132 -190
  8. package/.agents/docs/execution-reference.md +51 -25
  9. package/.agents/docs/quality-gates.md +25 -33
  10. package/.agents/docs/workflows.md +8 -8
  11. package/.agents/instructions.md +113 -139
  12. package/.agents/rules/ci-remediation.md +11 -15
  13. package/.agents/rules/git-conventions-reference.md +48 -58
  14. package/.agents/rules/git-conventions.md +16 -22
  15. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  16. package/.agents/schemas/agentrc.schema.json +83 -254
  17. package/.agents/schemas/audit-rules.json +59 -1
  18. package/.agents/schemas/audit-rules.schema.json +33 -1
  19. package/.agents/schemas/lifecycle/README.md +1 -2
  20. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  21. package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
  22. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
  23. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  24. package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
  25. package/.agents/schemas/signal-event.schema.json +3 -3
  26. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  27. package/.agents/schemas/validation-evidence.schema.json +1 -1
  28. package/.agents/scripts/acceptance-eval.js +22 -66
  29. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  30. package/.agents/scripts/audit-to-stories.js +7 -7
  31. package/.agents/scripts/boot-sweep.js +1 -1
  32. package/.agents/scripts/bootstrap.js +3 -3
  33. package/.agents/scripts/check-dead-exports.js +43 -104
  34. package/.agents/scripts/check-doc-links.js +2 -2
  35. package/.agents/scripts/check-lifecycle-lint.js +7 -10
  36. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  37. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  38. package/.agents/scripts/deliver-recover.js +122 -0
  39. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  40. package/.agents/scripts/evidence-gate.js +20 -50
  41. package/.agents/scripts/generate-skills-index.js +17 -1
  42. package/.agents/scripts/generate-workflows-doc.js +4 -4
  43. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  44. package/.agents/scripts/lib/Logger.js +6 -10
  45. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  46. package/.agents/scripts/lib/audit-suite/selector.js +328 -28
  47. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  48. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  49. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
  50. package/.agents/scripts/lib/baselines/reader.js +1 -6
  51. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  52. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  53. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  54. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  55. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  56. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  57. package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
  58. package/.agents/scripts/lib/checks/index.js +3 -2
  59. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  60. package/.agents/scripts/lib/checks/state.js +17 -248
  61. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
  62. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  63. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  64. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  65. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  66. package/.agents/scripts/lib/cli-args.js +39 -9
  67. package/.agents/scripts/lib/close-validation/gates.js +15 -15
  68. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  69. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  70. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  71. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  72. package/.agents/scripts/lib/config/ci.js +6 -31
  73. package/.agents/scripts/lib/config/delivery-routing.js +52 -35
  74. package/.agents/scripts/lib/config/explain.js +61 -48
  75. package/.agents/scripts/lib/config/github.js +7 -5
  76. package/.agents/scripts/lib/config/limits.js +29 -80
  77. package/.agents/scripts/lib/config/paths.js +0 -2
  78. package/.agents/scripts/lib/config/quality.js +12 -15
  79. package/.agents/scripts/lib/config/runners.js +20 -66
  80. package/.agents/scripts/lib/config/temp-paths.js +30 -63
  81. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  82. package/.agents/scripts/lib/config-resolver.js +2 -7
  83. package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
  84. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
  85. package/.agents/scripts/lib/config-settings-schema.js +100 -60
  86. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  87. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  88. package/.agents/scripts/lib/dependency-parser.js +3 -2
  89. package/.agents/scripts/lib/doc-tiers.js +2 -2
  90. package/.agents/scripts/lib/duplicate-search.js +242 -41
  91. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  92. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  93. package/.agents/scripts/lib/format-generated-json.js +97 -0
  94. package/.agents/scripts/lib/framework-version.js +19 -190
  95. package/.agents/scripts/lib/gh-exec.js +8 -0
  96. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  97. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
  98. package/.agents/scripts/lib/git-utils.js +32 -20
  99. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  100. package/.agents/scripts/lib/json-utils.js +1 -2
  101. package/.agents/scripts/lib/label-constants.js +10 -38
  102. package/.agents/scripts/lib/label-taxonomy.js +10 -55
  103. package/.agents/scripts/lib/observability/active-story-env.js +44 -165
  104. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  105. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  106. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  107. package/.agents/scripts/lib/observability/source-classifier.js +5 -5
  108. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  109. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  110. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  111. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  112. package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
  113. package/.agents/scripts/lib/orchestration/code-review.js +78 -436
  114. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  115. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
  116. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  117. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  118. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  119. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  120. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  121. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  122. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  123. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  124. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  125. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
  126. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  127. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  128. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  129. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
  130. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  131. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  132. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  133. package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
  134. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  135. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  136. package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
  137. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
  138. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
  139. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  140. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  141. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
  142. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
  143. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  144. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
  145. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
  146. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
  147. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  148. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  149. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  150. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
  151. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  152. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
  153. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  154. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  155. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  156. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  157. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  158. package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
  159. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  160. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  161. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  162. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  163. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  164. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  165. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  166. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  167. package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
  168. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
  169. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  170. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  171. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
  172. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  173. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
  174. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  175. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  176. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
  177. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  178. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  179. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
  180. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  181. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  182. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  183. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  184. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  185. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  186. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  187. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
  188. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  189. package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
  190. package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
  191. package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
  192. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  193. package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
  194. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  195. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
  196. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
  197. package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
  198. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
  199. package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
  200. package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
  201. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  202. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  203. package/.agents/scripts/lib/planning-corpus.js +12 -281
  204. package/.agents/scripts/lib/preflight-runner.js +4 -4
  205. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  206. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  207. package/.agents/scripts/lib/signals/index.js +4 -17
  208. package/.agents/scripts/lib/signals/read.js +35 -35
  209. package/.agents/scripts/lib/signals/schema.js +8 -11
  210. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  211. package/.agents/scripts/lib/signals/write.js +0 -1
  212. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  213. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  214. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  215. package/.agents/scripts/lib/story-adjacency.js +17 -19
  216. package/.agents/scripts/lib/story-body/story-body.js +130 -75
  217. package/.agents/scripts/lib/story-plan.js +2 -4
  218. package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
  219. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  220. package/.agents/scripts/lib/test-env.js +14 -1
  221. package/.agents/scripts/lib/test-tiers.js +0 -3
  222. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
  223. package/.agents/scripts/lib/validation-evidence.js +32 -60
  224. package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
  225. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  226. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  227. package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
  228. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  229. package/.agents/scripts/lint-issue-body.js +71 -40
  230. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  231. package/.agents/scripts/notify.js +4 -3
  232. package/.agents/scripts/plan-context.js +102 -80
  233. package/.agents/scripts/plan-persist.js +230 -279
  234. package/.agents/scripts/plan-run-epilogue.js +142 -0
  235. package/.agents/scripts/post-structured-comment.js +0 -38
  236. package/.agents/scripts/pr-watch-with-update.js +43 -22
  237. package/.agents/scripts/providers/github/compose.js +0 -1
  238. package/.agents/scripts/providers/github/errors.js +0 -19
  239. package/.agents/scripts/providers/github/issues.js +13 -39
  240. package/.agents/scripts/providers/github/mappers.js +5 -12
  241. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  242. package/.agents/scripts/providers/github/tickets.js +33 -156
  243. package/.agents/scripts/providers/github.js +17 -6
  244. package/.agents/scripts/resolve-stories.js +236 -0
  245. package/.agents/scripts/run-coverage.js +4 -1
  246. package/.agents/scripts/run-lint.js +2 -2
  247. package/.agents/scripts/run-verify.js +31 -2
  248. package/.agents/scripts/signals-view.js +25 -21
  249. package/.agents/scripts/single-story-close.js +178 -26
  250. package/.agents/scripts/single-story-confirm-merge.js +313 -24
  251. package/.agents/scripts/single-story-init.js +35 -30
  252. package/.agents/scripts/stories-wave-tick.js +85 -10
  253. package/.agents/scripts/story-plan.js +28 -49
  254. package/.agents/scripts/update-ticket-state.js +14 -65
  255. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  256. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  257. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  258. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  259. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  260. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  261. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  262. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  263. package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
  264. package/.agents/skills/core/scope-triage/SKILL.md +31 -172
  265. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  266. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  267. package/.agents/skills/skills.index.json +3 -53
  268. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  269. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  270. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  271. package/.agents/starter-agentrc.json +0 -5
  272. package/.agents/templates/agent-protocol.md +9 -10
  273. package/.agents/workflows/audit-architecture.md +6 -7
  274. package/.agents/workflows/audit-clean-code.md +7 -7
  275. package/.agents/workflows/audit-dependencies.md +3 -3
  276. package/.agents/workflows/audit-devops.md +3 -3
  277. package/.agents/workflows/audit-documentation.md +9 -10
  278. package/.agents/workflows/audit-lighthouse.md +11 -3
  279. package/.agents/workflows/audit-navigability.md +13 -2
  280. package/.agents/workflows/audit-performance.md +5 -6
  281. package/.agents/workflows/audit-privacy.md +3 -3
  282. package/.agents/workflows/audit-quality.md +11 -12
  283. package/.agents/workflows/audit-security.md +4 -5
  284. package/.agents/workflows/audit-seo.md +13 -3
  285. package/.agents/workflows/audit-sre.md +3 -3
  286. package/.agents/workflows/audit-to-stories.md +20 -20
  287. package/.agents/workflows/audit-ux-ui.md +10 -3
  288. package/.agents/workflows/deliver.md +177 -176
  289. package/.agents/workflows/git-cleanup.md +5 -6
  290. package/.agents/workflows/git-deliver.md +1 -1
  291. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  292. package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
  293. package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
  294. package/.agents/workflows/helpers/code-review.md +75 -196
  295. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
  296. package/.agents/workflows/helpers/deliver-story.md +606 -0
  297. package/.agents/workflows/helpers/diagnose.md +10 -10
  298. package/.agents/workflows/helpers/parallel-tooling.md +3 -3
  299. package/.agents/workflows/helpers/signals.md +16 -16
  300. package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
  301. package/.agents/workflows/mandrel-update.md +2 -1
  302. package/.agents/workflows/plan.md +277 -145
  303. package/.agents/workflows/qa-assist.md +27 -33
  304. package/.agents/workflows/qa-explore.md +29 -38
  305. package/.agents/workflows/qa-run.md +2 -6
  306. package/README.md +9 -8
  307. package/bin/mandrel.js +12 -1
  308. package/docs/CHANGELOG.md +70 -0
  309. package/lib/cli/registry.js +262 -19
  310. package/lib/cli/sync-agents.js +157 -0
  311. package/lib/cli/sync-commands.js +115 -6
  312. package/lib/cli/sync.js +168 -6
  313. package/lib/cli/update.js +105 -8
  314. package/lib/cli/version-helpers.js +131 -0
  315. package/lib/migrations/README.md +7 -5
  316. package/lib/migrations/index.js +12 -8
  317. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  318. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  319. package/package.json +2 -2
  320. package/.agents/agents/retro.md +0 -42
  321. package/.agents/personas/architect.md +0 -113
  322. package/.agents/personas/devops-engineer.md +0 -38
  323. package/.agents/personas/engineer.md +0 -33
  324. package/.agents/personas/project-manager.md +0 -114
  325. package/.agents/personas/qa-engineer.md +0 -95
  326. package/.agents/personas/security-engineer.md +0 -111
  327. package/.agents/personas/technical-writer.md +0 -101
  328. package/.agents/schemas/dispatch-manifest.json +0 -232
  329. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  330. package/.agents/schemas/epic-spec.schema.json +0 -153
  331. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  332. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  333. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  334. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  335. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  336. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  337. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  338. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  339. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  340. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  341. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  342. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  343. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  344. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  345. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  346. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  347. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  348. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  349. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  350. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  351. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  352. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  353. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  354. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  355. package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
  356. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
  357. package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
  358. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  359. package/.agents/schemas/risk-verdict.schema.json +0 -66
  360. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  361. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  362. package/.agents/scripts/analyze-execution.js +0 -444
  363. package/.agents/scripts/bookkeeping-reconcile.js +0 -117
  364. package/.agents/scripts/check-prepush-recovery.js +0 -90
  365. package/.agents/scripts/dispatcher.js +0 -295
  366. package/.agents/scripts/epic-audit-prepare.js +0 -497
  367. package/.agents/scripts/epic-audit-recheck.js +0 -274
  368. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  369. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  370. package/.agents/scripts/epic-deliver-prepare.js +0 -852
  371. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  372. package/.agents/scripts/epic-plan-clarity.js +0 -211
  373. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  374. package/.agents/scripts/epic-reconcile.js +0 -625
  375. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  376. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  377. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  378. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  379. package/.agents/scripts/lib/config/preflight.js +0 -58
  380. package/.agents/scripts/lib/config/retro.js +0 -77
  381. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  382. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  383. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  384. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  385. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  386. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
  387. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  388. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  389. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  390. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
  391. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
  392. package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
  393. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  394. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  395. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  396. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  397. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  398. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  399. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  400. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  401. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  402. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  403. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  404. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  405. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  406. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  407. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  408. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
  409. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  410. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  411. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  412. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
  413. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  414. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  415. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  416. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  417. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  418. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  419. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  420. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  421. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  422. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  423. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  424. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  425. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  426. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
  427. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  428. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  429. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  430. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  431. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  432. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  433. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  434. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  435. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  436. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  437. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  438. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
  439. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  440. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  441. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  442. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
  443. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  444. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  445. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  446. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
  447. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  448. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  449. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  450. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  451. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  452. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  453. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  454. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  455. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  456. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  457. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  458. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  459. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  460. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  461. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
  462. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  463. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  464. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  465. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
  466. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  467. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  468. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  469. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  470. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  471. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  472. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  473. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  474. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  475. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  476. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  477. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  478. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  479. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  480. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  481. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  482. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  483. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  484. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  485. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  486. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  487. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  488. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  489. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  490. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  491. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  492. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  493. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  494. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  495. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  496. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  497. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  498. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  499. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  500. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  501. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
  502. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  503. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  504. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  505. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  506. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  507. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  508. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  509. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  510. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  511. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  512. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  513. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  514. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  515. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  516. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  517. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  518. package/.agents/scripts/lib/spec/index.js +0 -36
  519. package/.agents/scripts/lib/spec/loader.js +0 -425
  520. package/.agents/scripts/lib/spec/state.js +0 -208
  521. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  522. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  523. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  524. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  525. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  526. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  527. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  528. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  529. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  530. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  531. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  532. package/.agents/scripts/lifecycle-emit.js +0 -510
  533. package/.agents/scripts/retro-run.js +0 -218
  534. package/.agents/scripts/slice-phase.js +0 -361
  535. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  536. package/.agents/scripts/story-close.js +0 -294
  537. package/.agents/scripts/story-init.js +0 -599
  538. package/.agents/scripts/story-phase.js +0 -369
  539. package/.agents/scripts/wave-tick.js +0 -464
  540. package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
  541. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  542. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  543. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  544. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  545. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  546. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  547. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
  548. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  549. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
  550. package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
  551. package/.agents/workflows/helpers/deliver-epic.md +0 -998
  552. package/.agents/workflows/helpers/deliver-stories.md +0 -450
  553. package/.agents/workflows/helpers/epic-audit.md +0 -189
  554. package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
  555. package/.agents/workflows/helpers/epic-testing.md +0 -125
  556. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  557. package/.agents/workflows/helpers/plan-epic.md +0 -353
  558. package/.agents/workflows/helpers/plan-story.md +0 -251
  559. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  560. package/.agents/workflows/helpers/single-story-deliver.md +0 -557
@@ -1,673 +0,0 @@
1
- // .agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js
2
- /**
3
- * Finalizer — lifecycle listener that owns the finalize-phase side
4
- * effects gated on a successful acceptance reconciliation. Story #2253
5
- * / Task #2254 (Epic #2172); promoted to a fully bus-owned writer in
6
- * Story #2894 / Task #2917 (Epic #2880).
7
- *
8
- * Subscribes to:
9
- * - `acceptance.reconcile.ok` → run finalize.
10
- * - `acceptance.reconcile.waived` → run finalize. Story #2893 split
11
- * the `acceptance::n-a` waiver path out of `.skipped` so the
12
- * Finalizer can route waived Epics through to PR creation while
13
- * `.skipped` (empty-spec) still terminates without a PR.
14
- * `.failed` is already routed to `epic.blocked` via the
15
- * AcceptanceReconciler and never reaches the Finalizer.
16
- *
17
- * Side effects executed inside `handle()`:
18
- * 1. Emit `epic.finalize.start`.
19
- * 2. Auto-graduate non-blocking findings in a SINGLE pass over the
20
- * unified `verification-results` comment (best-effort; never throws).
21
- * Story #4411 folded the former code-review + audit-results
22
- * structured-comment contracts into one comment; the lens-aware
23
- * audit-results graduator is the single canonical reader (its
24
- * 🟢→`suggestion` severity vocabulary matches `findings-renderer.js`).
25
- * Running a second code-review pass over the same comment would file
26
- * every non-blocking finding twice, so only the audit-results
27
- * graduation runs here.
28
- * 3. Idempotency probe — `gh pr list --head epic/<id>` returns any
29
- * existing PR URL. If one exists, short-circuit to `pr.created`
30
- * + `epic.finalize.end` carrying the existing URL.
31
- * 4. Otherwise, invoke `runFinalizeFn`. The production default
32
- * (`composeBusOwnedFinalize`) chains
33
- * a. `openOrLocatePr({ epicId, headBranch, baseBranch })`
34
- * b. `markPrReady({ pr })` when `delivery.ci.earlyPr` is on
35
- * (Story #4359) — the wave-1 draft is flipped ready-for-review;
36
- * skipped when `earlyPr` is off (the PR was opened here, never a
37
- * draft).
38
- * c. `postHandoffComment({ epicId, prNumber, prUrl, provider })`
39
- * and returns `{ prNumber, prUrl, handoff }`. (Story #4324 retired
40
- * the `closePlanningTickets` sweep with the context-ticket classes —
41
- * there are no planning tickets to close.)
42
- * 5. Emit `pr.created` then `epic.finalize.end`.
43
- *
44
- * Why the Finalizer does NOT emit `epic.merge.ready` (Story #3367):
45
- * `epic.merge.ready` is the SOLE trigger AutomergeArmer subscribes to,
46
- * and AutomergeArmer's `epic.merge.armed` cascades synchronously
47
- * through MergeWatcher → Cleaner → BranchCleaner (branch reap). If the
48
- * Finalizer emitted `epic.merge.ready` directly, firing `epic.close.end`
49
- * would run that entire destructive cascade in one synchronous pass —
50
- * arming auto-merge and reaping the `epic/<id>` branch BEFORE the PR is
51
- * merged, and BYPASSING the AutomergePredicate disqualification gate
52
- * (which only ever fires on `epic.watch.end`). The Finalizer's job ends
53
- * at opening the PR (`pr.created` + `epic.finalize.end`). The auto-merge
54
- * arm flows ONLY through the gated path: `pr.created` → `Watcher` →
55
- * `epic.watch.end` → `AutomergePredicate` → `epic.merge.ready` →
56
- * `AutomergeArmer`. AutomergePredicate is the SOLE emitter of
57
- * `epic.merge.ready` (merge-gate-ordering invariant).
58
- *
59
- * Idempotency contract (AC-10): two-layer defence.
60
- * 1. Per-instance `Set<string>` of `${event}:${seqId}` keys — bus-
61
- * level replays short-circuit.
62
- * 2. The `gh pr list --head` probe + `openOrLocatePr`'s internal
63
- * locate path — both defend against cross-process re-runs
64
- * (`/deliver` restarted on the same branch after a crash).
65
- *
66
- * Side-effect firewall: the listener emits on the bus, shells out to
67
- * `gh`/`git` (via the helpers), and upserts the `epic-handoff`
68
- * structured comment via `postHandoffComment`. It does NOT mutate Epic
69
- * state labels or call `notify` directly — those listeners receive
70
- * `pr.created` / `epic.finalize.end` and own their own side effects.
71
- */
72
-
73
- import { spawnSync } from 'node:child_process';
74
- import { getCiDelivery } from '../../../config/ci.js';
75
- import {
76
- graduateAuditResults as defaultGraduateAuditResults,
77
- isAutoFileEnabled as isAuditResultsAutoFileEnabled,
78
- } from '../../../feedback-loop/audit-results-graduator.js';
79
- import { parsePrNumberFromUrl } from '../../../github-url.js';
80
- import {
81
- markPrReady as defaultMarkPrReady,
82
- openOrLocatePr as defaultOpenOrLocatePr,
83
- } from '../../finalize/open-or-locate-pr.js';
84
- import { postHandoffComment as defaultPostHandoffComment } from '../../finalize/post-handoff-comment.js';
85
- import { probeRemoteBranch as defaultProbeRemoteBranch } from '../../remote-verifier.js';
86
-
87
- /**
88
- * Build the production default `runFinalizeFn` that composes the
89
- * bus-owned finalize helpers. Exported as a factory so the listener can
90
- * inject the provider (which the default needs but tests inject by
91
- * supplying their own `runFinalizeFn` outright).
92
- *
93
- * Returns `{ prNumber, prUrl, handoff }` on success, or
94
- * `{ blocker: { reason, detail } }` when a step fails with an
95
- * unrecoverable error that should keep the Epic at `agent::blocked`.
96
- *
97
- * Story #4359 (Epic #4355) — early-PR draft mode. When `earlyPr` is on
98
- * (the default resolved via `getCiDelivery`), the Epic PR already exists
99
- * as a draft (opened at wave 1). Finalize then **locates** it (the
100
- * `openOrLocatePr` probe short-circuits to `created: false`) and flips it
101
- * ready-for-review via `markPrReady` rather than opening the draft as a
102
- * `--draft`. When `earlyPr` is off, finalize opens the PR here with no
103
- * draft — the pre-Story close-time timing. In both modes the title/body
104
- * contract is identical and `markPrReady` is a no-op on an already-ready
105
- * PR, so replay stays idempotent.
106
- *
107
- * Issue #4483 — deterministic land-or-block backstop. Before opening (or
108
- * readying) the PR, finalize asserts the delivery branch `epic/<id>`
109
- * actually exists on origin. A delivery that was never pushed — e.g. an
110
- * agent that built the Epic inline on local `main` and skipped the
111
- * orchestration — MUST surface as an explicit
112
- * `delivery-branch-missing-on-origin` blocker (which keeps the Epic at
113
- * `agent::blocked`), never a declared success. The probe is bounded
114
- * (timeout + SIGKILL) so a hung remote degrades to a blocker too.
115
- *
116
- * @param {{
117
- * provider?: object|null,
118
- * earlyPr?: boolean,
119
- * openOrLocatePrFn?: typeof defaultOpenOrLocatePr,
120
- * markPrReadyFn?: typeof defaultMarkPrReady,
121
- * postHandoffCommentFn?: typeof defaultPostHandoffComment,
122
- * probeRemoteBranchFn?: typeof defaultProbeRemoteBranch,
123
- * }} deps
124
- */
125
- export function composeBusOwnedFinalize(deps = {}) {
126
- const openOrLocatePrFn = deps.openOrLocatePrFn ?? defaultOpenOrLocatePr;
127
- const markPrReadyFn = deps.markPrReadyFn ?? defaultMarkPrReady;
128
- const postHandoffCommentFn =
129
- deps.postHandoffCommentFn ?? defaultPostHandoffComment;
130
- const probeRemoteBranchFn =
131
- deps.probeRemoteBranchFn ?? defaultProbeRemoteBranch;
132
- const provider = deps.provider ?? null;
133
- const earlyPr = deps.earlyPr !== false;
134
-
135
- return async function runBusOwnedFinalize({ epicId, cwd } = {}) {
136
- if (!Number.isInteger(epicId) || epicId < 1) {
137
- return {
138
- blocker: {
139
- reason: 'invalid-epicId',
140
- detail: `runFinalizeFn called with invalid epicId ${epicId}`,
141
- },
142
- };
143
- }
144
-
145
- // Issue #4483 backstop — the delivery branch MUST be on origin before
146
- // finalize declares any success. A never-pushed branch is the silent
147
- // local-main failure shape; block explicitly with the probe detail.
148
- let branchProbe;
149
- try {
150
- branchProbe = probeRemoteBranchFn({ branch: `epic/${epicId}`, cwd });
151
- } catch (err) {
152
- branchProbe = { exists: false, detail: err?.message ?? String(err) };
153
- }
154
- if (!branchProbe.exists) {
155
- return {
156
- blocker: {
157
- reason: 'delivery-branch-missing-on-origin',
158
- detail: `epic/${epicId} is not on origin — the delivery was never pushed; refusing to finalize (issue #4483). Probe: ${branchProbe.detail}`,
159
- },
160
- };
161
- }
162
-
163
- let openResult;
164
- try {
165
- openResult = await openOrLocatePrFn({
166
- epicId,
167
- headBranch: `epic/${epicId}`,
168
- baseBranch: 'main',
169
- cwd,
170
- });
171
- } catch (err) {
172
- return {
173
- blocker: {
174
- reason: 'open-or-locate-pr-failed',
175
- detail: err?.message ?? String(err),
176
- },
177
- };
178
- }
179
- if (
180
- !openResult ||
181
- !Number.isInteger(openResult.prNumber) ||
182
- typeof openResult.url !== 'string'
183
- ) {
184
- return {
185
- blocker: {
186
- reason: 'open-or-locate-pr-empty',
187
- detail: 'openOrLocatePr returned no { prNumber, url } envelope',
188
- },
189
- };
190
- }
191
-
192
- // Story #4359: when earlyPr is on, the located PR is the wave-1 draft —
193
- // flip it ready-for-review. `gh pr ready` on an already-ready PR is a
194
- // no-op, so a replay (or an earlyPr-off PR that was never a draft) is
195
- // safe. A failure here is a hard blocker: leaving the PR a draft would
196
- // silently park the merge gate.
197
- if (earlyPr) {
198
- try {
199
- await markPrReadyFn({ pr: openResult.url, cwd });
200
- } catch (err) {
201
- return {
202
- blocker: {
203
- reason: 'mark-pr-ready-failed',
204
- detail: err?.message ?? String(err),
205
- },
206
- };
207
- }
208
- }
209
-
210
- // The handoff comment requires a provider. The lifecycle-emit CLI
211
- // may construct the Finalizer without one; in that case the step
212
- // short-circuits (the run itself still succeeds — the PR is open and
213
- // the bus can arm auto-merge).
214
- let handoff = null;
215
- if (provider) {
216
- try {
217
- handoff = await postHandoffCommentFn({
218
- epicId,
219
- prNumber: openResult.prNumber,
220
- prUrl: openResult.url,
221
- provider,
222
- });
223
- } catch (err) {
224
- // Handoff is best-effort — failing to upsert the marker comment
225
- // should not roll back the PR open. Surface as a non-blocker so
226
- // the Finalizer still emits pr.created and arms downstream.
227
- handoff = {
228
- marker: 'epic-handoff',
229
- commentId: null,
230
- error: err?.message ?? String(err),
231
- };
232
- }
233
- }
234
-
235
- return {
236
- epicId,
237
- prNumber: openResult.prNumber,
238
- prUrl: openResult.url,
239
- created: openResult.created,
240
- handoff,
241
- };
242
- };
243
- }
244
-
245
- /**
246
- * Parse `gh pr list --head <branch> --json url --jq '.[0].url'` output
247
- * into a PR URL or null. Pure — exported for tests so the regex pin is
248
- * explicit and reviewable.
249
- *
250
- * Accepted forms:
251
- * - `https://github.com/owner/repo/pull/123\n` — typical happy path.
252
- * - empty / whitespace — no PR open.
253
- * - JSON array `[{"url":"…"}]` — when the caller did not pass `--jq`.
254
- */
255
- export function extractPrUrl(stdout) {
256
- const trimmed = String(stdout || '').trim();
257
- if (trimmed.length === 0) return null;
258
- if (trimmed.startsWith('[')) {
259
- try {
260
- const parsed = JSON.parse(trimmed);
261
- if (Array.isArray(parsed) && parsed.length > 0) {
262
- const url = parsed[0]?.url;
263
- if (typeof url === 'string' && /^https:\/\/github\.com\//.test(url)) {
264
- return url;
265
- }
266
- }
267
- return null;
268
- } catch {
269
- return null;
270
- }
271
- }
272
- const match = trimmed.match(/^https:\/\/github\.com\/[^\s]+\/pull\/(\d+)/);
273
- return match ? match[0] : null;
274
- }
275
-
276
- /**
277
- * Default bounded timeout for the finalize idempotency probe. Story #4415
278
- * (Epic #4406) — a hung `gh pr list` spawn previously blocked finalize
279
- * forever; the probe now SIGKILLs the child at this bound so a stalled
280
- * `gh` cannot park the merge gate.
281
- */
282
- export const GH_PR_LIST_TIMEOUT_MS = 30000;
283
-
284
- /**
285
- * Default `gh` spawn used by the listener's idempotency probe.
286
- * Mirrors the `shell: false` contract `openOrLocatePr` and the other
287
- * listener helpers use so a future Windows audit doesn't have to grep
288
- * across two modules. Bounded by `timeoutMs` (default
289
- * {@link GH_PR_LIST_TIMEOUT_MS}) + `killSignal: 'SIGKILL'` so a hung `gh`
290
- * spawn cannot block finalize indefinitely (Story #4415). Exported so
291
- * tests can stub.
292
- */
293
- export function ghPrListHead({
294
- epicBranch,
295
- cwd,
296
- spawnFn = spawnSync,
297
- timeoutMs = GH_PR_LIST_TIMEOUT_MS,
298
- }) {
299
- const result = spawnFn(
300
- 'gh',
301
- ['pr', 'list', '--head', epicBranch, '--json', 'url', '--jq', '.[0].url'],
302
- {
303
- cwd,
304
- encoding: 'utf-8',
305
- shell: false,
306
- timeout: timeoutMs,
307
- killSignal: 'SIGKILL',
308
- },
309
- );
310
- return {
311
- status: result.status ?? 1,
312
- stdout: result.stdout ?? '',
313
- stderr: result.stderr ?? '',
314
- };
315
- }
316
-
317
- /**
318
- * Finalizer listener.
319
- */
320
- export class Finalizer {
321
- /**
322
- * @param {object} opts
323
- * @param {object} opts.bus
324
- * @param {number} opts.epicId
325
- * @param {string} [opts.cwd]
326
- * @param {boolean} [opts.fullScope] forwarded to `runFinalizeFn`;
327
- * defaults to false (diff-scope).
328
- * @param {Function} [opts.runFinalizeFn] override of the composed
329
- * bus-owned finalize default; tests inject their own stub.
330
- * @param {Function} [opts.ghPrListHeadFn] override of the
331
- * idempotency probe.
332
- * @param {object} [opts.provider] Ticketing provider forwarded to
333
- * the default `runFinalizeFn` (for `postHandoffComment`) and to the
334
- * graduators.
335
- * @param {object} [opts.config] Resolved agent config; forwarded to
336
- * the graduators.
337
- * @param {{owner:string,repo:string}} [opts.currentRepo]
338
- * @param {{owner:string,repo:string}} [opts.frameworkRepo]
339
- * @param {Function} [opts.graduateAuditResultsFn]
340
- * @param {{ info?: Function, warn?: Function, debug?: Function }} [opts.logger]
341
- */
342
- constructor(opts = {}) {
343
- if (
344
- !opts.bus ||
345
- typeof opts.bus.on !== 'function' ||
346
- typeof opts.bus.emit !== 'function'
347
- ) {
348
- throw new TypeError('Finalizer requires a bus with on() and emit()');
349
- }
350
- if (!Number.isInteger(opts.epicId) || opts.epicId < 1) {
351
- throw new TypeError('Finalizer requires a numeric epicId');
352
- }
353
- this.bus = opts.bus;
354
- this.epicId = opts.epicId;
355
- this.cwd = opts.cwd ?? process.cwd();
356
- this.fullScope = opts.fullScope === true;
357
- this.provider = opts.provider ?? null;
358
- // Story #4359: gate the finalize PR-open/ready branch on
359
- // `delivery.ci.earlyPr` (default true) resolved via getCiDelivery.
360
- // On → the wave-1 draft is located and marked ready; off → the PR is
361
- // opened here at close time (pre-Story timing).
362
- const earlyPr = getCiDelivery(opts.config ?? null).earlyPr;
363
- this.runFinalizeFn =
364
- opts.runFinalizeFn ??
365
- composeBusOwnedFinalize({ provider: this.provider, earlyPr });
366
- this.ghPrListHeadFn = opts.ghPrListHeadFn ?? ghPrListHead;
367
- // ultrareview bug_007: the existing-PR short-circuit must run the
368
- // handoff-comment upsert (idempotent) so crash-recovery replays
369
- // don't skip it. Stored as an instance override so tests can swap
370
- // it with a no-op stub.
371
- this.postHandoffCommentFn =
372
- opts.postHandoffCommentFn ?? defaultPostHandoffComment;
373
- this.config = opts.config ?? null;
374
- this.currentRepo = opts.currentRepo ?? null;
375
- this.frameworkRepo = opts.frameworkRepo ?? null;
376
- this.graduateAuditResultsFn =
377
- opts.graduateAuditResultsFn ?? defaultGraduateAuditResults;
378
- this.logger = opts.logger ?? console;
379
- /** @type {Set<string>} `${event}:${seqId}` idempotency cache. */
380
- this._seen = new Set();
381
- /**
382
- * Classification log — every `acceptance.reconcile.ok` or `.waived`
383
- * we observe lands here with the outcome (`opened`, `existing`,
384
- * `failed`, `skipped-duplicate`). Mirrors the BlockerHandler /
385
- * Reconciler "no silent skip" surface.
386
- */
387
- this.classifications = [];
388
- this.events = Object.freeze([
389
- 'acceptance.reconcile.ok',
390
- 'acceptance.reconcile.waived',
391
- ]);
392
- }
393
-
394
- register() {
395
- return this.events.map((event) =>
396
- this.bus.on(event, async (ctx) => this.handle(ctx)),
397
- );
398
- }
399
-
400
- async handle({ event, seqId, payload: _payload }) {
401
- const key = `${event}:${seqId}`;
402
- if (this._seen.has(key)) {
403
- this.classifications.push({
404
- event,
405
- seqId,
406
- outcome: 'skipped',
407
- reason: 'duplicate-seqId',
408
- });
409
- this.logger.debug?.(`[Finalizer] skip duplicate ${key} (idempotent)`);
410
- return;
411
- }
412
- this._seen.add(key);
413
-
414
- const epicId = this.epicId;
415
- const epicBranch = `epic/${epicId}`;
416
-
417
- // 1. Announce finalize.start.
418
- try {
419
- await this.bus.emit('epic.finalize.start', { epicId });
420
- } catch (err) {
421
- this.classifications.push({
422
- event,
423
- seqId,
424
- outcome: 'failed',
425
- reason: `start-emit-failed:${err?.message ?? err}`,
426
- });
427
- this.logger.warn?.(
428
- `[Finalizer] epic.finalize.start emit failed: ${err?.message ?? err}`,
429
- );
430
- return;
431
- }
432
-
433
- // 1b. Auto-graduate non-blocking findings in a SINGLE pass over the
434
- // unified `verification-results` comment (Story #2615; unified in
435
- // Story #4411). The lens-aware audit-results graduator is the sole
436
- // canonical reader — a second code-review pass over the same comment
437
- // would file every non-blocking finding twice.
438
- await this._runAuditResultsGraduation();
439
-
440
- // 2. Idempotency probe — does a PR already exist on the head
441
- // branch? If yes, short-circuit to a pr.created emit with the
442
- // existing URL. This is the AC-10 contract for the most-risky
443
- // non-trivial idempotency case (cross-process re-run after a
444
- // crash between `gh pr create` and `pr.created` emit). The
445
- // bus-owned default `runFinalizeFn` also re-checks via
446
- // `openOrLocatePr`; the listener-level probe is defence in
447
- // depth and keeps the existing test surface stable.
448
- const probe = this.ghPrListHeadFn({ epicBranch, cwd: this.cwd });
449
- if (probe.status === 0) {
450
- const existingUrl = extractPrUrl(probe.stdout);
451
- if (existingUrl) {
452
- this.logger.info?.(
453
- `[Finalizer] PR already open for ${epicBranch}: ${existingUrl} — short-circuiting create.`,
454
- );
455
- const prNumber = parsePrNumberFromUrl(existingUrl);
456
- // ultrareview bug_007: even on the short-circuit path we MUST
457
- // run the handoff-comment upsert. The helper is idempotent —
458
- // postHandoffComment edits an existing marker comment in place.
459
- // Without this call, a crash-recovery replay against an
460
- // already-open PR never posts the handoff comment, even though
461
- // epic.merge.ready is still emitted.
462
- try {
463
- await this.postHandoffCommentFn({
464
- epicId: this.epicId,
465
- prNumber,
466
- prUrl: existingUrl,
467
- provider: this.provider,
468
- cwd: this.cwd,
469
- });
470
- } catch (err) {
471
- this.logger.warn?.(
472
- `[Finalizer] postHandoffComment on short-circuit failed (swallowed; replay will retry): ${err?.message ?? err}`,
473
- );
474
- }
475
- await this._emitFinalize({
476
- event,
477
- seqId,
478
- prUrl: existingUrl,
479
- prNumber,
480
- epicBranch,
481
- base: this._resolveBase(),
482
- outcome: 'existing',
483
- });
484
- return;
485
- }
486
- } else {
487
- this.logger.warn?.(
488
- `[Finalizer] gh pr list probe failed (status=${probe.status}): ${probe.stderr} — proceeding with create.`,
489
- );
490
- }
491
-
492
- // 3. Run the composed bus-owned finalize (or the test-injected
493
- // `runFinalizeFn`). The default chains openOrLocatePr →
494
- // postHandoffComment in order and returns
495
- // `{ prNumber, prUrl, handoff }`.
496
- let finalize;
497
- try {
498
- finalize = await this.runFinalizeFn({
499
- epicId,
500
- cwd: this.cwd,
501
- fullScope: this.fullScope,
502
- loggerImpl: this.logger,
503
- });
504
- } catch (err) {
505
- this.classifications.push({
506
- event,
507
- seqId,
508
- outcome: 'failed',
509
- reason: `finalize-threw:${err?.message ?? err}`,
510
- });
511
- this.logger.warn?.(
512
- `[Finalizer] runFinalizeFn threw (swallowed): ${err?.message ?? err}`,
513
- );
514
- return;
515
- }
516
-
517
- if (finalize?.blocker) {
518
- this.classifications.push({
519
- event,
520
- seqId,
521
- outcome: 'failed',
522
- reason: `blocker:${finalize.blocker.reason}`,
523
- });
524
- this.logger.warn?.(
525
- `[Finalizer] finalize reported blocker (${finalize.blocker.reason}): ${finalize.blocker.detail ?? ''}`,
526
- );
527
- return;
528
- }
529
- const prUrl = finalize?.prUrl;
530
- if (typeof prUrl !== 'string' || prUrl.length === 0) {
531
- this.classifications.push({
532
- event,
533
- seqId,
534
- outcome: 'failed',
535
- reason: 'no-pr-url',
536
- });
537
- this.logger.warn?.(
538
- '[Finalizer] runFinalizeFn returned no prUrl — cannot emit pr.created.',
539
- );
540
- return;
541
- }
542
- const prNumber =
543
- typeof finalize?.prNumber === 'number'
544
- ? finalize.prNumber
545
- : parsePrNumberFromUrl(prUrl);
546
-
547
- await this._emitFinalize({
548
- event,
549
- seqId,
550
- prUrl,
551
- prNumber,
552
- epicBranch,
553
- base: this._resolveBase(),
554
- outcome: 'opened',
555
- });
556
- }
557
-
558
- /**
559
- * Invoke the audit-results graduator best-effort — the SINGLE canonical
560
- * graduation pass over the unified `verification-results` comment. Wired
561
- * into finalize so that non-blocking findings (high/medium/low/suggestion)
562
- * get auto-filed as routed follow-up issues — Story #2615 / Epic #2586,
563
- * unified into one pass in Story #4411 / Epic #4405. The lens-aware
564
- * audit-results graduator is canonical because its 🟢→`suggestion`
565
- * severity vocabulary matches `findings-renderer.js`; running a parallel
566
- * code-review pass over the same comment would double-file every finding.
567
- */
568
- async _runAuditResultsGraduation() {
569
- if (!this.provider || !this.currentRepo) {
570
- this.logger.debug?.(
571
- '[Finalizer] audit-results graduation skipped: provider or currentRepo not wired',
572
- );
573
- return;
574
- }
575
- if (!isAuditResultsAutoFileEnabled(this.config)) {
576
- this.logger.debug?.(
577
- '[Finalizer] audit-results graduation skipped: auditResultsAutoFile toggle disabled',
578
- );
579
- return;
580
- }
581
- try {
582
- const summary = await this.graduateAuditResultsFn({
583
- epicId: this.epicId,
584
- provider: this.provider,
585
- config: this.config,
586
- currentRepo: this.currentRepo,
587
- frameworkRepo: this.frameworkRepo,
588
- cwd: this.cwd,
589
- logger: this.logger,
590
- });
591
- const filed = Array.isArray(summary?.filed) ? summary.filed.length : 0;
592
- const skipped = Array.isArray(summary?.skipped)
593
- ? summary.skipped.length
594
- : 0;
595
- const errors = Array.isArray(summary?.errors) ? summary.errors.length : 0;
596
- this.logger.info?.(
597
- `[Finalizer] audit-results graduation: filed=${filed} skipped=${skipped} errors=${errors}`,
598
- );
599
- if (errors > 0) {
600
- this.logger.warn?.(
601
- `[Finalizer] audit-results graduator errors: ${summary.errors.join('; ')}`,
602
- );
603
- }
604
- } catch (err) {
605
- this.logger.warn?.(
606
- `[Finalizer] audit-results graduator threw (swallowed): ${err?.message ?? err}`,
607
- );
608
- }
609
- }
610
-
611
- /**
612
- * Emit the canonical finalize-success pair: `pr.created` →
613
- * `epic.finalize.end` in strict order. Helper carved out so the
614
- * existing-PR short-circuit and the freshly-opened path share the
615
- * same emit sequence.
616
- *
617
- * The Finalizer deliberately STOPS at `epic.finalize.end` and never
618
- * emits `epic.merge.ready` (Story #3367). Emitting `epic.merge.ready`
619
- * here would let `epic.close.end` cascade synchronously into the
620
- * auto-merge arm + branch reap, bypassing the AutomergePredicate gate.
621
- * The auto-merge arm flows ONLY through `pr.created` → Watcher →
622
- * `epic.watch.end` → AutomergePredicate → `epic.merge.ready`.
623
- *
624
- * `prNumber` stays in the classification log for trace/audit even
625
- * though it is no longer threaded onto an `epic.merge.ready` payload.
626
- */
627
- async _emitFinalize({
628
- event,
629
- seqId,
630
- prUrl,
631
- prNumber,
632
- epicBranch,
633
- base,
634
- outcome,
635
- }) {
636
- this.classifications.push({ event, seqId, outcome, prUrl, prNumber });
637
- try {
638
- await this.bus.emit('pr.created', {
639
- prUrl,
640
- head: epicBranch,
641
- base,
642
- });
643
- } catch (err) {
644
- this.logger.warn?.(
645
- `[Finalizer] pr.created emit failed (swallowed): ${err?.message ?? err}`,
646
- );
647
- }
648
- try {
649
- await this.bus.emit('epic.finalize.end', {
650
- epicId: this.epicId,
651
- prUrl,
652
- });
653
- } catch (err) {
654
- this.logger.warn?.(
655
- `[Finalizer] epic.finalize.end emit failed (swallowed): ${err?.message ?? err}`,
656
- );
657
- }
658
- }
659
-
660
- /**
661
- * Resolve the base branch for the PR. Always `main` today; pulled
662
- * into a helper so the listener owns the decision (and tests can
663
- * stub when a non-`main` base is wired in).
664
- */
665
- _resolveBase() {
666
- return 'main';
667
- }
668
-
669
- reset() {
670
- this._seen.clear();
671
- this.classifications = [];
672
- }
673
- }