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
@@ -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
@@ -37,9 +37,21 @@ const ENV_LOCAL_BASENAME = '.env.local';
37
37
  /**
38
38
  * Names of the env vars we own. Keeping the list central makes the
39
39
  * round-trip (set on init, clear on close) trivially auditable —
40
- * grep `CC_EPIC_ID` / `CC_STORY_ID` to find every read site.
40
+ * grep `CC_EPIC_ID` / `CC_STORY_ID` / `CC_SLICE_ID` to find every read site.
41
+ *
42
+ * Epic #4476 (M5) added `CC_SLICE_ID` (the single-delivery analogue of
43
+ * `CC_STORY_ID` — set on the ONE long guarded session so the PostToolUse
44
+ * hook can emit `slice.heartbeat` off the token stream) and `CC_OPERATOR`
45
+ * (the resolved lease-owner handle, stamped onto hook-emitted heartbeats so
46
+ * `latestHeartbeatForOwner` keeps resolving a live claim). The clear path
47
+ * wipes all four so no stale context leaks past close.
41
48
  */
42
- export const ACTIVE_STORY_ENV_KEYS = ['CC_EPIC_ID', 'CC_STORY_ID'];
49
+ export const ACTIVE_STORY_ENV_KEYS = [
50
+ 'CC_EPIC_ID',
51
+ 'CC_STORY_ID',
52
+ 'CC_SLICE_ID',
53
+ 'CC_OPERATOR',
54
+ ];
43
55
 
