mandrel 2.0.0 → 2.2.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 (323) hide show
  1. package/.agents/README.md +59 -28
  2. package/.agents/agents/acceptance-critic.md +20 -9
  3. package/.agents/agents/story-worker.md +45 -48
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +60 -46
  6. package/.agents/docs/agentrc-reference.json +8 -13
  7. package/.agents/docs/configuration.md +33 -57
  8. package/.agents/docs/execution-reference.md +39 -10
  9. package/.agents/docs/quality-gates.md +17 -19
  10. package/.agents/docs/workflows.md +6 -6
  11. package/.agents/instructions.md +64 -79
  12. package/.agents/rules/ci-remediation.md +3 -3
  13. package/.agents/rules/gherkin-standards.md +10 -0
  14. package/.agents/rules/git-conventions-reference.md +42 -51
  15. package/.agents/schemas/acceptance-eval-verdict.schema.json +2 -2
  16. package/.agents/schemas/agentrc.schema.json +35 -46
  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/story.merged.schema.json +1 -1
  24. package/.agents/schemas/signal-event.schema.json +3 -3
  25. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  26. package/.agents/schemas/validation-evidence.schema.json +1 -1
  27. package/.agents/scripts/acceptance-eval.js +24 -68
  28. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  29. package/.agents/scripts/bootstrap.js +3 -3
  30. package/.agents/scripts/check-dead-exports.js +43 -104
  31. package/.agents/scripts/check-doc-links.js +2 -2
  32. package/.agents/scripts/check-lifecycle-lint.js +1 -1
  33. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  34. package/.agents/scripts/deliver-recover.js +122 -0
  35. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  36. package/.agents/scripts/evidence-gate.js +20 -50
  37. package/.agents/scripts/generate-skills-index.js +17 -1
  38. package/.agents/scripts/generate-workflows-doc.js +4 -4
  39. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  40. package/.agents/scripts/lib/audit-suite/selector.js +323 -23
  41. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
  42. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  43. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  44. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  45. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  46. package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
  47. package/.agents/scripts/lib/checks/index.js +1 -1
  48. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  49. package/.agents/scripts/lib/checks/state.js +17 -248
  50. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
  51. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  52. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  53. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  54. package/.agents/scripts/lib/cli-args.js +23 -2
  55. package/.agents/scripts/lib/close-validation/gates.js +13 -13
  56. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  57. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  58. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  59. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  60. package/.agents/scripts/lib/config/acceptance-eval.js +2 -2
  61. package/.agents/scripts/lib/config/delivery-routing.js +7 -6
  62. package/.agents/scripts/lib/config/explain.js +10 -16
  63. package/.agents/scripts/lib/config/github.js +7 -5
  64. package/.agents/scripts/lib/config/limits.js +15 -25
  65. package/.agents/scripts/lib/config/quality.js +11 -14
  66. package/.agents/scripts/lib/config/runners.js +8 -21
  67. package/.agents/scripts/lib/config/temp-paths.js +18 -56
  68. package/.agents/scripts/lib/config-settings-schema-delivery.js +34 -16
  69. package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
  70. package/.agents/scripts/lib/config-settings-schema.js +48 -22
  71. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  72. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  73. package/.agents/scripts/lib/duplicate-search.js +38 -7
  74. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  75. package/.agents/scripts/lib/format-generated-json.js +97 -0
  76. package/.agents/scripts/lib/framework-version.js +19 -189
  77. package/.agents/scripts/lib/gh-exec.js +8 -0
  78. package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
  79. package/.agents/scripts/lib/git-utils.js +0 -14
  80. package/.agents/scripts/lib/json-utils.js +1 -2
  81. package/.agents/scripts/lib/label-constants.js +0 -15
  82. package/.agents/scripts/lib/label-taxonomy.js +1 -12
  83. package/.agents/scripts/lib/observability/active-story-env.js +42 -163
  84. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  85. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  86. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  87. package/.agents/scripts/lib/observability/source-classifier.js +2 -2
  88. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  89. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  90. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  91. package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
  92. package/.agents/scripts/lib/orchestration/change-set.js +103 -0
  93. package/.agents/scripts/lib/orchestration/code-review.js +70 -191
  94. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
  95. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  96. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  97. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  98. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  99. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  100. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
  101. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  102. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  103. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
  104. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  105. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  106. package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
  107. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  108. package/.agents/scripts/lib/orchestration/plan-context.js +116 -33
  109. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +26 -36
  110. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +31 -22
  111. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  112. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
  113. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
  114. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  115. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -100
  116. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
  117. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
  118. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  119. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +230 -0
  120. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
  121. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -2
  122. package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
  123. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  124. package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
  125. package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
  126. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  127. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  128. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  129. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  130. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  131. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  132. package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
  133. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
  134. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  135. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  136. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +4 -13
  137. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  138. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
  139. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  140. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  141. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
  142. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  143. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  144. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
  145. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  146. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  147. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +104 -279
  148. package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +191 -0
  149. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +120 -0
  150. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  151. package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
  152. package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
  153. package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
  154. package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
  155. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  156. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
  157. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
  158. package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
  159. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
  160. package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
  161. package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
  162. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  163. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  164. package/.agents/scripts/lib/planning-corpus.js +12 -286
  165. package/.agents/scripts/lib/preflight-runner.js +2 -2
  166. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  167. package/.agents/scripts/lib/signals/index.js +4 -17
  168. package/.agents/scripts/lib/signals/read.js +35 -35
  169. package/.agents/scripts/lib/signals/schema.js +8 -11
  170. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  171. package/.agents/scripts/lib/signals/write.js +0 -1
  172. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  173. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  174. package/.agents/scripts/lib/story-adjacency.js +8 -7
  175. package/.agents/scripts/lib/story-body/story-body.js +81 -13
  176. package/.agents/scripts/lib/templates/decomposer-prompts.js +15 -16
  177. package/.agents/scripts/lib/test-env.js +14 -1
  178. package/.agents/scripts/lib/test-tiers.js +0 -3
  179. package/.agents/scripts/lib/ticket-body-sections.js +0 -14
  180. package/.agents/scripts/lib/validation-evidence.js +31 -59
  181. package/.agents/scripts/lib/wave-runner/live-probe.js +315 -0
  182. package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
  183. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  184. package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
  185. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  186. package/.agents/scripts/plan-context.js +38 -7
  187. package/.agents/scripts/plan-critics.js +203 -0
  188. package/.agents/scripts/plan-persist.js +145 -35
  189. package/.agents/scripts/plan-run-epilogue.js +83 -38
  190. package/.agents/scripts/post-structured-comment.js +0 -38
  191. package/.agents/scripts/pr-watch-with-update.js +43 -22
  192. package/.agents/scripts/providers/github/compose.js +0 -1
  193. package/.agents/scripts/providers/github/errors.js +0 -19
  194. package/.agents/scripts/providers/github/issues.js +1 -11
  195. package/.agents/scripts/providers/github/mappers.js +5 -0
  196. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  197. package/.agents/scripts/providers/github/tickets.js +33 -153
  198. package/.agents/scripts/providers/github.js +17 -6
  199. package/.agents/scripts/quality-preview.js +13 -6
  200. package/.agents/scripts/resolve-stories.js +236 -0
  201. package/.agents/scripts/run-coverage.js +4 -1
  202. package/.agents/scripts/run-lint.js +2 -2
  203. package/.agents/scripts/run-verify.js +31 -2
  204. package/.agents/scripts/signals-view.js +9 -10
  205. package/.agents/scripts/single-story-close.js +173 -18
  206. package/.agents/scripts/single-story-confirm-merge.js +288 -15
  207. package/.agents/scripts/single-story-init.js +6 -10
  208. package/.agents/scripts/stories-wave-tick.js +380 -53
  209. package/.agents/scripts/story-plan.js +3 -3
  210. package/.agents/scripts/update-ticket-state.js +8 -50
  211. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  212. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  213. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  214. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  215. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  216. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  217. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  218. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  219. package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
  220. package/.agents/skills/core/scope-triage/SKILL.md +3 -0
  221. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  222. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  223. package/.agents/skills/skills.index.json +2 -12
  224. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  225. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  226. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  227. package/.agents/workflows/audit-architecture.md +3 -4
  228. package/.agents/workflows/audit-clean-code.md +4 -4
  229. package/.agents/workflows/audit-documentation.md +4 -5
  230. package/.agents/workflows/audit-lighthouse.md +8 -0
  231. package/.agents/workflows/audit-navigability.md +10 -0
  232. package/.agents/workflows/audit-performance.md +2 -3
  233. package/.agents/workflows/audit-quality.md +8 -9
  234. package/.agents/workflows/audit-security.md +1 -2
  235. package/.agents/workflows/audit-seo.md +10 -0
  236. package/.agents/workflows/audit-ux-ui.md +7 -0
  237. package/.agents/workflows/deliver.md +133 -45
  238. package/.agents/workflows/git-cleanup.md +2 -2
  239. package/.agents/workflows/git-deliver.md +1 -1
  240. package/.agents/workflows/helpers/acceptance-self-eval.md +34 -17
  241. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -12
  242. package/.agents/workflows/helpers/code-review.md +14 -12
  243. package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
  244. package/.agents/workflows/helpers/deliver-story.md +209 -118
  245. package/.agents/workflows/helpers/parallel-tooling.md +2 -2
  246. package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
  247. package/.agents/workflows/plan.md +239 -19
  248. package/.agents/workflows/qa-assist.md +6 -6
  249. package/.agents/workflows/qa-explore.md +3 -3
  250. package/.agents/workflows/qa-run.md +1 -5
  251. package/bin/mandrel.js +12 -1
  252. package/docs/CHANGELOG.md +62 -0
  253. package/lib/cli/registry.js +262 -19
  254. package/lib/cli/sync-agents.js +157 -0
  255. package/lib/cli/sync-commands.js +115 -6
  256. package/lib/cli/sync.js +168 -6
  257. package/lib/cli/update.js +105 -8
  258. package/lib/cli/version-helpers.js +131 -0
  259. package/lib/migrations/README.md +7 -5
  260. package/lib/migrations/index.js +17 -9
  261. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  262. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  263. package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +154 -0
  264. package/package.json +2 -2
  265. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  266. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  267. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  268. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  269. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  270. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  271. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  272. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  273. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  274. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  275. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  276. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  277. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  278. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  279. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  280. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  281. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  282. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  283. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  284. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  285. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  286. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  287. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  288. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  289. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  290. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  291. package/.agents/schemas/risk-verdict.schema.json +0 -53
  292. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  293. package/.agents/scripts/analyze-execution.js +0 -444
  294. package/.agents/scripts/check-prepush-recovery.js +0 -90
  295. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  296. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  297. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
  298. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  299. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  300. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  301. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
  302. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
  303. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  304. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  305. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  306. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  307. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  308. package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
  309. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  310. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  311. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  312. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
  313. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  314. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  315. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  316. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
  317. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  318. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
  319. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  320. package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
  321. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
  322. package/.agents/scripts/resolve-plan-run.js +0 -117
  323. package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
