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
@@ -4,15 +4,31 @@
4
4
  *
5
5
  * Ordered, fail-closed pipeline:
6
6
  *
7
- * 1. Risk-verdict presence (schema validation is CLI-owned)
8
- * 2. Ticket validator + file-assumption + DAG + capacity + budget
9
- * 3. Draft reachability (named soft failure, exit 3)
10
- * 4. Split-policy partition (`assertAcceptancePartition`) + spec fold/spill
11
- * 5. Create Story issues (`type::story` + `agent::ready`; `plan-run::`
12
- * label when N>1)
13
- * 6. Upsert `risk-verdict` + `story-plan-state` on every created Story;
14
- * upsert `plan-summary` on the primary Story
15
- * 7. Temp cleanup at terminal success only
7
+ * 1. Ticket validator + file-assumption + DAG + capacity + budget
8
+ * 2. Draft reachability (named soft failure, exit 3)
9
+ * 3. Split-policy partition (`assertAcceptancePartition`) + spec fold/spill
10
+ * 4. Create Story issues (`type::story` + sanitized authored labels —
11
+ * deliberately NOT `agent::ready`), resumably via a plan fingerprint
12
+ * 5. Upsert `story-plan-state` on every created Story; upsert `plan-summary`
13
+ * on the primary Story
14
+ * 6. Flip every Story to `agent::ready` the terminal step, so `ready`
15
+ * always implies "checkpoints written"
16
+ * 7. Comment on + close the superseded `--tickets` source issues
17
+ * (Story #4535) — bookkeeping only; never fails the run
18
+ * 8. Temp cleanup at terminal success + a stale-plan-dir reap
19
+ *
20
+ * **Why `agent::ready` moved to the end (Story #4541).** Issues used to be
21
+ * born `agent::ready` in the creating POST while the checkpoints were
22
+ * written afterwards. Anything picking a Story up in that window — or after
23
+ * a comment failure aborted the loop — read the checkpoint as `null`
24
+ * (`story-plan-state.js` degrades missing/malformed to `null`). Creating
25
+ * unlabelled, writing checkpoints, then flipping closes that race.
26
+ *
27
+ * **No authored risk artifact (Story #4542).** Persist neither requires nor
28
+ * accepts a risk verdict, derives no envelope from one, and computes no
29
+ * review routing. Review depth and the acceptance-critic mode are derived from
30
+ * the diff at close time (`review-depth.js#deriveChangeLevel`); `--force-review`
31
+ * is an explicit operator flag, recorded here as a receipt and never inferred.
16
32
  *
17
33
  * Hard cutover: no Epic parent, no reconciler, no `deliveryShape`, no
18
34
  * `--amend` tree cascades. Those surfaces die with Stages 4–5 for any
@@ -21,12 +37,13 @@
21
37
  * @module lib/orchestration/plan-persist/run-plan-persist
22
38
  */
23
39
 
24
- import { rm } from 'node:fs/promises';
40
+ import { readdir, rm, stat } from 'node:fs/promises';
41
+ import path from 'node:path';
25
42
 
43
+ import { anchorTempRoot, tempRootFrom } from '../../config/temp-paths.js';
26
44
  import { getLimits, PROJECT_ROOT } from '../../config-resolver.js';
27
45
  import { gitSpawn } from '../../git-utils.js';
28
46
  import { Logger } from '../../Logger.js';
29
- import { evaluatePlanCritics } from '../plan-critics-evaluate.js';
30
47
  import {
31
48
  appendCriticSkip,
32
49
  readPlanMetrics,
@@ -37,20 +54,23 @@ import {
37
54
  evaluateDraftReachability,
38
55
  renderReachabilityOrphans,
39
56
  } from '../plan-reachability.js';
40
- import { resolveReviewRouting } from '../plan-review-routing.js';
41
- import { deriveRiskEnvelope } from '../planning-risk.js';
42
57
  import { upsertStructuredComment } from '../ticketing.js';
43
58
  import {
44
59
  enforceFanOutGate,
45
60
  surfaceSoftConflictFindings,
46
61
  } from './fan-out-gate.js';
47
- import { validateTickets } from './persist-helpers.js';
48
- import { assemblePlanStories, createStoryIssues } from './story-ops.js';
62
+ import { resolveBaseBranchRef, validateTickets } from './persist-helpers.js';
63
+ import {
64
+ assemblePlanStories,
65
+ createStoryIssues,
66
+ markStoriesReady,
67
+ } from './story-ops.js';
49
68
  import {
50
69
  buildPlanSummaryCommentBody,
51
70
  buildWaveTable,
52
71
  PLAN_SUMMARY_COMMENT_TYPE,
53
72
  } from './summary.js';
73
+ import { closeSupersededTickets } from './supersede-ops.js';
54
74
 
55
75
  /** Checkpoint schema version written on each Story's story-plan-state. */
56
76
  const PLAN_CHECKPOINT_SCHEMA_VERSION_V2 = 2;
@@ -88,6 +108,25 @@ export async function writeCheckpointV2(provider, storyId, state) {
88
108
  return state;
89
109
  }
90
110
 
111
+ /**
112
+ * Enforce the ticket validator's findings and report what the file-assumption
113
+ * gate actually concluded.
114
+ *
115
+ * The return value feeds the posted `plan-summary`'s freshness line, which
116
+ * used to be hard-coded `{ stale: 0, ambiguous: 0 }` — so the comment read
117
+ * "Spec freshness: clean" even on the one run where the gate had *given up*
118
+ * (Story #4541's unresolvable-base-ref downgrade). The summary asserted a
119
+ * clean result precisely when it had the least evidence for one.
120
+ *
121
+ * The mapping is deliberate. A confirmed mismatch is never reported here at
122
+ * all — it throws, and the run posts no summary. The only findings that
123
+ * survive to the summary are ones the gate could not *verify*, because the
124
+ * base ref they were computed against does not resolve; unverifiable is
125
+ * `ambiguous`, not `stale`.
126
+ *
127
+ * @returns {{ stale: number, ambiguous: number }} Freshness counts for the
128
+ * posted summary.
129
+ */
91
130
  function enforceTicketValidation(validated, { config, settings, cwd }) {
92
131
  const validationErrors = validated.errors ?? [];
93
132
  const assumptionFailures = validationErrors.filter((error) =>
@@ -102,8 +141,15 @@ function enforceTicketValidation(validated, { config, settings, cwd }) {
102
141
  `hard error(s):\n${blockingErrors.map((error) => ` - ${error}`).join('\n')}`,
103
142
  );
104
143
  }
105
- if (assumptionFailures.length === 0) return;
106
- const gateBaseRef = config?.baseBranch ?? settings?.baseBranch ?? 'main';
144
+ if (assumptionFailures.length === 0) return { stale: 0, ambiguous: 0 };
145
+ // Story #4541: resolve through the canonical `project.baseBranch` (the
146
+ // shape `config-resolver` actually emits) with the legacy settings bag as
147
+ // a fallback — reading a bare `config.baseBranch` meant this probe always
148
+ // targeted the literal `main`.
149
+ const gateBaseRef =
150
+ config?.project?.baseBranch ??
151
+ settings?.baseBranch ??
152
+ resolveBaseBranchRef(config);
107
153
  const refResolves =
108
154
  gitSpawn(
109
155
  cwd ?? process.cwd(),
@@ -122,16 +168,155 @@ function enforceTicketValidation(validated, { config, settings, cwd }) {
122
168
  `[plan-persist] file-assumption gate skipped: base ref '${gateBaseRef}' ` +
123
169
  `does not resolve — ${assumptionFailures.length} finding(s) downgraded.`,
124
170
  );
171
+ return { stale: 0, ambiguous: assumptionFailures.length };
125
172
  }
126
173
 
127
- function riskVerdictCommentBody(riskVerdict) {
128
- return [
129
- '### risk-verdict',
130
- '',
131
- '```json',
132
- JSON.stringify(riskVerdict, null, 2),
133
- '```',
134
- ].join('\n');
174
+ /**
175
+ * Run the supersede close phase behind a belt-and-braces guard.
176
+ *
177
+ * `closeSupersededTickets` already swallows per-ticket failures, but this
178
+ * phase runs *after* `createIssue`, so an unexpected throw anywhere in it
179
+ * would leave the run half-done with Stories already live. Degrade to a
180
+ * reported failure instead.
181
+ *
182
+ * @returns {Promise<import('./supersede-ops.js').SupersedeReport>}
183
+ */
184
+ async function runSupersedePhase(args) {
185
+ try {
186
+ return await closeSupersededTickets(args);
187
+ } catch (err) {
188
+ Logger.warn(
189
+ `[plan-persist] supersede close phase failed: ${err.message} — ` +
190
+ 'Stories were created; close the source tickets by hand.',
191
+ );
192
+ return {
193
+ enabled: true,
194
+ dryRun: args.dryRun === true,
195
+ reason: `phase-error: ${err.message}`,
196
+ closed: [],
197
+ planned: [],
198
+ skipped: [],
199
+ failed: (args.sourceTicketIds ?? []).map((ticket) => ({
200
+ ticket,
201
+ reason: err.message,
202
+ })),
203
+ };
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Render the plan-metrics line for the **posted** `plan-summary` comment,
209
+ * scoped to this invocation.
210
+ *
211
+ * The ordering hazard this closes: the ledger record for the current run is
212
+ * appended by `recordPlanInvocation`'s `finally`, which by construction only
213
+ * fires once `runPlanPersist` has *resolved* — long after this comment body
214
+ * is composed. A plain `since`-filtered ledger read therefore summarized
215
+ * every run except the one being summarized, and on an otherwise-quiet
216
+ * ledger it rendered "plan-metrics: no invocations recorded" onto the very
217
+ * comment reporting the run. (The stdout envelope was always correct: its
218
+ * `attachPlanMetrics` read runs after the wrapper returns.)
219
+ *
220
+ * **The `finally` stays where it is.** It is what guarantees a persist that
221
+ * throws is still recorded, so moving the append earlier — or moving this
222
+ * read later, past the GitHub writes it feeds — would trade a cosmetic bug
223
+ * for a real one. Instead of racing it, fold in a synthetic record standing
224
+ * for the in-flight invocation. It is the same record the wrapper is about to
225
+ * write, minus a final duration; it cannot double-count, because the real one
226
+ * does not exist yet at this point in the pipeline.
227
+ *
228
+ * `ok: true` is honest here: this line is only ever composed on the success
229
+ * path, after `createStoryIssues` has returned.
230
+ *
231
+ * @param {{ config: object, since: string, startedAt: string, mode: string }} args
232
+ * @returns {Promise<string|null>}
233
+ */
234
+ async function renderRunScopedPlanMetricsLine({
235
+ config,
236
+ since,
237
+ startedAt,
238
+ mode,
239
+ }) {
240
+ try {
241
+ const ledger = await readPlanMetrics(null, config);
242
+ const endedAt = new Date().toISOString();
243
+ const inFlight = {
244
+ v: 1,
245
+ cli: 'plan-persist',
246
+ mode,
247
+ epicId: null,
248
+ startedAt,
249
+ endedAt,
250
+ durationMs: Math.max(0, Date.parse(endedAt) - Date.parse(startedAt)) || 0,
251
+ ok: true,
252
+ };
253
+ const summary = summarizePlanMetrics(
254
+ { ...ledger, entries: [...(ledger.entries ?? []), inFlight] },
255
+ { since },
256
+ );
257
+ return renderPlanMetricsSummaryLine(summary);
258
+ } catch (err) {
259
+ Logger.warn(`[plan-persist] plan-metrics summary skipped: ${err.message}`);
260
+ return null;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Age after which an abandoned `temp/plan-*` directory is reaped. A plan run
266
+ * that is still being authored is minutes-to-hours old; a week is far past
267
+ * any live run and comfortably past an operator returning to a paused one.
268
+ */
269
+ const STALE_PLAN_DIR_MS = 7 * 24 * 60 * 60 * 1000;
270
+
271
+ /**
272
+ * Reap abandoned `plan-*` directories under the temp root (Story #4541).
273
+ *
274
+ * Terminal-success cleanup only ever removed the *current* run's `planDir`,
275
+ * so every plan that failed a gate, was abandoned mid-authoring, or ran
276
+ * `--dry-run` left its directory behind forever. This sweeps the stragglers
277
+ * on each persist.
278
+ *
279
+ * Best-effort throughout: this is hygiene, never a reason to fail a run that
280
+ * has already created Stories. The current run's own `planDir` is always
281
+ * excluded — its cleanup is the caller's decision.
282
+ *
283
+ * @param {{ config?: object, keepDir?: string|null, now?: number }} args
284
+ * @returns {Promise<{ reaped: string[] }>}
285
+ */
286
+ export async function reapStalePlanDirs({
287
+ config = {},
288
+ keepDir = null,
289
+ now = Date.now(),
290
+ } = {}) {
291
+ const reaped = [];
292
+ const tempRoot = anchorTempRoot(tempRootFrom(config));
293
+ let entries;
294
+ try {
295
+ entries = await readdir(tempRoot, { withFileTypes: true });
296
+ } catch {
297
+ return { reaped }; // No temp root yet — nothing to reap.
298
+ }
299
+ const keep = keepDir ? path.resolve(keepDir) : null;
300
+ for (const entry of entries) {
301
+ if (!entry.isDirectory() || !entry.name.startsWith('plan-')) continue;
302
+ const dir = path.resolve(tempRoot, entry.name);
303
+ if (keep !== null && dir === keep) continue;
304
+ try {
305
+ const { mtimeMs } = await stat(dir);
306
+ if (now - mtimeMs < STALE_PLAN_DIR_MS) continue;
307
+ await rm(dir, { recursive: true, force: true });
308
+ reaped.push(dir);
309
+ } catch {
310
+ // A racing writer or a permission error: leave it for the next run.
311
+ }
312
+ }
313
+ if (reaped.length > 0) {
314
+ Logger.info(
315
+ `[plan-persist] reaped ${reaped.length} abandoned plan director(ies) ` +
316
+ `older than 7d under ${tempRoot}.`,
317
+ );
318
+ }
319
+ return { reaped };
135
320
  }
136
321
 
137
322
  /**
@@ -141,7 +326,6 @@ function riskVerdictCommentBody(riskVerdict) {
141
326
  * provider: object,
142
327
  * artifacts: {
143
328
  * stories: Array<object>,
144
- * riskVerdict: object,
145
329
  * techSpecContent?: string|null,
146
330
  * planAcceptance?: string[]|null,
147
331
  * },
@@ -153,10 +337,12 @@ function riskVerdictCommentBody(riskVerdict) {
153
337
  * allowLargeFanOut?: boolean,
154
338
  * skipCleanup?: boolean,
155
339
  * dryRun?: boolean,
156
- * planRunId?: string,
157
340
  * planDir?: string,
158
341
  * fanOutCounter?: Function,
159
342
  * cwd?: string,
343
+ * sourceTicketIds?: number[],
344
+ * sourceTicketOrigin?: 'flag'|'envelope'|'none',
345
+ * closeSuperseded?: boolean,
160
346
  * },
161
347
  * }} input
162
348
  */
@@ -169,7 +355,6 @@ export async function runPlanPersist({
169
355
  }) {
170
356
  const {
171
357
  stories: rawStories = null,
172
- riskVerdict,
173
358
  techSpecContent = null,
174
359
  planAcceptance = null,
175
360
  } = artifacts ?? {};
@@ -179,24 +364,20 @@ export async function runPlanPersist({
179
364
  allowLargeFanOut = false,
180
365
  skipCleanup = false,
181
366
  dryRun = false,
182
- planRunId,
183
367
  planDir = null,
184
368
  fanOutCounter = undefined,
185
369
  cwd = PROJECT_ROOT,
370
+ sourceTicketIds = [],
371
+ sourceTicketOrigin = 'none',
372
+ closeSuperseded = true,
186
373
  } = opts;
187
374
 
188
- if (!riskVerdict || !Array.isArray(riskVerdict.axes)) {
189
- throw new Error(
190
- '[plan-persist] risk verdict is required author risk-verdict.json ' +
191
- 'and pass it with --risk-verdict.',
192
- );
193
- }
194
- if ('deliveryShape' in (riskVerdict ?? {})) {
195
- throw new Error(
196
- '[plan-persist] risk-verdict.deliveryShape was removed in v2 Stage 3 — ' +
197
- 'delete the field; persist always creates Story issue(s).',
198
- );
199
- }
375
+ // Boundary for the plan-metrics summary below: everything this invocation
376
+ // appends to the ledger is stamped at or after this instant, so filtering
377
+ // on it scopes the counts to *this* run rather than every plan ever run
378
+ // through the shared standalone ledger (Story #4541).
379
+ const runStartedAt = opts.metricsSince ?? new Date().toISOString();
380
+
200
381
  if (!Array.isArray(rawStories) || rawStories.length === 0) {
201
382
  throw new Error(
202
383
  '[plan-persist] stories payload must be a non-empty array ' +
@@ -228,7 +409,11 @@ export async function runPlanPersist({
228
409
  });
229
410
  enforceFanOutGate(validated.findings, allowLargeFanOut, 'plan-persist');
230
411
  surfaceSoftConflictFindings(validated.findings, 'plan-persist');
231
- enforceTicketValidation(validated, { config, settings, cwd });
412
+ const freshness = enforceTicketValidation(validated, {
413
+ config,
414
+ settings,
415
+ cwd,
416
+ });
232
417
 
233
418
  const reachability = evaluateDraftReachability({
234
419
  tickets: rawStories,
@@ -252,46 +437,17 @@ export async function runPlanPersist({
252
437
  );
253
438
  }
254
439
 
255
- const critics = evaluatePlanCritics({
256
- techSpecContent: techSpecContent ?? '',
257
- riskVerdict,
258
- tickets: rawStories,
259
- config,
260
- });
261
- for (const decision of [critics.consolidation, critics.premortem]) {
262
- Logger.info(
263
- `[plan-persist] critic ${decision.critic}: ` +
264
- `${decision.dispatch ? 'dispatch' : 'skip'} — ` +
265
- decision.reasons.join('; '),
266
- );
267
- if (!decision.dispatch) {
268
- await appendCriticSkip(
269
- {
270
- critic: decision.critic,
271
- reasons: decision.reasons,
272
- cli: 'plan-persist',
273
- },
274
- config,
275
- );
276
- }
277
- }
278
-
279
440
  // Split policy + inline Spec fold (over-budget Specs fail closed — no docs/).
280
441
  const { stories } = assemblePlanStories(rawStories, {
281
442
  sharedSpec: techSpecContent,
282
443
  planAcceptance: planAcceptance ?? undefined,
444
+ sourceTicketIds,
283
445
  });
284
446
 
285
- const planningRisk = deriveRiskEnvelope(riskVerdict);
286
- const reviewRouting = resolveReviewRouting({
287
- planningRisk,
288
- forceReview,
289
- });
290
-
291
- const { created, planRunLabel } = await createStoryIssues({
447
+ const { created } = await createStoryIssues({
292
448
  provider,
293
449
  stories,
294
- opts: { planRunId, dryRun },
450
+ opts: { dryRun },
295
451
  });
296
452
 
297
453
  const primary = created[0];
@@ -303,44 +459,39 @@ export async function runPlanPersist({
303
459
  })),
304
460
  );
305
461
 
306
- let planMetricsLine = null;
307
- try {
308
- const metrics = summarizePlanMetrics(await readPlanMetrics(config));
309
- planMetricsLine = renderPlanMetricsSummaryLine(metrics);
310
- } catch {
311
- planMetricsLine = null;
312
- }
462
+ // Story #4541: `readPlanMetrics` is declared `(epicId, config)` but was
463
+ // called with `config` first, so the ledger path resolver received the
464
+ // config object as an `epicId` and threw its guard on every single run —
465
+ // a throw this try/catch then swallowed into a silently absent summary.
466
+ // v2 persist is always Epic-less, hence the explicit `null`. The `since`
467
+ // filter keeps the counts about this invocation, and the in-flight record
468
+ // is folded in because the wrapper has not written it yet — see
469
+ // `renderRunScopedPlanMetricsLine`.
470
+ const planMetricsLine = await renderRunScopedPlanMetricsLine({
471
+ config,
472
+ since: runStartedAt,
473
+ startedAt: runStartedAt,
474
+ mode: dryRun ? 'dry-run' : 'persist',
475
+ });
313
476
 
314
477
  const summaryBody = buildPlanSummaryCommentBody({
315
478
  epicId: primary.id,
316
479
  ticketCount: created.length,
317
- planningRisk,
318
- reviewRouting,
319
- freshness: { stale: 0, ambiguous: 0 },
480
+ forceReview,
481
+ freshness,
320
482
  healthcheck: { skipped: true },
321
483
  waveTable,
322
484
  mode: 'stories',
323
485
  planMetricsLine,
324
486
  stories: created,
325
- planRunLabel,
326
487
  });
327
488
 
328
489
  if (!dryRun) {
329
490
  for (const story of created) {
330
- await upsertStructuredComment(
331
- provider,
332
- story.id,
333
- 'risk-verdict',
334
- riskVerdictCommentBody(riskVerdict),
335
- );
336
491
  await writeCheckpointV2(provider, story.id, {
337
- planningRisk,
338
- riskVerdict,
339
- reviewRouting,
340
492
  persist: {
341
493
  completedAt: new Date().toISOString(),
342
494
  storyCount: created.length,
343
- planRunLabel,
344
495
  primaryStoryId: primary.id,
345
496
  stories: created.map((createdStory) => ({
346
497
  slug: createdStory.slug,
@@ -355,8 +506,26 @@ export async function runPlanPersist({
355
506
  PLAN_SUMMARY_COMMENT_TYPE,
356
507
  summaryBody,
357
508
  );
509
+
510
+ // Terminal step: every checkpoint above is now on every Story, so
511
+ // `agent::ready` can honestly mean "fully persisted" (Story #4541).
512
+ // Anything that picks a Story up from here reads a real checkpoint.
513
+ await markStoriesReady({ provider, created });
358
514
  }
359
515
 
516
+ const supersede = await runSupersedePhase({
517
+ provider,
518
+ stories,
519
+ created,
520
+ sourceTicketIds,
521
+ dryRun,
522
+ closeSuperseded,
523
+ });
524
+ // Record which channel the ids came from so a run that superseded nothing
525
+ // says *why* (`none` = neither the envelope nor --source-tickets carried
526
+ // any) rather than reading as a clean no-op — Story #4554.
527
+ supersede.sourceTicketOrigin = sourceTicketOrigin;
528
+
360
529
  if (!skipCleanup && planDir) {
361
530
  try {
362
531
  await rm(planDir, { recursive: true, force: true });
@@ -364,21 +533,32 @@ export async function runPlanPersist({
364
533
  Logger.warn(`[plan-persist] temp cleanup skipped: ${err.message}`);
365
534
  }
366
535
  }
536
+ // Terminal-success cleanup only ever removes *this* run's planDir, so
537
+ // abandoned ones accumulated forever. Sweep them (Story #4541).
538
+ await reapStalePlanDirs({ config, keepDir: skipCleanup ? planDir : null });
367
539
 
540
+ const adopted = created.filter((story) => story.adopted);
541
+ if (adopted.length > 0) {
542
+ Logger.info(
543
+ `[plan-persist] resumed ${adopted.length} of ${created.length} Story(ies) ` +
544
+ `from a previous persist: ${adopted.map((s2) => `#${s2.id}`).join(', ')}.`,
545
+ );
546
+ }
368
547
  Logger.info(
369
548
  `[plan-persist] Persisted ${created.length} Story(ies)` +
370
- (planRunLabel ? ` under ${planRunLabel}` : '') +
371
549
  `; primary #${primary.id} is agent::ready.`,
372
550
  );
551
+ Logger.info(
552
+ `[plan-persist] Deliver with: /deliver ${created.map((s2) => s2.id).join(' ')}`,
553
+ );
373
554
 
374
555
  return {
375
556
  stories: created,
376
557
  primaryStoryId: primary.id,
377
- planRunLabel,
378
- planningRisk,
379
- reviewRouting,
380
- critics,
558
+ forceReview,
381
559
  reachability,
560
+ freshness,
382
561
  waveTable,
562
+ supersede,
383
563
  };
384
564
  }