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
@@ -0,0 +1,142 @@
1
+ /**
2
+ * emit-ledger-event.js — shared validate-and-append core for the
3
+ * merge-terminal lifecycle emitters (Story #4539).
4
+ *
5
+ * `emit-merge-unlanded.js` and `emit-merge-flip-failed.js` report the two
6
+ * terminal outcomes a landing attempt can reach without ending at
7
+ * `agent::done`. They share everything except their schema and their
8
+ * payload's meaning, so the Ajv compile/cache, the scope→ledger routing,
9
+ * and the NDJSON append live here once rather than being copy-pasted.
10
+ *
11
+ * Like its callers, this is a bare `appendFileSync` rather than a bus
12
+ * publish: these events fire from the `single-story-close` flow, which has
13
+ * no bus at all, so a direct append keeps the call site dependency-free.
14
+ */
15
+
16
+ import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
17
+ import path from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ import Ajv2020 from 'ajv/dist/2020.js';
21
+ import addFormats from 'ajv-formats';
22
+
23
+ import { storyLedgerPath } from '../../config/temp-paths.js';
24
+
25
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
+ const SCHEMA_DIR = path.resolve(
27
+ __dirname,
28
+ '..',
29
+ '..',
30
+ '..',
31
+ '..',
32
+ 'schemas',
33
+ 'lifecycle',
34
+ );
35
+
36
+ /**
37
+ * The scopes a merge-terminal event may be WRITTEN with. v2.0.0 removed the
38
+ * Epic tier, so `'story'` is the only emittable scope.
39
+ *
40
+ * Note the deliberate asymmetry with the `merge.unlanded` /
41
+ * `merge.flip-failed` schema enums, which still accept `'epic'`: archived
42
+ * ledger records carry `scope: 'epic'` and the schemas must keep validating
43
+ * them on read. The value stays READABLE; only the writer path is gone —
44
+ * the same split `merge-block-class.js` keeps for `predicate-refused`.
45
+ */
46
+ const VALID_SCOPES = new Set(['story']);
47
+
48
+ /** @type {Map<string, Function>} */
49
+ const _validators = new Map();
50
+
51
+ /**
52
+ * Compile (once) and return the validator for a lifecycle schema file.
53
+ *
54
+ * @param {string} schemaFile Basename under `.agents/schemas/lifecycle/`.
55
+ * @returns {Function}
56
+ */
57
+ function getValidator(schemaFile) {
58
+ const cached = _validators.get(schemaFile);
59
+ if (cached) return cached;
60
+ const schema = JSON.parse(
61
+ readFileSync(path.resolve(SCHEMA_DIR, schemaFile), 'utf8'),
62
+ );
63
+ const ajv = new Ajv2020({ allErrors: true, strict: false });
64
+ addFormats(ajv);
65
+ const validator = ajv.compile(schema);
66
+ _validators.set(schemaFile, validator);
67
+ return validator;
68
+ }
69
+
70
+ /**
71
+ * Assert the fields every merge-terminal event shares. Throws with the
72
+ * caller's own emitter name so the message stays attributable.
73
+ *
74
+ * @param {string} emitter
75
+ * @param {{ scope: string, ticketId: number, prNumber: number, reason: string, elapsedSeconds: number }} fields
76
+ */
77
+ export function assertMergeTerminalFields(
78
+ emitter,
79
+ { scope, ticketId, prNumber, reason, elapsedSeconds },
80
+ ) {
81
+ if (!VALID_SCOPES.has(scope)) {
82
+ throw new Error(
83
+ `${emitter}: scope "${scope}" must be one of: ${[...VALID_SCOPES].join(', ')}`,
84
+ );
85
+ }
86
+ if (!Number.isInteger(ticketId) || ticketId < 1) {
87
+ throw new Error(`${emitter}: ticketId must be a positive integer`);
88
+ }
89
+ if (!Number.isInteger(prNumber) || prNumber < 1) {
90
+ throw new Error(`${emitter}: prNumber must be a positive integer`);
91
+ }
92
+ if (typeof reason !== 'string' || reason.length === 0) {
93
+ throw new Error(`${emitter}: reason must be a non-empty string`);
94
+ }
95
+ if (typeof elapsedSeconds !== 'number' || elapsedSeconds < 0) {
96
+ throw new Error(`${emitter}: elapsedSeconds must be a non-negative number`);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Validate a payload against its lifecycle schema and append exactly one
102
+ * NDJSON record to the scope-resolved ledger.
103
+ *
104
+ * @param {object} args
105
+ * @param {string} args.emitter Caller name, for error attribution.
106
+ * @param {string} args.schemaFile Basename under `schemas/lifecycle/`.
107
+ * @param {object} args.payload The event payload (already assembled).
108
+ * @param {number} args.ticketId
109
+ * @param {string} args.timestamp
110
+ * @param {object} [args.config]
111
+ * @param {string} [args.ledgerPath] Override for tests / non-default layouts.
112
+ * @returns {{ ledgerPath: string, record: object }}
113
+ */
114
+ export function appendLedgerEvent({
115
+ emitter,
116
+ schemaFile,
117
+ payload,
118
+ ticketId,
119
+ timestamp,
120
+ config,
121
+ ledgerPath: ledgerPathOverride,
122
+ }) {
123
+ const validator = getValidator(schemaFile);
124
+ if (!validator(payload)) {
125
+ const detail = (validator.errors ?? [])
126
+ .map((e) => `${e.instancePath || '/'} ${e.message}`)
127
+ .join('; ');
128
+ throw new Error(`${emitter}: payload failed schema validation: ${detail}`);
129
+ }
130
+
131
+ const ledgerPath =
132
+ ledgerPathOverride ?? storyLedgerPath(null, ticketId, config);
133
+ mkdirSync(path.dirname(ledgerPath), { recursive: true });
134
+ const record = {
135
+ kind: 'emitted',
136
+ ts: timestamp,
137
+ event: payload.event,
138
+ payload,
139
+ };
140
+ appendFileSync(ledgerPath, `${JSON.stringify(record)}\n`, 'utf8');
141
+ return { ledgerPath, record };
142
+ }
@@ -9,14 +9,12 @@
9
9
  * appends an inspectable `emitted` line a reconciler can read for
10
10
  * forward-progress evidence.
11
11
  *
12
- * Distinct from `story.heartbeat` (emit-story-heartbeat.js): the
13
- * heartbeat carries Story-phase info for a single in-flight Story and is
14
- * always Epic-scoped (its ledger path is `epicLedgerPath(epicId)`). A
15
- * host loop is not bound to a Story tier, so `loop.tick` carries a
12
+ * A host loop is not bound to a Story tier, so `loop.tick` carries a
16
13
  * free-form `loopName`, a monotonic `round` counter, the loop's
17
- * configured `cadence` label, and a per-round `status` instead. Keeping
18
- * the two events separate means a loop tick never masquerades as Story
19
- * progress (and vice versa).
14
+ * configured `cadence` label, and a per-round `status`. It is now the only
15
+ * forward-progress beat: the Story-scoped `story.heartbeat` it was once
16
+ * contrasted against was Epic-scoped by construction, could never fire under
17
+ * v2 (which has no Epics), and was deleted (A22).
20
18
  *
21
19
  * Bus path (Story acceptance: "Emitting a loop.tick event THROUGH the
22
20
  * lifecycle bus appends a record to the per-run ledger"): this helper
@@ -39,15 +37,15 @@
39
37
  * Ledger path resolution: a caller supplies EITHER an explicit
40
38
  * `ledgerPath` (the host-loop case — the loop owns where its ledger
41
39
  * lives) OR an `epicId`, in which case the canonical
42
- * `epicLedgerPath(epicId)` is used so an Epic-scoped loop's ticks land
43
- * in the same `temp/epic-<id>/lifecycle.ndjson` the rest of the run
40
+ * `runLedgerPath(epicId)` is used so an Epic-scoped loop's ticks land
41
+ * in the same `temp/run-<id>/lifecycle.ndjson` the rest of the run
44
42
  * reads. Exactly one of the two MUST be supplied.
45
43
  */
46
44
 
47
45
  import path from 'node:path';
48
46
  import { fileURLToPath } from 'node:url';
49
47
 
50
- import { epicLedgerPath } from '../../config/temp-paths.js';
48
+ import { runLedgerPath } from '../../config/temp-paths.js';
51
49
  import { createBus } from './bus.js';
52
50
  import { createLedgerWriter } from './ledger-writer.js';
53
51
 
@@ -65,8 +63,8 @@ const SCHEMA_DIR = path.resolve(
65
63
  const VALID_STATUSES = new Set(['running', 'done', 'blocked']);
66
64
 
67
65
  /**
68
- * Parse `temp/epic-<id>/lifecycle.ndjson` (or any
69
- * `<dir>/epic-<id>/lifecycle.ndjson`) back into `{ tempRoot, epicId }`
66
+ * Parse `temp/run-<id>/lifecycle.ndjson` (or any
67
+ * `<dir>/run-<id>/lifecycle.ndjson`) back into `{ tempRoot, epicId }`
70
68
  * so a LedgerWriter — which is constructed from `{ epicId, tempRoot }`
71
69
  * rather than a raw path — can be bound to the supplied ledger path.
72
70
  *
@@ -79,13 +77,13 @@ const VALID_STATUSES = new Set(['running', 'done', 'blocked']);
79
77
  * @returns {{ tempRoot: string, epicId: number }}
80
78
  */
81
79
  function decomposeLedgerPath(ledgerPath) {
82
- const epicDir = path.dirname(ledgerPath);
83
- const tempRoot = path.dirname(epicDir);
84
- const epicDirName = path.basename(epicDir);
85
- const m = /^epic-(\d+)$/.exec(epicDirName);
80
+ const runDir = path.dirname(ledgerPath);
81
+ const tempRoot = path.dirname(runDir);
82
+ const runDirName = path.basename(runDir);
83
+ const m = /^run-(\d+)$/.exec(runDirName);
86
84
  if (!m) {
87
85
  throw new Error(
88
- `emitLoopTick: ledgerPath does not match <tempRoot>/epic-<id>/lifecycle.ndjson layout (got ${ledgerPath})`,
86
+ `emitLoopTick: ledgerPath does not match <tempRoot>/run-<id>/lifecycle.ndjson layout (got ${ledgerPath})`,
89
87
  );
90
88
  }
91
89
  const epicId = Number.parseInt(m[1], 10);
@@ -104,7 +102,7 @@ function decomposeLedgerPath(ledgerPath) {
104
102
  * One of running|done|blocked.
105
103
  * @param {string} [opts.timestamp] ISO-8601 wall clock. Defaults to now().
106
104
  * @param {number} [opts.epicId] When supplied (and no `ledgerPath`),
107
- * the canonical `epicLedgerPath(epicId)`
105
+ * the canonical `runLedgerPath(epicId)`
108
106
  * is used for the ledger.
109
107
  * @param {object} [opts.config] Optional resolved config for tempRoot
110
108
  * (only consulted on the `epicId` path).
@@ -158,7 +156,7 @@ export async function emitLoopTick(opts) {
158
156
  if (!Number.isInteger(epicId) || epicId < 1) {
159
157
  throw new Error('emitLoopTick: epicId must be a positive integer');
160
158
  }
161
- ledgerPath = epicLedgerPath(epicId, config);
159
+ ledgerPath = runLedgerPath(epicId, config);
162
160
  }
163
161
 
164
162
  const payload = {
@@ -0,0 +1,86 @@
1
+ /**
2
+ * emit-merge-flip-failed.js — Story #4539.
3
+ *
4
+ * Appends a single `merge.flip-failed` NDJSON record when a delivery run
5
+ * observed a **confirmed merge** but the `agent::closing → agent::done`
6
+ * label write itself failed.
7
+ *
8
+ * Why this is not `merge.unlanded`: the merge landed. Reporting it as
9
+ * unlanded points the operator at branch protection, required checks, and
10
+ * poll budgets — none of which are the fault — while the real remedy is a
11
+ * re-run of `single-story-confirm-merge.js`, which is idempotent and flips
12
+ * the label from the already-merged PR. The two events are deliberately
13
+ * distinct so the ledger attributes a label-write API failure differently
14
+ * from a merge that never happened.
15
+ *
16
+ * The Story is still transitioned to `agent::blocked` by the caller: a
17
+ * merged PR whose Story sits at `agent::closing` is exactly the silent
18
+ * strand the must-land contract exists to prevent, so it terminates
19
+ * explicitly — but it terminates with the truth.
20
+ */
21
+
22
+ import {
23
+ appendLedgerEvent,
24
+ assertMergeTerminalFields,
25
+ } from './emit-ledger-event.js';
26
+
27
+ /**
28
+ * The only block class this event carries. Distinct from every
29
+ * `merge.unlanded` attribution (`MERGE_UNLANDED_BLOCK_CLASSES`) because the
30
+ * merge is not in question here — only the label write is.
31
+ */
32
+ export const MERGED_FLIP_FAILED_BLOCK_CLASS = 'merged-flip-failed';
33
+
34
+ /**
35
+ * Append exactly one `merge.flip-failed` record to the resolved ledger.
36
+ *
37
+ * @param {object} opts
38
+ * @param {'story'} opts.scope
39
+ * @param {number} opts.ticketId The storyId.
40
+ * @param {number} opts.prNumber The PR that merged.
41
+ * @param {string} opts.reason What failed on the label write.
42
+ * @param {number} opts.elapsedSeconds Elapsed poll time when the flip failed.
43
+ * @param {string} [opts.timestamp]
44
+ * @param {object} [opts.config]
45
+ * @param {string} [opts.ledgerPath]
46
+ * @returns {{ ledgerPath: string, record: object }}
47
+ */
48
+ export function emitMergeFlipFailed(opts) {
49
+ const {
50
+ scope,
51
+ ticketId,
52
+ prNumber,
53
+ reason,
54
+ elapsedSeconds,
55
+ timestamp = new Date().toISOString(),
56
+ config,
57
+ ledgerPath,
58
+ } = opts ?? {};
59
+
60
+ assertMergeTerminalFields('emitMergeFlipFailed', {
61
+ scope,
62
+ ticketId,
63
+ prNumber,
64
+ reason,
65
+ elapsedSeconds,
66
+ });
67
+
68
+ return appendLedgerEvent({
69
+ emitter: 'emitMergeFlipFailed',
70
+ schemaFile: 'merge.flip-failed.schema.json',
71
+ payload: {
72
+ event: 'merge.flip-failed',
73
+ scope,
74
+ ticketId,
75
+ prNumber,
76
+ blockClass: MERGED_FLIP_FAILED_BLOCK_CLASS,
77
+ reason,
78
+ elapsedSeconds,
79
+ timestamp,
80
+ },
81
+ ticketId,
82
+ timestamp,
83
+ config,
84
+ ledgerPath,
85
+ });
86
+ }
@@ -6,23 +6,16 @@
6
6
  * finishes its work without a confirmed merge. Pattern mirrors
7
7
  * `emit-story-heartbeat.js`: direct schema validation via Ajv followed by
8
8
  * a synchronous `appendFileSync` — this event is NOT routed through the
9
- * bus (unlike `emit-loop-tick.js`) because it can fire from either the
10
- * epic-path finalize flow (which already owns an Epic-scoped bus
11
- * instance for its own run) or the standalone `single-story-close` flow,
12
- * which has no bus at all. A bare append keeps both call sites simple
13
- * and dependency-free.
9
+ * bus (unlike `emit-loop-tick.js`) because it fires from the standalone
10
+ * `single-story-close` flow, which has no bus at all. A bare append keeps
11
+ * the call site simple and dependency-free.
14
12
  *
15
- * Ledger destination is scope-driven (Story #4426 AC4):
16
- * - `scope: 'epic'` → `epicLedgerPath(ticketId)` the same
17
- * `temp/epic-<id>/lifecycle.ndjson` every other Epic-scoped event
18
- * lands in. `ticketId` is the epicId.
19
- * - `scope: 'story'` `storyLedgerPath(null, ticketId)` the
20
- * standalone story-scope destination
21
- * `temp/standalone/stories/story-<id>/lifecycle.ndjson`. `ticketId`
22
- * is the storyId. The standalone `single-story-close` path has no
23
- * parent Epic to anchor a `temp/epic-<id>/` directory to, mirroring
24
- * the `eid === null` standalone convention `signalsFile` already
25
- * uses for Story-level signals.
13
+ * Ledger destination (Story #4426 AC4): `storyLedgerPath(null, ticketId)`
14
+ * the standalone story-scope destination
15
+ * `temp/standalone/stories/story-<id>/lifecycle.ndjson`, where `ticketId`
16
+ * is the storyId. The `single-story-close` path has no parent Epic to
17
+ * anchor a run directory to, mirroring the `eid === null` standalone
18
+ * convention `signalsFile` already uses for Story-level signals.
26
19
  *
27
20
  * A caller may always override the destination via `ledgerPath` (tests,
28
21
  * or a future caller with a non-default temp layout).
@@ -57,50 +50,20 @@
57
50
  * `predicate-refused` / a classified arm failure directly.
58
51
  */
59
52
 
60
- import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
61
- import path from 'node:path';
62
- import { fileURLToPath } from 'node:url';
63
-
64
- import Ajv2020 from 'ajv/dist/2020.js';
65
- import addFormats from 'ajv-formats';
66
-
67
- import { epicLedgerPath, storyLedgerPath } from '../../config/temp-paths.js';
68
53
  import { isValidBlockClass } from '../merge-block-class.js';
69
-
70
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
71
- const SCHEMA_PATH = path.resolve(
72
- __dirname,
73
- '..',
74
- '..',
75
- '..',
76
- '..',
77
- 'schemas',
78
- 'lifecycle',
79
- 'merge.unlanded.schema.json',
80
- );
81
-
82
- const VALID_SCOPES = new Set(['epic', 'story']);
83
-
84
- let _validator;
85
-
86
- function getValidator() {
87
- if (_validator) return _validator;
88
- const schema = JSON.parse(readFileSync(SCHEMA_PATH, 'utf8'));
89
- const ajv = new Ajv2020({ allErrors: true, strict: false });
90
- addFormats(ajv);
91
- _validator = ajv.compile(schema);
92
- return _validator;
93
- }
54
+ import {
55
+ appendLedgerEvent,
56
+ assertMergeTerminalFields,
57
+ } from './emit-ledger-event.js';
94
58
 
95
59
  /**
96
60
  * Append exactly one `merge.unlanded` NDJSON record to the resolved
97
61
  * lifecycle ledger.
98
62
  *
99
63
  * @param {object} opts
100
- * @param {'epic'|'story'} opts.scope Which delivery path is reporting the
64
+ * @param {'story'} opts.scope Which delivery path is reporting the
101
65
  * unlanded merge.
102
- * @param {number} opts.ticketId epicId when `scope === 'epic'`,
103
- * storyId when `scope === 'story'`.
66
+ * @param {number} opts.ticketId The storyId.
104
67
  * @param {number} opts.prNumber The PR number that did not land.
105
68
  * @param {string} opts.blockClass A valid `merge.unlanded` attribution
106
69
  * (`MERGE_UNLANDED_BLOCK_CLASSES` in
@@ -130,64 +93,35 @@ export function emitMergeUnlanded(opts) {
130
93
  ledgerPath: ledgerPathOverride,
131
94
  } = opts ?? {};
132
95
 
133
- if (!VALID_SCOPES.has(scope)) {
134
- throw new Error(
135
- `emitMergeUnlanded: scope "${scope}" must be one of: ${[...VALID_SCOPES].join(', ')}`,
136
- );
137
- }
138
- if (!Number.isInteger(ticketId) || ticketId < 1) {
139
- throw new Error('emitMergeUnlanded: ticketId must be a positive integer');
140
- }
141
- if (!Number.isInteger(prNumber) || prNumber < 1) {
142
- throw new Error('emitMergeUnlanded: prNumber must be a positive integer');
143
- }
144
- if (!isValidBlockClass(blockClass)) {
145
- throw new Error(
146
- `emitMergeUnlanded: blockClass "${blockClass}" is not a recognised merge-block-class value`,
147
- );
148
- }
149
- if (typeof reason !== 'string' || reason.length === 0) {
150
- throw new Error('emitMergeUnlanded: reason must be a non-empty string');
151
- }
152
- if (typeof elapsedSeconds !== 'number' || elapsedSeconds < 0) {
153
- throw new Error(
154
- 'emitMergeUnlanded: elapsedSeconds must be a non-negative number',
155
- );
156
- }
157
-
158
- const payload = {
159
- event: 'merge.unlanded',
96
+ assertMergeTerminalFields('emitMergeUnlanded', {
160
97
  scope,
161
98
  ticketId,
162
99
  prNumber,
163
- blockClass,
164
100
  reason,
165
101
  elapsedSeconds,
166
- timestamp,
167
- };
168
-
169
- const validator = getValidator();
170
- if (!validator(payload)) {
171
- const detail = (validator.errors ?? [])
172
- .map((e) => `${e.instancePath || '/'} ${e.message}`)
173
- .join('; ');
102
+ });
103
+ if (!isValidBlockClass(blockClass)) {
174
104
  throw new Error(
175
- `emitMergeUnlanded: payload failed schema validation: ${detail}`,
105
+ `emitMergeUnlanded: blockClass "${blockClass}" is not a recognised merge-block-class value`,
176
106
  );
177
107
  }
178
108
 
179
- const ledgerPath =
180
- ledgerPathOverride ??
181
- (scope === 'epic'
182
- ? epicLedgerPath(ticketId, config)
183
- : storyLedgerPath(null, ticketId, config));
184
- mkdirSync(path.dirname(ledgerPath), { recursive: true });
185
- const record = {
186
- kind: 'emitted',
187
- ts: timestamp,
188
- event: 'merge.unlanded',
189
- payload,
190
- };
191
- appendFileSync(ledgerPath, `${JSON.stringify(record)}\n`, 'utf8');
192
- return { ledgerPath, record };
109
+ return appendLedgerEvent({
110
+ emitter: 'emitMergeUnlanded',
111
+ schemaFile: 'merge.unlanded.schema.json',
112
+ payload: {
113
+ event: 'merge.unlanded',
114
+ scope,
115
+ ticketId,
116
+ prNumber,
117
+ blockClass,
118
+ reason,
119
+ elapsedSeconds,
120
+ timestamp,
121
+ },
122
+ ticketId,
123
+ timestamp,
124
+ config,
125
+ ledgerPath: ledgerPathOverride,
126
+ });
193
127
  }
@@ -25,6 +25,7 @@
25
25
 
26
26
  import { appendFileSync, mkdirSync } from 'node:fs';
27
27
  import path from 'node:path';
28
+ import { runTempDir } from '../../config/temp-paths.js';
28
29
 
29
30
  /**
30
31
  * Static deny-list pulled from the Tech Spec § Security & Privacy. Keys
@@ -85,7 +86,7 @@ export class LedgerWriter {
85
86
  * @param {object} opts
86
87
  * @param {number} opts.epicId
87
88
  * @param {string} opts.tempRoot - absolute or repo-relative path; the
88
- * writer resolves `temp/epic-<id>/lifecycle.ndjson` underneath.
89
+ * writer resolves `temp/run-<id>/lifecycle.ndjson` underneath.
89
90
  * @param {() => number} [opts.now] - injectable clock for tests.
90
91
  */
91
92
  constructor(opts) {
@@ -101,7 +102,9 @@ export class LedgerWriter {
101
102
  }
102
103
  this._epicId = opts.epicId;
103
104
  this._tempRoot = opts.tempRoot;
104
- this._epicDir = path.join(this._tempRoot, `epic-${this._epicId}`);
105
+ this._epicDir = runTempDir(this._epicId, {
106
+ project: { paths: { tempRoot: this._tempRoot } },
107
+ });
105
108
  this._ledgerPath = path.join(this._epicDir, 'lifecycle.ndjson');
106
109
  this._now = typeof opts.now === 'function' ? opts.now : Date.now;
107
110
  }
@@ -127,7 +130,7 @@ export class LedgerWriter {
127
130
  * The naive optimization (cache "ensured" after the first call) is
128
131
  * unsound when a listener moves the ledger directory mid-handler —
129
132
  * which is exactly what the Cleaner does on Wave 8 (Story #2259):
130
- * it renames `temp/epic-<id>/` under `archive/` between the listener
133
+ * it renames `temp/run-<id>/` under `archive/` between the listener
131
134
  * body and the bus's `onCompleted` hook fire-time, and the next
132
135
  * append (the `completed` record for the outer event) would land
133
136
  * in a vanished directory. `mkdirSync({recursive: true})` is
@@ -1,45 +1,27 @@
1
1
  # Lifecycle Listeners
2
2
 
3
3
  Each listener in this directory subscribes to one or more lifecycle bus
4
- events and performs a single side effect. The canonical close-tail roster
5
- in registration order — is wired by
6
- [`index.js`](./index.js) (`buildDefaultListenerChain`), the production
7
- entrypoint the standalone `lifecycle-emit.js` CLI shells in for
8
- `/deliver`'s Phase 6 / 7.5 / 8 / 8.5 markdown invocations:
4
+ events and performs a single side effect. The full close-tail roster and
5
+ event taxonomy live in [`docs/LIFECYCLE.md`](../../../../../../docs/LIFECYCLE.md)
6
+ — that document is the SSOT. This README only indexes the **files that still
7
+ live in this folder**.
9
8
 
10
- - `ledger-writer.js` — privileged `onEmitted` hook that lands every
11
- `emitted` record on disk before any listener body runs (MUST be first).
12
- - `acceptance-reconciler.js` — gates Finalize on close-time acceptance
13
- coverage (`epic.close.end → acceptance.reconcile.*`).
14
- - `finalizer.js` — opens the PR on `acceptance.reconcile.{ok,waived}` and
15
- emits `epic.merge.ready`.
16
- - `automerge-armer.js` — arms `gh pr merge --auto --squash --delete-branch`
17
- on `epic.merge.ready`.
18
- - `automerge-predicate.js` — evaluates the clean-sprint predicate and
19
- emits `epic.merge.{ready,blocked}`.
20
- - `branch-cleaner.js` — reaps story/epic branches on `epic.cleanup.start`.
21
- - `merge-watcher.js` — polls `gh pr view` after `epic.merge.armed` until
22
- the PR's `mergeCommit` is observed, then emits `epic.merge.confirmed`.
23
- - `cleaner.js` — archives `temp/epic-<id>/` and emits the terminal
24
- `epic.cleanup.* → epic.complete` sequence on `epic.merge.confirmed`.
25
- - `label-transitioner.js` — flips the Epic ticket to `agent::done` (and
26
- closes it as completed, idempotently) on `epic.complete`. Skipped when
27
- no provider is wired. Re-homed onto this chain after the 2026-07-11
28
- incident (merged Epics stranding at `agent::executing`): the original
29
- LabelTransitioner died with the in-process runner stratum (#3936) and
30
- the flip had no owner.
31
- - `checkpoint-pointer-writer.js` — persists `{ lastCompletedSeqId, phase }`
32
- on every `*.end` event.
9
+ ## Files here
33
10
 
34
- Two further listeners are activated outside the close-tail chain by their
35
- own CLI entrypoints rather than `buildDefaultListenerChain`:
11
+ - [`watcher.js`](./watcher.js) the CI-poll loop (`watchPrToTerminal`)
12
+ driven by `pr-watch-with-update.js`.
36
13
 
37
- - `notify-dispatcher.js` fans out the curated webhook event subset
38
- (driven by `notify.js`).
39
- - `intervention-recorder.js` appends operator-intervention payloads to
40
- the epic-run-state (driven by `epic-deliver-note-intervention.js`).
41
- - `watcher.js` — the CI-poll loop (`watchPrToTerminal`) driven by
42
- `pr-watch-with-update.js`.
14
+ `merge-watcher.js` was deleted in Story #4545: the Epic-era `MergeWatcher`
15
+ listener had no production caller after the v2.0.0 Story-only cutover. The
16
+ poll defaults and `deriveChecksStatus` the live close path did import from it
17
+ now live in
18
+ [`lib/orchestration/merge-poll.js`](../../merge-poll.js), a home the close
19
+ path owns.
20
+
21
+ Other close-tail side effects (ledger write, finalize/PR open, automerge
22
+ arm, branch cleanup, label transition) are owned by the Story delivery
23
+ path (`helpers/deliver-story` / `single-story-close.js`) rather than a
24
+ `buildDefaultListenerChain` factory in this directory.
43
25
 
44
26
  ## Idempotency contract
45
27
 
@@ -50,10 +32,6 @@ canonical pattern is a per-instance `Set<seqId>` checked at the top of
50
32
  the listener body; the second invocation returns early without mutating
51
33
  external state.
52
34
 
53
- The seqId guard is the only correctness requirement we surface from the
54
- bus contract — downstream idempotency primitives (label-state diff,
55
- marker-keyed upsert, NDJSON dedupe by seqId) layer on top of it.
56
-
57
35
  ## Side-effect firewall
58
36
 
59
37
  Listeners MAY:
@@ -61,13 +39,13 @@ Listeners MAY:
61
39
  - read tickets via the injected `provider`,
62
40
  - write tickets via the injected `transitionTicketState`,
63
41
  - upsert structured comments via the injected `upsertStructuredComment`,
64
- - append to per-Epic ledger / signals files under `tempRoot`.
42
+ - append to per-run ledger / signals files under `tempRoot`.
65
43
 
66
44
  Listeners MUST NOT:
67
45
 
68
46
  - `bus.emit()` from inside a listener body (sequential mediator
69
- contract — the bus cannot re-entry safely),
70
- - import the runner state directly,
47
+ contract — the bus cannot re-enter safely),
48
+ - import runner state directly,
71
49
  - mutate cross-cutting globals.
72
50
 
73
51
  Trace observers (`bus.on('*', fn)`) live under