mandrel 1.94.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (393) hide show
  1. package/.agents/README.md +59 -73
  2. package/.agents/agents/story-worker.md +12 -13
  3. package/.agents/docs/SDLC.md +489 -1285
  4. package/.agents/docs/agentrc-reference.json +177 -67
  5. package/.agents/docs/configuration.md +104 -138
  6. package/.agents/docs/execution-reference.md +17 -20
  7. package/.agents/docs/quality-gates.md +13 -19
  8. package/.agents/docs/workflows.md +3 -3
  9. package/.agents/instructions.md +70 -81
  10. package/.agents/rules/ci-remediation.md +8 -12
  11. package/.agents/rules/git-conventions-reference.md +6 -7
  12. package/.agents/rules/git-conventions.md +16 -22
  13. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  14. package/.agents/schemas/agentrc.schema.json +54 -214
  15. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  16. package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
  17. package/.agents/schemas/risk-verdict.schema.json +0 -13
  18. package/.agents/scripts/analyze-execution.js +1 -1
  19. package/.agents/scripts/audit-to-stories.js +7 -7
  20. package/.agents/scripts/boot-sweep.js +1 -1
  21. package/.agents/scripts/check-lifecycle-lint.js +6 -9
  22. package/.agents/scripts/check-prepush-recovery.js +1 -1
  23. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  24. package/.agents/scripts/lib/Logger.js +6 -10
  25. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  26. package/.agents/scripts/lib/audit-suite/selector.js +5 -5
  27. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  28. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  29. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
  30. package/.agents/scripts/lib/baselines/reader.js +1 -6
  31. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  32. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  33. package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
  34. package/.agents/scripts/lib/checks/index.js +2 -1
  35. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
  36. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  37. package/.agents/scripts/lib/cli-args.js +16 -7
  38. package/.agents/scripts/lib/close-validation/gates.js +2 -2
  39. package/.agents/scripts/lib/config/ci.js +6 -31
  40. package/.agents/scripts/lib/config/delivery-routing.js +45 -29
  41. package/.agents/scripts/lib/config/explain.js +55 -36
  42. package/.agents/scripts/lib/config/limits.js +17 -58
  43. package/.agents/scripts/lib/config/paths.js +0 -2
  44. package/.agents/scripts/lib/config/quality.js +1 -1
  45. package/.agents/scripts/lib/config/runners.js +17 -50
  46. package/.agents/scripts/lib/config/temp-paths.js +19 -14
  47. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  48. package/.agents/scripts/lib/config-resolver.js +2 -7
  49. package/.agents/scripts/lib/config-settings-schema-delivery.js +24 -148
  50. package/.agents/scripts/lib/config-settings-schema-quality.js +8 -14
  51. package/.agents/scripts/lib/config-settings-schema.js +52 -38
  52. package/.agents/scripts/lib/dependency-parser.js +3 -2
  53. package/.agents/scripts/lib/doc-tiers.js +2 -2
  54. package/.agents/scripts/lib/duplicate-search.js +211 -41
  55. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  56. package/.agents/scripts/lib/findings/promote-finding.js +5 -5
  57. package/.agents/scripts/lib/framework-version.js +2 -3
  58. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  59. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
  60. package/.agents/scripts/lib/git-utils.js +32 -6
  61. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  62. package/.agents/scripts/lib/label-constants.js +10 -23
  63. package/.agents/scripts/lib/label-taxonomy.js +9 -43
  64. package/.agents/scripts/lib/observability/active-story-env.js +1 -1
  65. package/.agents/scripts/lib/observability/hook-heartbeat.js +20 -52
  66. package/.agents/scripts/lib/observability/source-classifier.js +3 -3
  67. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  68. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
  69. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +8 -5
  70. package/.agents/scripts/lib/orchestration/ceremony-routing.js +83 -20
  71. package/.agents/scripts/lib/orchestration/code-review.js +20 -268
  72. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  73. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
  74. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  75. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  76. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  77. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  78. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
  79. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
  80. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  81. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
  82. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  83. package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
  84. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  85. package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
  86. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
  87. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  88. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
  89. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
  90. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
  91. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
  92. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
  93. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  94. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  95. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +13 -13
  96. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  97. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
  98. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
  99. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
  100. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
  101. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  102. package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
  103. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  104. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
  105. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  106. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
  107. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  108. package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
  109. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  110. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  111. package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
  112. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
  113. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
  114. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
  115. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
  116. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
  117. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  118. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  119. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  120. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  121. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  122. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
  123. package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
  124. package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
  125. package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
  126. package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
  127. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  128. package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
  129. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
  130. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
  131. package/.agents/scripts/lib/orchestration/ticket-validator.js +2 -7
  132. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
  133. package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
  134. package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
  135. package/.agents/scripts/lib/planning-corpus.js +16 -11
  136. package/.agents/scripts/lib/preflight-runner.js +2 -2
  137. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  138. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
  139. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  140. package/.agents/scripts/lib/story-adjacency.js +11 -14
  141. package/.agents/scripts/lib/story-body/story-body.js +124 -70
  142. package/.agents/scripts/lib/story-plan.js +2 -4
  143. package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
  144. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  145. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
  146. package/.agents/scripts/lib/validation-evidence.js +1 -1
  147. package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
  148. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  149. package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
  150. package/.agents/scripts/lint-issue-body.js +71 -40
  151. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  152. package/.agents/scripts/notify.js +4 -3
  153. package/.agents/scripts/plan-context.js +64 -74
  154. package/.agents/scripts/plan-persist.js +121 -280
  155. package/.agents/scripts/plan-run-epilogue.js +97 -0
  156. package/.agents/scripts/providers/github/issues.js +17 -33
  157. package/.agents/scripts/providers/github/mappers.js +0 -12
  158. package/.agents/scripts/providers/github/tickets.js +2 -5
  159. package/.agents/scripts/resolve-plan-run.js +117 -0
  160. package/.agents/scripts/signals-view.js +24 -19
  161. package/.agents/scripts/single-story-close.js +11 -14
  162. package/.agents/scripts/single-story-confirm-merge.js +39 -23
  163. package/.agents/scripts/single-story-init.js +29 -20
  164. package/.agents/scripts/stories-wave-tick.js +6 -6
  165. package/.agents/scripts/story-plan.js +26 -47
  166. package/.agents/scripts/update-ticket-state.js +6 -15
  167. package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
  168. package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
  169. package/.agents/skills/core/scope-triage/SKILL.md +28 -172
  170. package/.agents/skills/skills.index.json +3 -43
  171. package/.agents/starter-agentrc.json +0 -5
  172. package/.agents/templates/agent-protocol.md +9 -10
  173. package/.agents/workflows/audit-architecture.md +3 -3
  174. package/.agents/workflows/audit-clean-code.md +3 -3
  175. package/.agents/workflows/audit-dependencies.md +3 -3
  176. package/.agents/workflows/audit-devops.md +3 -3
  177. package/.agents/workflows/audit-documentation.md +5 -5
  178. package/.agents/workflows/audit-lighthouse.md +3 -3
  179. package/.agents/workflows/audit-navigability.md +3 -2
  180. package/.agents/workflows/audit-performance.md +3 -3
  181. package/.agents/workflows/audit-privacy.md +3 -3
  182. package/.agents/workflows/audit-quality.md +3 -3
  183. package/.agents/workflows/audit-security.md +3 -3
  184. package/.agents/workflows/audit-seo.md +3 -3
  185. package/.agents/workflows/audit-sre.md +3 -3
  186. package/.agents/workflows/audit-to-stories.md +20 -20
  187. package/.agents/workflows/audit-ux-ui.md +3 -3
  188. package/.agents/workflows/deliver.md +122 -174
  189. package/.agents/workflows/git-cleanup.md +3 -4
  190. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  191. package/.agents/workflows/helpers/acceptance-self-eval.md +16 -29
  192. package/.agents/workflows/helpers/code-review.md +70 -193
  193. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
  194. package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
  195. package/.agents/workflows/helpers/diagnose.md +10 -10
  196. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  197. package/.agents/workflows/helpers/signals.md +16 -16
  198. package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
  199. package/.agents/workflows/mandrel-update.md +2 -1
  200. package/.agents/workflows/plan.md +112 -145
  201. package/.agents/workflows/qa-assist.md +24 -30
  202. package/.agents/workflows/qa-explore.md +29 -38
  203. package/.agents/workflows/qa-run.md +2 -2
  204. package/README.md +9 -8
  205. package/docs/CHANGELOG.md +30 -0
  206. package/lib/migrations/index.js +6 -5
  207. package/package.json +2 -2
  208. package/.agents/agents/retro.md +0 -42
  209. package/.agents/personas/architect.md +0 -113
  210. package/.agents/personas/devops-engineer.md +0 -38
  211. package/.agents/personas/engineer.md +0 -33
  212. package/.agents/personas/project-manager.md +0 -114
  213. package/.agents/personas/qa-engineer.md +0 -95
  214. package/.agents/personas/security-engineer.md +0 -111
  215. package/.agents/personas/technical-writer.md +0 -101
  216. package/.agents/schemas/dispatch-manifest.json +0 -232
  217. package/.agents/schemas/epic-spec.schema.json +0 -153
  218. package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
  219. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
  220. package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
  221. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  222. package/.agents/scripts/bookkeeping-reconcile.js +0 -117
  223. package/.agents/scripts/dispatcher.js +0 -295
  224. package/.agents/scripts/epic-audit-prepare.js +0 -497
  225. package/.agents/scripts/epic-audit-recheck.js +0 -274
  226. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  227. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  228. package/.agents/scripts/epic-deliver-prepare.js +0 -852
  229. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  230. package/.agents/scripts/epic-plan-clarity.js +0 -211
  231. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  232. package/.agents/scripts/epic-reconcile.js +0 -625
  233. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  234. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  235. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  236. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  237. package/.agents/scripts/lib/config/preflight.js +0 -58
  238. package/.agents/scripts/lib/config/retro.js +0 -77
  239. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  240. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  241. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  242. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
  243. package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
  244. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  245. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  246. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  247. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  248. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  249. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  250. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  251. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  252. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  253. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  254. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  255. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  256. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  257. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  258. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  259. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  260. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  261. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  262. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
  263. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  264. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  265. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  266. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  267. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  268. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  269. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  270. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  271. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  272. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  273. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  274. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  275. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  276. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  277. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  278. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  279. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  280. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  281. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  282. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  283. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  284. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  285. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  286. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
  287. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  288. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
  289. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  290. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  291. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  292. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
  293. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  294. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  295. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  296. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  297. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  298. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  299. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  300. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  301. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  302. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  303. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  304. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  305. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  306. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  307. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  308. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  309. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  310. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  311. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  312. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  313. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  314. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  315. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  316. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  317. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  318. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  319. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  320. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  321. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  322. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  323. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  324. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  325. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  326. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  327. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  328. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  329. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  330. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  331. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  332. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  333. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  334. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  335. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
  336. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  337. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  338. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  339. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  340. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  341. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  342. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  343. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  344. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  345. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  346. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  347. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  348. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  349. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  350. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  351. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  352. package/.agents/scripts/lib/spec/index.js +0 -36
  353. package/.agents/scripts/lib/spec/loader.js +0 -425
  354. package/.agents/scripts/lib/spec/state.js +0 -208
  355. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  356. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  357. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  358. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  359. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  360. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  361. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  362. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  363. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  364. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  365. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  366. package/.agents/scripts/lifecycle-emit.js +0 -510
  367. package/.agents/scripts/retro-run.js +0 -218
  368. package/.agents/scripts/slice-phase.js +0 -361
  369. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  370. package/.agents/scripts/story-close.js +0 -294
  371. package/.agents/scripts/story-init.js +0 -599
  372. package/.agents/scripts/story-phase.js +0 -369
  373. package/.agents/scripts/wave-tick.js +0 -464
  374. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  375. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  376. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  377. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  378. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  379. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  380. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
  381. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  382. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
  383. package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
  384. package/.agents/workflows/helpers/deliver-epic.md +0 -998
  385. package/.agents/workflows/helpers/deliver-stories.md +0 -450
  386. package/.agents/workflows/helpers/epic-audit.md +0 -189
  387. package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
  388. package/.agents/workflows/helpers/epic-testing.md +0 -125
  389. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  390. package/.agents/workflows/helpers/plan-epic.md +0 -353
  391. package/.agents/workflows/helpers/plan-story.md +0 -251
  392. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  393. /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