@@ -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
  }
@@ -8,12 +8,16 @@ live in this folder**.
8
8
 
9
9
  ## Files here
10
10
 
11
- - [`merge-watcher.js`](./merge-watcher.js) — polls `gh pr view` after
12
- auto-merge is armed until the PR's `mergeCommit` is observed, then emits
13
- the merge-confirmed event.
14
11
  - [`watcher.js`](./watcher.js) — the CI-poll loop (`watchPrToTerminal`)
15
12
  driven by `pr-watch-with-update.js`.
16
13
 
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
+
17
21
  Other close-tail side effects (ledger write, finalize/PR open, automerge
18
22
  arm, branch cleanup, label transition) are owned by the Story delivery
19
23
  path (`helpers/deliver-story` / `single-story-close.js`) rather than a
@@ -5,10 +5,9 @@
5
5
  *
6
6
  * Subscribes to:
7
7
  * - `pr.created` → resolve the required-check names from GitHub at
8
- * runtime via `gh pr checks <pr> --required`, emit
9
- * `epic.watch.start` carrying that list, poll until every check
10
- * reaches a terminal state (or a per-listener wall-clock deadline
11
- * expires), then emit `epic.watch.end` with the outcome map.
8
+ * runtime via `gh pr checks <pr> --required`, then poll until every
9
+ * check reaches a terminal state (or a per-listener wall-clock
10
+ * deadline expires), recording the outcome in `classifications`.
12
11
  *
