mandrel 1.94.0 → 2.0.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 (393) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/story-worker.md +12 -13
  3. package/.agents/docs/SDLC.md +489 -1285
  4. package/.agents/docs/agentrc-reference.json +177 -67
  5. package/.agents/docs/configuration.md +104 -138
  6. package/.agents/docs/execution-reference.md +17 -20
  7. package/.agents/docs/quality-gates.md +13 -19
  8. package/.agents/docs/workflows.md +3 -3
  9. package/.agents/instructions.md +70 -81
  10. package/.agents/rules/ci-remediation.md +8 -12
  11. package/.agents/rules/git-conventions-reference.md +6 -7
  12. package/.agents/rules/git-conventions.md +16 -22
  13. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  14. package/.agents/schemas/agentrc.schema.json +54 -214
  15. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  16. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  17. package/.agents/schemas/risk-verdict.schema.json +0 -13
  18. package/.agents/scripts/analyze-execution.js +1 -1
  19. package/.agents/scripts/audit-to-stories.js +7 -7
  20. package/.agents/scripts/boot-sweep.js +1 -1
  21. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  22. package/.agents/scripts/check-prepush-recovery.js +1 -1
  23. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  24. package/.agents/scripts/lib/Logger.js +6 -10
  25. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  26. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  27. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  28. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  29. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  30. package/.agents/scripts/lib/baselines/reader.js +1 -6
  31. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  32. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  33. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  34. package/.agents/scripts/lib/checks/index.js +2 -1
  35. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  36. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  37. package/.agents/scripts/lib/cli-args.js +16 -7
  38. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  39. package/.agents/scripts/lib/config/ci.js +6 -31
  40. package/.agents/scripts/lib/config/delivery-routing.js +45 -29
  41. package/.agents/scripts/lib/config/explain.js +55 -36
  42. package/.agents/scripts/lib/config/limits.js +17 -58
  43. package/.agents/scripts/lib/config/paths.js +0 -2
  44. package/.agents/scripts/lib/config/quality.js +1 -1
  45. package/.agents/scripts/lib/config/runners.js +17 -50
  46. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  47. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  48. package/.agents/scripts/lib/config-resolver.js +2 -7
  49. package/.agents/scripts/lib/config-settings-schema-delivery.js +24 -148
  50. package/.agents/scripts/lib/config-settings-schema-quality.js +8 -14
  51. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  52. package/.agents/scripts/lib/dependency-parser.js +3 -2
  53. package/.agents/scripts/lib/doc-tiers.js +2 -2
  54. package/.agents/scripts/lib/duplicate-search.js +211 -41
  55. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  56. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  57. package/.agents/scripts/lib/framework-version.js +2 -3
  58. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  59. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  60. package/.agents/scripts/lib/git-utils.js +32 -6
  61. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  62. package/.agents/scripts/lib/label-constants.js +10 -23
  63. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  64. package/.agents/scripts/lib/observability/active-story-env.js +1 -1
  65. package/.agents/scripts/lib/observability/hook-heartbeat.js +20 -52
  66. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  67. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  68. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  69. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +8 -5
  70. package/.agents/scripts/lib/orchestration/ceremony-routing.js +83 -20
  71. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  72. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  73. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  74. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  75. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  76. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  77. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  78. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  79. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  80. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  81. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  82. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  83. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  84. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  85. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  86. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  87. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  88. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  89. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  90. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  91. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  92. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  93. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  94. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  95. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +13 -13
  96. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  97. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  98. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  99. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  100. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  101. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  102. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  103. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  104. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  105. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  106. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  107. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  108. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  109. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  110. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  111. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  112. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  113. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  116. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  117. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  118. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  119. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  120. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  121. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  122. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  123. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  124. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  125. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  126. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  127. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  128. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  129. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  130. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  131. package/.agents/scripts/lib/orchestration/ticket-validator.js +2 -7
  132. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  133. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  134. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  135. package/.agents/scripts/lib/planning-corpus.js +16 -11
  136. package/.agents/scripts/lib/preflight-runner.js +2 -2
  137. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  138. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  139. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  140. package/.agents/scripts/lib/story-adjacency.js +11 -14
  141. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  142. package/.agents/scripts/lib/story-plan.js +2 -4
  143. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  144. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  145. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  146. package/.agents/scripts/lib/validation-evidence.js +1 -1
  147. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  148. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  149. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  150. package/.agents/scripts/lint-issue-body.js +71 -40
  151. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  152. package/.agents/scripts/notify.js +4 -3
  153. package/.agents/scripts/plan-context.js +64 -74
  154. package/.agents/scripts/plan-persist.js +121 -280
  155. package/.agents/scripts/plan-run-epilogue.js +97 -0
  156. package/.agents/scripts/providers/github/issues.js +17 -33
  157. package/.agents/scripts/providers/github/mappers.js +0 -12
  158. package/.agents/scripts/providers/github/tickets.js +2 -5
  159. package/.agents/scripts/resolve-plan-run.js +117 -0
  160. package/.agents/scripts/signals-view.js +24 -19
  161. package/.agents/scripts/single-story-close.js +11 -14
  162. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  163. package/.agents/scripts/single-story-init.js +29 -20
  164. package/.agents/scripts/stories-wave-tick.js +6 -6
  165. package/.agents/scripts/story-plan.js +26 -47
  166. package/.agents/scripts/update-ticket-state.js +6 -15
  167. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  168. package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
  169. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  170. package/.agents/skills/skills.index.json +3 -43
  171. package/.agents/starter-agentrc.json +0 -5
  172. package/.agents/templates/agent-protocol.md +9 -10
  173. package/.agents/workflows/audit-architecture.md +3 -3
  174. package/.agents/workflows/audit-clean-code.md +3 -3
  175. package/.agents/workflows/audit-dependencies.md +3 -3
  176. package/.agents/workflows/audit-devops.md +3 -3
  177. package/.agents/workflows/audit-documentation.md +5 -5
  178. package/.agents/workflows/audit-lighthouse.md +3 -3
  179. package/.agents/workflows/audit-navigability.md +3 -2
  180. package/.agents/workflows/audit-performance.md +3 -3
  181. package/.agents/workflows/audit-privacy.md +3 -3
  182. package/.agents/workflows/audit-quality.md +3 -3
  183. package/.agents/workflows/audit-security.md +3 -3
  184. package/.agents/workflows/audit-seo.md +3 -3
  185. package/.agents/workflows/audit-sre.md +3 -3
  186. package/.agents/workflows/audit-to-stories.md +20 -20
  187. package/.agents/workflows/audit-ux-ui.md +3 -3
  188. package/.agents/workflows/deliver.md +122 -174
  189. package/.agents/workflows/git-cleanup.md +3 -4
  190. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  191. package/.agents/workflows/helpers/acceptance-self-eval.md +16 -29
  192. package/.agents/workflows/helpers/code-review.md +70 -193
  193. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  194. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  195. package/.agents/workflows/helpers/diagnose.md +10 -10
  196. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  197. package/.agents/workflows/helpers/signals.md +16 -16
  198. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  199. package/.agents/workflows/mandrel-update.md +2 -1
  200. package/.agents/workflows/plan.md +112 -145
  201. package/.agents/workflows/qa-assist.md +24 -30
  202. package/.agents/workflows/qa-explore.md +29 -38
  203. package/.agents/workflows/qa-run.md +2 -2
  204. package/README.md +9 -8
  205. package/docs/CHANGELOG.md +30 -0
  206. package/lib/migrations/index.js +6 -5
  207. package/package.json +2 -2
  208. package/.agents/agents/retro.md +0 -42
  209. package/.agents/personas/architect.md +0 -113
  210. package/.agents/personas/devops-engineer.md +0 -38
  211. package/.agents/personas/engineer.md +0 -33
  212. package/.agents/personas/project-manager.md +0 -114
  213. package/.agents/personas/qa-engineer.md +0 -95
  214. package/.agents/personas/security-engineer.md +0 -111
  215. package/.agents/personas/technical-writer.md +0 -101
  216. package/.agents/schemas/dispatch-manifest.json +0 -232
  217. package/.agents/schemas/epic-spec.schema.json +0 -153
  218. package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
  219. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
  220. package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
  221. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  222. package/.agents/scripts/bookkeeping-reconcile.js +0 -117
  223. package/.agents/scripts/dispatcher.js +0 -295
  224. package/.agents/scripts/epic-audit-prepare.js +0 -497
  225. package/.agents/scripts/epic-audit-recheck.js +0 -274
  226. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  227. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  228. package/.agents/scripts/epic-deliver-prepare.js +0 -852
  229. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  230. package/.agents/scripts/epic-plan-clarity.js +0 -211
  231. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  232. package/.agents/scripts/epic-reconcile.js +0 -625
  233. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  234. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  235. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  236. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  237. package/.agents/scripts/lib/config/preflight.js +0 -58
  238. package/.agents/scripts/lib/config/retro.js +0 -77
  239. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  240. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  241. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  242. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
  243. package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
  244. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  245. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  246. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  247. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  248. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  249. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  250. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  251. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  252. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  253. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  254. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  255. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  256. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  257. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  258. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  259. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  260. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  261. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  262. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
  263. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  264. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  265. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  266. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  267. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  268. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  269. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  270. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  271. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  272. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  273. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  274. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  275. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  276. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  277. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  278. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  279. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  280. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  281. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  282. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  283. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  284. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  285. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  286. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
  287. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  288. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
  289. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  290. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  291. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  292. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
  293. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  294. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  295. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  296. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  297. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  298. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  299. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  300. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  301. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  302. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  303. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  304. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  305. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  306. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  307. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  308. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  309. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  310. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  311. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  312. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  313. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  314. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  315. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  316. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  317. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  318. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  319. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  320. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  321. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  322. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  323. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  324. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  325. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  326. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  327. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  328. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  329. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  330. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  331. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  332. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  333. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  334. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  335. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
  336. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  337. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  338. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  339. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  340. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  341. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  342. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  343. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  344. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  345. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  346. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  347. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  348. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  349. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  350. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  351. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  352. package/.agents/scripts/lib/spec/index.js +0 -36
  353. package/.agents/scripts/lib/spec/loader.js +0 -425
  354. package/.agents/scripts/lib/spec/state.js +0 -208
  355. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  356. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  357. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  358. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  359. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  360. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  361. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  362. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  363. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  364. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  365. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  366. package/.agents/scripts/lifecycle-emit.js +0 -510
  367. package/.agents/scripts/retro-run.js +0 -218
  368. package/.agents/scripts/slice-phase.js +0 -361
  369. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  370. package/.agents/scripts/story-close.js +0 -294
  371. package/.agents/scripts/story-init.js +0 -599
  372. package/.agents/scripts/story-phase.js +0 -369
  373. package/.agents/scripts/wave-tick.js +0 -464
  374. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  375. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  376. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  377. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  378. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  379. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  380. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
  381. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  382. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
  383. package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
  384. package/.agents/workflows/helpers/deliver-epic.md +0 -998
  385. package/.agents/workflows/helpers/deliver-stories.md +0 -450
  386. package/.agents/workflows/helpers/epic-audit.md +0 -189
  387. package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
  388. package/.agents/workflows/helpers/epic-testing.md +0 -125
  389. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  390. package/.agents/workflows/helpers/plan-epic.md +0 -353
  391. package/.agents/workflows/helpers/plan-story.md +0 -251
  392. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  393. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -4,59 +4,30 @@
