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,450 +0,0 @@
1
- /**
2
- * phases/gather-signals.js — retro Phase 1: aggregate retro signals.
3
- *
4
- * Reads child Stories' `story-perf-summary` comments, descendant labels
5
- * (HITL events), the Epic's `parked-follow-ons` structured comment, and
6
- * per-Story `signals.ndjson` streams. Composes the routed-proposal
7
- * sections via `composeRoutedProposals`.
8
- *
9
- * Exported as `gatherRetroSignals` for the parent sequencer (and tests).
10
- */
11
-
12
- import { TYPE_LABELS } from '../../../label-constants.js';
13
- import {
14
- forEachEpicLine,
15
- forEachLine,
16
- } from '../../../observability/signals-writer.js';
17
- import { concurrentMap } from '../../../util/concurrent-map.js';
18
- import { read as readEpicRunState } from '../../epic-run-state-store.js';
19
- import { composeRoutedProposals } from '../../retro-proposals.js';
20
- import { parseFencedJsonComment } from '../../structured-comment-parser.js';
21
- import { findStructuredComment } from '../../ticketing.js';
22
-
23
- /**
24
- * Default framework repo (the Mandrel mirror) used when the caller does
25
- * not supply an override. Consumer projects re-routing framework friction
26
- * back to mandrel rely on this constant.
27
- */
28
- export const DEFAULT_FRAMEWORK_REPO = 'dsj1984/mandrel';
29
-
30
- const RECUT_BODY_MARKER = /<!--\s*recut-of:\s*#?\d+\s*-->/;
31
-
32
- // Story #3347 — bounded fan-out cap for the per-Story `signals.ndjson`
33
- // reads below. Mirrors the read-concurrency convention used elsewhere in
34
- // the orchestrator (e.g. SUBTICKET_HYDRATION_CONCURRENCY,
35
- // CASCADE_SIBLING_READ_CONCURRENCY); keeps a large Epic from opening an
36
- // unbounded number of concurrent file handles while still collapsing N
37
- // sequential disk reads into one wall-clock fan-out.
38
- const SIGNALS_READ_CONCURRENCY = 8;
39
-
40
- // Detects #NNN issue references in an Epic body — any match means the Epic
41
- // likely has child Stories enumerated in the planning artifact, so a
42
- // zero-descendant walk is suspicious.
43
- const EPIC_BODY_REFERENCE = /#\d+/;
44
-
45
- /**
46
- * Fallback category stamped on a snapshot-derived blocked event that
47
- * carries no `category` of its own. A non-empty category is what promotes
48
- * the event to an actionable routed proposal in `composeRoutedProposals`,
49
- * so a category-less `agent::blocked` record must still resolve to a
50
- * concrete bucket rather than being silently dropped.
51
- */
52
- const BLOCKED_EVENT_FALLBACK_CATEGORY = 'agent-blocked';
53
-
54
- /**
55
- * Pure: aggregate `frictionByCategory` payloads into a single integer.
56
- */
57
- function sumFriction(byCategory) {
58
- if (!byCategory || typeof byCategory !== 'object') return 0;
59
- let total = 0;
60
- for (const v of Object.values(byCategory)) {
61
- if (typeof v === 'number' && Number.isFinite(v) && v >= 0) total += v;
62
- }
63
- return total;
64
- }
65
-
66
- /**
67
- * Fold one parsed `signals.ndjson` record into the routed-signal
68
- * accumulator. Shared by the per-Story and per-Epic scans so both streams
69
- * contribute to the SAME unified counts that feed the routed proposals AND
70
- * the compact/full retro decision. Reads the canonical envelope: top-level
71
- * `category` and the string `source` classifier tag (Epic #4406). Non-object
72
- * records and records without a `category` contribute nothing.
73
- *
74
- * @param {unknown} parsed
75
- * @param {Array<{ category: string, source: 'framework'|'consumer' }>} routedSignals
76
- */
77
- function accumulateSignalRecord(parsed, routedSignals) {
78
- if (parsed === null || typeof parsed !== 'object') return;
79
- const record = /** @type {Record<string, unknown>} */ (parsed);
80
- const category = typeof record.category === 'string' ? record.category : null;
81
- if (!category) return;
82
- const source = record.source === 'framework' ? 'framework' : 'consumer';
83
- routedSignals.push({ category, source });
84
- }
85
-
86
- /**
87
- * Read the `epic-run-state` snapshot and project every Story still recorded
88
- * as `blocked` into an `unresolvedBlockedEvents` entry for
89
- * `composeRoutedProposals`. Each event carries a **non-empty** category —
90
- * the snapshot record's own `category` when present, otherwise the
91
- * `agent-blocked` fallback — so it force-promotes an actionable proposal
92
- * even when no friction signal shares its category. Best-effort: a read
93
- * failure or shapeless snapshot degrades to an empty array (observability
94
- * MUST NOT take down the retro path).
95
- *
96
- * @param {{
97
- * epicId: number,
98
- * provider: object,
99
- * logger?: { warn?: Function },
100
- * readFn: typeof readEpicRunState,
101
- * }} args
102
- * @returns {Promise<Array<{ ticketId?: number, category: string, source: 'framework'|'consumer' }>>}
103
- */
104
- async function readUnresolvedBlockedEvents({
105
- epicId,
106
- provider,
107
- logger,
108
- readFn,
109
- }) {
110
- let snapshot;
111
- try {
112
- snapshot = await readFn({ provider, epicId });
113
- } catch (err) {
114
- logger?.warn?.(
115
- `[retro-runner] Failed to read epic-run-state for blocked events (continuing): ${
116
- err?.message ?? err
117
- }`,
118
- );
119
- return [];
120
- }
121
- const storiesMap =
122
- snapshot && typeof snapshot.stories === 'object' && snapshot.stories
123
- ? snapshot.stories
124
- : {};
125
- const events = [];
126
- for (const [key, record] of Object.entries(storiesMap)) {
127
- if (!record || typeof record !== 'object' || record.status !== 'blocked') {
128
- continue;
129
- }
130
- const rawCategory =
131
- typeof record.category === 'string' ? record.category.trim() : '';
132
- const category =
133
- rawCategory.length > 0 ? rawCategory : BLOCKED_EVENT_FALLBACK_CATEGORY;
134
- const source = record.source === 'framework' ? 'framework' : 'consumer';
135
- const ticketId = Number(key);
136
- const event = { category, source };
137
- if (Number.isInteger(ticketId) && ticketId > 0) event.ticketId = ticketId;
138
- events.push(event);
139
- }
140
- return events;
141
- }
142
-
143
- /**
144
- * Walk every descendant ticket of `epicId` once. Returns the flat list with
145
- * each ticket's labels + body + state — the consumer derives its own
146
- * counts from this snapshot. Pure with respect to the provider injection.
147
- *
148
- * `provider.getSubTickets` is part of the `ITicketingProvider` contract;
149
- * the call is **not** optional-chained so a missing method (or a typo'd
150
- * test stub) surfaces as a thrown error rather than a silent empty graph.
151
- */
152
- async function collectDescendants(provider, epicId) {
153
- const visited = new Set([epicId]);
154
- const out = [];
155
- // Story #2853 — level-order BFS so every parent at the current depth
156
- // fires its getSubTickets call concurrently. Previously this loop
157
- // awaited one parent at a time, serializing one network round-trip per
158
- // descendant tier (a 2-level Epic with 10 Stories was 11 sequential
159
- // calls). Each `getSubTickets` already fans out child hydration
160
- // internally with concurrency=8 (issues.js:SUBTICKET_HYDRATION_CONCURRENCY),
161
- // so outer parallelism is consistent with the existing design.
162
- let frontier = [epicId];
163
- while (frontier.length > 0) {
164
- const results = await Promise.all(
165
- frontier.map((id) => provider.getSubTickets(id)),
166
- );
167
- const nextFrontier = [];
168
- for (const subs of results) {
169
- for (const sub of subs ?? []) {
170
- const subId = Number(sub?.id ?? sub?.number);
171
- if (!Number.isInteger(subId) || visited.has(subId)) continue;
172
- visited.add(subId);
173
- out.push(sub);
174
- nextFrontier.push(subId);
175
- }
176
- }
177
- frontier = nextFrontier;
178
- }
179
- return out;
180
- }
181
-
182
- /**
183
- * Defensive guard: when the descendant walker returned zero, probe the
184
- * Epic ticket itself. If the Epic exists and its body references child
185
- * issues (e.g., `#123` planning refs), the empty walk is almost certainly
186
- * a contract drift in the underlying provider — emit a loud warn so the
187
- * silent failure surfaces. Probe errors degrade silently (the guard is
188
- * best-effort observability, not a correctness gate).
189
- */
190
- async function warnIfEpicLooksPopulated({ epicId, provider, logger }) {
191
- let epic;
192
- try {
193
- epic = await provider.getTicket?.(epicId);
194
- } catch (err) {
195
- logger?.warn?.(
196
- `[retro-runner] guard: getTicket(#${epicId}) failed (continuing): ${err?.message ?? err}`,
197
- );
198
- return;
199
- }
200
- if (!epic) return;
201
- const body = typeof epic.body === 'string' ? epic.body : '';
202
- if (!EPIC_BODY_REFERENCE.test(body)) return;
203
- logger?.warn?.(
204
- `[retro-runner] WARNING: Epic #${epicId} body references child issues, ` +
205
- 'but the descendant walker returned zero — retro will under-report ' +
206
- 'friction. Possible provider contract drift in getSubTickets.',
207
- );
208
- }
209
-
210
- /**
211
- * Read raw retro signals from the GitHub graph. Pure with respect to the
212
- * provider injection — exported so tests can drive the predicate end-to-end
213
- * with a stub provider.
214
- *
215
- * When `descendants.length === 0`, the function probes `provider.getTicket`
216
- * to distinguish "Epic legitimately empty" from "the descendant walker
217
- * silently returned nothing under a populated Epic" (the failure mode the
218
- * pre-Story #2289 `getSubIssues` contract drift produced). A populated
219
- * Epic with zero walked descendants emits a loud warn via the supplied
220
- * `logger` so the silent failure becomes visible. Probe failure degrades
221
- * gracefully — the function never throws on the guard alone.
222
- *
223
- * **2-tier ledgers (Story #3151, Story #3200).** Under the 2-tier
224
- * hierarchy (Epic → Story; no Task-tier children), `friction`
225
- * / `parked` / `recuts` / `storyPerfSummaries` are all Story-scoped, so
226
- * the function continues to produce a non-empty signals report. The
227
- * empty-walk guard above is **not** triggered for this shape because
228
- * `descendants` is populated (it contains the Stories themselves); the
229
- * guard fires only when the walker returned literally nothing under a
230
- * body that references children.
231
- *
232
- * @returns {Promise<{
233
- * stories: Array<{ id: number, body?: string, labels?: string[] }>,
234
- * counts: { friction: number, parked: number, recuts: number, hitl: number },
235
- * storyPerfSummaries: object[],
236
- * epicPerfReport: object|null,
237
- * parkedFollowOns: { recuts: object[], parked: object[], present: boolean },
238
- * }>}
239
- */
240
- export async function gatherRetroSignals({
241
- epicId,
242
- provider,
243
- logger,
244
- frameworkRepo,
245
- consumerRepo,
246
- forEachLineFn = forEachLine,
247
- forEachEpicLineFn = forEachEpicLine,
248
- epicRunStateReadFn = readEpicRunState,
249
- composeRoutedProposalsFn = composeRoutedProposals,
250
- }) {
251
- const descendants = await collectDescendants(provider, epicId);
252
- if (descendants.length === 0) {
253
- await warnIfEpicLooksPopulated({ epicId, provider, logger });
254
- }
255
- const stories = descendants.filter((t) =>
256
- (t.labels ?? []).includes(TYPE_LABELS.STORY),
257
- );
258
- // HITL count: distinct descendants that currently or historically carry
259
- // `agent::blocked`. We can only see "currently" here without an event
260
- // stream — counts undercount but never overcount.
261
- const hitl = descendants.filter((t) =>
262
- (t.labels ?? []).includes('agent::blocked'),
263
- ).length;
264
-
265
- // Aggregate per-Story `story-perf-summary` payloads for friction totals.
266
- // Story #2853 — fan out the per-Story `findStructuredComment` lookups
267
- // concurrently. Each Story's lookup resolves to a single paginated
268
- // `getTicketComments` round-trip (the raw-comments cache at
269
- // ticketing/reads.js:381-412 dedupes the per-type lookups within a
270
- // ticket), so parallelization adds no extra wire calls — it just
271
- // collapses N sequential round-trips into one wall-clock fan-out.
272
- // `Promise.all` preserves index order, so the resulting
273
- // `storyPerfSummaries` array stays deterministic relative to `stories`.
274
- const perStoryComments = await Promise.all(
275
- stories.map((story) =>
276
- findStructuredComment(
277
- provider,
278
- story.id ?? story.number,
279
- 'story-perf-summary',
280
- ),
281
- ),
282
- );
283
- const storyPerfSummaries = [];
284
- let frictionFromSummaries = 0;
285
- for (const comment of perStoryComments) {
286
- const parsed = parseFencedJsonComment(comment);
287
- if (parsed) {
288
- storyPerfSummaries.push(parsed);
289
- frictionFromSummaries += sumFriction(parsed.frictionByCategory);
290
- }
291
- }
292
-
293
- // Epic-level perf report (used by the full retro's "Top hotspots").
294
- const epicPerfComment = await findStructuredComment(
295
- provider,
296
- epicId,
297
- 'epic-perf-report',
298
- );
299
- const epicPerfReport = parseFencedJsonComment(epicPerfComment);
300
-
301
- // Parked + recut counts: prefer the structured comment; fall back to body
302
- // grep for the recut marker when the comment is absent.
303
- const parkedComment = await findStructuredComment(
304
- provider,
305
- epicId,
306
- 'parked-follow-ons',
307
- );
308
- const parkedParsed = parseFencedJsonComment(parkedComment);
309
- let parkedFollowOns;
310
- if (parkedParsed) {
311
- parkedFollowOns = {
312
- present: true,
313
- recuts: Array.isArray(parkedParsed.recuts) ? parkedParsed.recuts : [],
314
- parked: Array.isArray(parkedParsed.parked) ? parkedParsed.parked : [],
315
- };
316
- } else {
317
- const recutsByBody = stories.filter(
318
- (s) => typeof s.body === 'string' && RECUT_BODY_MARKER.test(s.body),
319
- );
320
- parkedFollowOns = {
321
- present: false,
322
- recuts: recutsByBody.map((s) => ({ storyId: s.id ?? s.number })),
323
- parked: [],
324
- };
325
- }
326
-
327
- // Story #4417 — the compact/full retro decision, the automerge-verdict
328
- // cleanSprint flag, and the routed proposals must all read from ONE
329
- // signals scan. We fold the per-Story `signals.ndjson` streams AND the
330
- // per-Epic `signals.ndjson` stream (the `appendEpicSignal` wave-lifecycle
331
- // writers, e.g. lifecycle-emit) into a single `routedSignals`
332
- // accumulation; `counts.friction` then derives from that same scan so a
333
- // retro can never read "clean" from the comment substrate while
334
- // actionable friction sits unrendered in the ndjson substrate.
335
- //
336
- // Story #2558 — the streams are source-tagged by the writer's
337
- // `tagSignalSource`; `accumulateSignalRecord` reads the canonical
338
- // envelope (top-level `category` + string `source`, Epic #4406). Read
339
- // failures degrade silently — observability MUST NOT take down the retro.
340
- //
341
- // Story #3347 — the per-Story reads fan out via `concurrentMap` with a
342
- // bounded cap (`SIGNALS_READ_CONCURRENCY`); `concurrentMap` preserves
343
- // input order so we concatenate the per-Story `routedSignals` in
344
- // `stories` order, keeping the composed `routedProposals` deterministic.
345
- const perStorySignals = await concurrentMap(
346
- stories,
347
- async (story) => {
348
- const sid = Number(story.id ?? story.number);
349
- if (!Number.isInteger(sid) || sid <= 0) {
350
- return { routedSignals: [] };
351
- }
352
- const localRoutedSignals = [];
353
- try {
354
- await forEachLineFn(epicId, sid, (parsed) => {
355
- accumulateSignalRecord(parsed, localRoutedSignals);
356
- });
357
- } catch (err) {
358
- logger?.warn?.(
359
- `[retro-runner] forEachLine failed for story #${sid} (continuing): ${
360
- err?.message ?? err
361
- }`,
362
- );
363
- }
364
- return { routedSignals: localRoutedSignals };
365
- },
366
- { concurrency: SIGNALS_READ_CONCURRENCY },
367
- );
368
- const routedSignals = [];
369
- for (const perStory of perStorySignals) {
370
- routedSignals.push(...perStory.routedSignals);
371
- }
372
-
373
- // Story #4417 — fold in the per-Epic `signals.ndjson` stream
374
- // (`appendEpicSignal` wave-lifecycle writers, e.g. lifecycle-emit) so the
375
- // unified scan is not blind to Epic-scoped friction that never lands on
376
- // an individual Story stream.
377
- try {
378
- await forEachEpicLineFn(epicId, (parsed) => {
379
- accumulateSignalRecord(parsed, routedSignals);
380
- });
381
- } catch (err) {
382
- logger?.warn?.(
383
- `[retro-runner] forEachEpicLine failed for epic #${epicId} (continuing): ${
384
- err?.message ?? err
385
- }`,
386
- );
387
- }
388
-
389
- const counts = {
390
- // `friction` derives from the unified ndjson scan (every categorized
391
- // signal across the per-Story and per-Epic streams) PLUS the legacy
392
- // story-perf-summary totals, so a non-empty ndjson substrate forces the
393
- // full retro even when no `story-perf-summary` comment was posted.
394
- friction: frictionFromSummaries + routedSignals.length,
395
- parked: parkedFollowOns.parked.length,
396
- recuts: parkedFollowOns.recuts.length,
397
- hitl,
398
- };
399
-
400
- // Story #4417 — project unresolved `agent::blocked` Stories from the
401
- // epic-run-state snapshot into force-flag events for the routed-proposal
402
- // composer. This replaces the formerly hardwired empty array: a blocked
403
- // Story now force-promotes an actionable proposal even absent a matching
404
- // friction category (its category falls back to `agent-blocked`).
405
- const unresolvedBlockedEvents = await readUnresolvedBlockedEvents({
406
- epicId,
407
- provider,
408
- logger,
409
- readFn: epicRunStateReadFn,
410
- });
411
-
412
- // Resolve repos. The framework repo falls back to the Mandrel mirror
413
- // constant (a known, stable default). The consumer repo does NOT fall
414
- // back to the framework repo: when the caller resolves no consumer repo
415
- // from `config.github`, the consumer proposal pane is DISABLED loudly
416
- // (Story #4417) rather than silently routing consumer-tagged friction at
417
- // the framework mirror.
418
- const resolvedFrameworkRepo =
419
- typeof frameworkRepo === 'string' && frameworkRepo.length > 0
420
- ? frameworkRepo
421
- : DEFAULT_FRAMEWORK_REPO;
422
- const resolvedConsumerRepo =
423
- typeof consumerRepo === 'string' && consumerRepo.length > 0
424
- ? consumerRepo
425
- : '';
426
- if (resolvedConsumerRepo.length === 0) {
427
- logger?.warn?.(
428
- '[retro-runner] No consumer repo resolved from config.github — the ' +
429
- 'consumer proposal pane is DISABLED for this retro. Set ' +
430
- 'github.owner / github.repo to route consumer-tagged friction.',
431
- );
432
- }
433
-
434
- const routedProposals = composeRoutedProposalsFn({
435
- epicId,
436
- frameworkRepo: resolvedFrameworkRepo,
437
- consumerRepo: resolvedConsumerRepo,
438
- signals: routedSignals,
439
- unresolvedBlockedEvents,
440
- });
441
-
442
- return {
443
- stories,
444
- counts,
445
- storyPerfSummaries,
446
- epicPerfReport,
447
- parkedFollowOns,
448
- routedProposals,
449
- };
450
- }
@@ -1,191 +0,0 @@
1
- /**
2
- * phases/post-and-mirror.js — retro Phase 4: compose + upsert + mirror.
3
- *
4
- * Runs gather → compose → checks → upsert structured comment → mirror to
5
- * the per-Epic temp dir, and emits the `retro.end` boundary on the bus
6
- * once the write side-effects settle.
7
- *
8
- * Extracted so `runRetro` can wrap the full body in a try/catch for the
9
- * `retro.end` boundary emit on throw without cluttering the happy-path
10
- * read.
11
- */
12
-
13
- import nodeFs from 'node:fs';
14
- import path from 'node:path';
15
-
16
- import { epicRetroMirrorPath } from '../../../config/temp-paths.js';
17
- import { fileRetroProposals as defaultFileRetroProposals } from '../../../feedback-loop/retro-proposals-graduator.js';
18
- import { upsertStructuredComment } from '../../ticketing.js';
19
- import { appendChecksSection, collectRetroFindings } from './checks.js';
20
- import {
21
- composeRetroBody as defaultComposeRetroBody,
22
- deriveDefectClasses,
23
- normalizeInterventionCount,
24
- } from './compose-body.js';
25
- import { gatherRetroSignals as defaultGatherRetroSignals } from './gather-signals.js';
26
-
27
- /**
28
- * Inner compose-and-post helper. Extracted so `runRetro` can wrap the
29
- * full body in a try/catch for the `retro.end` boundary emit without
30
- * cluttering the happy-path read.
31
- */
32
- export async function composeAndPostRetro({
33
- epicId,
34
- provider,
35
- logger,
36
- forceFull,
37
- timestamp,
38
- bus,
39
- now,
40
- manualInterventions,
41
- frameworkRepo,
42
- consumerRepo,
43
- config = null,
44
- gatherFn = defaultGatherRetroSignals,
45
- composeFn = defaultComposeRetroBody,
46
- upsertFn = upsertStructuredComment,
47
- fileRetroProposalsFn = defaultFileRetroProposals,
48
- ghPath,
49
- spawnImpl,
50
- runChecksFn,
51
- assembleStateFn,
52
- cwd,
53
- fsImpl = nodeFs,
54
- startedAt,
55
- onMirrorWritten,
56
- perfThresholds = null,
57
- }) {
58
- const signals = await gatherFn({
59
- epicId,
60
- provider,
61
- logger,
62
- frameworkRepo,
63
- consumerRepo,
64
- });
65
-
66
- // Story #4418 — auto-file the retro's actionable routed proposals BEFORE
67
- // the body composes, so the rendered retro sections list the real filed
68
- // issue numbers instead of paste-ready `gh` command stanzas. Runs behind
69
- // the `delivery.feedbackLoop.retroProposals` toggle (default ON); when
70
- // OFF (or when filing is skipped / fails) the proposals pass through
71
- // unenriched and the composer falls back to the command stanzas. Never
72
- // throws — a filing failure degrades gracefully.
73
- const { routedProposals: filedRoutedProposals, summary: filingSummary } =
74
- await fileRetroProposalsFn({
75
- epicId,
76
- provider,
77
- config,
78
- frameworkRepo,
79
- consumerRepo,
80
- routedProposals: signals.routedProposals,
81
- ghPath,
82
- spawnImpl,
83
- cwd,
84
- logger,
85
- });
86
- // Surface (never throw on) per-proposal filing failures. Dropping
87
- // `summary.errors` silently made a failed `gh issue create` — e.g. a
88
- // `friction::<category>` label that doesn't exist in the target repo —
89
- // invisible except as an unenriched command stanza in the retro body.
90
- if (Array.isArray(filingSummary?.errors) && filingSummary.errors.length) {
91
- for (const err of filingSummary.errors) {
92
- logger?.warn?.(
93
- `[retro] proposal auto-file error (degrading to command stanza): ${
94
- typeof err === 'string' ? err : JSON.stringify(err)
95
- }`,
96
- );
97
- }
98
- }
99
-
100
- // Best-effort fetch of the Epic title for the heading.
101
- let epicTitle;
102
- try {
103
- const epic = await provider.getTicket?.(epicId);
104
- epicTitle = epic?.title;
105
- } catch (err) {
106
- logger?.warn?.(
107
- `[retro-runner] Failed to fetch Epic #${epicId} title (using fallback): ${err?.message ?? err}`,
108
- );
109
- }
110
-
111
- const interventions = normalizeInterventionCount(manualInterventions);
112
- const { body, compact, scorecard } = composeFn({
113
- epicId,
114
- epicTitle,
115
- counts: { ...signals.counts, interventions },
116
- storyPerfSummaries: signals.storyPerfSummaries,
117
- epicPerfReport: signals.epicPerfReport,
118
- parkedFollowOns: signals.parkedFollowOns,
119
- routedProposals: filedRoutedProposals,
120
- timestamp,
121
- forceFull,
122
- perfThresholds,
123
- });
124
-
125
- // Story #4135 (Epic #4131, F11) — derive the recurring-defect-class signal
126
- // from the same routed proposals the body composer consumed. The signal is
127
- // surfaced on the runRetro envelope (`defectClasses`) so callers/tests can
128
- // observe the recurring classes the routed-proposal `gh issue create`
129
- // commands stamp with `friction::<class>` labels — the join key the
130
- // `/plan` Phase 0 fetcher reads back. No-op-safe: a clean sprint (no
131
- // routed proposals) yields `[]`.
132
- const defectClasses = deriveDefectClasses(signals.routedProposals);
133
-
134
- const findings = await collectRetroFindings({
135
- runChecksFn,
136
- assembleStateFn,
137
- cwd,
138
- logger,
139
- });
140
- const bodyWithChecks = appendChecksSection(body, findings);
141
-
142
- logger?.info?.(
143
- `[retro-runner] Posting ${compact ? 'compact' : 'full'} retro on Epic #${epicId}${findings.length > 0 ? ` (${findings.length} finding(s))` : ''}...`,
144
- );
145
- const result = await upsertFn(provider, epicId, 'retro', bodyWithChecks);
146
-
147
- // Story #2089: also mirror the retro body to the per-Epic temp dir so
148
- // operators can read it locally without re-fetching from GitHub. GitHub
149
- // remains SSOT — a write failure logs a warn and does not fail the
150
- // phase. The path is resolved relative to `cwd` when supplied so that
151
- // worktree-scoped invocations land under the worktree's temp tree.
152
- let mirrorAbsPath = null;
153
- try {
154
- const rel = epicRetroMirrorPath(epicId);
155
- const absPath = path.isAbsolute(rel)
156
- ? rel
157
- : path.join(cwd ?? process.cwd(), rel);
158
- fsImpl.mkdirSync(path.dirname(absPath), { recursive: true });
159
- fsImpl.writeFileSync(absPath, bodyWithChecks, 'utf8');
160
- mirrorAbsPath = absPath;
161
- onMirrorWritten?.(absPath);
162
- logger?.info?.(`[retro-runner] Mirrored retro to ${absPath}`);
163
- } catch (err) {
164
- logger?.warn?.(
165
- `[retro-runner] Failed to write retro mirror (retro.md) for Epic #${epicId} (continuing — GitHub upsert succeeded): ${err?.message ?? err}`,
166
- );
167
- }
168
-
169
- // Story #2252 — emit `retro.end` after the upsert + mirror settle so
170
- // the lifecycle ledger captures the closing boundary with the
171
- // posted/compact flags AND the resolved mirror path (when present).
172
- const endedAt = typeof now === 'function' ? now() : Date.now();
173
- const retroEndPayload = {
174
- epicId,
175
- posted: true,
176
- compact: Boolean(compact),
177
- durationMs: Math.max(0, Math.floor(endedAt - startedAt)),
178
- };
179
- if (mirrorAbsPath) retroEndPayload.retroPath = mirrorAbsPath;
180
- await bus.emit('retro.end', retroEndPayload);
181
-
182
- return {
183
- posted: true,
184
- compact,
185
- scorecard,
186
- body: bodyWithChecks,
187
- findings,
188
- defectClasses,
189
- commentId: result?.commentId,
190
- };
191
- }
@@ -1,57 +0,0 @@
1
- /**
2
- * Heuristics that drive which retro path the `epic-retro` helper composes.
3
- *
4
- * The compact three-section retro fires when the Epic's dispatch manifest
5
- * carries zero friction signals. Non-zero on any of the five dimensions
6
- * routes back to the full six-section retro. Keeping the predicate here
7
- * (pure, no I/O) guards the clean-sprint definition from drifting as the
8
- * retro helper markdown evolves.
9
- *
10
- * Epic #1030 Story #1046 — the `friction` count is now sourced from the
11
- * aggregated `story-perf-summary.frictionByCategory` totals across the
12
- * Epic's descendants (the unified summary comment posted by
13
- * `analyze-execution.js`), not the legacy per-Task `friction` structured
14
- * comment fan-out. The predicate signature stays unchanged — the input
15
- * is still a single integer count — so callers only need to swap their
16
- * data source.
17
- *
18
- * Story #2289 — added `interventions`, sourced from the
19
- * `manualInterventions` array on the `epic-run-state` snapshot read
20
- * via `epic-run-state-store.read`. The same list disqualifies the
21
- * Epic from auto-merge, so the retro shape and the auto-merge gate
22
- * now agree on what "clean" means.
23
- */
24
-
25
- /**
26
- * Evaluate whether an Epic's dispatch manifest counts qualify for the
27
- * compact retro path.
28
- *
29
- * All five signals must be zero. Any non-zero value (including negatives,
30
- * which are invalid but treated defensively as "non-clean") returns false.
31
- * Non-number inputs are treated as missing and default to zero so callers
32
- * can omit dimensions they have not yet gathered — a call with no arguments
33
- * returns true.
34
- *
35
- * @param {Object} [counts]
36
- * @param {number} [counts.friction=0] Aggregate friction-event count, summed from `story-perf-summary.frictionByCategory` across the Epic's descendants (Story #1046).
37
- * @param {number} [counts.parked=0] Count of parked follow-on Stories (no manifest lineage).
38
- * @param {number} [counts.recuts=0] Count of Stories carrying a `<!-- recut-of: #N -->` marker.
39
- * @param {number} [counts.hitl=0] Count of tickets that raised an `agent::blocked` event mid-sprint (the runtime HITL pause point).
40
- * @param {number} [counts.interventions=0] Count of recorded manual interventions (the same `manualInterventions` list the auto-merge predicate consults — Story #2289).
41
- * @returns {boolean}
42
- */
43
- export function isCleanManifest(counts = {}) {
44
- const { friction, parked, recuts, hitl, interventions } = counts;
45
- return (
46
- normalize(friction) === 0 &&
47
- normalize(parked) === 0 &&
48
- normalize(recuts) === 0 &&
49
- normalize(hitl) === 0 &&
50
- normalize(interventions) === 0
51
- );
52
- }
53
-
54
- function normalize(value) {
55
- if (typeof value !== 'number' || !Number.isFinite(value)) return 0;
56
- return value;
57
- }