13
12
  * Critical contract:
14
13
  * - Required-check **names** are resolved from `gh pr checks` at
@@ -28,11 +27,13 @@
28
27
  * re-runs the poll loop (which is itself idempotent: the outcome map
29
28
  * always reflects the live GitHub state).
30
29
  *
31
- * Side-effect firewall: the listener emits on the bus and shells out
32
- * to `gh`. It does NOT mutate ticket labels, post comments, or call
33
- * `notify` those listeners receive `epic.watch.end` (via the
34
- * downstream AutomergePredicate epic.merge.ready/blocked chain) and
35
- * own their own side effects.
30
+ * Side-effect firewall: the listener shells out to `gh` and records
31
+ * its outcome in the in-memory `classifications` log. It does NOT
32
+ * mutate ticket labels, post comments, call `notify`, or emit on the
33
+ * bus the production consumer (`pr-watch-with-update.js`) drives
34
+ * `watchPrToTerminal` directly without a bus, and the retired
35
+ * `epic.watch.start` / `epic.watch.end` bus events went with the
36
+ * Epic-orchestration stratum.
36
37
  */
37
38
 
38
39
  import { spawnSync } from 'node:child_process';
@@ -40,20 +41,20 @@ import { spawnSync } from 'node:child_process';
40
41
  import { parsePrNumberFromUrl } from '../../../github-url.js';