4
4
  *
5
5
  * All label names are sourced from `label-constants.js` so renames only need
6
6
  * to happen in one place. Colors come from `LABEL_COLORS` in the same module.
7
+ *
8
+ * v2 deleted the behavioral persona concept (`.agents/personas/` +
9
+ * `persona::*` labels). Role framing for spawns lives in `.agents/agents/`
10
+ * via `delivery.routing.roleScopedAgents`; QA auth identities live in
11
+ * `qa.personas` — neither is a GitHub label axis.
7
12
  */
8
13
 
9
- import fs from 'node:fs';
10
- import path from 'node:path';
11
- import { fileURLToPath } from 'node:url';
12
14
  import {
13
15
  ACCEPTANCE_LABELS,
14
16
  AGENT_LABELS,
15
17
  DELIVERY_LABELS,
16
18
  LABEL_COLORS,
17
- PERSONA_LABEL_PREFIX,
18
19
  PLANNING_LABELS,
19
20
  STATUS_LABELS,
20
21
  TYPE_LABELS,
21
22
  } from './label-constants.js';
22
23
 
23
- const PERSONAS_DIR = path.resolve(
24
- path.dirname(fileURLToPath(import.meta.url)),
25
- '..',
26
- '..',
27
- 'personas',
28
- );
29
-
30
- /**
31
- * Discover persona labels from `.agents/personas/*.md`. The filename
32
- * (without extension) is the label suffix — this is the same value the
33
- * context hydrator uses to resolve `persona::<name>` to its markdown file.
34
- */
35
- function buildPersonaLabels() {
36
- return fs
37
- .readdirSync(PERSONAS_DIR)
38
- .filter((f) => f.endsWith('.md'))
39
- .map((f) => f.slice(0, -3))
40
- .sort()
41
- .map((name) => ({
42
- name: `${PERSONA_LABEL_PREFIX}${name}`,
43
- color: LABEL_COLORS.PERSONA,
44
- description: `${name} persona`,
45
- }));
46
- }
47
-
48
24
  /** @type {Array<{ name: string, color: string, description: string }>} */