44
56
  /**
45
57
  * Render the `.env.local` body. One `KEY=value` line per var, LF
@@ -180,3 +192,100 @@ export function clearActiveStoryEnv({
180
192
  }
181
193
  return { envCleared: true, fileRemoved, filePath };
182
194
  }
195
+
196
+ /**
197
+ * Render the single-delivery `.env.local` body. The single-delivery executor
198
+ * (`deliver-epic-single.md`) walks the Delivery Slicing table inside ONE long
199
+ * session; there is no Story fan-out and thus no `CC_STORY_ID`. Instead the
200
+ * "current slice" is exported as `CC_SLICE_ID` so the PostToolUse hook emits
201
+ * `slice.heartbeat` — the watchdog's forward-progress signal for the single
202
+ * session — as a free byproduct of tool activity.
203
+ *
204
+ * `CC_STORY_ID` MUST be absent (its presence would make the hook emit
205
+ * `story.heartbeat` and the trace path key off a non-existent Story). The
206
+ * optional `operator` handle is emitted as `CC_OPERATOR` only when supplied.
207
+ *
208
+ * Exported for testing.
209
+ *
210
+ * @param {{ epicId: number, sliceId: string, operator?: string }} input
211
+ * @returns {string}
212
+ */
213
+ export function renderActiveSliceEnvFile({ epicId, sliceId, operator }) {
214
+ const lines = [
215
+ '# Auto-managed by .agents/scripts/lib/observability/active-story-env.js',
216
+ '# Re-generated on every slice-start; deleted on epic-close.',
217
+ `CC_EPIC_ID=${epicId}`,
218
+ `CC_SLICE_ID=${sliceId}`,
219
+ ];
220
+ if (typeof operator === 'string' && operator.length > 0) {
221
+ lines.push(`CC_OPERATOR=${operator}`);
222
+ }
223
+ lines.push('');
224
+ return lines.join('\n');
225
+ }
226
+
227
+ /**
228
+ * Set `CC_EPIC_ID` / `CC_SLICE_ID` (+ optional `CC_OPERATOR`) on the current
229
+ * process and, when `workCwd` is provided, export them to
230
+ * `<workCwd>/.env.local` for the harness to reload on the next tool call. The
231
+ * single-delivery analogue of {@link setActiveStoryEnv}; called at each
232
+ * `slice.start` boundary so the hook's throttled `slice.heartbeat` is keyed to
233
+ * the slice currently being implemented.
234
+ *
235
+ * `CC_STORY_ID` is explicitly removed so a prior Story context (if any ever
236
+ * leaked in) cannot make the hook emit the wrong heartbeat shape.
237
+ *
238
+ * @param {{ epicId: number, sliceId: string, operator?: string|null,
239
+ * workCwd?: string, env?: NodeJS.ProcessEnv, fs?: typeof nodeFs,
240
+ * logger?: { warn?: (m: string) => void } }} args
241
+ * @returns {{ envSet: boolean, fileWritten: boolean, filePath: string|null }}
242
+ */
243
+ export function setActiveSliceEnv({
244
+ epicId,
245
+ sliceId,
246
+ operator,
247
+ workCwd,
248
+ env = process.env,
249
+ fs = nodeFs,
250
+ logger,
251
+ } = {}) {
252
+ if (!Number.isInteger(epicId) || epicId <= 0) {
253
+ throw new Error(
254
+ `[active-story-env] epicId must be a positive integer; got ${epicId}`,
255
+ );
256
+ }
257
+ if (typeof sliceId !== 'string' || sliceId.length === 0) {
258
+ throw new Error(
259
+ `[active-story-env] sliceId must be a non-empty string; got ${sliceId}`,
260
+ );
261
+ }
262
+ const normOperator =
263
+ typeof operator === 'string' && operator.length > 0 ? operator : undefined;
264
+
265
+ if ('CC_STORY_ID' in env) delete env.CC_STORY_ID;
266
+ env.CC_EPIC_ID = String(epicId);
267
+ env.CC_SLICE_ID = sliceId;
268
+ if (normOperator) env.CC_OPERATOR = normOperator;
269
+ else if ('CC_OPERATOR' in env) delete env.CC_OPERATOR;
270
+
271
+ let fileWritten = false;
272
+ let filePath = null;
273
+ if (typeof workCwd === 'string' && workCwd.length > 0) {
274
+ filePath = nodePath.join(workCwd, ENV_LOCAL_BASENAME);
275
+ try {
276
+ fs.writeFileSync(
277
+ filePath,
278
+ renderActiveSliceEnvFile({ epicId, sliceId, operator: normOperator }),
279
+ { encoding: 'utf8' },
280
+ );
281
+ fileWritten = true;
282
+ } catch (err) {
283
+ logger?.warn?.(
284
+ `[active-story-env] Failed to write ${filePath}: ${
285
+ err instanceof Error ? err.message : String(err)
286
+ }`,
287
+ );
288
+ }
289
+ }
290
+ return { envSet: true, fileWritten, filePath };
291
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * hook-heartbeat.js — Epic #4476 (M5): heartbeats OFF the token stream.
3
+ *
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
+ * `story-phase.js` / `slice-phase.js --event heartbeat` at least once per
8
+ * meaningful step, and every such call was a full-priced LLM turn re-reading
9
+ * ~100k of cached context just to append one NDJSON line.
10
+ *
11
+ * This module makes that liveness signal a **free byproduct of the agent
12
+ * doing any work**. It is invoked from the existing PostToolUse trace hook
13
+ * (`tool-trace-hook.js`), so every tool call the agent makes refreshes the
14
+ * heartbeat — no dedicated bookkeeping turn required. The record it appends is
15
+ * byte-for-byte the same `story.heartbeat` / `slice.heartbeat` shape the
16
+ * watchdog already consumes (emitted through the same
17
+ * `emit-story-heartbeat.js` / `emit-slice-lifecycle.js` emitters), so the
18
+ * watchdog needs no change: the signal is preserved, only its emission
19
+ * mechanism moves off the token stream.
20
+ *
21
+ * ## Robustness contract (mirrors `tool-trace-hook.js`)
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.
26
+ * - **Best-effort.** Every failure is swallowed. A heartbeat is
27
+ * observability, not state; a hook must never block tool execution.
28
+ * - **Throttled across processes.** Command hooks are spawned as fresh
29
+ * `node` processes per tool call, so in-process state cannot throttle. The
30
+ * throttle is anchored to the **mtime of a sidecar marker file** under the
31
+ * Epic temp dir: at most one heartbeat per `HEARTBEAT_MIN_INTERVAL_MS`
32
+ * lands, regardless of how many tool calls fire in that window. This keeps
33
+ * the ledger from ballooning while still refreshing liveness faster than
34
+ * the watchdog's threshold.
35
+ */
36
+
37
+ import { statSync, utimesSync, writeFileSync } from 'node:fs';
38
+ import path from 'node:path';
39
+
40
+ import { epicTempDir } from '../config/temp-paths.js';
41
+ import { emitStoryHeartbeat } from '../orchestration/lifecycle/emit-story-heartbeat.js';
42
+
43
+ /**
44
+ * Default minimum wall-clock gap between two hook-emitted heartbeats for the
45
+ * same target. Comfortably below the watchdog's default 30-minute staleness
46
+ * threshold, so an actively-working agent never trips a false stall, while
47
+ * still bounding ledger growth to ~1 line/minute of activity.
48
+ */
49
+ const HEARTBEAT_MIN_INTERVAL_MS = 60_000;
50
+
51
+ /**
52
+ * Resolve the heartbeat target from the active-Story / active-slice env vars.
53
+ *
54
+ * A `story.heartbeat` requires a parent `epicId` (its schema pins
55
+ * `epicId >= 1`), so a **standalone** Story (`CC_STORY_ID` set, `CC_EPIC_ID`
56
+ * absent) yields `null` — there is no Epic-scoped ledger to write to and the
57
+ * standalone path is not watched by the Epic idle watchdog. The trace hook
58
+ * still records that context's traces; only the heartbeat is skipped.
59
+ *
60
+ * Inert `slice.*` lifecycle (and `CC_SLICE_ID` heartbeats) were removed in
61
+ * the v2 ceremony lock-in — `## Slicing` is prose-only.
62
+ *
63
+ * @param {NodeJS.ProcessEnv} [env]
64
+ * @returns {{ kind: 'story', epicId: number, storyId: number, operator?: string }
65
+ * | null}
66
+ */
67
+ export function resolveHeartbeatTarget(env = process.env) {
68
+ const epicRaw = env.CC_EPIC_ID;
69
+ const storyRaw = env.CC_STORY_ID;
70
+ const operator =
71
+ typeof env.CC_OPERATOR === 'string' && env.CC_OPERATOR.length > 0
72
+ ? env.CC_OPERATOR
73
+ : undefined;
74
+
75
+ const epicId = epicRaw ? Number.parseInt(epicRaw, 10) : Number.NaN;
76
+ const epicOk = Number.isInteger(epicId) && epicId > 0;
77
+
78
+ if (storyRaw) {
79
+ const storyId = Number.parseInt(storyRaw, 10);
80
+ if (!Number.isInteger(storyId) || storyId <= 0) return null;
81
+ // story.heartbeat has no meaning without a parent Epic ledger.
82
+ if (!epicOk) return null;
83
+ return {
84
+ kind: 'story',
85
+ epicId,
86
+ storyId,
87
+ ...(operator ? { operator } : {}),
88
+ };
89
+ }
90
+
91
+ return null;
92
+ }
93
+
94
+ /**
95
+ * Stable, filesystem-safe marker basename for a target. The marker's mtime is
96
+ * the throttle anchor; its contents are irrelevant.
97
+ *
98
+ * @param {{ kind: string, storyId?: number, sliceId?: string }} target
99
+ * @returns {string}
100
+ */
101
+ export function heartbeatMarkerName(target) {
102
+ return `.heartbeat-story-${target.storyId}`;
103
+ }
104
+
105
+ /**
106
+ * Throttle predicate. Returns `true` when no marker exists yet, or the marker
107
+ * is older than `intervalMs`. Any stat error (missing file / unreadable) is
108
+ * treated as "emit" — the safe direction for a liveness signal.
109
+ *
110
+ * @param {{ markerPath: string, now: Date, intervalMs: number, statFn?: typeof statSync }} args
111
+ * @returns {boolean}
112
+ */
113
+ export function shouldEmitHeartbeat({
114
+ markerPath,
115
+ now,
116
+ intervalMs,
117
+ statFn = statSync,
118
+ }) {
119
+ try {
120
+ const st = statFn(markerPath);
121
+ return now.getTime() - st.mtimeMs >= intervalMs;
122
+ } catch {
123
+ return true;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Update (or create) the throttle marker so its mtime is `now`. Best-effort:
129
+ * a failure here at worst lets the next tool call emit a second heartbeat.
130
+ *
131
+ * @param {string} markerPath
132
+ * @param {Date} now
133
+ */
134
+ function touchMarker(markerPath, now) {
135
+ try {
136
+ writeFileSync(markerPath, '', { flag: 'a' });
137
+ utimesSync(markerPath, now, now);
138
+ } catch {
139
+ // swallow — throttle degrades to "emit again next call", never fatal.
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Emit one throttled heartbeat for the active target, if any. The whole body
145
+ * is best-effort: any failure returns a `{ emitted: false }` envelope rather
146
+ * than throwing, so the calling PostToolUse hook never blocks the tool.
147
+ *
148
+ * @param {object} [opts]
149
+ * @param {NodeJS.ProcessEnv} [opts.env] Defaults to `process.env`.
150
+ * @param {Date} [opts.now] Injected clock (tests).
151
+ * @param {number} [opts.intervalMs] Throttle window.
152
+ * @param {object} [opts.config] Resolved config (tempRoot).
153
+ * @returns {{ emitted: boolean, reason?: string, kind?: string, ledgerPath?: string }}
154
+ */
155
+ export function emitHeartbeatFromHook({
156
+ env = process.env,
157
+ now = new Date(),
158
+ intervalMs = HEARTBEAT_MIN_INTERVAL_MS,
159
+ config,
160
+ } = {}) {
161
+ try {
162
+ const target = resolveHeartbeatTarget(env);
163
+ if (!target) return { emitted: false, reason: 'no-target' };
164
+
165
+ const markerPath = path.join(
166
+ epicTempDir(target.epicId, config),
167
+ heartbeatMarkerName(target),
168
+ );
169
+ if (!shouldEmitHeartbeat({ markerPath, now, intervalMs })) {
170
+ return { emitted: false, reason: 'throttled', kind: target.kind };
171
+ }
172
+
173
+ const timestamp = now.toISOString();
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
+ });
182
+ touchMarker(markerPath, now);
183
+ return { emitted: true, kind: target.kind, ledgerPath: res?.ledgerPath };
184
+ } catch {
185
+ return { emitted: false, reason: 'error' };
186
+ }
187
+ }
@@ -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) {
@@ -48,6 +48,7 @@
48
48
 
49
49
  import { createHash } from 'node:crypto';
50
50
 
51
+ import { emitHeartbeatFromHook } from './hook-heartbeat.js';
51
52
  import { appendTrace } from './signals-writer.js';
52
53
 
53
54
  /**
@@ -381,15 +382,25 @@ export async function handlePost(event, active) {
381
382
  */
382
383
  export async function main(event) {
383
384
  try {
384
- const active = resolveActiveStory();
385
- if (!active) return; // No active Story => zero filesystem calls.
386
385
  if (!event || typeof event !== 'object') return;
386
+ const active = resolveActiveStory();
387
387
 
388
388
  const phase = event.hook_event_name;
389
389
  if (phase === 'PreToolUse') {
390
- handlePre(event);
390
+ // Pre-pairing only matters for the trace-line duration, which only
391
+ // the Story-scoped trace path records; slice-only context (single
392
+ // delivery) needs no Pre.
393
+ if (active) handlePre(event);
391
394
  } else if (phase === 'PostToolUse') {
392
- await handlePost(event, active);
395
+ if (active) await handlePost(event, active);
396
+ // Heartbeat OFF the token stream (Epic #4476 M5). A throttled
397
+ // story.heartbeat / slice.heartbeat, keyed off the same active-Story /
398
+ // active-slice env vars, so the §2e Idle Watchdog's forward-progress
399
+ // signal is a free byproduct of ANY tool call — no dedicated
400
+ // bookkeeping LLM turn. Best-effort and self-guarded; resolves its own
401
+ // target (fires for slice context even when `active` is null because
402
+ // there is no CC_STORY_ID under single delivery).
403
+ emitHeartbeatFromHook();
393
404
  }
394
405
  // Any other phase is silently ignored — the hook is registered for
395
406
  // Pre/Post only; receiving anything else is a configuration error
@@ -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,111 @@
1
+ /**
2
+ * acceptance-clusters.js — Epic #4475 (M4-B), design §S2a.
3
+ *
4
+ * The **acceptance-dilution guard** for single delivery. In the fan-out
5
+ * shape, each Story ran its own fresh-context acceptance self-eval critic, so
6
+ * a 14-AC Epic decomposed into 4 Stories got ~4 independent critic passes for
7
+ * free. Single delivery collapses the whole Epic into ONE guarded session — if
8
+ * we ran a single critic over all 14 ACs it would degrade to two redraft
9
+ * rounds scoring everything at once (the exact "acceptance dilution" risk the
10
+ * adopted design calls out as blocking).
11
+ *
12
+ * This module restores the distributed coverage: the Epic's
13
+ * `## Acceptance Table` AC ids are split into clusters of at most
14
+ * `clusterCeiling` (config `delivery.acceptanceEval.clusterCeiling`, default 4,
15
+ * hard-clamped to `[1, 8]`), and the executor spawns ONE maker-blind
16
+ * fresh-context `Agent` critic per cluster. The cluster count is therefore
17
+ * `ceil(totalACs / clusterCeiling)` — a deterministic fan-out width the
18
+ * `deliver-epic-single.md` executor and its isolation test both key off.
19
+ *
20
+ * Pure and total — inputs in, clusters out. No I/O, no throws on malformed
21
+ * input (a non-array / empty AC set yields zero clusters — the executor then
22
+ * has nothing to critique, which the front gate already forecloses by refusing
23
+ * `acceptance::n-a`).
24
+ */
25
+
26
+ import { getAcceptanceEval } from '../config/acceptance-eval.js';
27
+
28
+ /**
29
+ * @typedef {object} AcceptanceCluster
30
+ * @property {number} clusterIndex Zero-based position in the fan-out.
31
+ * @property {string} clusterId Stable id (`ac-cluster-<n>`, 1-based).
32
+ * @property {string[]} acIds The AC ids this critic scores (≤ ceiling).
33
+ */
34
+
35
+ /**
36
+ * Split an ordered list of AC ids into fresh-context critic clusters of at
37
+ * most `ceiling` ids each. The count is exactly `ceil(acIds.length / ceiling)`
38
+ * — the load-bearing fan-out-width invariant the isolation test pins.
39
+ *
40
+ * The input order is preserved (the executor orders ACs by their associated
41
+ * Delivery-Slicing slice before calling this, so same-slice ACs land adjacent
42
+ * and tend to share a cluster), but ordering is a nicety — the count guarantee
43
+ * is what forecloses dilution.
44
+ *
45
+ * @param {string[]} acIds Ordered AC ids (e.g. `['AC-1', 'AC-2', …]`).
46
+ * @param {number} [ceiling=4] Max ACs per cluster. A non-positive / non-
47
+ * integer ceiling degrades to 1 (one AC per
48
+ * cluster — the maximally-distributed, never-
49
+ * collapsed fan-out).
50
+ * @returns {AcceptanceCluster[]} `ceil(n / ceiling)` clusters; `[]` when there
51
+ * are no AC ids.
52
+ */
53
+ export function clusterAcceptanceCriteria(acIds, ceiling = 4) {
54
+ const ids = Array.isArray(acIds)
55
+ ? acIds.filter((id) => typeof id === 'string' && id.length > 0)
56
+ : [];
57
+ if (ids.length === 0) return [];
58
+
59
+ const size =
60
+ typeof ceiling === 'number' && Number.isInteger(ceiling) && ceiling >= 1
61
+ ? ceiling
62
+ : 1;
63
+
64
+ const clusters = [];
65
+ for (let start = 0; start < ids.length; start += size) {
66
+ const clusterIndex = clusters.length;
67
+ clusters.push({
68
+ clusterIndex,
69
+ clusterId: `ac-cluster-${clusterIndex + 1}`,
70
+ acIds: ids.slice(start, start + size),
71
+ });
72
+ }
73
+ return clusters;
74
+ }
75
+
76
+ /**
77
+ * The expected fan-out width for `total` ACs at `ceiling` — the number of
78
+ * independent maker-blind critic passes the single-delivery executor spawns.
79
+ * Exported so the executor (and its isolation test) can assert the number of
80
+ * `Agent` critic spawns equals this without re-deriving the ceil math.
81
+ *
82
+ * @param {number} total Total AC count.
83
+ * @param {number} ceiling Max ACs per cluster (≥ 1; degrades to 1 otherwise).
84
+ * @returns {number} `ceil(total / ceiling)`; `0` when `total <= 0`.
85
+ */
86
+ export function expectedClusterCount(total, ceiling) {
87
+ const n = Number.isInteger(total) && total > 0 ? total : 0;
88
+ if (n === 0) return 0;
89
+ const size =
90
+ typeof ceiling === 'number' && Number.isInteger(ceiling) && ceiling >= 1
91
+ ? ceiling
92
+ : 1;
93
+ return Math.ceil(n / size);
94
+ }
95
+
96
+ /**
97
+ * Convenience wrapper: resolve the effective `clusterCeiling` from config
98
+ * (applying the framework default + the undisableable `[1, 8]` clamp) and
99
+ * cluster `acIds` with it. The single home the executor's `slice-phase`
100
+ * substrate and any future CLI use so the clamp is applied once.
101
+ *
102
+ * @param {string[]} acIds
103
+ * @param {object | null | undefined} config Resolved `.agentrc.json`.
104
+ * @returns {{ clusters: AcceptanceCluster[], clusterCeiling: number, totalAcs: number }}
105
+ */
106
+ export function clusterAcceptanceForConfig(acIds, config) {
107
+ const { clusterCeiling } = getAcceptanceEval(config);
108
+ const clusters = clusterAcceptanceCriteria(acIds, clusterCeiling);
109
+ const ids = Array.isArray(acIds) ? acIds.filter(Boolean) : [];
110
+ return { clusters, clusterCeiling, totalAcs: ids.length };
111
+ }
@@ -41,7 +41,10 @@
41
41
 
42
42
  import { readFileSync } from 'node:fs';
43
43
 
44
- import { signalsFile } from '../config/temp-paths.js';
44
+ import { epicArtifactPath, signalsFile } from '../config/temp-paths.js';
45
+
46
+ /** Epic-level signals stream basename (mirrors signals-writer). */
47
+ const EPIC_SIGNALS_BASENAME = 'signals.ndjson';
45
48
 
46
49
  /**
47
50
  * Verdicts that clear a criterion. Anything else (`partial`, `unmet`, or
@@ -168,11 +171,18 @@ export function buildAcceptanceEvalSignal({
168
171
  epicId,
169
172
  outcome,
170
173
  phase = 'implement',
174
+ clusterId = null,
171
175
  }) {
172
176
  return {
173
177
  kind: 'acceptance-eval',
174
178
  epicId: epicId ?? null,
175
- storyId,
179
+ storyId: storyId ?? null,
180
+ // Epic #4475 (M4-B): single-delivery acceptance critics score an AC
181
+ // *cluster*, not a Story. `clusterId` scopes the per-cluster round count
182
+ // on the epic-level signals stream; omitted (null) for the per-Story path.
183
+ ...(typeof clusterId === 'string' && clusterId.length > 0
184
+ ? { clusterId }
185
+ : {}),
176
186
  phase,
177
187
  emitter: { tool: 'acceptance-eval.js' },
178
188
  details: {
@@ -216,13 +226,27 @@ export function buildAcceptanceEvalSignal({
216
226
  export function deriveAcceptanceEvalRound({
217
227
  epicId,
218
228
  storyId,
229
+ clusterId = null,
219
230
  config,
220
231
  readFile = (p) => readFileSync(p, 'utf8'),
221
232
  signalsPathResolver = signalsFile,
233
+ epicSignalsPathResolver = (eid, cfg) =>
234
+ epicArtifactPath(eid, EPIC_SIGNALS_BASENAME, cfg),
222
235
  }) {
236
+ // Epic #4475 (M4-B): single-delivery critics score AC clusters, not
237
+ // Stories. When `clusterId` is supplied the round is counted per cluster
238
+ // off the epic-level signals stream; otherwise the per-Story path
239
+ // (unchanged) counts by `storyId` off the Story's stream.
240
+ const clusterMode =
241
+ typeof clusterId === 'string' &&
242
+ clusterId.length > 0 &&
243
+ Number.isInteger(epicId);
244
+
223
245
  let text;
224
246
  try {
225
- text = readFile(signalsPathResolver(epicId ?? null, storyId, config));
247
+ text = clusterMode
248
+ ? readFile(epicSignalsPathResolver(epicId, config))
249
+ : readFile(signalsPathResolver(epicId ?? null, storyId, config));
226
250
  } catch (_err) {
227
251
  // No ledger yet → no prior rounds.
228
252
  return 1;
@@ -240,7 +264,11 @@ export function deriveAcceptanceEvalRound({
240
264
  }
241
265
  if (!record || typeof record !== 'object') continue;
242
266
  if (record.kind !== 'acceptance-eval') continue;
243
- if (record.storyId !== storyId) continue;
267
+ if (clusterMode) {
268
+ if (record.clusterId !== clusterId) continue;
269
+ } else if (record.storyId !== storyId) {
270
+ continue;
271
+ }
244
272
  priorRounds += 1;
245
273
  }
246
274
  return priorRounds + 1;