@@ -1,117 +0,0 @@
1
- import nodeFs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- import { calculateForSource } from '../../../maintainability-engine.js';
5
- import { formatNumber } from './_bullet-format.js';
6
- import {
7
- createDriftDetector,
8
- walkComponentRegressions,
9
- } from './component-drift.js';
10
-
11
- const DEFAULT_THRESHOLD = 2.0;
12
- // Distinct from the canonical ratchet baseline at `baselines/maintainability.json`
13
- // (Epic #730 Story 5.5) — this file is a per-wave drift SNAPSHOT, not the
14
- // committed score baseline. Filename intentionally differs so a repo-wide
15
- // grep for the canonical baseline no longer hits the snapshot.
16
- const BASELINE_FILENAME = 'wave-mi-snapshot.json';
17
-
18
- /**
19
- * Detect per-component maintainability regressions from a baseline rollup
20
- * against the gate's configured floors. Pure — the caller loads the
21
- * baseline via `lib/baselines/reader.js#load('maintainability')` and passes
22
- * the resulting `{ rollup }` plus the gate config block.
23
- *
24
- * Bullet shape (Task #1919, Epic #1786):
25
- *
26
- * 📉 maintainability: <component> <axis> <value> < floor <floor>
27
- *
28
- * Maintainability is "higher is better" — every axis breach reports when
29
- * `value < floor`. Component-scoped breaches do NOT trigger a `*` bullet
30
- * unless `*` itself breaches. The walk itself is the shared
31
- * `component-drift.js` helper (Story #3984).
32
- *
33
- * @param {{
34
- * rollup?: Record<string, Record<string, number>>,
35
- * gateConfig?: { floors?: Record<string, Record<string, number>> } & object,
36
- * }} params
37
- * @returns {string[]}
38
- */
39
- export function detectComponentRegressions(params = {}) {
40
- return walkComponentRegressions(params, {
41
- isBreach: (value, target) => value < target,
42
- formatBullet: (name, axis, value, target) =>
43
- `📉 maintainability: ${name} ${axis} ${formatNumber(value)} < floor ${formatNumber(target)}`,
44
- });
45
- }
46
-
47
- /**
48
- * Detects per-file maintainability drop versus a wave-start baseline.
49
- *
50
- * Usage:
51
- * const detector = createMaintainabilityDriftDetector({ cwd, files });
52
- * detector.captureBaseline(); // call once at wave-start
53
- * await detector.detect(rows); // call each progress fire
54
- *
55
- * A baseline is a map of file -> score, persisted to
56
- * `<cwd>/<baselineDir>/wave-mi-snapshot.json` so that a resumed epic
57
- * run can re-read the snapshot from the previous wave rather than lose the
58
- * anchor. Any file whose score has dropped by >= `threshold` since baseline
59
- * is surfaced as a bullet:
60
- *
61
- * 📉 Maintainability drift: <file> -<n.nn> vs wave-start baseline
62
- *
63
- * Errors while reading/scoring individual files are swallowed — a single
64
- * bad file must not take the progress reporter down.
65
- *
66
- * @param {{
67
- * cwd?: string,
68
- * files?: string[], // files to watch; repo-relative paths
69
- * fs?: { readFileSync: Function, writeFileSync?: Function, mkdirSync?: Function, existsSync?: Function },
70
- * calculate?: (source: string) => number,
71
- * threshold?: number, // drop (baseline - current) that trips a bullet
72
- * baselineDir?: string, // directory (under cwd) to persist snapshot
73
- * }} [opts]
74
- */
75
- export function createMaintainabilityDriftDetector(opts = {}) {
76
- const fs = opts.fs ?? nodeFs;
77
- const cwd = opts.cwd ?? process.cwd();
78
- const calculate = opts.calculate ?? calculateForSource;
79
- const threshold = Number.isFinite(opts.threshold)
80
- ? opts.threshold
81
- : DEFAULT_THRESHOLD;
82
-
83
- function scoreFile(relPath) {
84
- try {
85
- const abs = path.join(cwd, relPath);
86
- const src = fs.readFileSync(abs, 'utf-8');
87
- const score = calculate(src);
88
- return Number.isFinite(score) ? score : null;
89
- } catch {
90
- return null;
91
- }
92
- }
93
-
94
- return createDriftDetector({
95
- fs,
96
- cwd,
97
- files: opts.files,
98
- baselineDir: opts.baselineDir,
99
- baselineFilename: BASELINE_FILENAME,
100
- scoreFile,
101
- captureScore: (score) => score,
102
- async detect({ baseline, scoreFile: score }) {
103
- const bullets = [];
104
- for (const [relPath, baseScore] of Object.entries(baseline)) {
105
- const current = score(relPath);
106
- if (current == null) continue;
107
- const drop = baseScore - current;
108
- if (drop >= threshold) {
109
- bullets.push(
110
- `📉 Maintainability drift: ${relPath} -${drop.toFixed(2)} vs wave-start baseline`,
111
- );
112
- }
113
- }
114
- return bullets;
115
- },
116
- });
117
- }
@@ -1,37 +0,0 @@
1
- import nodeFs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- /**
5
- * Detects "worktree residue" — stories that the ticket graph reports as done,
6
- * but whose `.worktrees/story-<id>/` directory is still on disk. This usually
7
- * means story-close failed to reap the worktree (see the Windows
8
- * partial-reap failure mode in friction memory), and a future run of the
9
- * same story id would collide with the stale directory.
10
- *
11
- * The detector is pure (aside from fs reads) and accepts an fs adapter so the
12
- * unit test can drive present/absent cases without touching disk.
13
- *
14
- * @param {{
15
- * fs?: { existsSync: (p: string) => boolean },
16
- * cwd?: string,
17
- * }} [opts]
18
- * @returns {(rows: Array<{id:number|string, state:string}>) => Promise<string[]>}
19
- */
20
- export function createStalledWorktreeDetector(opts = {}) {
21
- const fs = opts.fs ?? nodeFs;
22
- const cwd = opts.cwd ?? process.cwd();
23
- return async function detect(rows = []) {
24
- const bullets = [];
25
- for (const row of rows) {
26
- if (row?.state !== 'done') continue;
27
- const relPath = `.worktrees/story-${row.id}`;
28
- const absPath = path.join(cwd, relPath);
29
- if (fs.existsSync(absPath)) {
30
- bullets.push(
31
- `⚠️ Worktree residue: #${row.id} marked done but .worktrees/story-${row.id}/ still present`,
32
- );
33
- }
34
- }
35
- return bullets;
36
- };
37
- }
@@ -1,127 +0,0 @@
1
- /**
2
- * StoryLauncher — produces the per-wave dispatch plan and (optionally)
3
- * delegates execution to an injected `dispatch` adapter.
4
- *
5
- * After Story #908, in-session Agent-tool fan-out replaces the subprocess
6
- * spawn pipeline. The launcher's primary responsibility is `planWave(stories)`:
7
- * given a wave's Story tickets it returns a stable list of
8
- * `{ storyId, worktree }` entries. The `/deliver` skill consumes that
9
- * list (one wave at a time) to format one assistant turn containing N
10
- * parallel `Agent` tool calls (subagent_type `general-purpose`), each of
11
- * which drives `/deliver <storyId>` for one Story.
12
- *
13
- * `launchWave(stories)` is a convenience for callers that already hold a
14
- * concrete dispatch adapter (tests, future programmatic harnesses). It calls
15
- * `planWave` and forwards the plan to `dispatch({ plan, concurrencyCap, signal })`,
16
- * returning the adapter's result list. The CLI does not provide a default
17
- * dispatch adapter — invoking the engine without one is an explicit error.
18
- */
19
-
20
- const DEFAULT_TIMEOUT_MS = 6 * 60 * 60 * 1000;
21
-
22
- export class StoryLauncher {
23
- /**
24
- * @param {{
25
- * ctx?: { dispatch?: Function, concurrencyCap?: number, worktreeResolver?: (storyId: number) => string, logger?: object },
26
- * concurrencyCap?: number,
27
- * dispatch?: (args: { plan: Array<{ storyId: number, worktree?: string }>, concurrencyCap: number, signal?: AbortSignal }) => Promise<Array<{ storyId: number, status: string, detail?: string }>>,
28
- * worktreeResolver?: (storyId: number) => string,
29
- * timeoutMs?: number,
30
- * logger?: { info: Function, warn: Function, error: Function }
31
- * }} opts
32
- */
33
- constructor(opts = {}) {
34
- const ctx = opts?.ctx;
35
- const dispatch = opts?.dispatch ?? ctx?.dispatch ?? null;
36
- const concurrencyCap = opts?.concurrencyCap ?? ctx?.concurrencyCap;
37
- if (!Number.isInteger(concurrencyCap) || concurrencyCap < 1) {
38
- throw new RangeError('concurrencyCap must be a positive integer');
39
- }
40
- this.concurrencyCap = concurrencyCap;
41
- this.dispatch = dispatch;
42
- this.worktreeResolver = opts?.worktreeResolver ?? ctx?.worktreeResolver;
43
- this.timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
44
- this.logger = opts?.logger ?? ctx?.logger ?? console;
45
- }
46
-
47
- /**
48
- * Produce the dispatch plan for a wave. Pure: no side effects, no IO. The
49
- * caller (the `/deliver` skill's wave loop, or `launchWave` below)
50
- * decides what to do with the plan.
51
- *
52
- * @param {Array<number|{id?:number,storyId?:number,number?:number}>} stories
53
- * @returns {Array<{ storyId: number, worktree?: string }>}
54
- */
55
- planWave(stories) {
56
- return (stories ?? []).map((s) => {
57
- const storyId =
58
- typeof s === 'object' && s !== null
59
- ? (s.id ?? s.storyId ?? s.number)
60
- : s;
61
- const id = Number(storyId);
62
- return {
63
- storyId: id,
64
- worktree: this.worktreeResolver?.(id),
65
- };
66
- });
67
- }
68
-
69
- /**
70
- * Plan + dispatch the wave through the injected adapter. Returns one result
71
- * entry per input Story preserving input order. Throws when no dispatch
72
- * adapter was injected — the engine is no longer callable without one.
73
- *
74
- * @param {object[]} stories
75
- * @param {AbortSignal} [signal]
76
- * @returns {Promise<Array<{ storyId: number, status: string, detail?: string }>>}
77
- */
78
- async launchWave(stories, signal) {
79
- if (typeof this.dispatch !== 'function') {
80
- throw new TypeError(
81
- 'StoryLauncher.launchWave requires a dispatch adapter (in-session Agent-tool fan-out is the responsibility of the /deliver skill).',
82
- );
83
- }
84
- const plan = this.planWave(stories);
85
- if (plan.length === 0) return [];
86
-
87
- const controller = new AbortController();
88
- const timer = setTimeout(
89
- () =>
90
- controller.abort(new Error(`wave timed out after ${this.timeoutMs}ms`)),
91
- this.timeoutMs,
92
- );
93
- const onParentAbort = () => controller.abort(signal?.reason);
94
- signal?.addEventListener?.('abort', onParentAbort, { once: true });
95
- try {
96
- const results = await this.dispatch({
97
- plan,
98
- concurrencyCap: this.concurrencyCap,
99
- signal: controller.signal,
100
- });
101
- return this.#alignResults(plan, results);
102
- } catch (err) {
103
- return plan.map((p) => ({
104
- storyId: p.storyId,
105
- status: 'failed',
106
- detail: err?.message ?? String(err),
107
- }));
108
- } finally {
109
- clearTimeout(timer);
110
- signal?.removeEventListener?.('abort', onParentAbort);
111
- }
112
- }
113
-
114
- #alignResults(plan, results) {
115
- const list = Array.isArray(results) ? results : [];
116
- const byId = new Map(list.map((r) => [Number(r.storyId), r]));
117
- return plan.map((p) => {
118
- const r = byId.get(p.storyId);
119
- if (r) return { storyId: p.storyId, ...r };
120
- return {
121
- storyId: p.storyId,
122
- status: 'failed',
123
- detail: 'dispatch returned no result for this story',
124
- };
125
- });
126
- }
127
- }
@@ -1,276 +0,0 @@
1
- /**
2
- * sub-agent-return.js — parse and reconcile per-Story sub-agent return text.
3
- *
4
- * `/deliver` Step 2 dispatches one `Agent` tool call per Story per
5
- * wave. Each sub-agent owes its parent the JSON return contract documented
6
- * in `.agents/workflows/helpers/deliver-epic.md`:
7
- *
8
- * {
9
- * "storyId": <number>,
10
- * "status": "done" | "blocked" | "failed",
11
- * "phase": "init|implementing|closing|blocked|done",
12
- * "branchDeleted": <boolean>,
13
- * "blockerCommentId": <string|null>,
14
- * "detail": <string|undefined>,
15
- * "renderedBody": <string|undefined>
16
- * }
17
- *
18
- * On the current model tier a sub-agent that does real work returns the
19
- * envelope verbatim; a return that fails to parse is a rare protocol error,
20
- * not a normal recovery path. Parsing is therefore strict — an already-parsed
21
- * object or a return string that is pure JSON — with **no free-form
22
- * extraction heuristics**: the prose-wrapped extraction candidates
23
- * (fenced ```json``` blocks, balanced-`{...}`-substring scans, chat-prelude
24
- * tolerance) were measured to never fire in practice (Story #3864:
25
- * zero malformed terminal returns across the sampled delivered Epics) and
26
- * were deleted in a hard cutover. A return that does not parse routes
27
- * directly to GitHub-state reconciliation plus a friction record — the
28
- * stronger backstop that already caught everything the heuristics caught.
29
- *
30
- * This module provides the two helpers `/deliver`'s wave dispatcher
31
- * now uses:
32
- *
33
- * - `parseStoryAgentReturn(raw)` — accept an already-parsed object or a
34
- * return string that is pure JSON, then schema-validate it. Returns
35
- * `{ ok: true, value }` on success, `{ ok: false, error }` otherwise.
36
- *
37
- * - `reconcileStoryFromGitHub({ provider, storyId })` — reads the Story
38
- * ticket's labels and `story-run-progress` structured comment to derive
39
- * an authoritative result row. Used as a fallback whenever the
40
- * sub-agent's return text fails to parse. Status is downgraded to
41
- * `failed` unless the live ticket actually carries `agent::done`
42
- * (in which case the operator may have manually closed the Story).
43
- */
44
-
45
- import { parseFencedJsonComment } from '../structured-comment-parser.js';
46
- import { findStructuredComment } from '../ticketing.js';
47
- import { STORY_RUN_PROGRESS_TYPE } from './story-run-progress-writer.js';
48
-
49
- const VALID_STATUS = new Set(['done', 'blocked', 'failed']);
50
-
51
- /**
52
- * Strict parse of a per-Story sub-agent return. Accepts:
53
- * 1. an object that already looks parsed, or
54
- * 2. a return string that is pure JSON.
55
- *
56
- * There is no free-form extraction: a string that is not pure JSON (prose,
57
- * a fenced block, a chat-prelude + envelope) yields `ok: false` and the
58
- * caller MUST reconcile from GitHub. Parsing is strict on both sides —
59
- * callers always get a fully-validated object or `ok: false`.
60
- *
61
- * @param {unknown} raw
62
- * @returns {{ ok: true, value: object } | { ok: false, error: string }}
63
- */
64
- export function parseStoryAgentReturn(raw) {
65
- if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
66
- return validateStoryReturnShape(raw);
67
- }
68
- if (typeof raw !== 'string') {
69
- return {
70
- ok: false,
71
- error: `expected string or object, got ${raw === null ? 'null' : typeof raw}`,
72
- };
73
- }
74
- const trimmed = raw.trim();
75
- if (trimmed.length === 0) {
76
- return { ok: false, error: 'empty return text' };
77
- }
78
-
79
- let parsed;
80
- try {
81
- parsed = JSON.parse(trimmed);
82
- } catch {
83
- return {
84
- ok: false,
85
- error: `return text is not a JSON envelope (${quote(trimmed)})`,
86
- };
87
- }
88
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
89
- return {
90
- ok: false,
91
- error: `return text parsed to a non-object (${quote(trimmed)})`,
92
- };
93
- }
94
- return validateStoryReturnShape(parsed);
95
- }
96
-
97
- /**
98
- * Validate the per-Story return contract. Pure helper — no IO.
99
- *
100
- * @param {object} obj
101
- * @returns {{ ok: true, value: object } | { ok: false, error: string }}
102
- */
103
- function validateStoryReturnShape(obj) {
104
- const storyId = Number(obj.storyId ?? obj.id);
105
- if (!Number.isInteger(storyId) || storyId <= 0) {
106
- return {
107
- ok: false,
108
- error: `storyId must be a positive integer; got ${JSON.stringify(obj.storyId ?? obj.id)}`,
109
- };
110
- }
111
- const status = String(obj.status ?? '');
112
- if (!VALID_STATUS.has(status)) {
113
- return {
114
- ok: false,
115
- error: `status "${status}" must be one of: ${[...VALID_STATUS].join(', ')}`,
116
- };
117
- }
118
- const value = { storyId, status };
119
- if (typeof obj.phase === 'string') value.phase = obj.phase;
120
- if (typeof obj.branchDeleted === 'boolean') {
121
- value.branchDeleted = obj.branchDeleted;
122
- }
123
- if (obj.blockerCommentId != null) {
124
- value.blockerCommentId = String(obj.blockerCommentId);
125
- }
126
- if (typeof obj.detail === 'string') value.detail = obj.detail;
127
- if (typeof obj.renderedBody === 'string')
128
- value.renderedBody = obj.renderedBody;
129
- return { ok: true, value };
130
- }
131
-
132
- function quote(text) {
133
- const oneline = text.replace(/\s+/g, ' ').trim();
134
- return oneline.length > 120 ? `${oneline.slice(0, 117)}...` : oneline;
135
- }
136
-
137
- /**
138
- * Authoritative reconciliation: rebuild a per-Story result row from the
139
- * Story ticket's live state when the sub-agent return cannot be trusted.
140
- *
141
- * The result is always conservative — `status: 'failed'` unless the live
142
- * ticket carries `agent::done` (or `state: 'closed'`). The phase is
143
- * best-effort, sourced from the Story's `story-run-progress` comment;
144
- * absence of the comment is non-fatal.
145
- *
146
- * Story #3907 — a Story carrying `agent::blocked` reconciles to
147
- * `status: 'blocked'`, not `failed`. A garbled / unparseable return from a
148
- * genuinely blocked child previously collapsed to `failed`, which erased the
149
- * `blockerCommentId` and steered the operator toward the wrong remediation
150
- * (re-run vs. resolve-the-blocker). The blocked arm recovers the blocker
151
- * comment id from the Story's latest `friction` structured comment so the
152
- * wave row carries it through to `epic-run-progress`.
153
- *
154
- * @param {{
155
- * provider: { getTicket: Function, getTicketComments: Function },
156
- * storyId: number,
157
- * }} args
158
- * @returns {Promise<{
159
- * storyId: number,
160
- * status: 'done' | 'blocked' | 'failed',
161
- * phase?: string,
162
- * blockerCommentId?: string,
163
- * reconciledFromGitHub: true,
164
- * reconcileError?: string,
165
- * }>}
166
- */
167
- export async function reconcileStoryFromGitHub({ provider, storyId } = {}) {
168
- if (!Number.isInteger(storyId) || storyId <= 0) {
169
- throw new TypeError(
170
- 'reconcileStoryFromGitHub: storyId must be a positive integer',
171
- );
172
- }
173
- if (!provider || typeof provider.getTicket !== 'function') {
174
- throw new TypeError(
175
- 'reconcileStoryFromGitHub: provider.getTicket is required',
176
- );
177
- }
178
-
179
- let ticket;
180
- try {
181
- ticket = await provider.getTicket(storyId, { fresh: true });
182
- } catch (err) {
183
- return {
184
- storyId,
185
- status: 'failed',
186
- reconciledFromGitHub: true,
187
- reconcileError: err?.message ?? String(err),
188
- };
189
- }
190
-
191
- const labels = Array.isArray(ticket?.labels) ? ticket.labels : [];
192
- const isDone = labels.includes('agent::done') || ticket?.state === 'closed';
193
- const isBlocked = !isDone && labels.includes('agent::blocked');
194
- let status = 'failed';
195
- if (isDone) status = 'done';
196
- else if (isBlocked) status = 'blocked';
197
-
198
- const out = { storyId, status, reconciledFromGitHub: true };
199
-
200
- // Story #3907 — for a blocked Story, recover the blocker comment id from
201
- // the latest `friction` structured comment so the operator is routed to the
202
- // blocker (and its evidence), not a blind re-run. Best-effort: a missing
203
- // friction comment leaves `status: 'blocked'` with no id, which is still
204
- // strictly more accurate than the old `failed` downgrade.
205
- if (isBlocked) {
206
- try {
207
- const friction = await findStructuredComment(
208
- provider,
209
- storyId,
210
- 'friction',
211
- );
212
- if (friction && friction.id != null) {
213
- out.blockerCommentId = String(friction.id);
214
- }
215
- } catch (err) {
216
- out.reconcileError = err?.message ?? String(err);
217
- }
218
- }
219
-
220
- // Cross-look the story-run-progress comment for the phase when present.
221
- // Story #3909 retired the per-Story story-run-progress *comment*
222
- // (the redundant mid-flight surface), so this read now usually finds nothing
223
- // and the reconciled row degrades to label-only — which is fine: the labels
224
- // are the authoritative state. Failure here is non-fatal.
225
- try {
226
- const comment = await findStructuredComment(
227
- provider,
228
- storyId,
229
- STORY_RUN_PROGRESS_TYPE,
230
- );
231
- const payload = comment ? parseFencedJsonComment(comment) : null;
232
- if (payload && typeof payload === 'object') {
233
- if (typeof payload.phase === 'string') out.phase = payload.phase;
234
- }
235
- } catch (err) {
236
- out.reconcileError = err?.message ?? String(err);
237
- }
238
-
239
- return out;
240
- }
241
-
242
- /**
243
- * Render a single friction-comment body listing every malformed sub-agent
244
- * return for a recorder beat. Pure helper — no provider call. Exposed so
245
- * tests can pin the body shape.
246
- *
247
- * Story #4155 — under the ready-set runtime there is no wave index; the
248
- * recorder records the Stories it was handed, so the body is keyed by Epic
249
- * only.
250
- *
251
- * @param {{
252
- * epicId: number,
253
- * failures: Array<{ storyId: number, error: string, returnText: string }>,
254
- * }} args
255
- * @returns {string}
256
- */
257
- export function renderMalformedReturnsFriction({ epicId, failures }) {
258
- const lines = [
259
- `### 🚧 epic-execute friction — Epic #${epicId}`,
260
- '',
261
- `**Reason:** \`malformed-subagent-return\``,
262
- '',
263
- `${failures.length} sub-agent return(s) did not match the /deliver return contract.`,
264
- 'Each Story below was reconciled from GitHub (labels + `story-run-progress`)',
265
- 'and its recorded status downgraded to `failed` unless the live ticket',
266
- 'already carried `agent::done`.',
267
- '',
268
- ];
269
- for (const f of failures) {
270
- lines.push(`- **Story #${f.storyId}** — ${f.error}`);
271
- if (f.returnText) {
272
- lines.push(` Original return: \`${quote(f.returnText)}\``);
273
- }
274
- }
275
- return lines.join('\n');
276
- }
@@ -1,66 +0,0 @@
1
- /**
2
- * WaveScheduler — iterates the wave DAG returned by `Graph.computeWaves()`.
3
- *
4
- * Tracks progress (current wave index, waves completed) but never spawns
5
- * workers. The `story-launcher` consumes the stories it yields.
6
- */
7
-
8
- export class WaveScheduler {
9
- /**
10
- * @param {object[][]} waves - Array of waves, each a list of story objects.
11
- */
12
- constructor(waves) {
13
- if (!Array.isArray(waves)) {
14
- throw new TypeError('WaveScheduler expects a waves array');
15
- }
16
- this.waves = waves;
17
- this.currentIndex = 0;
18
- this.completed = new Set();
19
- }
20
-
21
- get totalWaves() {
22
- return this.waves.length;
23
- }
24
-
25
- get currentWave() {
26
- return this.currentIndex;
27
- }
28
-
29
- hasMoreWaves() {
30
- return this.currentIndex < this.waves.length;
31
- }
32
-
33
- /**
34
- * Returns the next wave to dispatch and advances the pointer. Returns null
35
- * if no waves remain.
36
- *
37
- * @returns {{ index: number, stories: object[] } | null}
38
- */
39
- nextWave() {
40
- if (!this.hasMoreWaves()) return null;
41
- const index = this.currentIndex;
42
- const stories = this.waves[index];
43
- this.currentIndex += 1;
44
- return { index, stories };
45
- }
46
-
47
- /**
48
- * Marks a previously-yielded wave as complete. Idempotent. Throws if the
49
- * index is out of range or refers to a wave not yet yielded.
50
- *
51
- * @param {number} index
52
- */
53
- markWaveComplete(index) {
54
- if (!Number.isInteger(index) || index < 0 || index >= this.waves.length) {
55
- throw new RangeError(`Wave index ${index} is out of range`);
56
- }
57
- if (index >= this.currentIndex) {
58
- throw new Error(`Wave ${index} has not been yielded yet`);
59
- }
60
- this.completed.add(index);
61
- }
62
-
63
- completedWaves() {
64
- return [...this.completed].sort((a, b) => a - b);
65
- }
66
- }