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,378 +0,0 @@
1
- // .agents/scripts/lib/orchestration/lifecycle/listeners/index.js
2
- /**
3
- * Default listener-chain builder for the standalone `lifecycle-emit`
4
- * CLI surface (Story #2510, Epic #2501).
5
- *
6
- * This is the SOLE production wiring path for the close-tail listener
7
- * roster. (The in-process `epic-runner/factory.js` that previously
8
- * mirrored this roster for an in-session runner was deleted with the
9
- * dead epic-runner stratum in Story #3908; the host-LLM-drives-CLIs
10
- * model reaches every close-tail listener through the `lifecycle-emit.js`
11
- * CLI shells in `/deliver`'s Phase 6 / 7.5 / 8 markdown invocations,
12
- * which call this builder.)
13
- *
14
- * Canonical roster (registration order):
15
- * 1. LedgerWriter (privileged hooks via `register(bus)`)
16
- * 2. AcceptanceReconciler (epic.close.end → acceptance.reconcile.*)
17
- * 3. Finalizer (acceptance.reconcile.{ok,waived} → pr.created)
18
- * 4. AutomergeArmer (epic.merge.ready → epic.merge.armed)
19
- * 5. AutomergePredicate (epic.watch.end → epic.merge.{ready,blocked})
20
- * 6. BranchCleaner (epic.cleanup.start → branch reap)
21
- * 7. MergeWatcher (epic.merge.armed → epic.merge.confirmed)
22
- * 8. Cleaner (epic.merge.confirmed → epic.cleanup.* / epic.complete)
23
- * 9. LabelTransitioner (epic.complete → Epic ticket flips to agent::done)
24
- * 10. CheckpointPointerWriter (every *.end → checkpoint.json)
25
- *
26
- * The bus contract requires LedgerWriter first: its `onEmitted` hook
27
- * lands the `emitted` ledger record on disk BEFORE any listener body
28
- * executes, so a crash mid-chain leaves a recoverable trail.
29
- *
30
- * Listeners whose constructors require collaborators that are not
31
- * available outside the runner (e.g. AutomergePredicate's `provider`,
32
- * BranchCleaner's `checkpointer`) are SKIPPED with a debug log rather
33
- * than constructed. This matches the factory's defensive guard pattern
34
- * — registration is best-effort; the chain still wires every listener
35
- * whose dependencies are satisfiable.
36
- *
37
- * Signature: `buildDefaultListenerChain({ bus, ledgerPath, repoRoot })`.
38
- * The Tech Spec for Epic #2501 (§ Story 4) fixes the public shape at
39
- * those three keys. `ledgerPath` is decomposed into `tempRoot` and
40
- * `epicId` so the listener constructors receive what they need;
41
- * `repoRoot` threads through as `cwd` for listeners that shell out
42
- * (`Finalizer`, `AutomergeArmer`).
43
- *
44
- * Maintainability exemption (refs #3685): this module is listed under
45
- * `delivery.quality.gates.maintainability.ignoreGlobs` in `.agentrc.json`.
46
- * Its sole content is a linear, low-branching listener-registration
47
- * sequence; the maintainability index mis-gauges that shape (the same
48
- * reason the declarative `config-settings-schema*` files are exempt).
49
- * Splitting the sequence across sibling builder modules purely to clear
50
- * the floor would add indirection without making the wiring easier to
51
- * read, so the debt is carried explicitly here rather than hidden behind
52
- * a blanket low floor.
53
- */
54
-
55
- import path from 'node:path';
56
-
57
- import { createLedgerWriter } from '../ledger-writer.js';
58
- import { AcceptanceReconciler } from './acceptance-reconciler.js';
59
- import { AutomergeArmer } from './automerge-armer.js';
60
- import { AutomergePredicate } from './automerge-predicate.js';
61
- import { BranchCleaner } from './branch-cleaner.js';
62
- import { CheckpointPointerWriter } from './checkpoint-pointer-writer.js';
63
- import { Cleaner } from './cleaner.js';
64
- import { Finalizer } from './finalizer.js';
65
- import { LabelTransitioner } from './label-transitioner.js';
66
- import { MergeWatcher } from './merge-watcher.js';
67
-
68
- /**
69
- * Parse `temp/epic-<id>/lifecycle.ndjson` into `{ tempRoot, epicId }`.
70
- *
71
- * Throws when the input does not match the canonical layout. The
72
- * standalone `lifecycle-emit` CLI is the sole production caller and it
73
- * always feeds the canonical `epicLedgerPath(eid)` value, so a mismatch
74
- * here is a programmer error — surface it loudly.
75
- *
76
- * @param {string} ledgerPath
77
- * @returns {{ tempRoot: string, epicId: number }}
78
- */
79
- export function parseLedgerPath(ledgerPath) {
80
- if (typeof ledgerPath !== 'string' || ledgerPath.length === 0) {
81
- throw new TypeError(
82
- 'buildDefaultListenerChain: ledgerPath must be a non-empty string',
83
- );
84
- }
85
- const epicDir = path.dirname(ledgerPath);
86
- const tempRoot = path.dirname(epicDir);
87
- const epicDirName = path.basename(epicDir);
88
- const m = /^epic-(\d+)$/.exec(epicDirName);
89
- if (!m) {
90
- throw new Error(
91
- `buildDefaultListenerChain: ledgerPath does not match temp/epic-<id>/lifecycle.ndjson layout (got ${ledgerPath})`,
92
- );
93
- }
94
- const epicId = Number.parseInt(m[1], 10);
95
- if (!Number.isInteger(epicId) || epicId < 1) {
96
- throw new Error(
97
- `buildDefaultListenerChain: parsed epicId from ledgerPath is not a positive integer (got ${m[1]})`,
98
- );
99
- }
100
- return { tempRoot, epicId };
101
- }
102
-
103
- /**
104
- * Build and subscribe the canonical default listener chain onto the
105
- * supplied bus.
106
- *
107
- * @param {object} opts
108
- * @param {object} opts.bus Lifecycle bus instance (must expose `on`,
109
- * `emit`, and the privileged `onEmitted` / `onCompleted` / `onFailed`
110
- * hook seam).
111
- * @param {string} opts.ledgerPath Absolute or repo-relative path to the
112
- * Epic's `lifecycle.ndjson`. Decomposed into `{ tempRoot, epicId }`
113
- * for downstream constructors.
114
- * @param {string} opts.repoRoot Absolute path used as `cwd` for
115
- * listeners that shell out (Finalizer, AutomergeArmer, BranchCleaner).
116
- * @param {object} [opts.provider] Ticketing provider. When omitted,
117
- * AutomergePredicate is skipped (the listener constructor throws on
118
- * a missing provider).
119
- * @param {object} [opts.config] Resolved agent config. Forwarded to the
120
- * AcceptanceReconciler.
121
- * @param {object} [opts.checkpointer] Epic-run-state checkpoint reader.
122
- * When omitted, BranchCleaner is skipped.
123
- * @param {object} [opts.logger] Logger surface (`debug`/`warn`/`error`).
124
- * @param {boolean} [opts.headless] Explicit must-land signal (Story
125
- * #4427), threaded straight through to `MergeWatcher`. Defaults to
126
- * `false` (attended-mode behavior unchanged). `lifecycle-emit.js`
127
- * resolves this from its own `--headless` runtime flag — an explicit
128
- * input, never an ambient global.
129
- *
130
- * @returns {Promise<{
131
- * ledgerWriter: object,
132
- * acceptanceReconciler: object,
133
- * finalizer: object,
134
- * automergeArmer: object,
135
- * automergePredicate: object|null,
136
- * branchCleaner: object|null,
137
- * cleaner: object,
138
- * checkpointPointerWriter: object,
139
- * order: string[]
140
- * }>}
141
- */
142
- export async function buildDefaultListenerChain(opts = {}) {
143
- const {
144
- bus,
145
- ledgerPath,
146
- repoRoot,
147
- provider = null,
148
- config = null,
149
- checkpointer = null,
150
- logger = console,
151
- headless = false,
152
- } = opts;
153
- if (
154
- !bus ||
155
- typeof bus.on !== 'function' ||
156
- typeof bus.emit !== 'function' ||
157
- typeof bus.onEmitted !== 'function'
158
- ) {
159
- throw new TypeError(
160
- 'buildDefaultListenerChain: bus must expose on/emit and the privileged onEmitted/onCompleted/onFailed seam',
161
- );
162
- }
163
- if (typeof repoRoot !== 'string' || repoRoot.length === 0) {
164
- throw new TypeError(
165
- 'buildDefaultListenerChain: repoRoot must be a non-empty string',
166
- );
167
- }
168
- const { tempRoot, epicId } = parseLedgerPath(ledgerPath);
169
-
170
- const order = [];
171
-
172
- // 1. LedgerWriter — MUST be first. Uses the privileged hook seam, so
173
- // its registration call is `register(bus)` rather than a regular
174
- // `bus.on()` subscription. The bus contract guarantees `emitted`
175
- // lands on disk before any listener body executes.
176
- const ledgerWriter = createLedgerWriter({ epicId, tempRoot });
177
- ledgerWriter.register(bus);
178
- order.push('LedgerWriter');
179
-
180
- // 2. AcceptanceReconciler — gates Finalize on close-time AC coverage.
181
- const acceptanceReconciler = new AcceptanceReconciler({
182
- bus,
183
- epicId,
184
- cwd: repoRoot,
185
- provider,
186
- config,
187
- logger,
188
- });
189
- acceptanceReconciler.register();
190
- order.push('AcceptanceReconciler');
191
-
192
- // 3. Finalizer — opens the PR on acceptance.reconcile.ok or .waived
193
- // (Story #2893 split waiver out of .skipped) via the bus-owned
194
- // default (`composeBusOwnedFinalize`, Story #2894), which chains
195
- // openOrLocatePr → postHandoffComment and
196
- // emits `epic.merge.ready` on success. The legacy
197
- // `d1-default-no-op` blocker is gone; the listener constructed
198
- // with no `runFinalizeFn` override always runs the real flow.
199
- // The code-review / audit-results graduator steps (Stories
200
- // #2555 / #2615) remain best-effort and are silently skipped
201
- // when `provider` / `currentRepo` are not wired in. `currentRepo`
202
- // is resolved from `config.github.{owner,repo}`; `frameworkRepo`
203
- // from `config.github.frameworkRepo` when distinct, otherwise omitted.
204
- const currentRepo =
205
- config?.github?.owner && config?.github?.repo
206
- ? { owner: config.github.owner, repo: config.github.repo }
207
- : null;
208
- const frameworkRepo =
209
- config?.github?.frameworkRepo?.owner && config?.github?.frameworkRepo?.repo
210
- ? {
211
- owner: config.github.frameworkRepo.owner,
212
- repo: config.github.frameworkRepo.repo,
213
- }
214
- : null;
215
- const finalizer = new Finalizer({
216
- bus,
217
- epicId,
218
- cwd: repoRoot,
219
- provider,
220
- config,
221
- currentRepo,
222
- frameworkRepo,
223
- logger,
224
- });
225
- finalizer.register();
226
- order.push('Finalizer');
227
-
228
- // 4. AutomergeArmer — arms `gh pr merge --auto --squash --delete-branch`
229
- // on epic.merge.ready. Story #4472: `headless` gates the direct-merge
230
- // fallback's terminal escalation (a genuine arm failure emits
231
- // `merge.unlanded` + `epic.blocked` in a `--yes` run instead of
232
- // returning silently); `epicId` scopes the `merge.unlanded` ledger row.
233
- const automergeArmer = new AutomergeArmer({
234
- bus,
235
- epicId,
236
- cwd: repoRoot,
237
- headless,
238
- logger,
239
- });
240
- automergeArmer.register();
241
- order.push('AutomergeArmer');
242
-
243
- // 5. AutomergePredicate — emits epic.merge.{ready,blocked} based on
244
- // the runtime predicate evaluation. Requires a truthy `provider`;
245
- // skip cleanly when the caller omitted one (lifecycle-emit CLI
246
- // has no provider wired in by default). `config` selects the
247
- // `delivery.ci.autoMerge` posture (trust-ci default vs strict) and
248
- // `cwd` (repoRoot) is where the live `gh pr checks --required`
249
- // probe shells out (Story #4361).
250
- let automergePredicate = null;
251
- if (provider) {
252
- automergePredicate = new AutomergePredicate({
253
- bus,
254
- epicId,
255
- provider,
256
- config,
257
- cwd: repoRoot,
258
- headless,
259
- logger,
260
- });
261
- automergePredicate.register();
262
- order.push('AutomergePredicate');
263
- } else {
264
- logger?.debug?.(
265
- '[lifecycle] buildDefaultListenerChain: skipping AutomergePredicate (no provider)',
266
- );
267
- }
268
-
269
- // 6. BranchCleaner — reaps story/epic branches on epic.cleanup.start.
270
- // Requires a checkpointer exposing `read()`; skip cleanly when
271
- // the caller omitted one.
272
- let branchCleaner = null;
273
- if (checkpointer && typeof checkpointer.read === 'function') {
274
- branchCleaner = new BranchCleaner({
275
- bus,
276
- epicId,
277
- checkpointer,
278
- cwd: repoRoot,
279
- logger,
280
- });
281
- branchCleaner.register();
282
- order.push('BranchCleaner');
283
- } else {
284
- logger?.debug?.(
285
- '[lifecycle] buildDefaultListenerChain: skipping BranchCleaner (no checkpointer)',
286
- );
287
- }
288
-
289
- // 7. MergeWatcher (Story #2896, Epic #2880) — polls `gh pr view`
290
- // after `epic.merge.armed` until the PR's mergeCommit is
291
- // non-null, then emits `epic.merge.confirmed`. Cleaner now
292
- // waits on the confirmed event rather than the armed event so
293
- // the Epic only transitions to its terminal state after the
294
- // merge is actually observed on GitHub. Reads `intervalSeconds`
295
- // and `maxBudgetSeconds` from `config.delivery.mergeWatch.*`
296
- // when supplied; otherwise uses the listener's framework
297
- // defaults (30s / 3600s).
298
- const mergeWatchConfig = config?.delivery?.mergeWatch ?? {};
299
- const mergeWatcher = new MergeWatcher({
300
- bus,
301
- epicId,
302
- tempRoot,
303
- cwd: repoRoot,
304
- intervalSeconds: mergeWatchConfig.intervalSeconds,
305
- maxBudgetSeconds: mergeWatchConfig.maxBudgetSeconds,
306
- headless,
307
- logger,
308
- });
309
- mergeWatcher.register();
310
- order.push('MergeWatcher');
311
-
312
- // 8. Cleaner — archives temp/epic-<id>/ and emits the terminal
313
- // epic.cleanup.start → .end → epic.complete sequence. Story
314
- // #2896 rebound this listener from `epic.merge.armed` to
315
- // `epic.merge.confirmed`.
316
- const cleaner = new Cleaner({
317
- bus,
318
- epicId,
319
- tempRoot,
320
- logger,
321
- });
322
- cleaner.register();
323
- order.push('Cleaner');
324
-
325
- // 9. LabelTransitioner — flips the Epic ticket to `agent::done` (and
326
- // closes it as completed, idempotently) on the terminal
327
- // `epic.complete` event. Requires a truthy `provider`; skip
328
- // cleanly when the caller omitted one — the same guard pattern as
329
- // AutomergePredicate. Restores the contract the Cleaner /
330
- // BranchCleaner / MergeWatcher docstrings have referenced since
331
- // the original listener was deleted with the epic-runner stratum
332
- // (#3936): without this registration the flip had NO owner and
333
- // cleanly-merged Epics stranded at `agent::executing`.
334
- let labelTransitioner = null;
335
- if (provider) {
336
- labelTransitioner = new LabelTransitioner({
337
- bus,
338
- epicId,
339
- provider,
340
- logger,
341
- });
342
- labelTransitioner.register();
343
- order.push('LabelTransitioner');
344
- } else {
345
- logger?.debug?.(
346
- '[lifecycle] buildDefaultListenerChain: skipping LabelTransitioner (no provider)',
347
- );
348
- }
349
-
350
- // 10. CheckpointPointerWriter — persists `{ lastCompletedSeqId, phase }`
351
- // on every `*.end` event.
352
- const checkpointPointerWriter = new CheckpointPointerWriter({
353
- bus,
354
- epicId,
355
- tempRoot,
356
- logger,
357
- });
358
- checkpointPointerWriter.register();
359
- order.push('CheckpointPointerWriter');
360
-
361
- logger?.debug?.(
362
- `[lifecycle] buildDefaultListenerChain registered listeners: ${order.join(' → ')}`,
363
- );
364
-
365
- return {
366
- ledgerWriter,
367
- acceptanceReconciler,
368
- finalizer,
369
- automergeArmer,
370
- automergePredicate,
371
- branchCleaner,
372
- mergeWatcher,
373
- cleaner,
374
- labelTransitioner,
375
- checkpointPointerWriter,
376
- order,
377
- };
378
- }
@@ -1,140 +0,0 @@
1
- // .agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js
2
- /**
3
- * InterventionRecorder — lifecycle listener that persists out-of-band
4
- * manual interventions to the `epic-run-state` structured comment.
5
- *
6
- * Subscribes to (per Story #2410 / Task #2416):
7
- * - `intervention.recorded` → calls `appendIntervention` on the
8
- * `epic-run-state-store` so the `manualInterventions` array on the
9
- * epic-run-state checkpoint comment grows by exactly one record.
10
- *
11
- * The lifecycle event is the canonical signal that the host LLM has
12
- * performed an out-of-band recovery (`AskUserQuestion`, manual
13
- * `git restore`, `--no-ff` recovery merge, `story-close --skipValidation`,
14
- * etc.). The auto-merge predicate reads the persisted array and only
15
- * fires when it is empty, so getting one record per emit is load-bearing.
16
- *
17
- * Idempotency contract (listeners/README.md): the listener keeps a
18
- * per-instance `Set<seqId>` of seqIds it has handled. A repeat
19
- * invocation with the same seqId short-circuits without calling the
20
- * store. (`epic-run-state-store.appendIntervention` is NOT idempotent
21
- * on its own — it always appends — so the seqId guard is the only
22
- * thing standing between a bus replay and a duplicated record.)
23
- *
24
- * Side-effect firewall (listeners/README.md):
25
- * - MAY read/write the epic-run-state comment via the injected
26
- * `provider` (through `epic-run-state-store`).
27
- * - MUST NOT `bus.emit()` from inside the handler body — the bus is a
28
- * sequential mediator and cannot re-enter safely.
29
- * - MUST NOT import any module from
30
- * `STATE_MUTATING_MODULES` in `check-lifecycle-lint.js`. The
31
- * epic-run-state-store module is NOT on that blocklist; the lint
32
- * rule treats it as a safe persistence boundary for listeners.
33
- */
34
-
35
- import { appendIntervention } from '../../epic-run-state-store.js';
36
-
37
- export const INTERVENTION_RECORDED_EVENT = 'intervention.recorded';
38
-
39
- export class InterventionRecorder {
40
- /**
41
- * @param {object} opts
42
- * @param {object} opts.provider Ticketing provider threaded through to
43
- * `appendIntervention`.
44
- * @param {number} opts.epicId Target ticket id (the Epic). Used both
45
- * to scope `appendIntervention` and to filter incoming emits — a
46
- * payload whose `epicId` does not match this listener's Epic is
47
- * ignored (defensive; production wiring only ever emits with the
48
- * listener's own epicId).
49
- * @param {{ warn?: Function, debug?: Function }} [opts.logger]
50
- * @param {Function} [opts.appendIntervention] Injected store fn —
51
- * defaults to the canonical import. Tests pass a spy.
52
- */
53
- constructor(opts = {}) {
54
- if (!opts.provider) {
55
- throw new TypeError('InterventionRecorder requires a provider');
56
- }
57
- if (!Number.isInteger(opts.epicId) || opts.epicId < 1) {
58
- throw new TypeError('InterventionRecorder requires a numeric epicId');
59
- }
60
- this.provider = opts.provider;
61
- this.epicId = opts.epicId;
62
- this.logger = opts.logger ?? console;
63
- this._appendIntervention =
64
- typeof opts.appendIntervention === 'function'
65
- ? opts.appendIntervention
66
- : appendIntervention;
67
- /** @type {Set<number>} seqIds we've already handled. */
68
- this._seen = new Set();
69
- this.events = Object.freeze([INTERVENTION_RECORDED_EVENT]);
70
- }
71
-
72
- /**
73
- * Subscribe to `intervention.recorded`. Returns the unsubscribe
74
- * handles so tests / teardown can detach without re-binding through
75
- * the bus's private registry.
76
- */
77
- register(bus) {
78
- if (!bus || typeof bus.on !== 'function') {
79
- throw new TypeError(
80
- 'InterventionRecorder.register requires a bus with on()',
81
- );
82
- }
83
- return this.events.map((event) =>
84
- bus.on(event, async (ctx) => this.handle(ctx)),
85
- );
86
- }
87
-
88
- /**
89
- * Handle one `intervention.recorded` emit. Idempotent on seqId. The
90
- * payload has already been schema-validated by the bus, so `reason`
91
- * is guaranteed to be a non-empty string and `epicId` a positive
92
- * integer.
93
- *
94
- * @param {{ event: string, seqId: number, payload: { epicId: number, reason: string, source?: string, ts?: string } }} ctx
95
- */
96
- async handle({ event, seqId, payload }) {
97
- if (event !== INTERVENTION_RECORDED_EVENT) return;
98
- if (this._seen.has(seqId)) {
99
- this.logger.debug?.(
100
- `[InterventionRecorder] skip duplicate seqId=${seqId} (idempotent)`,
101
- );
102
- return;
103
- }
104
- // Defensive epicId filter — a misrouted emit (e.g., a shared bus in
105
- // tests) should not pollute a different Epic's state comment.
106
- if (payload?.epicId !== this.epicId) {
107
- this.logger.debug?.(
108
- `[InterventionRecorder] skip payload.epicId=${payload?.epicId} (listener bound to #${this.epicId})`,
109
- );
110
- return;
111
- }
112
- this._seen.add(seqId);
113
-
114
- const entry = {
115
- reason: payload.reason,
116
- };
117
- if (typeof payload.source === 'string' && payload.source.length > 0) {
118
- entry.source = payload.source;
119
- }
120
- if (typeof payload.ts === 'string' && payload.ts.length > 0) {
121
- entry.ts = payload.ts;
122
- }
123
- try {
124
- await this._appendIntervention({
125
- provider: this.provider,
126
- epicId: this.epicId,
127
- entry,
128
- });
129
- } catch (err) {
130
- this.logger.warn?.(
131
- `[InterventionRecorder] appendIntervention failed for seqId=${seqId}: ${err?.message ?? err}`,
132
- );
133
- throw err;
134
- }
135
- }
136
-
137
- resetSeen() {
138
- this._seen.clear();
139
- }
140
- }
@@ -1,144 +0,0 @@
1
- // .agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js
2
- /**
3
- * LabelTransitioner — lifecycle listener that owns the terminal Epic
4
- * ticket-state flip: on `epic.complete` it transitions the Epic to
5
- * `agent::done` via the canonical `transitionTicketState` API (which
6
- * also closes the issue with `state_reason: completed` and mirrors the
7
- * Projects-v2 status column).
8
- *
9
- * Subscribes to:
10
- * - `epic.complete` → and ONLY this event.
11
- *
12
- * Why this listener exists (regression history): the original
13
- * LabelTransitioner lived in the in-process epic-runner stratum and was
14
- * deleted with it (Story #3908 / #3936) — but the `lifecycle-emit`
15
- * listener chain never re-registered a replacement, so the Epic
16
- * `agent::done` flip silently had NO owner. Every docstring in
17
- * `cleaner.js` / `branch-cleaner.js` / `merge-watcher.js` that says
18
- * "LabelTransitioner flips the Epic ticket to `agent::done` on
19
- * epic.complete" referenced a ghost. In practice the flip only happened
20
- * when a driving session (or the operator) ran `update-ticket-state.js`
21
- * by hand — observed live on 2026-07-11 when Epics #4405 / #4425 /
22
- * #4429 merged cleanly (Cleaner archived, `epic.complete` on the
23
- * ledger) yet stayed at `agent::executing`. This listener restores the
24
- * documented contract on the SOLE production wiring path
25
- * (`buildDefaultListenerChain`).
26
- *
27
- * Side effects executed inside `handle()`:
28
- * 1. `transitionTicketState(provider, epicId, STATE_LABELS.DONE)` —
29
- * adds `agent::done`, removes every other `agent::*` label, closes
30
- * the issue as completed (idempotent when the GitHub Closes-#N
31
- * linkage already closed it), syncs the board column, and runs the
32
- * upward cascade (a no-op sweep here: story-close already flipped
33
- * every child Story).
34
- *
35
- * Failure posture: a failed transition THROWS (per
36
- * `rules/orchestration-error-handling.md` — throw, never fatal). The
37
- * bus's `onFailed` hook records the failure on the ledger and
38
- * `lifecycle-emit`'s `collectOutcomes` → `emitBlockedSignal` path
39
- * surfaces it loudly, so a provider outage cannot silently strand the
40
- * Epic at `agent::executing` again — the exact failure mode this
41
- * listener exists to close.
42
- *
43
- * Idempotency contract: per-instance `Set<string>` of
44
- * `${event}:${seqId}` keys (the standard bus-replay defence). The
45
- * transition itself is also idempotent at the provider layer (label
46
- * add/remove and a close on an already-closed issue are no-ops), so a
47
- * cross-process replay after a crash re-runs the flip harmlessly.
48
- *
49
- * Side-effect firewall: exactly one provider call per handled event. No
50
- * filesystem writes, no follow-up bus emits, no `gh` shell-outs.
51
- */
52
-
53
- import { STATE_LABELS } from '../../ticketing/reads.js';
54
- import { transitionTicketState } from '../../ticketing/transition.js';
55
-
56
- /**
57
- * The single lifecycle event this listener subscribes to. `epic.complete`
58
- * is the terminal event of a successful Epic run, emitted by Cleaner
59
- * AFTER the MergeWatcher observed a non-null mergeCommit — so the flip
60
- * can never fire for an Epic whose PR did not actually merge.
61
- */
62
- export const SUBSCRIBED_EVENT = 'epic.complete';
63
-
64
- export class LabelTransitioner {
65
- /**
66
- * @param {object} opts
67
- * @param {object} opts.bus Lifecycle bus exposing `on()`.
68
- * @param {number} opts.epicId Epic ticket id.
69
- * @param {import('../../../ITicketingProvider.js').ITicketingProvider} opts.provider
70
- * Ticketing provider. Required — the chain builder skips this
71
- * listener entirely when no provider is wired (parity with
72
- * AutomergePredicate's guard), so construction can demand one.
73
- * @param {{ info?: Function, warn?: Function, debug?: Function }} [opts.logger]
74
- */
75
- constructor(opts = {}) {
76
- if (!opts.bus || typeof opts.bus.on !== 'function') {
77
- throw new TypeError('LabelTransitioner requires a bus with on()');
78
- }
79
- if (!Number.isInteger(opts.epicId) || opts.epicId < 1) {
80
- throw new TypeError('LabelTransitioner requires a numeric epicId');
81
- }
82
- if (!opts.provider) {
83
- // Truthiness-only, parity with AutomergePredicate: the chain
84
- // builder's best-effort registration must not explode on a
85
- // shape-minimal provider — a malformed one fails loudly at
86
- // handle time instead, where the ledger records the outcome.
87
- throw new TypeError('LabelTransitioner requires a provider');
88
- }
89
- this.bus = opts.bus;
90
- this.epicId = opts.epicId;
91
- this.provider = opts.provider;
92
- this.logger = opts.logger ?? console;
93
- /** @type {Set<string>} `${event}:${seqId}` idempotency cache. */
94
- this._seen = new Set();
95
- // Canonical subscription-set shape: the lifecycle doc-drift gate
96
- // (`check-lifecycle-doc-drift.js#extractCodeEvents`) and the
97
- // event-connectivity contract test both resolve this frozen array
98
- // (constant references included) to derive the subscriber table.
99
- this.events = Object.freeze([SUBSCRIBED_EVENT]);
100
- }
101
-
102
- /**
103
- * Register the listener on `epic.complete`. Returns the array of
104
- * unsubscribe callbacks the bus produced (parity with the sibling
105
- * listeners).
106
- */
107
- register() {
108
- return this.events.map((event) =>
109
- this.bus.on(event, async (ctx) => this.handle(ctx)),
110
- );
111
- }
112
-
113
- /**
114
- * Bus listener body. Idempotent on `(event, seqId)`; flips the Epic
115
- * to `agent::done` exactly once per observed `epic.complete`.
116
- */
117
- async handle({ event, seqId }) {
118
- const key = `${event}:${seqId}`;
119
- if (this._seen.has(key)) {
120
- this.logger.debug?.(
121
- `[LabelTransitioner] skip duplicate ${key} (idempotent)`,
122
- );
123
- return;
124
- }
125
- this._seen.add(key);
126
-
127
- this.logger.info?.(
128
- `[LabelTransitioner] epic.complete observed — transitioning Epic #${this.epicId} to ${STATE_LABELS.DONE}.`,
129
- );
130
- // Throws on failure by design: the ledger records the failed
131
- // listener outcome and lifecycle-emit surfaces it (see the failure
132
- // posture note in the module docstring). Swallowing here would
133
- // recreate the silent agent::executing strand this listener fixes.
134
- await transitionTicketState(this.provider, this.epicId, STATE_LABELS.DONE);
135
- }
136
-
137
- /**
138
- * Test-only — clear the idempotency cache so a single instance can
139
- * exercise replay scenarios without re-constructing the listener.
140
- */
141
- resetSeen() {
142
- this._seen.clear();
143
- }
144
- }