41
42
 
42
43
  /**
43
- * Map `gh pr checks` `state` values to the canonical outcome enum on
44
- * the `epic.watch.end` schema, with a fourth `'pending'` sentinel for
45
- * in-flight checks. Pure — exported for tests so the pin is explicit
46
- * and reviewable.
44
+ * Map `gh pr checks` `state` values to the canonical lowercase outcome
45
+ * vocabulary (`success` | `failure` | `timed_out` | `skipped`), with a
46
+ * fourth `'pending'` sentinel for in-flight checks. Pure — exported for
47
+ * tests so the pin is explicit and reviewable.
47
48
  *
48
49
  * `gh` returns capitalized SCREAMING_SNAKE values (`SUCCESS`,
49
- * `FAILURE`, `TIMED_OUT`, etc.); the schema enum is lowercase. An
50
- * empty / queued / in_progress state collapses to `'pending'` so the
51
- * poll loop can distinguish "still running" from terminal outcomes.
52
- * `'pending'` is intentionally NOT in the schema enum — `reduceOutcomes`
53
- * is called only on the live state, and the final emit gates on
54
- * `allTerminal()` so no `'pending'` ever leaks into `epic.watch.end`.
55
- * Unknown / non-pending unrecognized values collapse to `'skipped'`
56
- * so any future GitHub state we haven't enumerated still validates.
50
+ * `FAILURE`, `TIMED_OUT`, etc.). An empty / queued / in_progress state
51
+ * collapses to `'pending'` so the poll loop can distinguish "still
52
+ * running" from terminal outcomes. `'pending'` is intentionally NOT a
53
+ * terminal outcome — `reduceOutcomes` is called only on the live state,
54
+ * and the final outcome map gates on `allTerminal()` so no `'pending'`
55
+ * ever leaks into a finished watch. Unknown / non-pending unrecognized
56
+ * values collapse to `'skipped'` so any future GitHub state we haven't
57
+ * enumerated still maps into the vocabulary.
57
58
  */
