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,571 +0,0 @@
1
- /**
2
- * phases/compose-body.js — retro Phase 2: compose the retro markdown body.
3
- *
4
- * Pure: given an aggregated counts/signals input, produce the retro
5
- * markdown body plus the compact/scorecard envelope. Selects the compact
6
- * (clean-manifest) or full retro shape via `isCleanManifest`.
7
- *
8
- * `normalizeInterventionCount` is exported so the post-and-mirror phase
9
- * can reuse the same clamping logic when forwarding the runtime's
10
- * `manualInterventions` count into the body composer.
11
- */
12
-
13
- import { isCleanManifest } from '../../retro-heuristics.js';
14
- import { classifyPerfSignals } from '../../retro-perf-heuristics.js';
15
-
16
- /**
17
- * Pure: clamp a candidate count to a non-negative integer. Used to
18
- * normalize the `manualInterventions` count plumbed in from the
19
- * epic-run-state-store snapshot before it lands in the scorecard.
20
- * Non-finite, negative, or non-numeric values collapse to 0.
21
- */
22
- export function normalizeInterventionCount(value) {
23
- if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
24
- return 0;
25
- }
26
- return Math.trunc(value);
27
- }
28
-
29
- /**
30
- * Pure: does `routedProposals` carry at least one actionable item (a
31
- * framework- or consumer-routed `gh issue create` proposal)? Story #4417 —
32
- * the compact retro shape (and therefore the `automerge-verdict`
33
- * `cleanSprint: true` flag it drives) MUST be suppressed whenever an
34
- * actionable proposal exists, regardless of `forceFull` or the
35
- * comment-derived counts: the clean-sprint verdict is an auto-merge input
36
- * and must never read "clean" while actionable friction sits in the routed
37
- * proposals. Discarded (single-occurrence) items do NOT count — they are
38
- * the retro's explicit "nothing to file" bucket.
39
- *
40
- * @param {{ framework?: object[], consumer?: object[] } | null | undefined} routedProposals
41
- * @returns {boolean}
42
- */
43
- export function hasActionableProposals(routedProposals) {
44
- if (
45
- !routedProposals ||
46
- typeof routedProposals !== 'object' ||
47
- Array.isArray(routedProposals)
48
- ) {
49
- return false;
50
- }
51
- const framework = Array.isArray(routedProposals.framework)
52
- ? routedProposals.framework
53
- : [];
54
- const consumer = Array.isArray(routedProposals.consumer)
55
- ? routedProposals.consumer
56
- : [];
57
- return framework.length > 0 || consumer.length > 0;
58
- }
59
-
60
- /**
61
- * Pure: derive the recurring-defect-class signal from the routed-proposal
62
- * sections (Story #4135 / Epic #4131, F11).
63
- *
64
- * The routed-proposals composer (`retro-proposals.js`) already split the
65
- * Epic's source-tagged friction into `framework` / `consumer` actionable
66
- * items — a category lands there only when it recurred ≥2 times across the
67
- * Epic OR was force-flagged by an unresolved `agent::blocked` event. Those
68
- * are exactly the **recurring classes** of review/deliver-caught issues F11
69
- * tracks, so we lift them verbatim rather than re-deriving a parallel
70
- * threshold. Each entry carries the `friction::<category>` label the
71
- * composer stamps onto its `gh issue create` command — that label is the
72
- * join key the `/plan` Phase 0 prior-feedback fetcher reads back to surface
73
- * recurring classes to the planner.
74
- *
75
- * Determinism: the two actionable arrays are already sorted by `category`
76
- * ASC by the composer; we concatenate framework-then-consumer and re-sort by
77
- * `category` so the merged signal is stable regardless of which source
78
- * contributed a class.
79
- *
80
- * No-op-safe: a null / non-object / shapeless `routedProposals` (or one with
81
- * empty actionable arrays) yields `[]` — the common clean-sprint case.
82
- *
83
- * @param {{ framework?: object[], consumer?: object[] } | null | undefined} routedProposals
84
- * @returns {Array<{ category: string, occurrences: number, source: 'framework'|'consumer', label: string }>}
85
- */
86
- export function deriveDefectClasses(routedProposals) {
87
- if (
88
- !routedProposals ||
89
- typeof routedProposals !== 'object' ||
90
- Array.isArray(routedProposals)
91
- ) {
92
- return [];
93
- }
94
- const framework = Array.isArray(routedProposals.framework)
95
- ? routedProposals.framework
96
- : [];
97
- const consumer = Array.isArray(routedProposals.consumer)
98
- ? routedProposals.consumer
99
- : [];
100
-
101
- const out = [];
102
- for (const item of [...framework, ...consumer]) {
103
- if (!item || typeof item !== 'object') continue;
104
- const category =
105
- typeof item.category === 'string' ? item.category.trim() : '';
106
- if (category.length === 0) continue;
107
- const occurrences =
108
- typeof item.occurrences === 'number' && Number.isFinite(item.occurrences)
109
- ? item.occurrences
110
- : 0;
111
- const source = item.source === 'framework' ? 'framework' : 'consumer';
112
- out.push({
113
- category,
114
- occurrences,
115
- source,
116
- label: `friction::${category}`,
117
- });
118
- }
119
- out.sort((a, b) => a.category.localeCompare(b.category));
120
- return out;
121
- }
122
-
123
- /**
124
- * Pure: compose the retro markdown body. Exported for tests so they can
125
- * verify the body shape without round-tripping through a stub provider.
126
- *
127
- * Story #2289 adds `counts.interventions` — sourced from the
128
- * `manualInterventions` array on the `epic-run-state` snapshot (the
129
- * same list that disqualifies an Epic from auto-merge). Non-zero
130
- * interventions route to the full retro shape via `isCleanManifest`.
131
- *
132
- * @param {{
133
- * epicId: number,
134
- * epicTitle?: string,
135
- * counts: { friction: number, parked: number, recuts: number, hitl: number, interventions?: number },
136
- * storyPerfSummaries?: object[],
137
- * epicPerfReport?: object|null,
138
- * parkedFollowOns?: { recuts: object[], parked: object[] },
139
- * timestamp?: string,
140
- * forceFull?: boolean,
141
- * }} input
142
- * @returns {{ body: string, compact: boolean, scorecard: object }}
143
- */
144
- export function composeRetroBody(input) {
145
- const {
146
- epicId,
147
- epicTitle = `Epic ${epicId}`,
148
- counts,
149
- storyPerfSummaries = [],
150
- epicPerfReport = null,
151
- parkedFollowOns = { recuts: [], parked: [] },
152
- routedProposals = null,
153
- timestamp = new Date().toISOString(),
154
- forceFull = false,
155
- perfThresholds = null,
156
- } = input;
157
-
158
- const interventions = normalizeInterventionCount(counts?.interventions);
159
- // Story #4417 — the compact shape requires BOTH a clean manifest AND zero
160
- // actionable routed proposals. The routed proposals and the counts are
161
- // derived from the same unified signals scan (gather-signals.js), so this
162
- // guard closes the window where the comment-derived counts read "clean"
163
- // while the ndjson-derived routed proposals hold an actionable item.
164
- const compact =
165
- !forceFull &&
166
- isCleanManifest({ ...counts, interventions }) &&
167
- !hasActionableProposals(routedProposals);
168
- const heading = `## 🪞 Sprint Retrospective — Epic #${epicId}: ${epicTitle}`;
169
- const generatedLine = `_Generated ${timestamp}_`;
170
- const scorecardRows = [
171
- `| agent::blocked Events Raised | ${counts.hitl} |`,
172
- `| Manual Interventions | ${interventions} |`,
173
- `| Friction Events | ${counts.friction} |`,
174
- ];
175
- const scorecard = {
176
- hitl: counts.hitl,
177
- friction: counts.friction,
178
- parked: counts.parked,
179
- recuts: counts.recuts,
180
- interventions,
181
- };
182
- const completeMarker = `<!-- retro-complete: ${timestamp} -->`;
183
- // Machine-readable auto-merge verdict trailer (Story #3901). The
184
- // Phase 8.5 `AutomergePredicate` reads `cleanSprint` from THIS trailer
185
- // — a parsed JSON boolean — rather than string-matching the human-facing
186
- // "🟢 Clean sprint" prose, which was a brittle emoji `.includes()` that
187
- // false-positived on any retro body that happened to quote the marker
188
- // and false-negatived on any compact-body copy edit. `cleanSprint`
189
- // tracks the compact shape exactly (a full/forced retro is never a
190
- // clean sprint); the scorecard is mirrored so the predicate can surface
191
- // the disqualifying counts without re-parsing the markdown table.
192
- const automergeTrailer = `<!-- automerge-verdict: ${JSON.stringify({
193
- cleanSprint: compact,
194
- scorecard,
195
- })} -->`;
196
-
197
- if (compact) {
198
- const body = [
199
- heading,
200
- '',
201
- generatedLine,
202
- '',
203
- '🟢 Clean sprint — zero friction, zero parked follow-ons, zero recuts, zero agent::blocked events, zero manual interventions.',
204
- '',
205
- '### Sprint Scorecard',
206
- '',
207
- '| Metric | Value |',
208
- '| ---------------------------- | ----- |',
209
- ...scorecardRows,
210
- '',
211
- '### Session Observations',
212
- '',
213
- '_Nothing notable beyond the scorecard._',
214
- '',
215
- '### Action Items for Next Epic',
216
- '',
217
- '_None._',
218
- '',
219
- completeMarker,
220
- automergeTrailer,
221
- ].join('\n');
222
- return { body, compact: true, scorecard };
223
- }
224
-
225
- // Full path — six sections.
226
- const hotspotLines =
227
- epicPerfReport &&
228
- Array.isArray(epicPerfReport.topHotspots) &&
229
- epicPerfReport.topHotspots.length > 0
230
- ? epicPerfReport.topHotspots.map(
231
- (h) =>
232
- `- \`${h.phase}\` — ${h.occurrences} occurrence(s), avg ratio ${
233
- typeof h.avgRatio === 'number' ? h.avgRatio.toFixed(2) : 'n/a'
234
- }`,
235
- )
236
- : ['_No epic-perf-report available._'];
237
-
238
- const parkedLines =
239
- parkedFollowOns.parked.length > 0
240
- ? parkedFollowOns.parked.map(
241
- (p) =>
242
- `- Adopt or close parked follow-on #${p.storyId ?? p.id ?? '?'}`,
243
- )
244
- : [];
245
- const recutLines =
246
- parkedFollowOns.recuts.length > 0
247
- ? parkedFollowOns.recuts.map(
248
- (r) => `- Recut #${r.storyId ?? r.id ?? '?'} attributed to manifest`,
249
- )
250
- : [];
251
- const legacyActionItems = [...parkedLines, ...recutLines];
252
- const legacyActionItemsBody =
253
- legacyActionItems.length > 0 ? legacyActionItems.join('\n') : '_None._';
254
-
255
- // Story #2558 — routed-proposals mode. When routedProposals is supplied
256
- // AND any of the three buckets is non-empty, render the three explicit
257
- // sections in deterministic order ABOVE the retro-complete marker:
258
- // 1. Proposed issues — consumer repo
259
- // 2. Proposed issues — framework repo
260
- // 3. One-off / discarded
261
- // Otherwise the legacy "Action Items for Next Epic" section renders.
262
- const routedSectionsBlock = renderRoutedSections(routedProposals);
263
-
264
- const actionSection =
265
- routedSectionsBlock === null
266
- ? ['### Action Items for Next Epic', '', legacyActionItemsBody]
267
- : routedSectionsBlock;
268
-
269
- // Story #3042 — Performance Signals + Recommended Follow-Ons. Both
270
- // sections are emitted only when (1) the persisted epic-perf-report is
271
- // present AND (2) `classifyPerfSignals` returns at least one signal.
272
- // Otherwise the entire pair is suppressed so the retro stays compact.
273
- const perfReportForHeuristics = epicPerfReport
274
- ? { ...epicPerfReport, storyPerfSummaries }
275
- : null;
276
- const perfSignals = perfReportForHeuristics
277
- ? classifyPerfSignals(perfReportForHeuristics, perfThresholds)
278
- : [];
279
- const perfSignalsSection = renderPerfSignalsSection(perfSignals);
280
- const followOnsSection = renderFollowOnsSection(perfSignals);
281
-
282
- const body = [
283
- heading,
284
- '',
285
- generatedLine,
286
- '',
287
- '### Sprint Scorecard',
288
- '',
289
- '| Metric | Value |',
290
- '| ---------------------------- | ----- |',
291
- ...scorecardRows,
292
- '',
293
- '### What Went Well',
294
- '',
295
- '_(populate from execution telemetry — extracted retro module emits a placeholder; deeper analysis is the operator follow-up.)_',
296
- '',
297
- '### What Could Be Improved',
298
- '',
299
- '#### Top hotspots',
300
- '',
301
- ...hotspotLines,
302
- '',
303
- '### Architectural Debt',
304
- '',
305
- '_(no automated detection in v5.40.0 — operator review required.)_',
306
- '',
307
- '### Protocol Optimization Recommendations (Self-Healing)',
308
- '',
309
- '_(operator follow-up.)_',
310
- '',
311
- ...actionSection,
312
- '',
313
- ...perfSignalsSection,
314
- ...followOnsSection,
315
- completeMarker,
316
- automergeTrailer,
317
- ].join('\n');
318
- return { body, compact: false, scorecard };
319
- }
320
-
321
- /**
322
- * Pure: render the `## Performance Signals` section. Returns `[]` (so the
323
- * caller can spread it conditionally) when no signal trips. The renderer
324
- * emits one bullet per signal, naming the kind, the offending wave(s),
325
- * the observed value, and the threshold that produced the signal.
326
- *
327
- * @param {Array<object>} signals
328
- * @returns {string[]}
329
- */
330
- function renderPerfSignalsSection(signals) {
331
- if (!Array.isArray(signals) || signals.length === 0) return [];
332
- const lines = ['## Performance Signals', ''];
333
- for (const s of signals) {
334
- lines.push(`- ${describeSignal(s)}`);
335
- }
336
- lines.push('');
337
- return lines;
338
- }
339
-
340
- /**
341
- * Pure: render the `## Recommended Follow-Ons` section. One stanza per
342
- * signal, each carrying a Conventional-Commits-shaped title and a body
343
- * including the suggested `meta::framework-gap` label.
344
- *
345
- * Each stanza is a `gh issue create` command operators can paste directly:
346
- * the title is in Conventional-Commits form, and the body names the
347
- * trigger metric so the planner has the context for triage.
348
- *
349
- * @param {Array<object>} signals
350
- * @returns {string[]}
351
- */
352
- function renderFollowOnsSection(signals) {
353
- if (!Array.isArray(signals) || signals.length === 0) return [];
354
- const lines = ['## Recommended Follow-Ons', ''];
355
- for (const s of signals) {
356
- const title = followOnTitle(s);
357
- const body = followOnBody(s);
358
- lines.push(`- **${title}**`);
359
- lines.push('');
360
- lines.push('```sh');
361
- lines.push(
362
- `gh issue create --title ${shellEscape(title)} --label meta::framework-gap --body ${shellEscape(body)}`,
363
- );
364
- lines.push('```');
365
- lines.push('');
366
- }
367
- return lines;
368
- }
369
-
370
- /**
371
- * Human-readable one-liner for a perf signal — used as the bullet text
372
- * under `## Performance Signals`. Pure; signal kinds it doesn't know
373
- * collapse to a generic shape so the renderer never produces empty
374
- * strings for forward-compatible signal kinds.
375
- *
376
- * @param {object} s
377
- * @returns {string}
378
- */
379
- function describeSignal(s) {
380
- if (!s || typeof s !== 'object') return 'unknown perf signal';
381
- if (s.kind === 'low-utilisation') {
382
- const pct = formatPercent(s.utilisation);
383
- const thr = formatPercent(s.threshold);
384
- return `low-utilisation: wave ${s.waveIndex} ran at ${pct} (threshold ${thr})`;
385
- }
386
- if (s.kind === 'high-bootstrap-share') {
387
- const pct = formatPercent(s.share);
388
- const thr = formatPercent(s.threshold);
389
- return `high-bootstrap-share: story-init consumed ${pct} of Story execution time (threshold ${thr})`;
390
- }
391
- if (s.kind === 'cap-binding-run') {
392
- return `cap-binding-run: waves ${s.fromWaveIndex}–${s.toWaveIndex} (${s.runLength} consecutive waves) saturated the concurrency cap`;
393
- }
394
- return `${s.kind ?? 'unknown'} perf signal`;
395
- }
396
-
397
- /**
398
- * Conventional-Commits-shaped title for a perf signal's follow-on. Pure.
399
- *
400
- * @param {object} s
401
- * @returns {string}
402
- */
403
- function followOnTitle(s) {
404
- if (!s || typeof s !== 'object')
405
- return 'perf: investigate unknown perf signal';
406
- if (s.kind === 'low-utilisation') {
407
- return `perf(epic-deliver): investigate low utilisation in wave ${s.waveIndex}`;
408
- }
409
- if (s.kind === 'high-bootstrap-share') {
410
- return 'perf(story-init): reduce bootstrap share of Story execution time';
411
- }
412
- if (s.kind === 'cap-binding-run') {
413
- return `perf(deliver-runner): raise concurrency cap to break ${s.runLength}-wave cap-binding run`;
414
- }
415
- return `perf: investigate ${s.kind} signal`;
416
- }
417
-
418
- /**
419
- * Body for the paste-ready follow-on stanza. Includes the trigger metric
420
- * so the planner has the context for triage.
421
- *
422
- * @param {object} s
423
- * @returns {string}
424
- */
425
- function followOnBody(s) {
426
- const trigger = describeSignal(s);
427
- return [
428
- `Auto-suggested by retro-perf-heuristics (Story #3042).`,
429
- ``,
430
- `Trigger: ${trigger}.`,
431
- ``,
432
- `Apply the meta::framework-gap label so /plan Phase 0 surfaces it on the next planning pass.`,
433
- ].join('\n');
434
- }
435
-
436
- function formatPercent(n) {
437
- if (typeof n !== 'number' || !Number.isFinite(n)) return 'n/a';
438
- return `${(n * 100).toFixed(1)}%`;
439
- }
440
-
441
- /**
442
- * Minimal POSIX shell-quote for the `gh issue create` paste-ready
443
- * command. We single-quote and escape embedded single quotes so the
444
- * stanza works under bash/zsh/PowerShell-Core. The retro body is
445
- * read-only documentation, so the quoting is a UX convenience, not a
446
- * security boundary — but the helper is total nonetheless.
447
- *
448
- * @param {string} s
449
- * @returns {string}
450
- */
451
- function shellEscape(s) {
452
- const str = String(s ?? '');
453
- return `'${str.replace(/'/g, `'\\''`)}'`;
454
- }
455
-
456
- /**
457
- * Render the body lines (everything below a section heading and its trailing
458
- * blank) for a "proposed issues" bucket — the consumer and framework sections
459
- * share this shape. Empty buckets collapse to a single `_None._`; populated
460
- * buckets emit, per item, either the **filed issue reference** (when the
461
- * retro auto-filer already filed it — Story #4418, stamped as
462
- * `item.filedIssue`) or the paste-ready fenced `gh issue create` stanza (the
463
- * toggle-OFF / filing-skipped fallback).
464
- *
465
- * @param {object[]} items
466
- * @returns {string[]}
467
- */
468
- function renderIssueBucket(items) {
469
- if (items.length === 0) return ['_None._'];
470
- const lines = [];
471
- for (const item of items) {
472
- lines.push(`- **${item.title ?? item.category}**`);
473
- lines.push('');
474
- if (item.filedIssue && item.filedIssue.url) {
475
- const ref = Number.isInteger(item.filedIssue.number)
476
- ? `#${item.filedIssue.number}`
477
- : 'issue';
478
- lines.push(` Filed: [${ref}](${item.filedIssue.url})`);
479
- } else {
480
- lines.push('```sh');
481
- lines.push(String(item.command ?? ''));
482
- lines.push('```');
483
- }
484
- lines.push('');
485
- }
486
- return lines;
487
- }
488
-
489
- /**
490
- * Render the body lines for the "one-off / discarded" bucket — one bullet per
491
- * discarded class naming its occurrence count and source, or `_None._`.
492
- *
493
- * @param {object[]} items
494
- * @returns {string[]}
495
- */
496
- function renderDiscardedBucket(items) {
497
- if (items.length === 0) return ['_None._'];
498
- return items.map(
499
- (d) =>
500
- `- \`${d.category}\` (${d.occurrences ?? 1} occurrence, source: ${d.source ?? 'consumer'})`,
501
- );
502
- }
503
-
504
- /**
505
- * Descriptor table for the three routed-proposal sections, in deterministic
506
- * emit order (consumer → framework → discarded). Each descriptor pairs a
507
- * heading, the `routedProposals` field it reads, and a body renderer.
508
- * {@link renderRoutedSections} walks the table once, so reordering or adding a
509
- * section is a data edit here rather than another copy-pasted emit block.
510
- *
511
- * The former "Proposed memory updates" section was deleted in the Epic
512
- * #4406 signal-contract cutover — no writer ever produced the memory-pane
513
- * records it rendered.
514
- *
515
- * @type {Array<{ heading: string, field: string, renderBucket: (items: object[]) => string[] }>}
516
- */
517
- const ROUTED_SECTIONS = [
518
- {
519
- heading: '### Proposed issues — consumer repo',
520
- field: 'consumer',
521
- renderBucket: renderIssueBucket,
522
- },
523
- {
524
- heading: '### Proposed issues — framework repo',
525
- field: 'framework',
526
- renderBucket: renderIssueBucket,
527
- },
528
- {
529
- heading: '### One-off / discarded',
530
- field: 'discarded',
531
- renderBucket: renderDiscardedBucket,
532
- },
533
- ];
534
-
535
- /**
536
- * Pure: render the four routed-proposal sections in deterministic order.
537
- * Returns `null` when `routedProposals` is absent or fully empty — the
538
- * caller falls back to the legacy "Action Items for Next Epic" section so
539
- * back-compat callers see no shape change.
540
- *
541
- * @param {{ framework: object[], consumer: object[], memory: object[], discarded: object[] } | null} routedProposals
542
- * @returns {string[] | null}
543
- */
544
- function renderRoutedSections(routedProposals) {
545
- if (
546
- !routedProposals ||
547
- typeof routedProposals !== 'object' ||
548
- Array.isArray(routedProposals)
549
- ) {
550
- return null;
551
- }
552
- const buckets = ROUTED_SECTIONS.map((section) => {
553
- const items = Array.isArray(routedProposals[section.field])
554
- ? routedProposals[section.field]
555
- : [];
556
- return { section, items };
557
- });
558
- if (buckets.every(({ items }) => items.length === 0)) {
559
- return null;
560
- }
561
-
562
- // Each section renders as `[heading, '', ...body]`; a single blank-line
563
- // separator sits between consecutive sections (no trailing separator after
564
- // the last), reproducing the original hand-unrolled push sequence exactly.
565
- const out = [];
566
- for (const { section, items } of buckets) {
567
- if (out.length > 0) out.push('');
568
- out.push(section.heading, '', ...section.renderBucket(items));
569
- }
570
- return out;
571
- }