mandrel 1.93.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 (463) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/acceptance-critic.md +129 -0
  3. package/.agents/agents/story-worker.md +161 -0
  4. package/.agents/docs/SDLC.md +489 -1285
  5. package/.agents/docs/agentrc-reference.json +177 -67
  6. package/.agents/docs/configuration.md +108 -136
  7. package/.agents/docs/execution-reference.md +44 -22
  8. package/.agents/docs/quality-gates.md +13 -19
  9. package/.agents/docs/workflows.md +3 -3
  10. package/.agents/instructions.md +107 -108
  11. package/.agents/rules/ci-remediation.md +8 -12
  12. package/.agents/rules/git-conventions-reference.md +224 -0
  13. package/.agents/rules/git-conventions.md +42 -223
  14. package/.agents/rules/security-baseline.md +5 -0
  15. package/.agents/rules/testing-standards.md +106 -13
  16. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  17. package/.agents/schemas/agentrc.schema.json +71 -201
  18. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  20. package/.agents/schemas/risk-verdict.schema.json +0 -13
  21. package/.agents/scripts/acceptance-eval.js +62 -18
  22. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  23. package/.agents/scripts/analyze-execution.js +1 -1
  24. package/.agents/scripts/audit-to-stories.js +7 -7
  25. package/.agents/scripts/boot-sweep.js +1 -1
  26. package/.agents/scripts/check-context-budget.js +62 -5
  27. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  28. package/.agents/scripts/check-prepush-recovery.js +1 -1
  29. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  30. package/.agents/scripts/diagnose-friction.js +0 -6
  31. package/.agents/scripts/lib/Logger.js +6 -10
  32. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  33. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  34. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  35. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  36. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  37. package/.agents/scripts/lib/baselines/reader.js +1 -6
  38. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  39. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  40. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
  41. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  42. package/.agents/scripts/lib/checks/index.js +2 -1
  43. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  44. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  45. package/.agents/scripts/lib/cli-args.js +16 -7
  46. package/.agents/scripts/lib/close-validation/gates.js +160 -22
  47. package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
  48. package/.agents/scripts/lib/config/ci.js +6 -31
  49. package/.agents/scripts/lib/config/delivery-routing.js +103 -0
  50. package/.agents/scripts/lib/config/explain.js +57 -36
  51. package/.agents/scripts/lib/config/limits.js +17 -58
  52. package/.agents/scripts/lib/config/paths.js +0 -2
  53. package/.agents/scripts/lib/config/quality.js +1 -1
  54. package/.agents/scripts/lib/config/runners.js +17 -50
  55. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  56. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  57. package/.agents/scripts/lib/config-resolver.js +3 -8
  58. package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
  59. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -14
  60. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  61. package/.agents/scripts/lib/dependency-parser.js +3 -2
  62. package/.agents/scripts/lib/doc-tiers.js +39 -4
  63. package/.agents/scripts/lib/duplicate-search.js +211 -41
  64. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  65. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  66. package/.agents/scripts/lib/framework-version.js +2 -3
  67. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  68. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  69. package/.agents/scripts/lib/git-utils.js +32 -6
  70. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  71. package/.agents/scripts/lib/label-constants.js +10 -23
  72. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  73. package/.agents/scripts/lib/observability/active-story-env.js +112 -3
  74. package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
  75. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  76. package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
  77. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  78. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
  79. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
  80. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  81. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
  82. package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
  83. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  84. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  85. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  86. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  87. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  88. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  89. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  90. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  91. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  92. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  93. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  94. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  95. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  96. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  97. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  98. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
  99. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  100. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  101. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  102. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  103. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  104. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  105. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  106. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  107. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  108. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +14 -14
  109. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  110. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  111. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  112. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  113. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  114. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  115. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  116. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  117. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  118. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  119. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  120. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  121. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  122. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  123. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  124. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  125. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  126. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
  127. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  128. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  129. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  130. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  131. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  132. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  133. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  134. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  135. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  136. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  137. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  138. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  139. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  140. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  141. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  142. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  143. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  144. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  145. package/.agents/scripts/lib/orchestration/ticket-validator.js +3 -8
  146. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  147. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  148. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  149. package/.agents/scripts/lib/planning-corpus.js +16 -11
  150. package/.agents/scripts/lib/preflight-runner.js +2 -2
  151. package/.agents/scripts/lib/provider-factory.js +1 -1
  152. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  153. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  154. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  155. package/.agents/scripts/lib/story-adjacency.js +11 -14
  156. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  157. package/.agents/scripts/lib/story-plan.js +2 -4
  158. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  159. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  160. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  161. package/.agents/scripts/lib/validation-evidence.js +1 -1
  162. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  163. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  164. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  165. package/.agents/scripts/lint-issue-body.js +71 -40
  166. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  167. package/.agents/scripts/notify.js +4 -3
  168. package/.agents/scripts/plan-context.js +64 -74
  169. package/.agents/scripts/plan-persist.js +121 -280
  170. package/.agents/scripts/plan-run-epilogue.js +97 -0
  171. package/.agents/scripts/post-structured-comment.js +38 -0
  172. package/.agents/scripts/providers/github/issues.js +17 -33
  173. package/.agents/scripts/providers/github/mappers.js +0 -12
  174. package/.agents/scripts/providers/github/tickets.js +2 -5
  175. package/.agents/scripts/resolve-plan-run.js +117 -0
  176. package/.agents/scripts/signals-view.js +24 -19
  177. package/.agents/scripts/single-story-close.js +11 -14
  178. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  179. package/.agents/scripts/single-story-init.js +29 -20
  180. package/.agents/scripts/stories-wave-tick.js +6 -6
  181. package/.agents/scripts/story-plan.js +26 -47
  182. package/.agents/scripts/sync-claude-agents.js +165 -0
  183. package/.agents/scripts/update-ticket-state.js +37 -15
  184. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  185. package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
  186. package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
  187. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
  188. package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
  189. package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
  190. package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
  191. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  192. package/.agents/skills/skills.index.json +8 -418
  193. package/.agents/starter-agentrc.json +0 -5
  194. package/.agents/templates/agent-protocol.md +9 -10
  195. package/.agents/workflows/audit-architecture.md +3 -3
  196. package/.agents/workflows/audit-clean-code.md +3 -3
  197. package/.agents/workflows/audit-dependencies.md +3 -3
  198. package/.agents/workflows/audit-devops.md +3 -3
  199. package/.agents/workflows/audit-documentation.md +5 -5
  200. package/.agents/workflows/audit-lighthouse.md +3 -3
  201. package/.agents/workflows/audit-navigability.md +3 -2
  202. package/.agents/workflows/audit-performance.md +3 -3
  203. package/.agents/workflows/audit-privacy.md +3 -3
  204. package/.agents/workflows/audit-quality.md +3 -3
  205. package/.agents/workflows/audit-security.md +3 -3
  206. package/.agents/workflows/audit-seo.md +3 -3
  207. package/.agents/workflows/audit-sre.md +3 -3
  208. package/.agents/workflows/audit-to-stories.md +20 -20
  209. package/.agents/workflows/audit-ux-ui.md +3 -3
  210. package/.agents/workflows/deliver.md +122 -131
  211. package/.agents/workflows/git-cleanup.md +3 -4
  212. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  213. package/.agents/workflows/helpers/acceptance-self-eval.md +52 -40
  214. package/.agents/workflows/helpers/code-review.md +70 -193
  215. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  216. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  217. package/.agents/workflows/helpers/diagnose.md +10 -10
  218. package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
  219. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  220. package/.agents/workflows/helpers/signals.md +16 -16
  221. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  222. package/.agents/workflows/mandrel-update.md +3 -2
  223. package/.agents/workflows/plan.md +112 -145
  224. package/.agents/workflows/qa-assist.md +24 -30
  225. package/.agents/workflows/qa-explore.md +29 -38
  226. package/.agents/workflows/qa-run.md +2 -2
  227. package/README.md +9 -8
  228. package/docs/CHANGELOG.md +46 -0
  229. package/lib/cli/registry.js +95 -0
  230. package/lib/migrations/index.js +6 -5
  231. package/package.json +5 -3
  232. package/.agents/personas/architect.md +0 -113
  233. package/.agents/personas/devops-engineer.md +0 -38
  234. package/.agents/personas/engineer-mobile.md +0 -120
  235. package/.agents/personas/engineer-web.md +0 -111
  236. package/.agents/personas/engineer.md +0 -119
  237. package/.agents/personas/product.md +0 -94
  238. package/.agents/personas/project-manager.md +0 -114
  239. package/.agents/personas/qa-engineer.md +0 -95
  240. package/.agents/personas/refactorer.md +0 -113
  241. package/.agents/personas/security-engineer.md +0 -112
  242. package/.agents/personas/sre.md +0 -86
  243. package/.agents/personas/technical-writer.md +0 -101
  244. package/.agents/personas/ux-designer.md +0 -95
  245. package/.agents/schemas/dispatch-manifest.json +0 -232
  246. package/.agents/schemas/epic-spec.schema.json +0 -153
  247. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  248. package/.agents/scripts/dispatcher.js +0 -295
  249. package/.agents/scripts/epic-audit-prepare.js +0 -497
  250. package/.agents/scripts/epic-audit-recheck.js +0 -274
  251. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  252. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  253. package/.agents/scripts/epic-deliver-prepare.js +0 -590
  254. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  255. package/.agents/scripts/epic-plan-clarity.js +0 -211
  256. package/.agents/scripts/epic-plan-decompose.js +0 -54
  257. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  258. package/.agents/scripts/epic-plan-spec.js +0 -64
  259. package/.agents/scripts/epic-reconcile.js +0 -625
  260. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  261. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  262. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  263. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  264. package/.agents/scripts/lib/config/preflight.js +0 -58
  265. package/.agents/scripts/lib/config/retro.js +0 -77
  266. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  267. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  268. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  269. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -660
  270. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  271. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  272. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  273. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  274. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  275. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  276. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  277. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  278. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  279. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  280. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  281. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  282. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  283. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  284. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  285. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  286. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  287. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  288. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -388
  289. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  290. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  291. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  292. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  293. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  294. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  295. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  296. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  297. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  298. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  299. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  300. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  301. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  302. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  303. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  304. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  305. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  306. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  307. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  308. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  309. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  310. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  311. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  312. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  313. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
  314. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  315. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  316. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  317. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -278
  318. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  319. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  320. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  321. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  322. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  323. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  324. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  325. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  326. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  327. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  328. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  329. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  330. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  331. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  332. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  333. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  334. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  335. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
  336. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  337. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  338. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  339. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  340. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  341. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  342. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  343. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  344. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  345. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  346. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  347. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  348. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  349. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  350. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  351. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  352. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  353. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  354. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  355. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  356. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  357. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  358. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  359. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  360. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  361. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -301
  362. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  363. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  364. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  365. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  366. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  367. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  368. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  369. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  370. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  371. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  372. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  373. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  374. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  375. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  376. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  377. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  378. package/.agents/scripts/lib/spec/index.js +0 -36
  379. package/.agents/scripts/lib/spec/loader.js +0 -425
  380. package/.agents/scripts/lib/spec/state.js +0 -208
  381. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  382. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  383. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  384. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  385. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  386. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  387. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  388. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  389. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  390. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  391. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  392. package/.agents/scripts/lifecycle-emit.js +0 -510
  393. package/.agents/scripts/plan-critics.js +0 -199
  394. package/.agents/scripts/retro-run.js +0 -218
  395. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  396. package/.agents/scripts/story-close.js +0 -294
  397. package/.agents/scripts/story-init.js +0 -599
  398. package/.agents/scripts/story-phase.js +0 -369
  399. package/.agents/scripts/wave-tick.js +0 -335
  400. package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
  401. package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
  402. package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
  403. package/.agents/skills/core/code-simplification/SKILL.md +0 -389
  404. package/.agents/skills/core/context-engineering/SKILL.md +0 -309
  405. package/.agents/skills/core/context-engineering/examples.md +0 -58
  406. package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
  407. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  408. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  409. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  410. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  411. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  412. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  413. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -413
  414. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  415. package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
  416. package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
  417. package/.agents/skills/core/idea-refinement/examples.md +0 -437
  418. package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
  419. package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
  420. package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
  421. package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
  422. package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
  423. package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
  424. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
  425. package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
  426. package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
  427. package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
  428. package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
  429. package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
  430. package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
  431. package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
  432. package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
  433. package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
  434. package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
  435. package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
  436. package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
  437. package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
  438. package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
  439. package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
  440. package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
  441. package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
  442. package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
  443. package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
  444. package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
  445. package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
  446. package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
  447. package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
  448. package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
  449. package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
  450. package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
  451. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
  452. package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
  453. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
  454. package/.agents/workflows/helpers/deliver-epic.md +0 -955
  455. package/.agents/workflows/helpers/deliver-stories.md +0 -440
  456. package/.agents/workflows/helpers/epic-audit.md +0 -189
  457. package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
  458. package/.agents/workflows/helpers/epic-testing.md +0 -125
  459. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  460. package/.agents/workflows/helpers/plan-epic.md +0 -351
  461. package/.agents/workflows/helpers/plan-story.md +0 -251
  462. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  463. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -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
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * bookkeeping-outbox.js — Epic #4476 (M5): buffered GitHub bookkeeping.
3
+ *
4
+ * Delivery bookkeeping — structured-comment upserts (progress, friction,
5
+ * wave-stall, …) and `agent::*` label flips — is human-visible surface that,
6
+ * in an **unattended** (`--yes` / headless) run, no operator is watching in
7
+ * real time. Emitting each one as a live GitHub round-trip mid-run costs an
8
+ * LLM turn per transition for a surface nobody reads until the run finishes.
9
+ *
10
+ * This module lets those non-urgent mutations be **buffered to a local NDJSON
11
+ * outbox** during the run and **reconciled to GitHub once at finalize**.
12
+ * GitHub stays the source of truth *at rest* (post-reconcile); only the
13
+ * per-transition chatter moves off the token stream. The outbox file survives
14
+ * a crash, so crash recovery drains whatever was buffered before the finalize
15
+ * reconcile ran.
16
+ *
17
+ * ## What is NEVER buffered
18
+ * The `agent::blocked` HITL gate (`.agents/instructions.md` §1.J) is the single
19
+ * authoritative runtime pause point. A genuine blocker MUST surface on GitHub
20
+ * **immediately**, not batched to finalize — an operator can only resume a run
21
+ * they can see is blocked. The {@link transitionStateOrBuffer} facade forces
22
+ * `agent::blocked` (and any caller-marked `urgent`) transition through the
23
+ * live path regardless of headless mode.
24
+ *
25
+ * ## Attended runs are unchanged
26
+ * Buffering is gated on the explicit `headless` signal (Story #4427's
27
+ * `--headless`/`--yes` plumbing). An attended run passes `headless: false` and
28
+ * every comment/label posts live exactly as before — byte-for-byte behaviour.
29
+ */
30
+
31
+ import {
32
+ appendFileSync,
33
+ existsSync,
34
+ mkdirSync,
35
+ readFileSync,
36
+ writeFileSync,
37
+ } from 'node:fs';
38
+ import path from 'node:path';
39
+
40
+ import { runTempDir } from '../config/temp-paths.js';
41
+ import { STATE_LABELS } from './ticketing/reads.js';
42
+ import { upsertStructuredComment } from './ticketing/state.js';
43
+ import { transitionTicketState } from './ticketing/transition.js';
44
+
45
+ /** Canonical basename for the per-run bookkeeping outbox. */
46
+ const OUTBOX_BASENAME = 'bookkeeping-outbox.ndjson';
47
+
48
+ /**
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.
54
+ *
55
+ * @param {number} epicId
56
+ * @param {object} [config] Resolved config (tempRoot).
57
+ * @returns {string}
58
+ */
59
+ export function outboxPathFor(epicId, config) {
60
+ return path.join(runTempDir(epicId, config), OUTBOX_BASENAME);
61
+ }
62
+
63
+ /**
64
+ * Append one operation record to the outbox, creating the parent dir on
65
+ * demand. Best-effort at the storage layer is the caller's concern; here we
66
+ * let a genuine fs failure propagate so a mis-configured outbox path surfaces
67
+ * loudly in tests.
68
+ *
69
+ * @param {string} outboxPath
70
+ * @param {object} op
71
+ */
72
+ function appendOp(outboxPath, op) {
73
+ mkdirSync(path.dirname(outboxPath), { recursive: true });
74
+ appendFileSync(outboxPath, `${JSON.stringify(op)}\n`, 'utf8');
75
+ }
76
+
77
+ /**
78
+ * Buffer a structured-comment upsert. The op captures everything
79
+ * `upsertStructuredComment` needs at drain time so the reconcile is a pure
80
+ * replay with no re-derivation.
81
+ *
82
+ * @param {{ outboxPath: string, ticketId: number, marker: string,
83
+ * body: string, attrs?: Record<string, string|number>|null,
84
+ * ts?: string }} args
85
+ */
86
+ export function enqueueComment({
87
+ outboxPath,
88
+ ticketId,
89
+ marker,
90
+ body,
91
+ attrs = null,
92
+ ts = new Date().toISOString(),
93
+ }) {
94
+ appendOp(outboxPath, {
95
+ kind: 'comment',
96
+ ts,
97
+ ticketId,
98
+ marker,
99
+ body,
100
+ ...(attrs ? { attrs } : {}),
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Buffer an `agent::*` label transition.
106
+ *
107
+ * @param {{ outboxPath: string, ticketId: number, state: string,
108
+ * ts?: string }} args
109
+ */
110
+ export function enqueueLabel({
111
+ outboxPath,
112
+ ticketId,
113
+ state,
114
+ ts = new Date().toISOString(),
115
+ }) {
116
+ appendOp(outboxPath, { kind: 'label', ts, ticketId, state });
117
+ }
118
+
119
+ /**
120
+ * Parse the outbox into an ordered array of operation records. Malformed
121
+ * lines are skipped (never throw). A missing / empty outbox yields `[]`.
122
+ *
123
+ * @param {string} outboxPath
124
+ * @returns {Array<object>}
125
+ */
126
+ export function readOutbox(outboxPath) {
127
+ if (!outboxPath || !existsSync(outboxPath)) return [];
128
+ let raw;
129
+ try {
130
+ raw = readFileSync(outboxPath, 'utf8');
131
+ } catch {
132
+ return [];
133
+ }
134
+ if (!raw) return [];
135
+ const ops = [];
136
+ for (const line of raw.split(/\r?\n/)) {
137
+ if (!line) continue;
138
+ try {
139
+ const op = JSON.parse(line);
140
+ if (op && typeof op === 'object') ops.push(op);
141
+ } catch {
142
+ // skip a torn / malformed line — a partial write from a crash is not
143
+ // fatal to the rest of the batch.
144
+ }
145
+ }
146
+ return ops;
147
+ }
148
+
149
+ /**
150
+ * Drain the outbox to GitHub in FIFO order and clear it on success. Both sink
151
+ * operations are idempotent (comment upserts are marker-scoped;
152
+ * `transitionTicketState` applies one state via the canonical
153
+ * remove-all-then-add path), so a re-run after a partial drain converges. A
154
+ * per-op failure is recorded and the drain continues — a single bad ticket
155
+ * must not strand the rest of the batch. The outbox file is only truncated
156
+ * when EVERY op succeeded, so a crash mid-drain leaves the un-applied
157
+ * remainder for the next reconcile.
158
+ *
159
+ * @param {{ outboxPath: string,
160
+ * provider: import('../ITicketingProvider.js').ITicketingProvider,
161
+ * logger?: { warn?: (m: string) => void } }} args
162
+ * @returns {Promise<{ drained: number, comments: number, labels: number,
163
+ * errors: Array<{ op: object, error: string }>, cleared: boolean }>}
164
+ */
165
+ export async function reconcileOutbox({ outboxPath, provider, logger }) {
166
+ const ops = readOutbox(outboxPath);
167
+ const result = {
168
+ drained: 0,
169
+ comments: 0,
170
+ labels: 0,
171
+ errors: [],
172
+ cleared: false,
173
+ };
174
+ if (ops.length === 0) {
175
+ result.cleared = true;
176
+ return result;
177
+ }
178
+
179
+ for (const op of ops) {
180
+ try {
181
+ if (op.kind === 'comment') {
182
+ await upsertStructuredComment(
183
+ provider,
184
+ op.ticketId,
185
+ op.marker,
186
+ op.body,
187
+ op.attrs ?? null,
188
+ );
189
+ result.comments += 1;
190
+ result.drained += 1;
191
+ } else if (op.kind === 'label') {
192
+ await transitionTicketState(provider, op.ticketId, op.state);
193
+ result.labels += 1;
194
+ result.drained += 1;
195
+ } else {
196
+ result.errors.push({ op, error: `unknown op kind "${op.kind}"` });
197
+ }
198
+ } catch (err) {
199
+ const message = err instanceof Error ? err.message : String(err);
200
+ result.errors.push({ op, error: message });
201
+ logger?.warn?.(
202
+ `[bookkeeping-outbox] reconcile op failed (${op.kind} #${op.ticketId}): ${message}`,
203
+ );
204
+ }
205
+ }
206
+
207
+ // Only truncate when the whole batch landed — otherwise the un-applied
208
+ // ops must survive for the next reconcile (crash-recovery contract).
209
+ if (result.errors.length === 0) {
210
+ try {
211
+ writeFileSync(outboxPath, '', 'utf8');
212
+ result.cleared = true;
213
+ } catch {
214
+ // A clear failure is non-fatal: the ops all landed, and the idempotent
215
+ // sinks make a redundant re-drain a no-op.
216
+ }
217
+ }
218
+ return result;
219
+ }
220
+
221
+ /**
222
+ * Facade: post a structured comment live, OR buffer it to the outbox when the
223
+ * run is headless. Comments are never urgent (the operator reads them at
224
+ * finalize), so headless always buffers when an `outboxPath` is available.
225
+ *
226
+ * @param {{ provider: object, ticketId: number, marker: string, body: string,
227
+ * attrs?: Record<string, string|number>|null, headless?: boolean,
228
+ * outboxPath?: string|null }} args
229
+ * @returns {Promise<{ buffered: boolean }>}
230
+ */
231
+ export async function postCommentOrBuffer({
232
+ provider,
233
+ ticketId,
234
+ marker,
235
+ body,
236
+ attrs = null,
237
+ headless = false,
238
+ outboxPath = null,
239
+ }) {
240
+ if (headless && outboxPath) {
241
+ enqueueComment({ outboxPath, ticketId, marker, body, attrs });
242
+ return { buffered: true };
243
+ }
244
+ await upsertStructuredComment(provider, ticketId, marker, body, attrs);
245
+ return { buffered: false };
246
+ }
247
+
248
+ /**
249
+ * Facade: flip an `agent::*` state live, OR buffer it when headless — EXCEPT
250
+ * `agent::blocked` (and any caller-marked `urgent` flip), which ALWAYS goes
251
+ * live so the HITL gate surfaces immediately (§1.J).
252
+ *
253
+ * @param {{ provider: object, ticketId: number, state: string,
254
+ * headless?: boolean, outboxPath?: string|null,
255
+ * urgent?: boolean }} args
256
+ * @returns {Promise<{ buffered: boolean }>}
257
+ */
258
+ export async function transitionStateOrBuffer({
259
+ provider,
260
+ ticketId,
261
+ state,
262
+ headless = false,
263
+ outboxPath = null,
264
+ urgent = false,
265
+ }) {
266
+ const mustSurfaceNow = urgent || state === STATE_LABELS.BLOCKED;
267
+ if (headless && outboxPath && !mustSurfaceNow) {
268
+ enqueueLabel({ outboxPath, ticketId, state });
269
+ return { buffered: true };
270
+ }
271
+ await transitionTicketState(provider, ticketId, state);
272
+ return { buffered: false };
273
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * lib/orchestration/ceremony-routing.js — ceremony-profile + risk-routed
3
+ * acceptance ceremony resolver.
4
+ *
5
+ * The sibling of `review-depth.js` (risk → review depth) and
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.
19
+ *
20
+ * ## The load-bearing invariant (M4-B acceptance floor — DO NOT VIOLATE)
21
+ *
22
+ * Risk-routing chooses fresh-vs-inline **PER CLUSTER**. It NEVER changes the
23
+ * cluster COUNT. The cluster count is `ceil(totalACs / clusterCeiling)` with
24
+ * the non-disableable `[1, 8]` clamp, owned entirely by
25
+ * `acceptance-clusters.js` and untouched here. A low-risk Story still gets one
26
+ * verdict per cluster — just possibly authored inline instead of by a fresh
27
+ * sub-agent. This module takes the cluster index as an INPUT and returns a
28
+ * decision for that one cluster; it has no way to add or remove clusters.
29
+ *
30
+ * ## Tier rules (per cluster, `standard` profile)
31
+ *
32
+ * - `high` risk → `fresh` (a fresh-context maker-blind spawn).
33
+ * - `medium` risk → `fresh` (fail toward more ceremony, never less —
34
+ * matches `review-depth`'s fail-to-middle).
35
+ * - `low` risk → `inline` (the contract-identical inline critic),
36
+ * UNLESS the maker-checker sampling floor
37
+ * selects this cluster → `fresh`.
38
+ * - missing / unknown → `fresh` (fail-safe: a Story that skipped `/plan`
39
+ * has no risk verdict; treat it as needing
40
+ * the full fresh-context ceremony, exactly
41
+ * as `review-depth` degrades to `standard`
42
+ * and `deriveRiskEnvelope` degrades to
43
+ * review-required).
44
+ *
45
+ * ## Maker-checker sampling floor
46
+ *
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.
54
+ *
55
+ * Pure and total: inputs in, decision out. No I/O, no throws. `null` /
56
+ * `undefined` / malformed inputs degrade to `fresh` + `full` ceremony.
57
+ *
58
+ * @typedef {'fresh'|'inline'} CeremonyMode
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}
76
+ */
77
+ export function normalizeCeremonyProfile(value) {
78
+ if (value === 'minimal' || value === 'standard' || value === 'strict') {
79
+ return value;
80
+ }
81
+ return 'standard';
82
+ }
83
+
84
+ /**
85
+ * Decide whether the sampling floor forces this low-risk cluster fresh.
86
+ *
87
+ * Deterministic in the cluster index: with rate `r` (0 < r ≤ 1) the stride is
88
+ * `round(1 / r)` and every `stride`-th cluster (0-based indices 0, stride,
89
+ * 2·stride, …) is forced fresh, yielding ≈`r` of clusters fresh. `r <= 0`
90
+ * disables the floor (no cluster forced); `r >= 1` forces every cluster.
91
+ *
92
+ * @param {number} clusterIndex Zero-based cluster position (from the fixed
93
+ * `ceil(totalACs / clusterCeiling)` fan-out — an INPUT, never mutated here).
94
+ * @param {number} rate Sampling rate, already clamped into [0, 1] by
95
+ * `getDeliveryRouting`.
96
+ * @returns {boolean} `true` when the floor forces this cluster fresh.
97
+ */
98
+ export function sampledFresh(clusterIndex, rate) {
99
+ if (typeof rate !== 'number' || !Number.isFinite(rate) || rate <= 0) {
100
+ return false;
101
+ }
102
+ if (rate >= 1) return true;
103
+ const idx =
104
+ typeof clusterIndex === 'number' &&
105
+ Number.isInteger(clusterIndex) &&
106
+ clusterIndex >= 0
107
+ ? clusterIndex
108
+ : 0;
109
+ const stride = Math.max(1, Math.round(1 / rate));
110
+ return idx % stride === 0;
111
+ }
112
+
113
+ /**
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.
117
+ *
118
+ * @param {{
119
+ * overallLevel?: (RiskLevel|string|null|undefined),
120
+ * clusterIndex?: (number|null|undefined),
121
+ * freshCriticSampleRate?: (number|null|undefined),
122
+ * ceremonyProfile?: (CeremonyProfile|string|null|undefined),
123
+ * }} [input]
124
+ * @returns {{
125
+ * mode: CeremonyMode,
126
+ * reason: string,
127
+ * sampled: boolean,
128
+ * profile: CeremonyProfile,
129
+ * }}
130
+ */
131
+ export function resolveCeremonyForRisk(input = {}) {
132
+ const overallLevel =
133
+ input && typeof input === 'object' ? input.overallLevel : undefined;
134
+ const clusterIndex =
135
+ input && typeof input === 'object' ? input.clusterIndex : undefined;
136
+ const rate =
137
+ input && typeof input === 'object'
138
+ ? input.freshCriticSampleRate
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
+ }
160
+
161
+ if (overallLevel === 'high') {
162
+ return {
163
+ mode: 'fresh',
164
+ reason: 'high-risk: fresh-context critic',
165
+ sampled: false,
166
+ profile,
167
+ };
168
+ }
169
+ if (overallLevel === 'medium') {
170
+ return {
171
+ mode: 'fresh',
172
+ reason: 'medium-risk: fresh-context critic (fail toward more ceremony)',
173
+ sampled: false,
174
+ profile,
175
+ };
176
+ }
177
+ if (overallLevel === 'low') {
178
+ if (sampledFresh(clusterIndex, rate)) {
179
+ return {
180
+ mode: 'fresh',
181
+ reason:
182
+ 'low-risk cluster forced fresh by the maker-checker sampling floor',
183
+ sampled: true,
184
+ profile,
185
+ };
186
+ }
187
+ return {
188
+ mode: 'inline',
189
+ reason: 'low-risk: contract-identical inline critic',
190
+ sampled: false,
191
+ profile,
192
+ };
193
+ }
194
+ // Missing / unknown / malformed risk → fail-safe fresh + full ceremony,
195
+ // matching how review-depth.js and deriveRiskEnvelope degrade on an
196
+ // unjudged Story.
197
+ return {
198
+ mode: 'fresh',
199
+ reason:
200
+ 'risk absent/unknown: fail-safe fresh-context critic + full ceremony',
201
+ sampled: false,
202
+ profile,
203
+ };
204
+ }