58
59
  /**
59
60
  * The raw check-state tokens `normalizeCheckState` recognizes (lowercased).
@@ -264,8 +265,8 @@ export function parseGhPrChecks(stdout) {
264
265
  }
265
266
 
266
267
  /**
267
- * Reduce a list of check entries to the `{ checkName: outcome }` map
268
- * the `epic.watch.end` schema expects. Pure — exported for tests.
268
+ * Reduce a list of check entries to the canonical `{ checkName: outcome }`
269
+ * map. Pure — exported for tests.
269
270
  *
270
271
  * When `gh` returns the same `name` more than once (parallel matrix
271
272
  * builds, retries), the LAST entry wins. The poll loop calls this on
@@ -431,9 +432,9 @@ export async function pollUntilTerminal({
431
432
  * @param {{status:number,stdout:string,stderr:string}} [opts.firstProbe]
432
433
  * Optional already-issued `gh pr checks` result. When the caller (the
433
434
  * `Watcher` listener) has already probed once to resolve the required
434
- * names for `epic.watch.start`, it threads that result here so the
435
- * loop does not double-spend the first `gh pr checks` call. Omit it
436
- * (the CLI path) and the loop issues the first probe itself.
435
+ * check names, it threads that result here so the loop does not
436
+ * double-spend the first `gh pr checks` call. Omit it (the CLI path)
437
+ * and the loop issues the first probe itself.
437
438
  * @returns {Promise<{
438
439
  * outcomes: object,
439
440
  * requiredChecks: string[],
@@ -466,8 +467,8 @@ export async function watchPrToTerminal({
466
467
  firstProbe,
467
468
  }) {
468
469
  // First probe: resolve the required-check name set at runtime. Reuse a
469
- // caller-supplied probe (the listener already issued one for
470
- // `epic.watch.start`) so we never double-spend the first `gh` call.
470
+ // caller-supplied probe (the listener already issued one to resolve the
471
+ // required check names) so we never double-spend the first `gh` call.
471
472
  const first = firstProbe ?? ghPrChecksFn({ prUrl, cwd });
472
473
  // `gh` exits 8 when checks are still pending; this is expected and
473
474
  // does not indicate failure. Any other non-zero status with no
@@ -619,12 +620,8 @@ export class Watcher {
619
620
  * @param {{ info?: Function, warn?: Function, debug?: Function }} [opts.logger]
620
621
  */