49
25
  export const LABEL_TAXONOMY = [
50
26
  // Type
51
- {
52
- name: TYPE_LABELS.EPIC,
53
- color: LABEL_COLORS.TYPE,
54
- description: 'Epic-level work item',
55
- },
56
27
  {
57
28
  name: TYPE_LABELS.STORY,
58
29
  color: LABEL_COLORS.TYPE,
59
- description: 'User story under an Epic',
30
+ description: 'Story work item',
60
31
  },
61
32
 
62
33
  // Agent State
@@ -95,9 +66,6 @@ export const LABEL_TAXONOMY = [
95
66
  description: 'Blocked by a dependency',
96
67
  },
97
68
 
98
- // Persona — dynamically derived from .agents/personas/*.md
99
- ...buildPersonaLabels(),
100
-
101
69
  // Acceptance axis — explicit opt-out signal for Epics that
102
70
  // intentionally have no acceptance-table coverage (waives the Epic
103
71
  // body's ## Acceptance Table section — Story #4324).
@@ -108,15 +76,13 @@ export const LABEL_TAXONOMY = [
108
76
  },
109
77
 
110
78
  // Planning axis — operator-applied waivers for the planning → delivery
111
- // handoff gates. Currently the sole entry is the `healthcheck-waived`
112
- // override consumed by `plan-persist.js` when the inline healthcheck
113
- // (`runPlanHealthcheck`) returned `ok: false` for a reason the
114
- // operator has triaged and accepted.
79
+ // handoff gates. `healthcheck-waived` remains for tickets that still
80
+ // carry the historical label; the healthcheck CLI was retired.
115
81
  {
116
82
  name: PLANNING_LABELS.HEALTHCHECK_WAIVED,
117
83
  color: LABEL_COLORS.PLANNING,
118
84
  description:
119
- 'Operator override allows agent::ready handoff despite a failing post-plan healthcheck',
85
+ 'Historical operator override for the retired post-plan healthcheck',
120
86
  },
121
87
 
122
88
  // Delivery routing — applied by `plan-persist.js` when the risk verdict
@@ -7,7 +7,7 @@
7
7
  * This module is the single writer/clearer of those vars:
8
8
  *
9
9
  * - `setActiveStoryEnv({ epicId, storyId, workCwd })` is called from
10
- * `story-init.js` after the worktree is materialised. It sets the
10
+ * `single-story-init.js` after the worktree is materialised. It sets the
11
11
  * vars on the current `process.env` (so any child commands the
12
12
  * orchestrator spawns inherit them) and exports them to a sibling
13
13
  * `.env.local` inside the worktree. The harness re-spawns the
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * hook-heartbeat.js — Epic #4476 (M5): heartbeats OFF the token stream.
3
3
  *
4
- * Today the `story.heartbeat` / `slice.heartbeat` forward-progress signal the
5
- * `/deliver` §2e Idle Watchdog (`wave-tick.js --check-idle 30`) reads is an
6
- * **LLM obligation**: the delivery workflow instructs the agent to run
4
+ * Before M5, the `story.heartbeat` / `slice.heartbeat` forward-progress
5
+ * signal that idle-watchdog consumers read from the lifecycle ledger was an
6
+ * **LLM obligation**: the delivery workflow instructed the agent to run
7
7
  * `story-phase.js` / `slice-phase.js --event heartbeat` at least once per
8
- * meaningful step, and every such call is a full-priced LLM turn re-reading
8
+ * meaningful step, and every such call was a full-priced LLM turn re-reading
9
9
  * ~100k of cached context just to append one NDJSON line.
10
10
  *
11
11
  * This module makes that liveness signal a **free byproduct of the agent
@@ -19,11 +19,10 @@
19
19
  * mechanism moves off the token stream.
20
20
  *
21
21
  * ## Robustness contract (mirrors `tool-trace-hook.js`)
22
- * - **No-op outside an active Story / slice.** When neither a valid
23
- * `{ CC_EPIC_ID, CC_STORY_ID }` (fan-out child) nor a valid
24
- * `{ CC_EPIC_ID, CC_SLICE_ID }` (single-delivery session) pair is present
25
- * in the environment, `emitHeartbeatFromHook` returns without touching the
26
- * filesystem.
22
+ * - **No-op outside an active Story with an Epic-scoped ledger.** When a
23
+ * valid `{ CC_EPIC_ID, CC_STORY_ID }` pair is not present in the
24
+ * environment (v2 standalone Stories omit `CC_EPIC_ID`), `emitHeartbeatFromHook`
25
+ * returns without touching the filesystem.
27
26
  * - **Best-effort.** Every failure is swallowed. A heartbeat is
28
27
  * observability, not state; a hook must never block tool execution.
29
28
  * - **Throttled across processes.** Command hooks are spawned as fresh
@@ -39,7 +38,6 @@ import { statSync, utimesSync, writeFileSync } from 'node:fs';
39
38
  import path from 'node:path';
40
39
 
41
40
  import { epicTempDir } from '../config/temp-paths.js';
42
- import { emitSliceHeartbeat } from '../orchestration/lifecycle/emit-slice-lifecycle.js';
43
41
  import { emitStoryHeartbeat } from '../orchestration/lifecycle/emit-story-heartbeat.js';
44
42
 
45
43
  /**
@@ -53,25 +51,22 @@ const HEARTBEAT_MIN_INTERVAL_MS = 60_000;
53
51
  /**
54
52
  * Resolve the heartbeat target from the active-Story / active-slice env vars.
55
53
  *
56
- * Precedence: a present `CC_STORY_ID` (fan-out Story child) wins over
57
- * `CC_SLICE_ID` (single-delivery session) — the two are never set together in
58
- * practice, but the ordering keeps the resolution deterministic if they were.
59
- *
60
54
  * A `story.heartbeat` requires a parent `epicId` (its schema pins
61
55
  * `epicId >= 1`), so a **standalone** Story (`CC_STORY_ID` set, `CC_EPIC_ID`
62
56
  * absent) yields `null` — there is no Epic-scoped ledger to write to and the
63
57
  * standalone path is not watched by the Epic idle watchdog. The trace hook
64
58
  * still records that context's traces; only the heartbeat is skipped.
65
59
  *
60
+ * Inert `slice.*` lifecycle (and `CC_SLICE_ID` heartbeats) were removed in
61
+ * the v2 ceremony lock-in — `## Slicing` is prose-only.
62
+ *
66
63
  * @param {NodeJS.ProcessEnv} [env]
67
64
  * @returns {{ kind: 'story', epicId: number, storyId: number, operator?: string }
68
- * | { kind: 'slice', epicId: number, sliceId: string, operator?: string }
69
65
  * | null}
70
66
  */
71
67
  export function resolveHeartbeatTarget(env = process.env) {
72
68
  const epicRaw = env.CC_EPIC_ID;
73
69
  const storyRaw = env.CC_STORY_ID;
74
- const sliceRaw = env.CC_SLICE_ID;
75
70
  const operator =
76
71
  typeof env.CC_OPERATOR === 'string' && env.CC_OPERATOR.length > 0
77
72
  ? env.CC_OPERATOR
@@ -93,17 +88,6 @@ export function resolveHeartbeatTarget(env = process.env) {
93
88
  };
94
89
  }
95
90
 
96
- if (sliceRaw) {
97
- if (typeof sliceRaw !== 'string' || sliceRaw.length === 0) return null;
98
- if (!epicOk) return null;
99
- return {
100
- kind: 'slice',
101
- epicId,
102
- sliceId: sliceRaw,
103
- ...(operator ? { operator } : {}),
104
- };
105
- }
106
-
107
91
  return null;
108
92
  }
