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,267 +0,0 @@
1
- /**
2
- * progress-reporter/composition.js — body builders for the
3
- * `epic-run-progress` structured comment and the periodic ProgressReporter
4
- * snapshot.
5
- *
6
- * Extracted from the parent `progress-reporter.js` so the
7
- * structured-comment rendering surface is testable independently of the
8
- * orchestration shell (`ProgressReporter`) and the I/O webhook posters in
9
- * `transport.js`. Pure functions only — no provider calls, no clock reads
10
- * beyond the caller-supplied `now()`.
11
- *
12
- * `upsertEpicRunProgress` is the canonical body builder + persistence
13
- * surface for the rolled-up Epic-level table that lands on the Epic
14
- * ticket after every wave; the ProgressReporter class delegates its
15
- * per-poll `#render` / `#renderNotable` to the pure renderers below so
16
- * the same shape is generated either side of the boundary.
17
- */
18
-
19
- import { upsertStructuredComment } from '../../ticketing.js';
20
- import { EPIC_RUN_PROGRESS_TYPE, STATE_EMOJI } from './signals.js';
21
-
22
- /**
23
- * Truncate `s` to at most `n` characters, suffixing with a single ellipsis
24
- * (`…`) when the string was longer. Returns the empty string for any
25
- * falsy input so table cells never render `undefined`/`null`.
26
- */
27
- function truncate(s, n) {
28
- if (!s) return '';
29
- return s.length > n ? `${s.slice(0, n - 1)}…` : s;
30
- }
31
-
32
- /**
33
- * Escape pipe characters so a value can be inlined into a markdown table
34
- * cell without breaking the column separators.
35
- */
36
- export function escapePipes(s) {
37
- return String(s).replace(/\|/g, '\\|');
38
- }
39
-
40
- /**
41
- * Derive the high-level state classification for a single ticket. Reads
42
- * the canonical `agent::*` label set first, then falls back to the GitHub
43
- * `state` string for the closed-without-done case. Returns `'unknown'`
44
- * for any unrecognized shape so the renderer can flag unreadable rows in
45
- * the Notable section.
46
- */
47
- export function deriveState(ticket, AGENT_LABELS) {
48
- if (!ticket) return 'unknown';
49
- const labels = ticket.labels ?? [];
50
- const state = (ticket.state ?? '').toString().toUpperCase();
51
- if (state === 'CLOSED' || labels.includes(AGENT_LABELS.DONE)) return 'done';
52
- if (labels.includes(AGENT_LABELS.BLOCKED)) return 'blocked';
53
- if (labels.includes(AGENT_LABELS.EXECUTING)) return 'in-flight';
54
- if (labels.includes(AGENT_LABELS.READY)) return 'queued';
55
- return 'unknown';
56
- }
57
-
58
- /**
59
- * Declarative descriptor table that drives the Notable bullet block. Each
60
- * descriptor names a row state, the emoji prefix to render, and the
61
- * `label(count)` function that produces the count-aware human phrase (e.g.
62
- * "1 story blocked" vs "2 stories blocked"). Iteration order is the
63
- * canonical render order: blocked → in-flight → unknown, matching the
64
- * pre-refactor sequential filter walks so output stays byte-identical.
65
- */
66
- const STATE_NOTABLE_DESCRIPTORS = [
67
- {
68
- state: 'blocked',
69
- emoji: STATE_EMOJI.blocked,
70
- label: (n) => `${n} stor${n === 1 ? 'y' : 'ies'} blocked`,
71
- },
72
- {
73
- state: 'in-flight',
74
- emoji: STATE_EMOJI['in-flight'],
75
- label: (n) => `${n} in flight`,
76
- },
77
- {
78
- state: 'unknown',
79
- emoji: STATE_EMOJI.unknown,
80
- label: (n) => `${n} unreadable (token scope / network?)`,
81
- },
82
- ];
83
-
84
- /**
85
- * Single-pass grouping of `rows` keyed by the descriptor states. Returns a
86
- * Map<state, row[]> so callers can iterate descriptors and look up the
87
- * matching slice in O(1); states absent from `rows` get an empty array so
88
- * the renderer can skip them with a single `length` check.
89
- */
90
- function groupRowsByNotableState(rows) {
91
- const groups = new Map(STATE_NOTABLE_DESCRIPTORS.map((d) => [d.state, []]));
92
- for (const r of rows) {
93
- const bucket = groups.get(r.state);
94
- if (bucket) bucket.push(r);
95
- }
96
- return groups;
97
- }
98
-
99
- /**
100
- * Run a single detector against `rows`/`ctx`, swallowing any thrown or
101
- * rejected error so one misbehaving detector cannot kill the whole render
102
- * path. Failures are surfaced via `logger.warn` so operators still see the
103
- * signal; the bullet array is treated as empty on failure.
104
- */
105
- async function runDetector(detector, rows, ctx, logger) {
106
- try {
107
- const fn = typeof detector === 'function' ? detector : detector?.detect;
108
- if (typeof fn !== 'function') return [];
109
- const out = await fn.call(detector, rows, ctx);
110
- return Array.isArray(out) ? out : [];
111
- } catch (err) {
112
- logger?.warn?.(`[ProgressReporter] detector failed: ${err.message}`);
113
- return [];
114
- }
115
- }
116
-
117
- /**
118
- * Render the **Notable** bullet block: blocked-story summary, in-flight
119
- * counts, unreadable rows, plus any detector bullets that were collected
120
- * from the caller. Detectors receive `(rows, ctx)` and may return either
121
- * an array of strings or a thenable resolving to one — the caller is
122
- * responsible for awaiting and trapping detector failures (we trap them
123
- * here to keep the render path non-fatal).
124
- *
125
- * The state-driven bullets are emitted from `STATE_NOTABLE_DESCRIPTORS`
126
- * via a single grouping pass, which keeps the cyclomatic surface flat as
127
- * new notable states are added.
128
- *
129
- * Returns the rendered block (without the leading `**Notable**` header so
130
- * the caller can place it inside a larger composition).
131
- */
132
- export async function renderNotable({ rows, detectors = [], wave, logger }) {
133
- const items = [];
134
- const groups = groupRowsByNotableState(rows);
135
- for (const { state, emoji, label } of STATE_NOTABLE_DESCRIPTORS) {
136
- const matched = groups.get(state);
137
- if (!matched.length) continue;
138
- const ids = matched.map((r) => `#${r.id}`).join(', ');
139
- items.push(`- ${emoji} ${label(matched.length)}: ${ids}`);
140
- }
141
-
142
- const ctx = { wave };
143
- const detectorResults = await Promise.all(
144
- (detectors ?? []).map((detector) =>
145
- runDetector(detector, rows, ctx, logger),
146
- ),
147
- );
148
- for (const bullets of detectorResults) {
149
- for (const b of bullets) items.push(b.startsWith('- ') ? b : `- ${b}`);
150
- }
151
-
152
- if (!items.length) items.push('- (none)');
153
- return items.join('\n');
154
- }
155
-
156
- /**
157
- * Render and upsert the rolled-up `epic-run-progress` comment on the Epic.
158
- *
159
- * Called by `/deliver`'s per-Story status recorder
160
- * (`epic-execute-record-wave.js`) after each recorder beat. Story #4155
161
- * (Epic #4151) — the Epic `/deliver` runtime cut over from the wave-batch
162
- * scheduler to the continuous ready-set core, so the rollup is a **flat
163
- * per-Story table** keyed by the checkpoint's `stories` status map, not a
164
- * wave-grouped table. There is no `currentWave` / `totalWaves` / `waves[]`
165
- * in the payload any more.
166
- *
167
- * The payload schema:
168
- *
169
- * {
170
- * "kind": "epic-run-progress",
171
- * "epicId": <number>,
172
- * "stories": [ { id, title?, state, blockerCommentId? } ],
173
- * "startedAt"?: "<iso8601>",
174
- * "updatedAt": "<iso8601>"
175
- * }
176
- *
177
- * The function does not re-derive Story state from labels — it trusts the
178
- * `stories` map supplied by the caller (the checkpoint's recorded per-Story
179
- * statuses).
180
- *
181
- * @param {{
182
- * provider: import('../../../ITicketingProvider.js').ITicketingProvider,
183
- * epicId: number,
184
- * stories: Record<string, { status?: string, title?: string,
185
- * blockerCommentId?: string }>,
186
- * startedAt?: string,
187
- * now?: () => Date,
188
- * }} args
189
- * @returns {Promise<{ body: string, payload: object }>} the rendered body
190
- * and payload that were upserted onto the Epic.
191
- */
192
- export async function upsertEpicRunProgress({
193
- provider,
194
- epicId,
195
- stories,
196
- startedAt,
197
- now = () => new Date(),
198
- } = {}) {
199
- if (!provider || typeof provider.postComment !== 'function') {
200
- throw new TypeError(
201
- 'upsertEpicRunProgress requires a provider with postComment',
202
- );
203
- }
204
- const epicIdNum = Number(epicId);
205
- if (!Number.isInteger(epicIdNum) || epicIdNum <= 0) {
206
- throw new TypeError('upsertEpicRunProgress requires a numeric epicId');
207
- }
208
- const statusMap = stories && typeof stories === 'object' ? stories : {};
209
-
210
- const updatedAt = now().toISOString();
211
- const rows = Object.entries(statusMap)
212
- .map(([key, rec]) => {
213
- const id = Number(key);
214
- const state = String(rec?.status ?? 'pending');
215
- const row = { id, title: String(rec?.title ?? ''), state };
216
- if (rec?.blockerCommentId != null) {
217
- row.blockerCommentId = String(rec.blockerCommentId);
218
- }
219
- return row;
220
- })
221
- .filter((r) => Number.isInteger(r.id) && r.id > 0)
222
- .sort((a, b) => a.id - b.id);
223
-
224
- const payload = {
225
- kind: EPIC_RUN_PROGRESS_TYPE,
226
- epicId: epicIdNum,
227
- stories: rows,
228
- updatedAt,
229
- };
230
- if (typeof startedAt === 'string' && startedAt) {
231
- payload.startedAt = startedAt;
232
- }
233
-
234
- const totalStories = rows.length;
235
- const doneStories = rows.filter((s) => s.state === 'done').length;
236
- const header = `### 📊 Epic Progress — ${doneStories}/${totalStories} stories done`;
237
-
238
- const tableLines = ['| ID | State | Title |', '|---|---|---|'];
239
- if (rows.length === 0) {
240
- tableLines.push('| — | _(no stories yet)_ | — |');
241
- } else {
242
- for (const s of rows) {
243
- const emoji = STATE_EMOJI[s.state] ?? '';
244
- const title = escapePipes(truncate(s.title, 60));
245
- tableLines.push(`| #${s.id} | ${emoji} ${s.state} | ${title} |`);
246
- }
247
- }
248
-
249
- const body = [
250
- header,
251
- '',
252
- tableLines.join('\n'),
253
- '',
254
- '```json',
255
- JSON.stringify(payload, null, 2),
256
- '```',
257
- ].join('\n');
258
-
259
- await upsertStructuredComment(
260
- provider,
261
- epicIdNum,
262
- EPIC_RUN_PROGRESS_TYPE,
263
- body,
264
- );
265
-
266
- return { body, payload };
267
- }
@@ -1,210 +0,0 @@
1
- /**
2
- * progress-reporter/signals.js — parsing and aggregation helpers for the
3
- * `story-run-progress` and `phase-timings` structured comments.
4
- *
5
- * Extracted from the parent `progress-reporter.js` so the aggregator
6
- * surface (phase percentile rollups, story-run-progress parsing) is
7
- * testable independently of the comment composition (`composition.js`)
8
- * and webhook transport (`transport.js`). Pure functions only — no
9
- * provider calls, no I/O — which makes this the natural home for the
10
- * shared state lookup tables (`PHASE_TO_STATE`, `PHASE_ORDER`,
11
- * `STATE_EMOJI`) and the structured-comment kind constants.
12
- */
13
-
14
- import { parseFencedJsonComment } from '../../structured-comment-parser.js';
15
-
16
- /**
17
- * Structured-comment kind for the rolled-up Epic-level progress
18
- * comment. Persisted via `composition.upsertEpicRunProgress` and read
19
- * back by tests + reconcilers.
20
- */
21
- export const EPIC_RUN_PROGRESS_TYPE = 'epic-run-progress';
22
-
23
- /**
24
- * Structured-comment kind for the per-Story phase-timings summary
25
- * posted by `story-close.js`. Read back by the ProgressReporter so the
26
- * Epic-level snapshot can aggregate median/p95 across waves.
27
- */
28
- export const PHASE_TIMINGS_TYPE = 'phase-timings';
29
-
30
- /**
31
- * Structured-comment kind for the per-Story run-progress snapshot that
32
- * `/deliver` upserts on every Task transition. Read by
33
- * ProgressReporter so the Epic-level table reflects sub-agent state in
34
- * near-real time instead of label-derived classifications.
35
- */
36
- export const STORY_RUN_PROGRESS_TYPE = 'story-run-progress';
37
-
38
- /**
39
- * Phase → high-level state classification. Lookup table flattens the
40
- * previous switch so cyclomatic complexity stays at 1 (one branch in the
41
- * `??` fallback) rather than 6 — keeps the CRAP score floor-bound under
42
- * coverage variance.
43
- */
44
- export const PHASE_TO_STATE = {
45
- done: 'done',
46
- blocked: 'blocked',
47
- implementing: 'in-flight',
48
- closing: 'in-flight',
49
- init: 'queued',
50
- };
51
-
52
- /**
53
- * Fixed ordering for the rendered phase-timings table. Matches the enum
54
- * in lib/util/phase-timer.js so rows line up with how operators think
55
- * about the story lifecycle rather than re-sorting by alphabet or
56
- * frequency.
57
- */
58
- export const PHASE_ORDER = [
59
- 'worktree-create',
60
- 'bootstrap',
61
- 'install',
62
- 'implement',
63
- 'lint',
64
- 'test',
65
- 'close',
66
- 'api-sync',
67
- ];
68
-
69
- /**
70
- * Emoji prefix per high-level state. Consumed by the rolled-up Epic table
71
- * (`composition.upsertEpicRunProgress`) so operators see a consistent icon
72
- * per state across the progress surface.
73
- */
74
- export const STATE_EMOJI = {
75
- done: '✅',
76
- blocked: '🚧',
77
- 'in-flight': '🔧',
78
- queued: '⏳',
79
- unknown: '❓',
80
- };
81
-
82
- /**
83
- * Map a `story-run-progress` phase to the high-level state label used
84
- * by the rendered tables. Returns `'unknown'` for any phase value that
85
- * isn't in the lookup table so an unexpected phase surfaces as an
86
- * unreadable row rather than a crash.
87
- */
88
- export function phaseToState(phase) {
89
- return PHASE_TO_STATE[phase] ?? 'unknown';
90
- }
91
-
92
- /**
93
- * Parse a `story-run-progress` structured comment posted by `/deliver`.
94
- * Returns `null` for any malformed body — the caller falls back to the
95
- * ticket-label state derivation in that case.
96
- *
97
- * Expected payload shape (JSON inside a fenced json codeblock):
98
- * {
99
- * storyId: number,
100
- * branch?: string,
101
- * phase: 'init'|'implementing'|'closing'|'blocked'|'done',
102
- * title?: string,
103
- * updatedAt?: string,
104
- * }
105
- */
106
- export function parseStoryRunProgressComment(comment) {
107
- const payload = parseFencedJsonComment(comment);
108
- if (!payload || typeof payload !== 'object') return null;
109
- const phase = typeof payload.phase === 'string' ? payload.phase : undefined;
110
- return {
111
- storyId: Number(payload.storyId),
112
- title: typeof payload.title === 'string' ? payload.title : '',
113
- phase,
114
- state: phaseToState(phase),
115
- };
116
- }
117
-
118
- /**
119
- * Extract the `{ phases, ... }` payload from a `phase-timings` structured
120
- * comment. Comment body is the fenced-JSON format produced by
121
- * `renderPhaseTimingsCommentBody` in story-close. Returns `null`
122
- * for any parse failure — the caller treats that as "no summary
123
- * available" without erroring out progress rendering.
124
- */
125
- export function parsePhaseTimingsComment(comment) {
126
- const payload = parseFencedJsonComment(comment);
127
- if (!payload || typeof payload !== 'object') return null;
128
- if (!Array.isArray(payload.phases)) return null;
129
- return {
130
- storyId: Number(payload.storyId),
131
- totalMs: Number(payload.totalMs) || 0,
132
- phases: payload.phases
133
- .filter(
134
- (p) =>
135
- p &&
136
- typeof p.name === 'string' &&
137
- Number.isFinite(Number(p.elapsedMs)),
138
- )
139
- .map((p) => ({ name: p.name, elapsedMs: Number(p.elapsedMs) })),
140
- };
141
- }
142
-
143
- /**
144
- * Aggregate a list of `phase-timings` summaries into per-phase median,
145
- * p95, and sample count. Returns phases ordered by the canonical
146
- * `PHASE_ORDER` so the rendered table always has the same row sequence —
147
- * operators should never have to hunt for the `install` row.
148
- */
149
- export function aggregatePhaseTimings(summaries) {
150
- const buckets = new Map();
151
- for (const s of summaries) {
152
- if (!s || !Array.isArray(s.phases)) continue;
153
- for (const p of s.phases) {
154
- if (!buckets.has(p.name)) buckets.set(p.name, []);
155
- buckets.get(p.name).push(p.elapsedMs);
156
- }
157
- }
158
- const rows = [];
159
- for (const name of PHASE_ORDER) {
160
- const samples = buckets.get(name);
161
- if (!samples || samples.length === 0) continue;
162
- rows.push({
163
- name,
164
- median: percentile(samples, 0.5),
165
- p95: percentile(samples, 0.95),
166
- n: samples.length,
167
- });
168
- }
169
- // Include any unexpected phase names at the tail so a future enum
170
- // addition surfaces in the table instead of being silently dropped.
171
- for (const [name, samples] of buckets.entries()) {
172
- if (PHASE_ORDER.includes(name)) continue;
173
- rows.push({
174
- name,
175
- median: percentile(samples, 0.5),
176
- p95: percentile(samples, 0.95),
177
- n: samples.length,
178
- });
179
- }
180
- return rows;
181
- }
182
-
183
- function percentile(samples, q) {
184
- const sorted = [...samples].sort((a, b) => a - b);
185
- if (sorted.length === 0) return 0;
186
- // Nearest-rank method — clamped so q=1 picks the last element.
187
- const idx = Math.min(
188
- sorted.length - 1,
189
- Math.max(0, Math.ceil(q * sorted.length) - 1),
190
- );
191
- return sorted[idx];
192
- }
193
-
194
- /**
195
- * Render the aggregated phase-timings table. Returns `null` when there
196
- * are no summaries to render so the caller can elide the section
197
- * entirely rather than emitting an empty stub.
198
- */
199
- export function renderPhaseTimingsSection(summaries) {
200
- if (!Array.isArray(summaries) || summaries.length === 0) return null;
201
- const rows = aggregatePhaseTimings(summaries);
202
- if (rows.length === 0) return null;
203
- const header = `### Phase timings (last ${summaries.length} completed stor${summaries.length === 1 ? 'y' : 'ies'})`;
204
- const table = [
205
- '| Phase | median ms | p95 ms | n |',
206
- '| --- | --- | --- | --- |',
207
- ...rows.map((r) => `| ${r.name} | ${r.median} | ${r.p95} | ${r.n} |`),
208
- ].join('\n');
209
- return `${header}\n\n${table}`;
210
- }