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
@@ -3,7 +3,10 @@ import { detectCycle } from '../Graph.js';
3
3
  import { gitSpawn } from '../git-utils.js';
4
4
 
5
5
  import { Logger } from '../Logger.js';
6
- import { parse as parseStoryBody } from '../story-body/story-body.js';
6
+ import {
7
+ parse as parseStoryBody,
8
+ StoryBodyParseError,
9
+ } from '../story-body/story-body.js';
7
10
  import { validateStoryFileAssumptions } from './file-assumptions.js';
8
11
  import {
9
12
  computeConflictFindings,
@@ -11,14 +14,9 @@ import {
11
14
  } from './ticket-validator-conflicts.js';
12
15
  import {
13
16
  computeSizingFindings,
14
- DEFAULT_TASK_SIZING,
15
17
  renderHardFindingError,
16
18
  } from './ticket-validator-sizing.js';
17
19
 
18
- // Re-exported for callers that want the constants without reaching into the
19
- // sizing helper module directly.
20
- export { DEFAULT_TASK_SIZING };
21
-
22
20
  /**
23
21
  * Regex matching code-asset paths the freshness gate cares about. The three
24
22
  * roots — `.agents/scripts`, `lib`, and `tests` — cover the executable surface
@@ -48,6 +46,95 @@ function collectPathsFromText(text, paths) {
48
46
  }
49
47
  }
50
48
 
49
+ /**
50
+ * Parse a Story's serialized markdown body, translating a
51
+ * `StoryBodyParseError` into a `ValidationError` that names the offending
52
+ * **section** and **entry** (Story #4541).
53
+ *
54
+ * `StoryBodyParseError` already carries `field` (the section the parser was
55
+ * reading) and `raw` (the entry text that failed); this lifts both into an
56
+ * operator-legible message and a structured `violation` payload so an
57
+ * authoring loop can point at the exact bullet instead of re-deriving it
58
+ * from a downstream freshness miss.
59
+ *
60
+ * @param {object} story Story whose `body` is a non-empty markdown string.
61
+ * @returns {object} The structured body.
62
+ * @throws {ValidationError} `code: 'story-body-unparseable'`.
63
+ */
64
+ function parseStoryBodyOrThrow(story) {
65
+ try {
66
+ return parseStoryBody(story.body).body;
67
+ } catch (err) {
68
+ if (!(err instanceof StoryBodyParseError)) throw err;
69
+ const slug = story.slug ?? '<unknown>';
70
+ const section = err.field ?? 'body';
71
+ const entry = err.raw ?? null;
72
+ const entryLine = entry === null ? '' : `\n entry: ${entry}`;
73
+ const violation = { slug, section, entry, reason: err.message };
74
+ const error = new ValidationError(
75
+ `Cross-Validation Failed: Story "${slug}" has an unparseable body — ` +
76
+ `the ## ${section} section could not be read: ${err.message}` +
77
+ `${entryLine}\n\nFix the offending entry; this is a malformed body, ` +
78
+ 'not a stale path reference.',
79
+ { violations: [violation] },
80
+ );
81
+ error.code = 'story-body-unparseable';
82
+ error.violations = [violation];
83
+ throw error;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Refuse the plan when any Story's serialized body cannot be parsed, before
89
+ * either git-probe gate runs (Story #4541). Ordering matters: the freshness
90
+ * gate consults `body.changes` for its net-new whitelist, so an unparseable
91
+ * body used to reach the operator as a freshness miss naming declared paths.
92
+ *
93
+ * @param {{ tickets: object[] }} opts
94
+ * @throws {ValidationError} `code: 'story-body-unparseable'` on the first
95
+ * offending Story.
96
+ */
97
+ function assertStoryBodiesParse({ tickets }) {
98
+ for (const story of (tickets ?? []).filter((t) => t.type === 'story')) {
99
+ if (typeof story.body !== 'string' || story.body.trim().length === 0) {
100
+ continue;
101
+ }
102
+ parseStoryBodyOrThrow(story);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Resolve every acceptance line a Story declares, across both authoring
108
+ * shapes (Story #4541).
109
+ *
110
+ * The canonical shape is a **serialized string body** with the criteria at
111
+ * the ticket's **top level** — the machine contract persist syncs into the
112
+ * body. `validateAcceptanceSubjectPrefix` used to read `body.acceptance` on
113
+ * an object body only, so on every real plan it scanned nothing and the gate
114
+ * silently passed. Union both sources (deduplicated) so the gate fires on
115
+ * whichever surface the author used.
116
+ *
117
+ * @param {object} story
118
+ * @returns {string[]}
119
+ */
120
+ function resolveAcceptanceLines(story) {
121
+ const lines = new Set();
122
+ if (Array.isArray(story?.acceptance)) {
123
+ for (const item of story.acceptance) lines.add(String(item ?? ''));
124
+ }
125
+ const body = story?.body;
126
+ let bodyAcceptance = null;
127
+ if (typeof body === 'string' && body.trim().length > 0) {
128
+ bodyAcceptance = parseStoryBodyOrThrow(story).acceptance;
129
+ } else if (body !== null && typeof body === 'object') {
130
+ bodyAcceptance = body.acceptance;
131
+ }
132
+ if (Array.isArray(bodyAcceptance)) {
133
+ for (const item of bodyAcceptance) lines.add(String(item ?? ''));
134
+ }
135
+ return [...lines];
136
+ }
137
+
51
138
  function collectTaskPathReferences(task) {
52
139
  const paths = new Set();
53
140
  const body = task.body;
@@ -87,7 +174,7 @@ function collectTaskPathReferences(task) {
87
174
  * path out of the prose.
88
175
  * 3. **Object form** — `{ path: "<path>", assumption: "creates" | ... }`,
89
176
  * introduced by Story #2636 as the canonical declaration shape and
90
- * documented in `epic-plan-decompose-author/SKILL.md`. The path is
177
+ * documented in `lib/templates/decomposer-prompts.js`. The path is
91
178
  * trusted verbatim.
92
179
  *
93
180
  * Only `body.changes` (and `body.references`) is consulted —
@@ -105,15 +192,16 @@ function collectTaskChangesPaths(task) {
105
192
  // arrays before scanning. Without this, a string body causes the
106
193
  // object-form branch below to fall through on every item, leaving the
107
194
  // freshness gate blind to declared paths.
195
+ //
196
+ // Story #4541: a parse failure is NOT swallowed here. Swallowing it
197
+ // returned an empty whitelist, so a single malformed `## Changes` entry
198
+ // surfaced downstream as "files do not exist at main" naming the very
199
+ // paths the Story *had* declared — a misdiagnosis that cost two authoring
200
+ // round-trips. `assertStoryBodiesParse` runs before the freshness gate and
201
+ // owns that failure with a named error; the throw here is the same error
202
+ // for any caller that drives `validateAcFreshness` directly.
108
203
  if (typeof body === 'string' && body.trim().length > 0) {
109
- let parsed;
110
- try {
111
- parsed = parseStoryBody(body).body;
112
- } catch {
113
- // Unparseable body — no paths to whitelist; the freshness gate will
114
- // catch any real references in the text scan below.
115
- return paths;
116
- }
204
+ const parsed = parseStoryBodyOrThrow(task);
117
205
  for (const arrName of ['changes', 'references']) {
118
206
  const arr = parsed[arrName];
119
207
  if (!Array.isArray(arr)) continue;
@@ -312,11 +400,16 @@ const SUBJECT_PREFIX_RE = /Commit subject begins with ['"`]([^'"`]+):['"`]/g;
312
400
  * the form `baseline-refresh` is rejected because no Conventional-Commits
313
401
  * type starts with that token.
314
402
  *
315
- * Only `body.acceptance[]` is scanned; `body.goal` / `body.verify` /
403
+ * Only acceptance criteria are scanned; `body.goal` / `body.verify` /
316
404
  * `body.changes` are not commit-subject prescriptions by convention and
317
405
  * scanning them would surface false positives from prose that happens to
318
406
  * quote a forbidden prefix while explaining why it's forbidden.
319
407
  *
408
+ * Both authoring shapes are covered (Story #4541): the canonical top-level
409
+ * `acceptance[]` on a serialized string body, and the pre-serialize
410
+ * `body.acceptance[]` object shape. Scanning only the latter made the gate
411
+ * inert on every real plan.
412
+ *
320
413
  * @param {object} opts
321
414
  * @param {object[]} opts.tickets - Validated ticket hierarchy.
322
415
  * @throws {ValidationError} when one or more Story acceptance items
@@ -329,11 +422,7 @@ export function validateAcceptanceSubjectPrefix({ tickets }) {
329
422
  const violations = [];
330
423
  const stories = (tickets ?? []).filter((t) => t.type === 'story');
331
424
  for (const story of stories) {
332
- const body = story.body;
333
- if (body === null || typeof body !== 'object') continue;
334
- if (!Array.isArray(body.acceptance)) continue;
335
- for (const item of body.acceptance) {
336
- const line = String(item ?? '');
425
+ for (const line of resolveAcceptanceLines(story)) {
337
426
  // Reset the global regex between iterations.
338
427
  SUBJECT_PREFIX_RE.lastIndex = 0;
339
428
  let match = SUBJECT_PREFIX_RE.exec(line);
@@ -386,8 +475,8 @@ function renderMissLine({ slug, path }) {
386
475
  * The returned tickets array carries two extra non-array properties:
387
476
  * - `findings` — structured sizing findings (hard + soft) keyed by the
388
477
  * three-layer sizing model. The bounded re-decomposition loop in
389
- * `epic-plan-decompose` reads `findings.filter(f => f.severity === 'hard')`
390
- * to decide whether to re-prompt.
478
+ * `/plan` reads `findings.filter(f => f.severity === 'hard')` to decide
479
+ * whether to re-prompt.
391
480
  * - `errors` — human-readable strings, one per hard finding. Non-empty
392
481
  * `errors[]` is the AC-visible "block normalization" signal; the legacy
393
482
  * hierarchy/cycle/freshness checks continue to throw, so callers that
@@ -399,7 +488,7 @@ function renderMissLine({ slug, path }) {
399
488
  * @param {string} [opts.baseBranchRef] - When set, runs `validateAcFreshness` against this ref.
400
489
  * @param {Function} [opts.gitRunner] - Optional git probe override.
401
490
  * @param {string} [opts.cwd] - Repo cwd (forwarded to the freshness gate).
402
- * @param {object} [opts.taskSizing] - Override the three-layer sizing thresholds. Defaults to `DEFAULT_TASK_SIZING`.
491
+ * @param {object} [opts.modelCapacity] - Programmatic override of `DEFAULT_MODEL_CAPACITY` (tests only not read from `.agentrc.json`).
403
492
  * @param {object} [opts.conflictPolicy] - Severity controls for cross-Story conflict findings.
404
493
  * @param {boolean} [opts.conflictPolicy.failOnSharedEditors=false] - Upgrade `shared-editor` findings to `hard`.
405
494
  * @param {boolean} [opts.conflictPolicy.requireExplicitCrossStoryDeps=false] - Upgrade `implicit-cross-story-dep` findings to `hard`.
@@ -545,6 +634,14 @@ export function validateAndNormalizeTickets(tickets, opts = {}) {
545
634
 
546
635
  assertAcyclic(slugAdjacency);
547
636
 
637
+ // Story #4541 — refuse an unparseable Story body up front, with a named
638
+ // error pointing at the offending section + entry. Must precede both the
639
+ // subject-prefix scan and the freshness gate: each parses the body, and
640
+ // the freshness gate's net-new whitelist comes from `body.changes`, so a
641
+ // malformed body used to surface as a stale-path miss naming the paths the
642
+ // Story had legitimately declared.
643
+ assertStoryBodiesParse({ tickets });
644
+
548
645
  // Reject any Task acceptance item that prescribes a non-Conventional-Commits
549
646
  // subject prefix (e.g. legacy "Commit subject begins with 'baseline-refresh:'"
550
647
  // from pre-Epic-#2501 planner output). Runs before the freshness gate so
@@ -614,7 +711,7 @@ export function validateAndNormalizeTickets(tickets, opts = {}) {
614
711
 
615
712
  const sizingFindings = computeSizingFindings({
616
713
  stories,
617
- sizing: opts.taskSizing,
714
+ capacity: opts.modelCapacity,
618
715
  });
619
716
  // Cross-Story path-conflict pass observes the story-level depends_on
620
717
  // graph. Findings are appended to the same `findings` array consumed by
@@ -653,6 +750,7 @@ export function validateAndNormalizeTickets(tickets, opts = {}) {
653
750
 
654
751
  // Internal helpers exposed for unit tests; not part of the public surface.
655
752
  export const _internal = {
753
+ assertStoryBodiesParse,
656
754
  indexTicketsBySlug,
657
755
  assertAllTicketsAreStories,
658
756
  assertEveryStoryHasInlineContract,
@@ -20,7 +20,6 @@
20
20
  */
21
21
 
22
22
  import { Logger } from '../../Logger.js';
23
- import { TYPE_LABELS } from '../../label-constants.js';
24
23
  import { ALL_STATES, STATE_LABELS } from './reads.js';
25
24
  import {
26
25
  postStructuredComment,
@@ -290,30 +289,6 @@ async function processCascadeParentLocked(
290
289
  );
291
290
  if (!allDone) return { cascadedTo, failed };
292
291
 
293
- // EXCLUSION: Epics do not auto-close via cascade. Epics close via
294
- // formal /deliver (its own machinery handles branch merges,
295
- // PR-driven `Closes #N` auto-close, and a recovery transition in
296
- // `epic-deliver-finalize.js`).
297
- //
298
- // Legacy planning tickets (pre-#4324 `context::*` artifacts on
299
- // historical Epics) DO close via cascade (Story #1951): leaving
300
- // them open as native sub-issues of the Epic blocks GitHub from
301
- // honoring the Epic's `Closes #N` footer. New Epics carry their
302
- // planning content on the Epic body itself, so this branch is
303
- // purely a legacy-hygiene path.
304
- //
305
- // Reuse the parentSnapshot from the idempotency check above — it is
306
- // a fresh read (cache was invalidated before the getTicket call) and
307
- // the parent's type label is invariant within a single cascade lock
308
- // hold, so a second getTicket is wasteful and redundant.
309
- const isEpic = parentSnapshot.labels.includes(TYPE_LABELS.EPIC);
310
- if (isEpic) {
311
- logger.warn(
312
- `[Ticketing] Cascade reached Epic #${parentId}. Skipping auto-close (Epics close via the operator's PR merge).`,
313
- );
314
- return { cascadedTo, failed };
315
- }
316
-
317
292
  // Retry the parent transition on transient `gh` failures (rate limit,
318
293
  // 5xx, transport timeouts). Permanent failures fall through to the
319
294
  // outer catch on the first attempt so the operator sees the real
@@ -382,7 +357,7 @@ async function processCascadeParentLocked(
382
357
  * unset so the module-level {@link Logger} is used.
383
358
  * @returns {Promise<{ cascadedTo: number[], failed: Array<{ parentId: number, error: string }> }>}
384
359
  */
385
- export async function cascadeCompletion(provider, ticketId, opts = {}) {
360
+ async function cascadeCompletion(provider, ticketId, opts = {}) {
386
361
  const ticket = await provider.getTicket(ticketId);
387
362
 
388
363
  // Determine if this ticket is agent::done
@@ -390,43 +365,14 @@ export async function cascadeCompletion(provider, ticketId, opts = {}) {
390
365
  return { cascadedTo: [], failed: [] };
391
366
  }
392
367
 
368
+ // Story #4545 — one strategy, not three. The `parent: #N` body footer and
369
+ // the native sub-issue link were both written by `createTicket`, the
370
+ // Epic-hierarchy write surface deleted in the same Story; with no writer,
371
+ // the body regex could only ever miss and the native lookup could only
372
+ // ever spend an API call to learn the same. The operator-settable `blocks`
373
+ // annotation is the one parent edge that can still exist.
393
374
  const { blocks: parentIds } = await provider.getTicketDependencies(ticketId);
394
-
395
- // Fallback: parse `parent: #NNN` from the body when `blocks` syntax isn't used (C-5).
396
- let parsedParents = parentIds;
397
- if (!parsedParents || parsedParents.length === 0) {
398
- const parentMatch = ticket.body
399
- ? [...ticket.body.matchAll(/parent:\s*#(\d+)/gi)]
400
- : [];
401
- parsedParents = parentMatch.map((m) => Number.parseInt(m[1], 10));
402
- }
403
-
404
- // Story #2982 — third fallback: GitHub's native Sub-Issues API. The
405
- // resume reconciler can strip the `parent: #N` orchestrator footer
406
- // from a Story body (see Issue 2 in #2982); without the body marker
407
- // the cascade silently returned `{ cascadedTo: [], failed: [] }` and
408
- // left intermediate parent tickets stranded OPEN. The native link is
409
- // independent of body text, so consult it when the first two
410
- // strategies came back empty.
411
- if (
412
- parsedParents.length === 0 &&
413
- typeof provider._getNativeParent === 'function' &&
414
- ticket.nodeId
415
- ) {
416
- try {
417
- const nativeParent = await provider._getNativeParent(
418
- ticket.nodeId,
419
- ticketId,
420
- );
421
- if (typeof nativeParent === 'number') {
422
- parsedParents = [nativeParent];
423
- }
424
- } catch (err) {
425
- Logger.warn(
426
- `[cascadeCompletion] native parent lookup failed for #${ticketId}: ${err.message}`,
427
- );
428
- }
429
- }
375
+ const parsedParents = Array.isArray(parentIds) ? parentIds : [];
430
376
 
431
377
  if (parsedParents.length === 0) {
432
378
  return { cascadedTo: [], failed: [] };
@@ -562,22 +508,18 @@ export async function cascadeParentState(provider, ticketId, opts = {}) {
562
508
  }
563
509
 
564
510
  /**
565
- * Resolve the parent issue ids for a ticket: native `blocks:` dependency
566
- * annotations first, then `parent: #NNN` body references as a fallback.
567
- * Mirrors the resolution path used by {@link cascadeCompletion}.
511
+ * Resolve the parent issue ids for a ticket from its `blocks:` dependency
512
+ * annotations. Mirrors the resolution path used by {@link cascadeCompletion}
513
+ * see there for why the `parent: #NNN` body fallback is gone (Story #4545).
568
514
  *
569
515
  * @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
570
- * @param {object} ticket
516
+ * @param {object} _ticket
571
517
  * @param {number} ticketId
572
518
  * @returns {Promise<number[]>}
573
519
  */
574
- async function resolveParentIds(provider, ticket, ticketId) {
520
+ async function resolveParentIds(provider, _ticket, ticketId) {
575
521
  const { blocks: parentIds } = await provider.getTicketDependencies(ticketId);
576
- if (Array.isArray(parentIds) && parentIds.length > 0) return parentIds;
577
- const parentMatch = ticket?.body
578
- ? [...ticket.body.matchAll(/parent:\s*#(\d+)/gi)]
579
- : [];
580
- return parentMatch.map((m) => Number.parseInt(m[1], 10));
522
+ return Array.isArray(parentIds) ? parentIds : [];
581
523
  }
582
524
 
583
525
  /**
@@ -12,7 +12,8 @@
12
12
  */
13
13
 
14
14
  import { AGENT_LABELS } from '../../label-constants.js';
15
- import { WAVE_MARKER_RE } from '../wave-marker.js';
15
+
16
+ const WAVE_MARKER_RE = /^wave-([0-9]{1,3})-(start|end)$/;
16
17
 
17
18
  /**
18
19
  * Canonical agent-state label triad used by every state mutator. Kept on
@@ -23,7 +24,7 @@ export const STATE_LABELS = {
23
24
  READY: AGENT_LABELS.READY,
24
25
  EXECUTING: AGENT_LABELS.EXECUTING,
25
26
  // Story #2144 — intermediate state held by a Story between successful
26
- // close-preflight and a confirmed merge into `epic/<id>`. Included in
27
+ // close-preflight and a confirmed Story PR merge into `main`. Included in
27
28
  // the state enum so `transitionTicketState` can apply the label via
28
29
  // the canonical one-state-at-a-time path (which removes every other
29
30
  // `agent::*` label in the same call) and so the read-side `ALL_STATES`
@@ -72,35 +73,24 @@ export const STRUCTURED_COMMENT_TYPES = Object.freeze([
72
73
  'verification-results',
73
74
  'retro',
74
75
  'retro-partial',
76
+ // v2 Story closeout — actionable follow-ups from friction signals
77
+ 'follow-ups',
78
+ // v2 plan-run epilogue artifacts (posted on the primary Story)
79
+ 'plan-run-audit-roster',
80
+ 'plan-run-sibling-coherence',
75
81
  'epic-run-state',
76
82
  'epic-run-progress',
77
83
  'epic-plan-state',
78
84
  'parked-follow-ons',
79
- 'dispatch-manifest',
80
- // Story #566 — per-phase wall-clock summary posted by story-close
81
- // and consumed by the epic-runner progress reporter to surface median /
82
- // p95 phase timings across completed stories.
85
+ // Story #566 — per-phase wall-clock summary posted by single-story-close.js.
83
86
  'phase-timings',
84
87
  // Story #831 — story-init upserts a `story-init` comment that
85
88
  // surfaces `dependenciesInstalled` (and the underlying installStatus) so
86
89
  // downstream workflow steps don't have to infer install state from
87
90
  // node_modules presence.
88
91
  'story-init',
89
- // Story #908/deliver upserts a `story-run-progress` snapshot
90
- // on each Story per Task transition. The /deliver aggregator and
91
- // the epic-runner progress reporter both read this comment to derive
92
- // Story-level state without re-fetching ticket labels.
93
- 'story-run-progress',
94
- // Story #1123 — analyze-execution.js upserts perf summaries at close
95
- // time. Story-mode posts `story-perf-summary` on each Story; Epic-mode
96
- // posts `epic-perf-report` on the Epic. Both replace the legacy
97
- // per-Task `friction` fan-out and the standalone `phase-timings`
98
- // surface (Epic #1030).
99
- 'story-perf-summary',
100
- 'epic-perf-report',
101
- // Story #2128 — Phase 6 Epic Clarity Gate. `epic-plan-clarity.js` upserts
102
- // a `clarity-gate-update` comment on the Epic when the operator approves
103
- // a sharpened body rewrite, recording the persistence event for audit.
92
+ // Story #2128Phase 6 Epic Clarity Gate (CLI retired). Historical
93
+ // `clarity-gate-update` comments may still exist on older tickets.
104
94
  'clarity-gate-update',
105
95
  // Story #2635 — Tech Spec freshness check. `plan-persist.js`
106
96
  // upserts a `spec-freshness` comment on the Epic listing any
@@ -126,40 +116,34 @@ export const STRUCTURED_COMMENT_TYPES = Object.freeze([
126
116
  // ticket. Re-invocations upsert the same marker rather than appending
127
117
  // duplicates.
128
118
  'epic-handoff',
129
- // Story #2899 (Epic #2880, F13) — `epic-deliver-preflight.js` upserts a
130
- // `delivery-preflight` comment on the Epic at the start of
131
- // /deliver Phase 1, surfacing estimated story count, install cost,
119
+ // Story #2899 (Epic #2880, F13) — the deleted `epic-deliver-preflight.js`
120
+ // upserted a `delivery-preflight` comment on the Epic at the start of
121
+ // pre-v2 Epic `/deliver` Phase 1, surfacing estimated story count, install cost,
132
122
  // wave count, GitHub API request volume, Claude quota burn, and any
133
123
  // threshold breaches against `delivery.preflight.max*`. One entry per
134
124
  // Epic; re-runs replace prior content.
135
125
  'delivery-preflight',
136
- // Story #3062 (Epic #3051) — `wave-tick.js` upserts a
126
+ // Story #3062 (Epic #3051) — the deleted `wave-tick.js` upserted a
137
127
  // `recurring-failure-class` comment on the Epic when the cross-Story
138
128
  // detector finds two or more Stories that hit the same `failedGate` in
139
129
  // `close-validate.end`. One entry per Epic; re-ticks with the same
140
130
  // findings upsert in place (`upsertStructuredComment` diffs by body).
141
131
  'recurring-failure-class',
142
- // Story #3061 (Epic #3051) — the /deliver §2e Idle Watchdog
143
- // subsection instructs the parent host LLM to upsert a `wave-stall`
144
- // comment on the Epic whenever an in-flight Story has been silent for
145
- // longer than the configured cadence. `wave-tick.js --check-idle`
146
- // emits the matching envelope; registering the kind here is what makes
132
+ // Story #3061 (Epic #3051) — the pre-v2 `/deliver` idle-watchdog prose
133
+ // instructed the parent host LLM to upsert a `wave-stall` comment on the
134
+ // Epic whenever an in-flight Story had been silent for longer than the
135
+ // configured cadence. The deleted `wave-tick.js --check-idle` CLI emitted
136
+ // the matching envelope; registering the kind here keeps historical
147
137
  // the documented remediation actually executable
148
138
  // (assertValidStructuredCommentType would otherwise throw).
149
139
  'wave-stall',
150
- // Story #3873 (Epic #3865) — `plan-persist.js` upserts a `risk-verdict`
151
- // comment on the Epic at persist time, recording the planner-authored,
152
- // schema-validated risk verdict and the planningRisk envelope derived
153
- // from it (`deriveRiskEnvelope`). One entry per Epic; re-plans upsert in
154
- // place. Schema: `.agents/schemas/risk-verdict.schema.json`.
155
- 'risk-verdict',
156
- // Story #4019 `epic-plan-lease-guard.js` upserts a `plan-lease`
157
- // comment on the Epic at lease-acquire time, recording the claiming
158
- // operator and the claim timestamp. `/plan` emits no
159
- // `story.heartbeat`, so this claim-time is the liveness signal that
160
- // makes the documented `--steal` contract decidable: a foreign claim
161
- // older than the lease TTL is reclaimed automatically; a fresh one
162
- // refuses with the claim age. One entry per Epic; re-acquires upsert
140
+ // Story #4019 the plan-tier lease guard upserts a `plan-lease`
141
+ // comment at lease-acquire time, recording the claiming operator and the
142
+ // claim timestamp. Nothing emits a per-run liveness beat, so this
143
+ // claim-time is the only age signal a reader has when reasoning about the
144
+ // documented `--steal` contract. The guard itself fails closed regardless
145
+ // (it anchors liveness to `now`), so a stranded claim is cleared with
146
+ // `--steal`, not by TTL expiry. One entry per ticket; re-acquires upsert
163
147
  // in place.
164
148
  'plan-lease',
165
149
  // Story #4415 (Epic #4406) — the feedback-loop graduators
@@ -171,15 +155,21 @@ export const STRUCTURED_COMMENT_TYPES = Object.freeze([
171
155
  // `graduator="audit-results|code-review"` attr so the two graduators
172
156
  // upsert independent comments; re-runs upsert in place.
173
157
  'cross-repo-deferred',
174
- // Epic #4474 (PR3) — `plan-persist.js` upserts a single `plan-summary`
175
- // comment on the Epic at terminal persist success, carrying the risk /
176
- // routing / freshness / healthcheck receipts and the dry-run wave table
177
- // as closing text. Replaces the retired plan-time `dispatch-manifest`
178
- // comment (whose claimed consumer did not exist — the live manifest is
179
- // written at deliver time by `wave-record-io.js`) and the Phase 12
180
- // notify round-trip. One entry per Epic; a --force/--resume re-persist
181
- // upserts in place.
158
+ // Epic #4474 (PR3) / v2 Stage 3 — `plan-persist.js` upserts a single
159
+ // `plan-summary` comment on the primary Story at terminal persist
160
+ // success, carrying risk / routing receipts and the depends_on order
161
+ // table. One entry per plan; a re-persist upserts in place.
182
162
  'plan-summary',
163
+ // v2 Stage 3 — flat Story persist checkpoint on every created Story
164
+ // (replaces epic-plan-state for new plans). plan-summary stays primary-only.
165
+ 'story-plan-state',
166
+ // Story #4535 — `plan-persist.js` upserts a `superseded-by` comment on
167
+ // each `/plan --tickets` source issue at persist time, naming the single
168
+ // Story that claims it (plus any per-supersede note the plan authored),
169
+ // immediately before closing the issue as `not_planned`. Keying off this
170
+ // marker rather than a bare `postComment` is what makes a re-run
171
+ // non-double-commenting. One entry per source issue.
172
+ 'superseded-by',
183
173
  ]);
184
174
 
185
175
  export const WAVE_TYPE_PATTERN = WAVE_MARKER_RE;
@@ -265,8 +255,8 @@ export function structuredCommentMarker(type, attrs = null) {
265
255
  * recent `findStructuredComment` / `upsertStructuredComment` call.
266
256
  * Story #1795 — every Epic run owns its process for the duration of
267
257
  * the run, so a single seed-then-reuse window saves one
268
- * `getTicketComments` per repeat upsert on the hot path (wave-level
269
- * `story-run-progress`, `wave-N-end`, etc).
258
+ * `getTicketComments` per repeat upsert on the hot path (`story-init`,
259
+ * `verification-results`, etc).
270
260
  *
271
261
  * Lifecycle:
272
262
  * - First call to `findStructuredComment(provider, t, type, attrs)`
@@ -352,8 +342,8 @@ export function structuredCommentCacheKey(ticketId, type, attrs) {
352
342
  * array returned by the most recent `provider.getTicketComments(ticketId)`
353
343
  * call. Story #2465 — `findStructuredComment` is invoked back-to-back for
354
344
  * different `type` discriminators against the same ticket (e.g.
355
- * `story-run-progress` + `epic-run-progress` + a `friction` probe during
356
- * an Epic-close wave). Without this cache each lookup pays a full
345
+ * `story-init` + `verification-results` + a `friction` probe during a
346
+ * single close). Without this cache each lookup pays a full
357
347
  * pagination round-trip even when the prior call already fetched the
358
348
  * same comments. The structured-comment-id cache short-circuits *repeat*
359
349
  * lookups for the same `(type, attrs)` tuple but does not help across
@@ -37,6 +37,10 @@ import {
37
37
  eventSeverity,
38
38
  renderTransitionMessage,
39
39
  } from '../../notifications/notifier.js';
40
+ import {
41
+ emitRuntimeFriction,
42
+ RUNTIME_FRICTION_CATEGORIES,
43
+ } from '../../observability/runtime-friction.js';
40
44
  import { ColumnSync } from '../column-sync.js';
41
45
  import {
42
46
  ALL_STATES,
@@ -145,11 +149,11 @@ async function loadTicketSnapshot(provider, opts, ticketId) {
145
149
  /**
146
150
  * Mirror the post-flip label set onto the GitHub Projects v2 Status
147
151
  * column. Story #2548 — wiring this here makes every caller of
148
- * `transitionTicketState` (story-init, story-close, story-phase,
149
- * the LabelTransitioner lifecycle listener, the update-ticket-state CLI,
150
- * batch transitions) update the board automatically. Prior to #2548 the
151
- * sync was only wired from the epic-runner against the Epic ticket, so
152
- * Stories and Tasks never had their `agent::executing` /
152
+ * `transitionTicketState` (`single-story-init.js`, `single-story-close.js`,
153
+ * `story-phase.js`, the LabelTransitioner lifecycle listener, the
154
+ * update-ticket-state CLI, batch transitions) update the board automatically.
155
+ * Prior to #2548 the sync was only wired from the deleted epic-runner against
156
+ * the Epic ticket, so Stories and Tasks never had their `agent::executing` /
153
157
  * `agent::blocked` flips reflected on the board.
154
158
  *
155
159
  * Best-effort: a project-board misconfig, missing scope, or transient
@@ -279,6 +283,46 @@ function dispatchTransitionNotification(args) {
279
283
  });
280
284
  }
281
285
 
286
+ /**
287
+ * Emit a runtime-derived `friction` signal when a ticket parks at
288
+ * `agent::blocked` (Story #4578). No-op for every other target state.
289
+ *
290
+ * Why here: `agent::blocked` is the single runtime HITL pause point
291
+ * (`.agents/instructions.md` § 1.J), and this is its canonical mutator — so
292
+ * one hook catches every block regardless of which path drove it (the
293
+ * `merge.unlanded` and `merge.flip-failed` paths in
294
+ * `single-story-close/phases/confirm-merge.js`, the review-block path, an
295
+ * operator's `update-ticket-state.js`, a Story worker giving up). Before
296
+ * this, a block was only ever a *label* — it left no trace in the friction
297
+ * stream the retro reads, so a run could park a worker and still produce a
298
+ * zero-signal roll-up.
299
+ *
300
+ * This is also why the terminal-envelope hook deliberately skips `blocked`
301
+ * (see `frictionForTerminal`): the two would otherwise count one incident
302
+ * twice.
303
+ *
304
+ * Best-effort and awaited: `emitRuntimeFriction` swallows its own failures
305
+ * and resolves `false`, so this can neither throw nor block the transition.
306
+ * It is awaited rather than fire-and-forget because CLI entry points exit
307
+ * via `process.exit` as soon as `main` resolves (`cli-utils.runAsCli` with
308
+ * `propagateExitCode`), which would discard a still-pending append.
309
+ *
310
+ * @param {number} ticketId
311
+ * @param {string} newState
312
+ * @param {{ config?: object }} opts
313
+ * @returns {Promise<void>}
314
+ */
315
+ async function emitBlockedFriction(ticketId, newState, opts) {
316
+ if (newState !== STATE_LABELS.BLOCKED) return;
317
+ await emitRuntimeFriction({
318
+ storyId: ticketId,
319
+ category: RUNTIME_FRICTION_CATEGORIES.STORY_BLOCKED,
320
+ tool: 'transitionTicketState',
321
+ details: { toState: newState },
322
+ config: opts?.config,
323
+ });
324
+ }
325
+
282
326
  /**
283
327
  * Transitions a ticket's label to the new state.
284
328
  * Removes other agent:: state labels.
@@ -361,6 +405,10 @@ export async function transitionTicketState(
361
405
  _ticketSnapshot: ticketSnapshot,
362
406
  });
363
407
 
408
+ // Story #4578 — derive a friction signal from the block, at the point the
409
+ // runtime already knows. Best-effort; never blocks the transition.
410
+ await emitBlockedFriction(ticketId, newState, opts);
411
+
364
412
  // Story #2548 — mirror the new state onto the Projects v2 Status
365
413
  // column. Best-effort; never blocks the transition.
366
414
  // Story #3645 — thread the DIP seam so callers can inject a stub.
@@ -460,14 +508,25 @@ export async function toggleTasklistCheckbox(
460
508
  /**
461
509
  * Post a structured comment to a ticket.
462
510
  *
511
+ * Returns whatever the provider's `postComment` resolved to (Story #4543).
512
+ * Previously the result was swallowed, which made it impossible for a caller
513
+ * to reference the comment it had just written — the terminal envelope's
514
+ * `blocked.frictionCommentId` pointer needs exactly that, so an operator can
515
+ * be sent straight to the remediation instead of told to go find it. Callers
516
+ * that don't need the id simply ignore the return, so this is additive.
517
+ *
518
+ * The shape is provider-defined and may be `undefined` for providers that do
519
+ * not surface one; callers must treat the id as best-effort.
520
+ *
463
521
  * @param {import('../../ITicketingProvider.js').ITicketingProvider} provider
464
522
  * @param {number} ticketId
465
523
  * @param {'progress'|'friction'|'notification'} type
466
524
  * @param {string} payload
525
+ * @returns {Promise<unknown>} The provider's `postComment` result.
467
526
  */
468
527
  export async function postStructuredComment(provider, ticketId, type, payload) {
469
528
  assertValidStructuredCommentType(type);
470
- await provider.postComment(ticketId, {
529
+ const posted = await provider.postComment(ticketId, {
471
530
  type,
472
531
  body: payload,
473
532
  });
@@ -475,4 +534,5 @@ export async function postStructuredComment(provider, ticketId, type, payload) {
475
534
  // `findStructuredComment` against this ticket re-fetches and sees the
476
535
  // freshly-posted comment.
477
536
  invalidateRawCommentsCache(provider, ticketId);
537
+ return posted;
478
538
  }
@@ -26,7 +26,6 @@
26
26
  export {
27
27
  __resetParentCascadeLocks,
28
28
  __setCascadeRetryDelays,
29
- cascadeCompletion,
30
29
  logCascadePartialFailures,
31
30
  } from './ticketing/bulk.js';
32
31
  // Re-export the read surface.