109
93
 
@@ -115,11 +99,7 @@ export function resolveHeartbeatTarget(env = process.env) {
115
99
  * @returns {string}
116
100
  */
117
101
  export function heartbeatMarkerName(target) {
118
- const key =
119
- target.kind === 'story'
120
- ? `story-${target.storyId}`
121
- : `slice-${String(target.sliceId).replace(/[^A-Za-z0-9._-]/g, '_')}`;
122
- return `.heartbeat-${key}`;
102
+ return `.heartbeat-story-${target.storyId}`;
123
103
  }
124
104
 
125
105
  /**
@@ -191,26 +171,14 @@ export function emitHeartbeatFromHook({
191
171
  }
192
172
 
193
173
  const timestamp = now.toISOString();
194
- let res;
195
- if (target.kind === 'story') {
196
- res = emitStoryHeartbeat({
197
- epicId: target.epicId,
198
- storyId: target.storyId,
199
- phase: 'implementing',
200
- timestamp,
201
- ...(target.operator ? { operator: target.operator } : {}),
202
- config: config ?? undefined,
203
- });
204
- } else {
205
- res = emitSliceHeartbeat({
206
- epicId: target.epicId,
207
- sliceId: target.sliceId,
208
- phase: 'implementing',
209
- timestamp,
210
- ...(target.operator ? { operator: target.operator } : {}),
211
- config: config ?? undefined,
212
- });
213
- }
174
+ const res = emitStoryHeartbeat({
175
+ epicId: target.epicId,
176
+ storyId: target.storyId,
177
+ phase: 'implementing',
178
+ timestamp,
179
+ ...(target.operator ? { operator: target.operator } : {}),
180
+ config: config ?? undefined,
181
+ });
214
182
  touchMarker(markerPath, now);
215
183
  return { emitted: true, kind: target.kind, ledgerPath: res?.ledgerPath };
216
184
  } catch {
@@ -57,7 +57,7 @@ function toScanString(value) {
57
57
  /**
58
58
  * Return true when `haystack` contains any framework prefix as a
59
59
  * substring. We use `includes` (not `startsWith`) because a command line
60
- * like `node .agents/scripts/story-init.js` carries the prefix in the
60
+ * like `node .agents/scripts/single-story-init.js` carries the prefix in the
61
61
  * middle, and a failing-path like `repo/.agents/foo.js` may carry an
62
62
  * absolute prefix.
63
63
  *
@@ -83,13 +83,13 @@ function containsFrameworkPrefix(haystack) {
83
83
  * Framework-wins: if either input matches a framework prefix, the result
84
84
  * is `"framework"` even when the other input looks consumer-shaped. This
85
85
  * matters for mixed cases like a consumer test invoking
86
- * `node .agents/scripts/story-init.js` — that's framework friction even
86
+ * `node .agents/scripts/single-story-init.js` — that's framework friction even
87
87
  * though the failing test path lives under the consumer.
88
88
  *
89
89
  * @param {unknown} failingPath The path of the file or directory the
90
90
  * signal blames (e.g. `"tests/foo.test.js"`).
91
91
  * @param {unknown} command The command line the signal blames
92
- * (e.g. `"node .agents/scripts/story-init.js"`).
92
+ * (e.g. `"node .agents/scripts/single-story-init.js"`).
93
93
  * @returns {"framework"|"consumer"}
94
94
  */
95
95
  export function classifyPathSource(failingPath, command) {
@@ -37,9 +37,7 @@ import { STUB_MARKER, scaffoldDocs } from './scaffold-docs.js';
37
37
  */
38
38
  export const PLAN_HANDOFF_TEXT =
39
39
  '\n✅ Mandrel is ready. Start your first project:\n\n' +
40
- ' /plan --idea "<one-line description of what you want to build>"\n\n' +
41
- 'Or, if you already have a `type::epic` Issue open:\n\n' +
42
- ' /plan <epicId>\n';
40
+ ' /plan --seed "<one-line description of what you want to build>"\n';
43
41
 
44
42
  // ---------------------------------------------------------------------------
45
43
  // Internal helpers
@@ -0,0 +1,128 @@
1
+ /**
2
+ * audit-lens-routing.js — risk-envelope → audit-lens roster helpers.
3
+ *
4
+ * Extracted from `code-review.js` so the review runner stays focused on
5
+ * executing a review pass, while callers that only need risk→lens mapping
6
+ * (ceremony routing, plan-run epilogue consumers, unit tests) import a
7
+ * smaller pure module.
8
+ */
9
+
10
+ import { selectAuditStrategy } from '../dynamic-workflow/capability.js';
11
+
12
+ /**
13
+ * The axes whose presence (at `high` risk) routes a specific post-delivery
14
+ * audit lens. Mirrors the audit-workflow names under
15
+ * `.agents/workflows/audit-*.md`. Story #3939 broadened the routing so every
16
+ * high-risk REQUIRED axis maps to a lens rather than leaving three of them
17
+ * (`data-migration`, `destructive-mutation`, `billing`) routing nothing:
18
+ *
19
+ * - `security` → `audit-security` (auth/secret boundary)
20
+ * - `public-api` → `audit-architecture` (the canonical
21
+ * architectural axis; a breaking public surface)
22
+ * - `data-migration` → `audit-quality` (migration correctness +
23
+ * regression coverage)
24
+ * - `destructive-mutation` → `audit-security` (irreversible mutation is
25
+ * an auth/abuse boundary; co-routes with
26
+ * `security`, de-duplicated)
27
+ * - `billing` → `audit-privacy` (money + the PII/consent
28
+ * surface that travels with it)
29
+ * - `critical-workflow` → `audit-quality` (the load-bearing path
30
+ * warrants the deepest coverage pass)
31
+ *
32
+ * The `visible-behavior` axis intentionally routes NO lens: it forces the
33
+ * acceptance-spec disposition at plan time and no audit lens maps cleanly to
34
+ * it. Any other axis (or a low/medium-risk axis) contributes no lens.
35
+ *
36
+ * Every key here MUST be a value in the `axis` enum of
37
+ * `.agents/schemas/risk-verdict.schema.json` — the verdict-derived envelope
38
+ * can only ever carry schema-valid axes, so a key absent from that enum is
39
+ * unreachable dead routing (Story #3889 removed the unreachable
40
+ * `architecture` key; the architectural axis is `public-api`).
41
+ */
42
+ const AXIS_TO_LENS = Object.freeze({
43
+ security: 'audit-security',
44
+ 'public-api': 'audit-architecture',
45
+ 'data-migration': 'audit-quality',
46
+ 'destructive-mutation': 'audit-security',
47
+ billing: 'audit-privacy',
48
+ 'critical-workflow': 'audit-quality',
49
+ });
50
+
51
+ /**
52
+ * Stable output order for routed lenses so the lens list is deterministic
53
+ * regardless of axis ordering in the verdict. Every lens any axis can route
54
+ * appears here exactly once; `resolveAuditLenses` filters this canonical
55
+ * order down to the matched set (Story #3939).
56
+ */
57
+ const LENS_ORDER = Object.freeze([
58
+ 'audit-security',
59
+ 'audit-architecture',
60
+ 'audit-quality',
61
+ 'audit-privacy',
62
+ ]);
63
+
64
+ /**
65
+ * Resolve the set of post-delivery audit lenses a judged risk envelope routes.
66
+ *
67
+ * High-risk axes map to their audit lens via {@link AXIS_TO_LENS}; only axes
68
+ * judged `high` contribute (a `low`/`medium` axis carries no lens). The result
69
+ * is de-duplicated and stably ordered by {@link LENS_ORDER} so an envelope
70
+ * listing the `public-api` axis more than once routes `['audit-architecture']`
71
+ * once, not twice — and two distinct axes routing the same lens (e.g.
72
+ * `security` + `destructive-mutation` both → `audit-security`) collapse to a
73
+ * single entry. A low-risk envelope — or any envelope with no high-risk routed
74
+ * axis — resolves to an empty array (no lens beyond the existing baseline
75
+ * gates).
76
+ *
77
+ * Pure function — no I/O, no side effects.
78
+ *
79
+ * @param {{ axes?: Array<{ axis?: string, level?: string }> }} [envelope]
80
+ * @returns {string[]} Ordered, de-duplicated audit-lens identifiers.
81
+ */
82
+ export function resolveAuditLenses(envelope = {}) {
83
+ const axes = Array.isArray(envelope?.axes) ? envelope.axes : [];
84
+ const matched = new Set();
85
+ for (const entry of axes) {
86
+ if (!entry || entry.level !== 'high') continue;
87
+ const lens = AXIS_TO_LENS[entry.axis];
88
+ if (lens) matched.add(lens);
89
+ }
90
+ return LENS_ORDER.filter((lens) => matched.has(lens));
91
+ }
92
+
93
+ /**
94
+ * Build the post-delivery audit-lens execution plan for a judged risk
95
+ * envelope. Each routed lens (see {@link resolveAuditLenses}) is paired with a
96
+ * strategy decision from the **existing** `selectAuditStrategy` engine — no new
97
+ * audit machinery is introduced. A low-risk envelope resolves to an empty
98
+ * `lenses` array and runs no audit beyond the baseline gates (Story #3876).
99
+ *
100
+ * Pure with respect to the injected `selectAuditStrategyFn` (default is the
101
+ * shared dynamic-workflow engine, which is itself pure over its snapshot).
102
+ *
103
+ * @param {{ axes?: Array<{ axis?: string, level?: string }> }} [envelope]
104
+ * @param {{
105
+ * snapshot?: object,
106
+ * forceStrategy?: ('orchestrated'|'sequential'|null),
107
+ * selectAuditStrategyFn?: typeof selectAuditStrategy,
108
+ * }} [opts]
109
+ * @returns {{ lenses: string[], plan: Array<{ lens: string, strategy: string, reason: string, forced: boolean }> }}
110
+ */
111
+ export function planAuditLenses(envelope = {}, opts = {}) {
112
+ const {
113
+ snapshot = {},
114
+ forceStrategy = null,
115
+ selectAuditStrategyFn = selectAuditStrategy,
116
+ } = opts;
117
+ const lenses = resolveAuditLenses(envelope);
118
+ const plan = lenses.map((lens) => {
119
+ const decision = selectAuditStrategyFn({ snapshot, forceStrategy });
120
+ return {
121
+ lens,
122
+ strategy: decision.strategy,
123
+ reason: decision.reason,
124
+ forced: decision.forced,
125
+ };
126
+ });
127
+ return { lenses, plan };
128
+ }
@@ -37,24 +37,27 @@ import {
37
37
  } from 'node:fs';
38
38
  import path from 'node:path';
39
39
 
40
- import { epicTempDir } from '../config/temp-paths.js';
40
+ import { runTempDir } from '../config/temp-paths.js';
41
41
  import { STATE_LABELS } from './ticketing/reads.js';
42
42
  import { upsertStructuredComment } from './ticketing/state.js';
43
43
  import { transitionTicketState } from './ticketing/transition.js';
44
44
 
45
- /** Canonical basename for the per-Epic bookkeeping outbox. */
45
+ /** Canonical basename for the per-run bookkeeping outbox. */
46
46
  const OUTBOX_BASENAME = 'bookkeeping-outbox.ndjson';
47
47
 
48
48
  /**
49
- * Resolve the canonical outbox path for an Epic:
50
- * `temp/epic-<id>/bookkeeping-outbox.ndjson`.
49
+ * Resolve the canonical outbox path for a run:
50
+ * `temp/run-<id>/bookkeeping-outbox.ndjson`.
51
+ *
52
+ * Uses `runTempDir` directly; callers may still pass an `epicId`-named
53
+ * variable while the broader file/flag rename remains out of this pass.
51
54
  *
52
55
  * @param {number} epicId
53
56
  * @param {object} [config] Resolved config (tempRoot).
54
57
  * @returns {string}
55
58
  */
56
59
  export function outboxPathFor(epicId, config) {
57
- return path.join(epicTempDir(epicId, config), OUTBOX_BASENAME);
60
+ return path.join(runTempDir(epicId, config), OUTBOX_BASENAME);
58
61
  }
59
62
 
60
63
  /**
@@ -1,25 +1,33 @@
1
1
  /**
2
- * lib/orchestration/ceremony-routing.js — risk-routed acceptance ceremony
3
- * resolver (Epic #4478, M7-B, Part 2).
2
+ * lib/orchestration/ceremony-routing.js — ceremony-profile + risk-routed
3
+ * acceptance ceremony resolver.
4
4
  *
5
5
  * The sibling of `review-depth.js` (risk → review depth) and
6
- * `code-review.js#resolveAuditLenses` (risk → audit lens): it folds the
7
- * planner-judged risk envelope into a per-cluster ceremony decision for the
8
- * single-delivery acceptance critic — **fresh-context spawn** vs the
9
- * contract-identical **inline** critic. It does NOT invent a new risk score
10
- * and it does NOT own clustering.
6
+ * `audit-lens-routing.js#resolveAuditLenses` (risk → audit lens): it folds the
7
+ * operator ceremony profile and the planner-judged risk envelope into a
8
+ * per-cluster ceremony decision for the single-delivery acceptance critic —
9
+ * **fresh-context spawn** vs the contract-identical **inline** critic. It
10
+ * does NOT invent a new risk score and it does NOT own clustering.
11
+ *
12
+ * ## Ceremony profiles (`delivery.routing.ceremonyProfile`)
13
+ *
14
+ * - `minimal` — always `inline` (skip fresh critic + sampling floor).
15
+ * Use for tiny N=1 Stories the operator trusts.
16
+ * - `standard` — risk-routed (default). Low → inline (+ sampling floor);
17
+ * medium/high → fresh.
18
+ * - `strict` — always `fresh` regardless of risk.
11
19
  *
12
20
  * ## The load-bearing invariant (M4-B acceptance floor — DO NOT VIOLATE)
13
21
  *
14
22
  * Risk-routing chooses fresh-vs-inline **PER CLUSTER**. It NEVER changes the
15
23
  * cluster COUNT. The cluster count is `ceil(totalACs / clusterCeiling)` with
16
24
  * the non-disableable `[1, 8]` clamp, owned entirely by
17
- * `acceptance-clusters.js` and untouched here. A low-risk Epic still gets one
25
+ * `acceptance-clusters.js` and untouched here. A low-risk Story still gets one
18
26
  * verdict per cluster — just possibly authored inline instead of by a fresh
19
27
  * sub-agent. This module takes the cluster index as an INPUT and returns a
20
28
  * decision for that one cluster; it has no way to add or remove clusters.
21
29
  *
22
- * ## Tier rules (per cluster)
30
+ * ## Tier rules (per cluster, `standard` profile)
23
31
  *
24
32
  * - `high` risk → `fresh` (a fresh-context maker-blind spawn).
25
33
  * - `medium` risk → `fresh` (fail toward more ceremony, never less —
@@ -27,7 +35,7 @@
27
35
  * - `low` risk → `inline` (the contract-identical inline critic),
28
36
  * UNLESS the maker-checker sampling floor
29
37
  * selects this cluster → `fresh`.
30
- * - missing / unknown → `fresh` (fail-safe: an Epic that skipped `/plan`
38
+ * - missing / unknown → `fresh` (fail-safe: a Story that skipped `/plan`
31
39
  * has no risk verdict; treat it as needing
32
40
  * the full fresh-context ceremony, exactly
33
41
  * as `review-depth` degrades to `standard`
@@ -36,18 +44,42 @@
36
44
  *
37
45
  * ## Maker-checker sampling floor
38
46
  *
39
- * Even at `low` risk, a fraction of clusters (`freshCriticSampleRate`, default
40
- * 0.2) is forced `fresh` so low risk never means zero independent checking. The
41
- * selection is **deterministic** in the cluster index (a fixed stride), so it
42
- * is stable across re-runs and — critically — never changes the cluster count:
43
- * it only re-labels which of the fixed set of clusters run fresh.
47
+ * Even at `low` risk under `standard`, a fraction of clusters
48
+ * (`freshCriticSampleRate`, default 0.2) is forced `fresh` so low risk never
49
+ * means zero independent checking. The selection is **deterministic** in the
50
+ * cluster index (a fixed stride), so it is stable across re-runs and —
51
+ * critically — never changes the cluster count: it only re-labels which of
52
+ * the fixed set of clusters run fresh. Profiles `minimal` and `strict`
53
+ * ignore the sampling floor.
44
54
  *
45
55
  * Pure and total: inputs in, decision out. No I/O, no throws. `null` /
46
56
  * `undefined` / malformed inputs degrade to `fresh` + `full` ceremony.
47
57
  *
48
58
  * @typedef {'fresh'|'inline'} CeremonyMode
49
59
  * @typedef {'low'|'medium'|'high'} RiskLevel
60
+ * @typedef {'minimal'|'standard'|'strict'} CeremonyProfile
61
+ */
62
+
63
+ /** @type {readonly CeremonyProfile[]} */
64
+ export const CEREMONY_PROFILES = Object.freeze([
65
+ 'minimal',
66
+ 'standard',
67
+ 'strict',
68
+ ]);
69
+
70
+ /**
71
+ * Normalize an operator/config ceremony profile. Unknown values degrade to
72
+ * `standard` (fail toward the documented default, not toward less ceremony).
73
+ *
74
+ * @param {unknown} value
75
+ * @returns {CeremonyProfile}
50
76
  */
77
+ export function normalizeCeremonyProfile(value) {
78
+ if (value === 'minimal' || value === 'standard' || value === 'strict') {
79
+ return value;
80
+ }
81
+ return 'standard';
82
+ }
51
83
 
52
84
  /**
53
85
  * Decide whether the sampling floor forces this low-risk cluster fresh.
@@ -79,16 +111,22 @@ export function sampledFresh(clusterIndex, rate) {
79
111
  }
80
112
 
81
113
  /**
82
- * Resolve the acceptance ceremony for one cluster from the judged risk level
83
- * and the maker-checker sampling floor. See the module header for the tier
84
- * rules and the untouchable cluster-count invariant.
114
+ * Resolve the acceptance ceremony for one cluster from the ceremony profile,
115
+ * judged risk level, and the maker-checker sampling floor. See the module
116
+ * header for the tier rules and the untouchable cluster-count invariant.
85
117
  *
86
118
  * @param {{
87
119
  * overallLevel?: (RiskLevel|string|null|undefined),
88
120
  * clusterIndex?: (number|null|undefined),
89
121
  * freshCriticSampleRate?: (number|null|undefined),
122
+ * ceremonyProfile?: (CeremonyProfile|string|null|undefined),
90
123
  * }} [input]
91
- * @returns {{ mode: CeremonyMode, reason: string, sampled: boolean }}
124
+ * @returns {{
125
+ * mode: CeremonyMode,
126
+ * reason: string,
127
+ * sampled: boolean,
128
+ * profile: CeremonyProfile,
129
+ * }}
92
130
  */
93
131
  export function resolveCeremonyForRisk(input = {}) {
94
132
  const overallLevel =
@@ -99,12 +137,33 @@ export function resolveCeremonyForRisk(input = {}) {
99
137
  input && typeof input === 'object'
100
138
  ? input.freshCriticSampleRate
101
139
  : undefined;
140
+ const profile = normalizeCeremonyProfile(
141
+ input && typeof input === 'object' ? input.ceremonyProfile : undefined,
142
+ );
143
+
144
+ if (profile === 'minimal') {
145
+ return {
146
+ mode: 'inline',
147
+ reason: 'ceremonyProfile=minimal: inline critic (no fresh spawn)',
148
+ sampled: false,
149
+ profile,
150
+ };
151
+ }
152
+ if (profile === 'strict') {
153
+ return {
154
+ mode: 'fresh',
155
+ reason: 'ceremonyProfile=strict: fresh-context critic',
156
+ sampled: false,
157
+ profile,
158
+ };
159
+ }
102
160
 
103
161
  if (overallLevel === 'high') {
104
162
  return {
105
163
  mode: 'fresh',
106
164
  reason: 'high-risk: fresh-context critic',
107
165
  sampled: false,
166
+ profile,
108
167
  };
109
168
  }
110
169
  if (overallLevel === 'medium') {
@@ -112,6 +171,7 @@ export function resolveCeremonyForRisk(input = {}) {
112
171
  mode: 'fresh',
113
172
  reason: 'medium-risk: fresh-context critic (fail toward more ceremony)',
114
173
  sampled: false,
174
+ profile,
115
175
  };
116
176
  }
117
177
  if (overallLevel === 'low') {
@@ -121,21 +181,24 @@ export function resolveCeremonyForRisk(input = {}) {
121
181
  reason:
122
182
  'low-risk cluster forced fresh by the maker-checker sampling floor',
123
183
  sampled: true,
184
+ profile,
124
185
  };
125
186
  }
126
187
  return {
127
188
  mode: 'inline',
128
189
  reason: 'low-risk: contract-identical inline critic',
129
190
  sampled: false,
191
+ profile,
130
192
  };
131
193
  }
132
194
  // Missing / unknown / malformed risk → fail-safe fresh + full ceremony,
133
195
  // matching how review-depth.js and deriveRiskEnvelope degrade on an
134
- // unjudged Epic.
196
+ // unjudged Story.
135
197
  return {
136
198
  mode: 'fresh',
137
199
  reason:
138
200
  'risk absent/unknown: fail-safe fresh-context critic + full ceremony',
139
201
  sampled: false,
202
+ profile,
140
203
  };
141
204
  }