mandrel 1.93.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/acceptance-critic.md +129 -0
  3. package/.agents/agents/story-worker.md +161 -0
  4. package/.agents/docs/SDLC.md +489 -1285
  5. package/.agents/docs/agentrc-reference.json +177 -67
  6. package/.agents/docs/configuration.md +108 -136
  7. package/.agents/docs/execution-reference.md +44 -22
  8. package/.agents/docs/quality-gates.md +13 -19
  9. package/.agents/docs/workflows.md +3 -3
  10. package/.agents/instructions.md +107 -108
  11. package/.agents/rules/ci-remediation.md +8 -12
  12. package/.agents/rules/git-conventions-reference.md +224 -0
  13. package/.agents/rules/git-conventions.md +42 -223
  14. package/.agents/rules/security-baseline.md +5 -0
  15. package/.agents/rules/testing-standards.md +106 -13
  16. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  17. package/.agents/schemas/agentrc.schema.json +71 -201
  18. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  20. package/.agents/schemas/risk-verdict.schema.json +0 -13
  21. package/.agents/scripts/acceptance-eval.js +62 -18
  22. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  23. package/.agents/scripts/analyze-execution.js +1 -1
  24. package/.agents/scripts/audit-to-stories.js +7 -7
  25. package/.agents/scripts/boot-sweep.js +1 -1
  26. package/.agents/scripts/check-context-budget.js +62 -5
  27. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  28. package/.agents/scripts/check-prepush-recovery.js +1 -1
  29. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  30. package/.agents/scripts/diagnose-friction.js +0 -6
  31. package/.agents/scripts/lib/Logger.js +6 -10
  32. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  33. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  34. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  35. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  36. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  37. package/.agents/scripts/lib/baselines/reader.js +1 -6
  38. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  39. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  40. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
  41. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  42. package/.agents/scripts/lib/checks/index.js +2 -1
  43. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  44. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  45. package/.agents/scripts/lib/cli-args.js +16 -7
  46. package/.agents/scripts/lib/close-validation/gates.js +160 -22
  47. package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
  48. package/.agents/scripts/lib/config/ci.js +6 -31
  49. package/.agents/scripts/lib/config/delivery-routing.js +103 -0
  50. package/.agents/scripts/lib/config/explain.js +57 -36
  51. package/.agents/scripts/lib/config/limits.js +17 -58
  52. package/.agents/scripts/lib/config/paths.js +0 -2
  53. package/.agents/scripts/lib/config/quality.js +1 -1
  54. package/.agents/scripts/lib/config/runners.js +17 -50
  55. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  56. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  57. package/.agents/scripts/lib/config-resolver.js +3 -8
  58. package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
  59. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -14
  60. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  61. package/.agents/scripts/lib/dependency-parser.js +3 -2
  62. package/.agents/scripts/lib/doc-tiers.js +39 -4
  63. package/.agents/scripts/lib/duplicate-search.js +211 -41
  64. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  65. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  66. package/.agents/scripts/lib/framework-version.js +2 -3
  67. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  68. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  69. package/.agents/scripts/lib/git-utils.js +32 -6
  70. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  71. package/.agents/scripts/lib/label-constants.js +10 -23
  72. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  73. package/.agents/scripts/lib/observability/active-story-env.js +112 -3
  74. package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
  75. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  76. package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
  77. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  78. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
  79. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
  80. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  81. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
  82. package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
  83. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  84. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  85. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  86. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  87. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  88. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  89. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  90. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  91. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  92. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  93. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  94. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  95. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  96. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  97. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  98. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
  99. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  100. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  101. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  102. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  103. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  104. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  105. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  106. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  107. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  108. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +14 -14
  109. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  110. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  111. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  112. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  113. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  114. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  115. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  116. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  117. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  118. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  119. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  120. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  121. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  122. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  123. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  124. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  130. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  131. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  132. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  133. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  134. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  135. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  136. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  137. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  138. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  139. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  140. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  141. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  142. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  143. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  144. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  145. package/.agents/scripts/lib/orchestration/ticket-validator.js +3 -8
  146. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  147. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  148. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  149. package/.agents/scripts/lib/planning-corpus.js +16 -11
  150. package/.agents/scripts/lib/preflight-runner.js +2 -2
  151. package/.agents/scripts/lib/provider-factory.js +1 -1
  152. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  153. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  154. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  155. package/.agents/scripts/lib/story-adjacency.js +11 -14
  156. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  157. package/.agents/scripts/lib/story-plan.js +2 -4
  158. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  159. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  160. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  161. package/.agents/scripts/lib/validation-evidence.js +1 -1
  162. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  163. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  164. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  165. package/.agents/scripts/lint-issue-body.js +71 -40
  166. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  167. package/.agents/scripts/notify.js +4 -3
  168. package/.agents/scripts/plan-context.js +64 -74
  169. package/.agents/scripts/plan-persist.js +121 -280
  170. package/.agents/scripts/plan-run-epilogue.js +97 -0
  171. package/.agents/scripts/post-structured-comment.js +38 -0
  172. package/.agents/scripts/providers/github/issues.js +17 -33
  173. package/.agents/scripts/providers/github/mappers.js +0 -12
  174. package/.agents/scripts/providers/github/tickets.js +2 -5
  175. package/.agents/scripts/resolve-plan-run.js +117 -0
  176. package/.agents/scripts/signals-view.js +24 -19
  177. package/.agents/scripts/single-story-close.js +11 -14
  178. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  179. package/.agents/scripts/single-story-init.js +29 -20
  180. package/.agents/scripts/stories-wave-tick.js +6 -6
  181. package/.agents/scripts/story-plan.js +26 -47
  182. package/.agents/scripts/sync-claude-agents.js +165 -0
  183. package/.agents/scripts/update-ticket-state.js +37 -15
  184. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  185. package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
  186. package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
  187. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
  188. package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
  189. package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
  190. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
  191. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  192. package/.agents/skills/skills.index.json +8 -418
  193. package/.agents/starter-agentrc.json +0 -5
  194. package/.agents/templates/agent-protocol.md +9 -10
  195. package/.agents/workflows/audit-architecture.md +3 -3
  196. package/.agents/workflows/audit-clean-code.md +3 -3
  197. package/.agents/workflows/audit-dependencies.md +3 -3
  198. package/.agents/workflows/audit-devops.md +3 -3
  199. package/.agents/workflows/audit-documentation.md +5 -5
  200. package/.agents/workflows/audit-lighthouse.md +3 -3
  201. package/.agents/workflows/audit-navigability.md +3 -2
  202. package/.agents/workflows/audit-performance.md +3 -3
  203. package/.agents/workflows/audit-privacy.md +3 -3
  204. package/.agents/workflows/audit-quality.md +3 -3
  205. package/.agents/workflows/audit-security.md +3 -3
  206. package/.agents/workflows/audit-seo.md +3 -3
  207. package/.agents/workflows/audit-sre.md +3 -3
  208. package/.agents/workflows/audit-to-stories.md +20 -20
  209. package/.agents/workflows/audit-ux-ui.md +3 -3
  210. package/.agents/workflows/deliver.md +122 -131
  211. package/.agents/workflows/git-cleanup.md +3 -4
  212. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  213. package/.agents/workflows/helpers/acceptance-self-eval.md +52 -40
  214. package/.agents/workflows/helpers/code-review.md +70 -193
  215. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  216. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  217. package/.agents/workflows/helpers/diagnose.md +10 -10
  218. package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
  219. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  220. package/.agents/workflows/helpers/signals.md +16 -16
  221. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  222. package/.agents/workflows/mandrel-update.md +3 -2
  223. package/.agents/workflows/plan.md +112 -145
  224. package/.agents/workflows/qa-assist.md +24 -30
  225. package/.agents/workflows/qa-explore.md +29 -38
  226. package/.agents/workflows/qa-run.md +2 -2
  227. package/README.md +9 -8
  228. package/docs/CHANGELOG.md +46 -0
  229. package/lib/cli/registry.js +95 -0
  230. package/lib/migrations/index.js +6 -5
  231. package/package.json +5 -3
  232. package/.agents/personas/architect.md +0 -113
  233. package/.agents/personas/devops-engineer.md +0 -38
  234. package/.agents/personas/engineer-mobile.md +0 -120
  235. package/.agents/personas/engineer-web.md +0 -111
  236. package/.agents/personas/engineer.md +0 -119
  237. package/.agents/personas/product.md +0 -94
  238. package/.agents/personas/project-manager.md +0 -114
  239. package/.agents/personas/qa-engineer.md +0 -95
  240. package/.agents/personas/refactorer.md +0 -113
  241. package/.agents/personas/security-engineer.md +0 -112
  242. package/.agents/personas/sre.md +0 -86
  243. package/.agents/personas/technical-writer.md +0 -101
  244. package/.agents/personas/ux-designer.md +0 -95
  245. package/.agents/schemas/dispatch-manifest.json +0 -232
  246. package/.agents/schemas/epic-spec.schema.json +0 -153
  247. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  248. package/.agents/scripts/dispatcher.js +0 -295
  249. package/.agents/scripts/epic-audit-prepare.js +0 -497
  250. package/.agents/scripts/epic-audit-recheck.js +0 -274
  251. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  252. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  253. package/.agents/scripts/epic-deliver-prepare.js +0 -590
  254. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  255. package/.agents/scripts/epic-plan-clarity.js +0 -211
  256. package/.agents/scripts/epic-plan-decompose.js +0 -54
  257. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  258. package/.agents/scripts/epic-plan-spec.js +0 -64
  259. package/.agents/scripts/epic-reconcile.js +0 -625
  260. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  261. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  262. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  263. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  264. package/.agents/scripts/lib/config/preflight.js +0 -58
  265. package/.agents/scripts/lib/config/retro.js +0 -77
  266. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  267. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  268. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  269. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -660
  270. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  271. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  272. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  273. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  274. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  275. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  276. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  277. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  278. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  279. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  280. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  281. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  282. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  283. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  284. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  285. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  286. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  287. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  288. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -388
  289. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  290. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  291. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  292. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  293. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  294. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  295. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  296. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  297. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  298. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  299. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  300. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  301. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  302. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  303. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  304. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  305. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  306. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  307. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  308. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  309. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  310. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  311. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  312. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  313. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
  314. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  315. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  316. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  317. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -278
  318. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  319. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  320. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  321. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  322. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  323. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  324. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  325. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  326. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  327. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  328. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  329. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  330. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  331. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  332. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  333. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  334. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  335. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
  336. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  337. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  338. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  339. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  340. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  341. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  342. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  343. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  344. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  345. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  346. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  347. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  348. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  349. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  350. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  351. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  352. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  353. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  354. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  355. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  356. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  357. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  358. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  359. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  360. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  361. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -301
  362. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  363. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  364. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  365. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  366. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  367. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  368. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  369. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  370. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  371. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  372. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  373. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  374. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  375. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  376. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  377. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  378. package/.agents/scripts/lib/spec/index.js +0 -36
  379. package/.agents/scripts/lib/spec/loader.js +0 -425
  380. package/.agents/scripts/lib/spec/state.js +0 -208
  381. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  382. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  383. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  384. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  385. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  386. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  387. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  388. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  389. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  390. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  391. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  392. package/.agents/scripts/lifecycle-emit.js +0 -510
  393. package/.agents/scripts/plan-critics.js +0 -199
  394. package/.agents/scripts/retro-run.js +0 -218
  395. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  396. package/.agents/scripts/story-close.js +0 -294
  397. package/.agents/scripts/story-init.js +0 -599
  398. package/.agents/scripts/story-phase.js +0 -369
  399. package/.agents/scripts/wave-tick.js +0 -335
  400. package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
  401. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
  402. package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
  403. package/.agents/skills/core/code-simplification/SKILL.md +0 -389
  404. package/.agents/skills/core/context-engineering/SKILL.md +0 -309
  405. package/.agents/skills/core/context-engineering/examples.md +0 -58
  406. package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
  407. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  408. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  409. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  410. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  411. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  412. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  413. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -413
  414. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  415. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
  416. package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
  417. package/.agents/skills/core/idea-refinement/examples.md +0 -437
  418. package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
  419. package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
  420. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
  421. package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
  422. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
  423. package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
  424. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
  425. package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
  426. package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
  427. package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
  428. package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
  429. package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
  430. package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
  431. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
  432. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
  433. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
  434. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
  435. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
  436. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
  437. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
  438. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
  439. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
  440. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
  441. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
  442. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
  443. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
  444. package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
  445. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
  446. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
  447. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
  448. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
  449. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
  450. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
  451. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
  452. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
  453. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
  454. package/.agents/workflows/helpers/deliver-epic.md +0 -955
  455. package/.agents/workflows/helpers/deliver-stories.md +0 -440
  456. package/.agents/workflows/helpers/epic-audit.md +0 -189
  457. package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
  458. package/.agents/workflows/helpers/epic-testing.md +0 -125
  459. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  460. package/.agents/workflows/helpers/plan-epic.md +0 -351
  461. package/.agents/workflows/helpers/plan-story.md +0 -251
  462. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  463. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -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
- }