621
622
  constructor(opts = {}) {
622
- if (
623
- !opts.bus ||
624
- typeof opts.bus.on !== 'function' ||
625
- typeof opts.bus.emit !== 'function'
626
- ) {
627
- throw new TypeError('Watcher requires a bus with on() and emit()');
623
+ if (!opts.bus || typeof opts.bus.on !== 'function') {
624
+ throw new TypeError('Watcher requires a bus with on()');
628
625
  }
629
626
  this.bus = opts.bus;
630
627
  this.cwd = opts.cwd ?? process.cwd();
@@ -688,11 +685,10 @@ export class Watcher {
688
685
  return;
689
686
  }
690
687
 
691
- // First probe: resolve the required-check name set at runtime, so we
692
- // can carry the list on `epic.watch.start` BEFORE the (potentially
693
- // long) poll loop runs. We thread this probe into `watchPrToTerminal`
694
- // (via `firstProbe`) so the shared loop reuses it instead of
695
- // double-spending the first `gh pr checks` call.
688
+ // First probe: resolve the required-check name set at runtime BEFORE
689
+ // the (potentially long) poll loop runs. We thread this probe into
690
+ // `watchPrToTerminal` (via `firstProbe`) so the shared loop reuses it
691
+ // instead of double-spending the first `gh pr checks` call.
696
692
  const first = this.ghPrChecksFn({ prUrl, cwd: this.cwd });
697
693
  const firstEntries = parseGhPrChecks(first.stdout);
698
694
  if (firstEntries.length === 0 && first.status !== 0 && first.status !== 8) {
@@ -710,45 +706,24 @@ export class Watcher {
710
706
 
711
707
  const requiredChecks = firstEntries.map((e) => e.name);
712
708
 
713
- try {
714
- await this.bus.emit('epic.watch.start', { prUrl, requiredChecks });
715
- } catch (err) {
716
- this.classifications.push({
717
- event,
718
- seqId,
719
- outcome: 'failed',
720
- reason: `start-emit-failed:${err?.message ?? err}`,
721
- });
722
- this.logger.warn?.(
723
- `[Watcher] epic.watch.start emit failed: ${err?.message ?? err}`,
724
- );
725
- return;
726
- }
727
-
728
709
  // Delegate the poll + BEHIND-recovery loop to the shared plain
729
710
  // primitive so the CLI (`pr-watch-with-update.js`) and this listener
730
711
  // run identical logic.
731
- const {
732
- outcomes: emitOutcomes,
733
- polls,
734
- updatesApplied,
735
- resumesApplied,
736
- terminal,
737
- stillRunning,
738
- } = await watchPrToTerminal({
739
- prUrl,
740
- cwd: this.cwd,
741
- maxPolls: this.maxPolls,
742
- maxUpdates: this.maxUpdates,
743
- maxResumes: this.maxResumes,
744
- pollIntervalMs: this.pollIntervalMs,
745
- ghPrChecksFn: this.ghPrChecksFn,
746
- ghPrViewFn: this.ghPrViewFn,
747
- ghPrUpdateBranchFn: this.ghPrUpdateBranchFn,
748
- sleepFn: this.sleepFn,
749
- logger: this.logger,
750
- firstProbe: first,
751
- });
712
+ const { polls, updatesApplied, resumesApplied, terminal, stillRunning } =
713
+ await watchPrToTerminal({
714
+ prUrl,
715
+ cwd: this.cwd,
716
+ maxPolls: this.maxPolls,
717
+ maxUpdates: this.maxUpdates,
718
+ maxResumes: this.maxResumes,
719
+ pollIntervalMs: this.pollIntervalMs,
720
+ ghPrChecksFn: this.ghPrChecksFn,
721
+ ghPrViewFn: this.ghPrViewFn,
722
+ ghPrUpdateBranchFn: this.ghPrUpdateBranchFn,
723
+ sleepFn: this.sleepFn,
724
+ logger: this.logger,
725
+ firstProbe: first,
726
+ });
752
727
 
753
728
  // `still-running` (slow CI, not red) is a distinct classification from
754
729
  // a genuine `timed-out` — reserved for a check that never went
@@ -768,16 +743,6 @@ export class Watcher {
768
743
  resumesApplied,
769
744
  requiredChecks: requiredChecks.length,
770
745
  });
771
- try {
772
- await this.bus.emit('epic.watch.end', {
773
- prUrl,
774
- checkOutcomes: emitOutcomes,
775
- });
776
- } catch (err) {
777
- this.logger.warn?.(
778
- `[Watcher] epic.watch.end emit failed (swallowed): ${err?.message ?? err}`,
779
- );
780
- }
781
746
  }
782
747
 
783
748
  reset() {
@@ -40,28 +40,17 @@ function formatClock(iso) {
40
40
  * mapping is stable across runs.
41
41
  */
42
42
  const PHASE_BY_PREFIX = Object.freeze({
43
- 'epic.snapshot': 'Snapshot',
44
- 'epic.plan': 'Plan',
45
43
  'story.dispatch': 'Waves',
46
44
  'story.merged': 'Waves',
47
45
  'story.blocked': 'Waves',
48
- 'epic.blocked': 'Waves',
49
- 'epic.close': 'Close-tail',
50
- 'acceptance.reconcile': 'Acceptance Reconciliation',
51
- 'epic.finalize': 'Finalize',
52
46
  'pr.created': 'Finalize',
53
- 'epic.watch': 'Watch',
54
- 'epic.automerge': 'Automerge',
55
- 'epic.merge': 'Automerge',
56
- 'epic.cleanup': 'Cleanup',
57
- 'epic.complete': 'Complete',
58
47
  'notification.emitted': 'Notifications',
59
48
  'checkpoint.written': 'Checkpoint',
60
49
  });
61
50
 
62
51
  function phaseFor(eventName) {
63
- // Match longest prefix first so `epic.snapshot.start` resolves before
64
- // `epic.snapshot` would match an unrelated `epic.*` block.
52
+ // Match longest prefix first so `story.dispatch.start` resolves before
53
+ // `story.dispatch` would match an unrelated `story.*` block.
65
54
  const keys = Object.keys(PHASE_BY_PREFIX).sort((a, b) => b.length - a.length);
66
55
  for (const k of keys) {
67
56
  if (eventName === k || eventName.startsWith(`${k}.`)) {
@@ -277,7 +266,7 @@ export function render(ledger, opts = {}) {
277
266
  *
278
267
  * The wildcard-firewall rule (Tech Spec § Bus contract) requires that
279
268
  * trace observers do NOT import any module that mutates GitHub state,
280
- * the worktree, or the filesystem outside `temp/epic-<id>/`. This
269
+ * the worktree, or the filesystem outside `temp/run-<id>/`. This
281
270
  * module satisfies that constraint: the only filesystem writes are to
282
271
  * the companion path under the same temp directory the ledger lives
283
272
  * in.