mandrel 2.0.0 → 2.2.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 (323) hide show
  1. package/.agents/README.md +59 -28
  2. package/.agents/agents/acceptance-critic.md +20 -9
  3. package/.agents/agents/story-worker.md +45 -48
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +60 -46
  6. package/.agents/docs/agentrc-reference.json +8 -13
  7. package/.agents/docs/configuration.md +33 -57
  8. package/.agents/docs/execution-reference.md +39 -10
  9. package/.agents/docs/quality-gates.md +17 -19
  10. package/.agents/docs/workflows.md +6 -6
  11. package/.agents/instructions.md +64 -79
  12. package/.agents/rules/ci-remediation.md +3 -3
  13. package/.agents/rules/gherkin-standards.md +10 -0
  14. package/.agents/rules/git-conventions-reference.md +42 -51
  15. package/.agents/schemas/acceptance-eval-verdict.schema.json +2 -2
  16. package/.agents/schemas/agentrc.schema.json +35 -46
  17. package/.agents/schemas/audit-rules.json +59 -1
  18. package/.agents/schemas/audit-rules.schema.json +33 -1
  19. package/.agents/schemas/lifecycle/README.md +1 -2
  20. package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
  21. package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
  22. package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
  23. package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
  24. package/.agents/schemas/signal-event.schema.json +3 -3
  25. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  26. package/.agents/schemas/validation-evidence.schema.json +1 -1
  27. package/.agents/scripts/acceptance-eval.js +24 -68
  28. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  29. package/.agents/scripts/bootstrap.js +3 -3
  30. package/.agents/scripts/check-dead-exports.js +43 -104
  31. package/.agents/scripts/check-doc-links.js +2 -2
  32. package/.agents/scripts/check-lifecycle-lint.js +1 -1
  33. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  34. package/.agents/scripts/deliver-recover.js +122 -0
  35. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  36. package/.agents/scripts/evidence-gate.js +20 -50
  37. package/.agents/scripts/generate-skills-index.js +17 -1
  38. package/.agents/scripts/generate-workflows-doc.js +4 -4
  39. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  40. package/.agents/scripts/lib/audit-suite/selector.js +323 -23
  41. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
  42. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  43. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  44. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  45. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  46. package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
  47. package/.agents/scripts/lib/checks/index.js +1 -1
  48. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  49. package/.agents/scripts/lib/checks/state.js +17 -248
  50. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
  51. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  52. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  53. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  54. package/.agents/scripts/lib/cli-args.js +23 -2
  55. package/.agents/scripts/lib/close-validation/gates.js +13 -13
  56. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  57. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  58. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  59. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  60. package/.agents/scripts/lib/config/acceptance-eval.js +2 -2
  61. package/.agents/scripts/lib/config/delivery-routing.js +7 -6
  62. package/.agents/scripts/lib/config/explain.js +10 -16
  63. package/.agents/scripts/lib/config/github.js +7 -5
  64. package/.agents/scripts/lib/config/limits.js +15 -25
  65. package/.agents/scripts/lib/config/quality.js +11 -14
  66. package/.agents/scripts/lib/config/runners.js +8 -21
  67. package/.agents/scripts/lib/config/temp-paths.js +18 -56
  68. package/.agents/scripts/lib/config-settings-schema-delivery.js +34 -16
  69. package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
  70. package/.agents/scripts/lib/config-settings-schema.js +48 -22
  71. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  72. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  73. package/.agents/scripts/lib/duplicate-search.js +38 -7
  74. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  75. package/.agents/scripts/lib/format-generated-json.js +97 -0
  76. package/.agents/scripts/lib/framework-version.js +19 -189
  77. package/.agents/scripts/lib/gh-exec.js +8 -0
  78. package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
  79. package/.agents/scripts/lib/git-utils.js +0 -14
  80. package/.agents/scripts/lib/json-utils.js +1 -2
  81. package/.agents/scripts/lib/label-constants.js +0 -15
  82. package/.agents/scripts/lib/label-taxonomy.js +1 -12
  83. package/.agents/scripts/lib/observability/active-story-env.js +42 -163
  84. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  85. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  86. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  87. package/.agents/scripts/lib/observability/source-classifier.js +2 -2
  88. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  89. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  90. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  91. package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
  92. package/.agents/scripts/lib/orchestration/change-set.js +103 -0
  93. package/.agents/scripts/lib/orchestration/code-review.js +70 -191
  94. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
  95. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  96. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  97. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  98. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  99. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  100. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
  101. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  102. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  103. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
  104. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  105. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  106. package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
  107. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  108. package/.agents/scripts/lib/orchestration/plan-context.js +116 -33
  109. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +26 -36
  110. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +31 -22
  111. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  112. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
  113. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
  114. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  115. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -100
  116. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
  117. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
  118. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  119. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +230 -0
  120. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
  121. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -2
  122. package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
  123. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  124. package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
  125. package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
  126. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  127. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  128. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  129. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  130. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  131. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  132. package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
  133. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
  134. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  135. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  136. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +4 -13
  137. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  138. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
  139. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  140. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  141. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
  142. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  143. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  144. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
  145. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  146. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  147. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +104 -279
  148. package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +191 -0
  149. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +120 -0
  150. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  151. package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
  152. package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
  153. package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
  154. package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
  155. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  156. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
  157. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
  158. package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
  159. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
  160. package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
  161. package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
  162. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  163. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  164. package/.agents/scripts/lib/planning-corpus.js +12 -286
  165. package/.agents/scripts/lib/preflight-runner.js +2 -2
  166. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  167. package/.agents/scripts/lib/signals/index.js +4 -17
  168. package/.agents/scripts/lib/signals/read.js +35 -35
  169. package/.agents/scripts/lib/signals/schema.js +8 -11
  170. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  171. package/.agents/scripts/lib/signals/write.js +0 -1
  172. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  173. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  174. package/.agents/scripts/lib/story-adjacency.js +8 -7
  175. package/.agents/scripts/lib/story-body/story-body.js +81 -13
  176. package/.agents/scripts/lib/templates/decomposer-prompts.js +15 -16
  177. package/.agents/scripts/lib/test-env.js +14 -1
  178. package/.agents/scripts/lib/test-tiers.js +0 -3
  179. package/.agents/scripts/lib/ticket-body-sections.js +0 -14
  180. package/.agents/scripts/lib/validation-evidence.js +31 -59
  181. package/.agents/scripts/lib/wave-runner/live-probe.js +315 -0
  182. package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
  183. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  184. package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
  185. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  186. package/.agents/scripts/plan-context.js +38 -7
  187. package/.agents/scripts/plan-critics.js +203 -0
  188. package/.agents/scripts/plan-persist.js +145 -35
  189. package/.agents/scripts/plan-run-epilogue.js +83 -38
  190. package/.agents/scripts/post-structured-comment.js +0 -38
  191. package/.agents/scripts/pr-watch-with-update.js +43 -22
  192. package/.agents/scripts/providers/github/compose.js +0 -1
  193. package/.agents/scripts/providers/github/errors.js +0 -19
  194. package/.agents/scripts/providers/github/issues.js +1 -11
  195. package/.agents/scripts/providers/github/mappers.js +5 -0
  196. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  197. package/.agents/scripts/providers/github/tickets.js +33 -153
  198. package/.agents/scripts/providers/github.js +17 -6
  199. package/.agents/scripts/quality-preview.js +13 -6
  200. package/.agents/scripts/resolve-stories.js +236 -0
  201. package/.agents/scripts/run-coverage.js +4 -1
  202. package/.agents/scripts/run-lint.js +2 -2
  203. package/.agents/scripts/run-verify.js +31 -2
  204. package/.agents/scripts/signals-view.js +9 -10
  205. package/.agents/scripts/single-story-close.js +173 -18
  206. package/.agents/scripts/single-story-confirm-merge.js +288 -15
  207. package/.agents/scripts/single-story-init.js +6 -10
  208. package/.agents/scripts/stories-wave-tick.js +380 -53
  209. package/.agents/scripts/story-plan.js +3 -3
  210. package/.agents/scripts/update-ticket-state.js +8 -50
  211. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  212. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  213. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  214. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  215. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  216. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  217. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  218. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  219. package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
  220. package/.agents/skills/core/scope-triage/SKILL.md +3 -0
  221. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  222. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  223. package/.agents/skills/skills.index.json +2 -12
  224. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  225. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  226. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  227. package/.agents/workflows/audit-architecture.md +3 -4
  228. package/.agents/workflows/audit-clean-code.md +4 -4
  229. package/.agents/workflows/audit-documentation.md +4 -5
  230. package/.agents/workflows/audit-lighthouse.md +8 -0
  231. package/.agents/workflows/audit-navigability.md +10 -0
  232. package/.agents/workflows/audit-performance.md +2 -3
  233. package/.agents/workflows/audit-quality.md +8 -9
  234. package/.agents/workflows/audit-security.md +1 -2
  235. package/.agents/workflows/audit-seo.md +10 -0
  236. package/.agents/workflows/audit-ux-ui.md +7 -0
  237. package/.agents/workflows/deliver.md +133 -45
  238. package/.agents/workflows/git-cleanup.md +2 -2
  239. package/.agents/workflows/git-deliver.md +1 -1
  240. package/.agents/workflows/helpers/acceptance-self-eval.md +34 -17
  241. package/.agents/workflows/helpers/code-quality-guardrails.md +15 -12
  242. package/.agents/workflows/helpers/code-review.md +14 -12
  243. package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
  244. package/.agents/workflows/helpers/deliver-story.md +209 -118
  245. package/.agents/workflows/helpers/parallel-tooling.md +2 -2
  246. package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
  247. package/.agents/workflows/plan.md +239 -19
  248. package/.agents/workflows/qa-assist.md +6 -6
  249. package/.agents/workflows/qa-explore.md +3 -3
  250. package/.agents/workflows/qa-run.md +1 -5
  251. package/bin/mandrel.js +12 -1
  252. package/docs/CHANGELOG.md +62 -0
  253. package/lib/cli/registry.js +262 -19
  254. package/lib/cli/sync-agents.js +157 -0
  255. package/lib/cli/sync-commands.js +115 -6
  256. package/lib/cli/sync.js +168 -6
  257. package/lib/cli/update.js +105 -8
  258. package/lib/cli/version-helpers.js +131 -0
  259. package/lib/migrations/README.md +7 -5
  260. package/lib/migrations/index.js +17 -9
  261. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  262. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  263. package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +154 -0
  264. package/package.json +2 -2
  265. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  266. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  267. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  268. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  269. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  270. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  271. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  272. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  273. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  274. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  275. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  276. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  277. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  278. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  279. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  280. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  281. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  282. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  283. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  284. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  285. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  286. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  287. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  288. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  289. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  290. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  291. package/.agents/schemas/risk-verdict.schema.json +0 -53
  292. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  293. package/.agents/scripts/analyze-execution.js +0 -444
  294. package/.agents/scripts/check-prepush-recovery.js +0 -90
  295. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  296. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  297. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
  298. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  299. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  300. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  301. package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
  302. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
  303. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  304. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  305. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  306. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  307. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  308. package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
  309. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  310. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  311. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  312. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
  313. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  314. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  315. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  316. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
  317. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  318. package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
  319. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  320. package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
  321. package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
  322. package/.agents/scripts/resolve-plan-run.js +0 -117
  323. package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
@@ -2,310 +2,36 @@
2
2
  * planning-corpus.js — corpus-aware context for the standalone-Story
3
3
  * planning path (Story #4432).
4
4
  *
5
- * `/plan --seed` previously drafted a standalone Story from a blank
6
- * slate: the seed, the body template, and a title-only duplicate scan.
7
- * For a change request that is really a small delta against an
8
- * already-delivered surface, that blank slate throws away context the
9
- * project already has — the docs digest and the relevant Tech Spec
10
- * sections of existing Epics that cover the touched area.
11
- *
12
- * This module assembles that inherited context (`corpusContext`) for
13
- * `story-plan.js`'s `--emit-context` envelope:
5
+ * `/plan --seed` drafts a standalone Story from the seed, the body
6
+ * template, and a title-only duplicate scan. This module assembles the
7
+ * inherited context (`corpusContext`) for `story-plan.js`'s
8
+ * `--emit-context` envelope:
14
9
  *
15
10
  * 1. `docsDigest` — the same per-project docs digest
16
11
  * `orchestration/docs-digest.js` builds for `/deliver` Story
17
12
  * children, reused here so the standalone path gets the same
18
13
  * compact outline instead of re-reading the whole docs set.
19
14
  * `null` when `project.docsContextFiles` is not configured.
20
- * 2. `relevantSections` — a ranked list of existing Epic Tech Spec
21
- * (or lede, when no Tech Spec region exists) excerpts that overlap
22
- * with the seed, so the draft can build on prior art instead of
23
- * re-deriving it.
24
- *
25
- * The historical Epic list surface (`provider.getEpics`) returns a
26
- * list-scale payload with no `body`. Body content therefore requires an
27
- * **explicit**, bounded per-candidate fetch via `provider.getEpic(id)` —
28
- * never a silent assumption that the list response carries prose to score
29
- * against.
30
- *
31
- * Relevance scoring reuses the same `tokenize` / `overlapScore` Jaccard
32
- * primitives `duplicate-search.js` exports for Epic-dedupe and
33
- * `story-plan.js` reuses for Story-dedupe — one matcher, three
34
- * consumers, no forked scoring logic.
15
+ * 2. `relevantSections` — always `[]`. This field previously carried
16
+ * ranked Tech Spec excerpts mined from open Epics. v2.0.0 removed
17
+ * the Epic tier, and the provider's Epic-list surface was reduced
18
+ * to a `return []` stub, which made the entire ranking pipeline a
19
+ * permanent no-op. The pipeline has been removed; the field is
20
+ * retained so the envelope shape stays stable for its consumers.
35
21
  */
36
22
 
37
- import { overlapScore, tokenize } from './duplicate-search.js';
38
- import { Logger } from './Logger.js';
39
23
  import { buildDocsDigest } from './orchestration/docs-digest.js';
40
- import {
41
- extractTicketSection,
42
- hasTicketSection,
43
- } from './ticket-body-sections.js';
44
-
45
- /** Top-K Epics kept after the cheap title-only ranking pass. */
46
- const DEFAULT_CORPUS_MAX_CANDIDATES = 5;
47
-
48
- /**
49
- * Bound on the explicit per-candidate body fetch. Keeps corpus-context
50
- * assembly at a fixed, small number of GitHub reads regardless of how
51
- * many open Epics the repo carries.
52
- */
53
- export const DEFAULT_CORPUS_BODY_FETCH_TOP_K = 3;
54
-
55
- /** Minimum Jaccard overlap for a section excerpt to be worth surfacing. */
56
- const DEFAULT_CORPUS_MIN_SCORE = 0.1;
57
-
58
- /** Max relevant-section excerpts returned in the envelope. */
59
- const DEFAULT_CORPUS_MAX_SECTIONS = 5;
60
-
61
- /** Excerpt length cap (chars) so one oversized Tech Spec doesn't blow the envelope. */
62
- const EXCERPT_MAX_CHARS = 600;
63
-
64
- /**
65
- * Page-scan cap passed to `provider.getEpics({ state: 'open', pageCap })`.
66
- * The corpus lookup only ever ranks the list down to a top-5 shortlist
67
- * (`DEFAULT_CORPUS_MAX_CANDIDATES`), so there is no need to inherit
68
- * `paginateRest`'s full default ceiling (50 pages / 5000 items) to build
69
- * it — a bounded scan keeps this call a fixed, small number of GitHub
70
- * reads regardless of how many open Epics the repo carries.
71
- */
72
- const CORPUS_EPICS_PAGE_CAP = 5;
73
-
74
- /**
75
- * Rank open Epics by title-overlap with the seed. This is the cheap
76
- * first pass over the list surface (title only — no `body`), used solely
77
- * to pick the bounded top-K candidates
78
- * worth an explicit body fetch. It is not the final relevance signal;
79
- * `extractRelevantSections` re-scores against actual section content.
80
- *
81
- * @param {{ seed: string, epics: Array<{ id:number, title:string }>, maxResults?: number }} opts
82
- * @returns {Array<{ id:number, title:string, score:number }>}
83
- */
84
- export function rankCandidateEpics({
85
- seed,
86
- epics,
87
- maxResults = DEFAULT_CORPUS_MAX_CANDIDATES,
88
- }) {
89
- if (!seed || typeof seed !== 'string') {
90
- throw new Error('rankCandidateEpics: seed must be a non-empty string');
91
- }
92
- if (!Array.isArray(epics)) {
93
- throw new Error('rankCandidateEpics: epics must be an array');
94
- }
95
- const seedTokens = tokenize(seed);
96
- if (seedTokens.size === 0) return [];
97
-
98
- const ranked = [];
99
- for (const epic of epics) {
100
- if (!epic || typeof epic.title !== 'string') continue;
101
- const score = overlapScore(seedTokens, tokenize(epic.title));
102
- ranked.push({
103
- id: epic.id,
104
- title: epic.title,
105
- score: Number(score.toFixed(4)),
106
- });
107
- }
108
- ranked.sort((a, b) => b.score - a.score);
109
- return ranked.slice(0, maxResults);
110
- }
111
-
112
- /**
113
- * Fetch full bodies for the top-K ranked candidates via the single-issue
114
- * read (`provider.getEpic`), which — unlike the `getEpics` list mapper —
115
- * does carry `body`. This is the explicit, bounded fetch the corpus
116
- * lookup performs instead of assuming the list surface already has
117
- * prose to score: a candidate never contributes a relevant section
118
- * without this round-trip resolving its body.
119
- *
120
- * A single candidate's fetch failing (deleted issue, transient error) is
121
- * non-fatal — it is dropped from the result rather than aborting corpus
122
- * assembly for every other candidate. Failures are logged via
123
- * `Logger.debug` (stderr) so they are visible under
124
- * `AGENT_LOG_LEVEL=verbose` triage without violating the friction-
125
- * telemetry posture in `.agents/instructions.md` §1.H of never silently
126
- * swallowing an error.
127
- *
128
- * The bounded candidate slice is fetched concurrently
129
- * (`Promise.allSettled`) rather than sequentially — `topK` is a fixed
130
- * small ceiling (default 3), so this is a bounded fan-out, not an
131
- * unbounded one, and it removes the serial network-latency stacking a
132
- * plain `for`-await loop would otherwise incur.
133
- *
134
- * @param {{ provider: object, candidates: Array<{ id:number, title:string }>, topK?: number }} opts
135
- * @returns {Promise<Array<{ id:number, title:string, body:string }>>}
136
- */
137
- export async function fetchCandidateBodies({
138
- provider,
139
- candidates,
140
- topK = DEFAULT_CORPUS_BODY_FETCH_TOP_K,
141
- }) {
142
- if (!provider || typeof provider.getEpic !== 'function') return [];
143
- if (!Array.isArray(candidates) || candidates.length === 0) return [];
144
-
145
- const bounded = candidates.slice(0, topK);
146
- const settled = await Promise.allSettled(
147
- bounded.map(async (candidate) => {
148
- const epic = await provider.getEpic(candidate.id);
149
- return {
150
- id: candidate.id,
151
- title: candidate.title ?? epic?.title ?? '',
152
- body: epic?.body ?? '',
153
- };
154
- }),
155
- );
156
-
157
- const results = [];
158
- for (let i = 0; i < settled.length; i += 1) {
159
- const outcome = settled[i];
160
- if (outcome.status === 'fulfilled') {
161
- results.push(outcome.value);
162
- continue;
163
- }
164
- // Best-effort: one candidate failing to resolve must not abort
165
- // corpus-context assembly for the rest — but the failure is still
166
- // surfaced for triage rather than silently swallowed.
167
- Logger.debug(
168
- `[planning-corpus] fetchCandidateBodies: candidate #${bounded[i].id} failed to resolve: ${outcome.reason?.message ?? outcome.reason}`,
169
- );
170
- }
171
- return results;
172
- }
173
-
174
- /**
175
- * Extract a scoreable excerpt from an Epic body: the managed Tech Spec
176
- * region when present (the folded `## Delivery Slicing` section, #4324),
177
- * otherwise the ideation lede — the prose before the first `##` heading
178
- * — so a plain-body Epic still contributes something to score.
179
- *
180
- * @param {string} body
181
- * @returns {{ kind:'techSpec'|'lede', content:string }}
182
- */
183
- function extractScoreableExcerpt(body) {
184
- if (hasTicketSection(body, 'techSpec')) {
185
- return {
186
- kind: 'techSpec',
187
- content: extractTicketSection(body, 'techSpec'),
188
- };
189
- }
190
- const lede = (body ?? '').split(/^##\s+/m)[0].trim();
191
- return { kind: 'lede', content: lede };
192
- }
193
-
194
- /**
195
- * Rank existing-Epic body excerpts (Tech Spec section, or lede) by
196
- * overlap with the seed. Reuses the same `tokenize` / `overlapScore`
197
- * primitives as the title-ranking pass above and as
198
- * `duplicate-search.js` — one matcher shared across every corpus /
199
- * dedupe surface.
200
- *
201
- * @param {{ seed:string, epicBodies: Array<{ id:number, title:string, body:string }>, maxResults?: number, minScore?: number }} opts
202
- * @returns {Array<{ epicId:number, epicTitle:string, section:'techSpec'|'lede', score:number, excerpt:string }>}
203
- */
204
- export function extractRelevantSections({
205
- seed,
206
- epicBodies,
207
- maxResults = DEFAULT_CORPUS_MAX_SECTIONS,
208
- minScore = DEFAULT_CORPUS_MIN_SCORE,
209
- }) {
210
- if (!seed || typeof seed !== 'string') {
211
- throw new Error('extractRelevantSections: seed must be a non-empty string');
212
- }
213
- if (!Array.isArray(epicBodies)) {
214
- throw new Error('extractRelevantSections: epicBodies must be an array');
215
- }
216
- const seedTokens = tokenize(seed);
217
- if (seedTokens.size === 0) return [];
218
-
219
- const ranked = [];
220
- for (const epic of epicBodies) {
221
- if (!epic) continue;
222
- const { kind, content } = extractScoreableExcerpt(epic.body ?? '');
223
- if (!content) continue;
224
- const score = overlapScore(seedTokens, tokenize(content));
225
- if (score < minScore) continue;
226
- ranked.push({
227
- epicId: epic.id,
228
- epicTitle: epic.title ?? null,
229
- section: kind,
230
- score: Number(score.toFixed(4)),
231
- excerpt: content.slice(0, EXCERPT_MAX_CHARS),
232
- });
233
- }
234
- ranked.sort((a, b) => b.score - a.score);
235
- return ranked.slice(0, maxResults);
236
- }
237
24
 
238
25
  /**
239
26
  * Assemble the `corpusContext` field of the story-plan context envelope.
240
- * Pure orchestration over the three helpers above plus `buildDocsDigest`
241
- * — no I/O beyond what `provider` and the docs-digest reader perform.
242
- *
243
- * `relevantSections` is `[]` (not an error) when the provider has no
244
- * `getEpics` surface, the seed tokenizes to nothing, or no candidate
245
- * clears `minScore` — the standalone-Story draft path degrades to
246
- * exactly today's blank-slate behavior in that case.
247
27
  *
248
28
  * @param {{
249
- * seed: string,
250
- * provider?: { getEpics?: Function, getEpic?: Function },
251
29
  * docsContextFiles?: string[],
252
30
  * docsRoot?: string,
253
- * maxCandidates?: number,
254
- * bodyFetchTopK?: number,
255
- * maxSections?: number,
256
- * minScore?: number,
257
31
  * }} opts
258
32
  * @returns {Promise<{ docsDigest: string|null, relevantSections: Array<object> }>}
259
33
  */
260
- export async function buildCorpusContext({
261
- seed,
262
- provider,
263
- docsContextFiles,
264
- docsRoot,
265
- maxCandidates = DEFAULT_CORPUS_MAX_CANDIDATES,
266
- bodyFetchTopK = DEFAULT_CORPUS_BODY_FETCH_TOP_K,
267
- maxSections = DEFAULT_CORPUS_MAX_SECTIONS,
268
- minScore = DEFAULT_CORPUS_MIN_SCORE,
269
- }) {
34
+ export async function buildCorpusContext({ docsContextFiles, docsRoot }) {
270
35
  const docsDigest = await buildDocsDigest({ docsContextFiles, docsRoot });
271
-
272
- let relevantSections = [];
273
- if (provider && typeof provider.getEpics === 'function') {
274
- // A single candidate-listing call failing (rate limit, transient
275
- // network error, provider outage) must not abort the whole
276
- // `--emit-context` envelope build — degrade to an empty candidate
277
- // list instead of letting the rejection propagate out of the
278
- // caller's `Promise.all` and take down the rest of the envelope
279
- // (body template, tech-stack summary, docs digest) with it.
280
- let epics = [];
281
- try {
282
- epics = await provider.getEpics({
283
- state: 'open',
284
- pageCap: CORPUS_EPICS_PAGE_CAP,
285
- });
286
- } catch (err) {
287
- Logger.debug(
288
- `[planning-corpus] buildCorpusContext: provider.getEpics failed, degrading to an empty candidate list: ${err?.message ?? err}`,
289
- );
290
- epics = [];
291
- }
292
- const ranked = rankCandidateEpics({
293
- seed,
294
- epics: Array.isArray(epics) ? epics : [],
295
- maxResults: maxCandidates,
296
- });
297
- const bodies = await fetchCandidateBodies({
298
- provider,
299
- candidates: ranked,
300
- topK: bodyFetchTopK,
301
- });
302
- relevantSections = extractRelevantSections({
303
- seed,
304
- epicBodies: bodies,
305
- maxResults: maxSections,
306
- minScore,
307
- });
308
- }
309
-
310
- return { docsDigest, relevantSections };
36
+ return { docsDigest, relevantSections: [] };
311
37
  }
@@ -52,8 +52,8 @@ const DEFAULT_LOGGER = {
52
52
  * flag, finding/fix routing, and pretty-print of the blocker table.
53
53
  *
54
54
  * @param {object} opts
55
- * @param {string} opts.scope Consumer surface — `'epic-close'`,
56
- * `'story-close'`, `'npm-test'`, etc.
55
+ * @param {string} opts.scope Consumer surface — `'story-close'`,
56
+ * `'npm-test'`, `'diagnose'`, etc.
57
57
  * @param {boolean} [opts.autoFix=true] Forwarded to `runChecks`. Defaults
58
58
  * to `true` because every wiring call site at this Story's level wants
59
59
  * auto-correction (the `retro` consumer that needs `autoFix:false` does
@@ -142,7 +142,7 @@ export async function verifySurfaceMap(surfaceMap, gitPort, baseRef) {
142
142
  * with no network when the ports are fakes.
143
143
  *
144
144
  * @param {{
145
- * epicNumber: number,
145
+ * ticketNumber: number,
146
146
  * githubPort: GithubPort,
147
147
  * gitPort: GitPort,
148
148
  * surfaceMap?: SurfaceMapEntry[],
@@ -162,7 +162,7 @@ export async function verifySurfaceMap(surfaceMap, gitPort, baseRef) {
162
162
  */
163
163
  export async function hydrateQaContext(opts) {
164
164
  const {
165
- epicNumber,
165
+ ticketNumber,
166
166
  githubPort,
167
167
  gitPort,
168
168
  surfaceMap = [],
@@ -172,9 +172,9 @@ export async function hydrateQaContext(opts) {
172
172
  fsImpl,
173
173
  } = opts ?? {};
174
174
 
175
- if (!Number.isInteger(epicNumber)) {
175
+ if (!Number.isInteger(ticketNumber)) {
176
176
  throw new Error(
177
- 'hydrateQaContext: `epicNumber` is required and must be an integer',
177
+ 'hydrateQaContext: `ticketNumber` is required and must be an integer',
178
178
  );
179
179
  }
180
180
  if (!githubPort || typeof githubPort.fetchIssue !== 'function') {
@@ -192,7 +192,7 @@ export async function hydrateQaContext(opts) {
192
192
  );
193
193
  }
194
194
 
195
- const epicIssue = await githubPort.fetchIssue(epicNumber);
195
+ const epicIssue = await githubPort.fetchIssue(ticketNumber);
196
196
  const epic = {
197
197
  number: epicIssue.number,
198
198
  body: epicIssue.body ?? '',
@@ -10,8 +10,8 @@
10
10
  * + `schema`; `buildSpanTree` was a throwing placeholder.
11
11
  * - Epic #1181 / Story #1440 / Task #1461 — placeholder replaced by
12
12
  * the real export from `./span-tree.js`.
13
- * - Story #1476 — `appendSignal` / `appendTrace` / `appendEpicSignal`
14
- * re-exported here via `./write.js` so the gate scripts (and any
13
+ * - Story #1476 — `appendSignal` / `appendTrace` re-exported here
14
+ * via `./write.js` so the gate scripts (and any
15
15
  * new code) converge on `lib/signals/` instead of importing the
16
16
  * writer directly from `lib/observability/`.
17
17
  *
@@ -21,19 +21,6 @@
21
21
  import { read } from './read.js';
22
22
  import * as schema from './schema.js';
23
23
  import { buildSpanTree } from './span-tree.js';
24
- import {
25
- appendEpicSignal,
26
- appendSignal,
27
- appendTrace,
28
- forEachLine,
29
- } from './write.js';
24
+ import { appendSignal, appendTrace, forEachLine } from './write.js';
30
25
 
31
- export {
32
- appendEpicSignal,
33
- appendSignal,
34
- appendTrace,
35
- buildSpanTree,
36
- forEachLine,
37
- read,
38
- schema,
39
- };
26
+ export { appendSignal, appendTrace, buildSpanTree, forEachLine, read, schema };
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * Streaming signals reader (Epic #1181 / Story #1438 / Task #1459).
3
3
  *
4
- * Provides one async-iterator entry point — `read({ epic, story?, kind?,
4
+ * Provides one async-iterator entry point — `read({ run, story?, kind?,
5
5
  * config? })` — that consumers use instead of opening the NDJSON file
6
6
  * themselves. The reader streams line-by-line so callers never load the
7
7
  * full file into memory (50MB+ traces stay under file size in RSS).
8
8
  *
9
9
  * On-disk layout (resolved via `lib/config/temp-paths.js`):
10
10
  *
11
- * <tempRoot>/epic-<epic>/story-<story>/signals.ndjson
11
+ * <tempRoot>/run-<run>/story-<story>/signals.ndjson
12
12
  *
13
13
  * When `story` is omitted, the reader fans out across every
14
- * `story-<id>/signals.ndjson` under `<tempRoot>/epic-<epic>/`. When the
15
- * Epic directory is missing, the iterator yields nothing (consumers
14
+ * `story-<id>/signals.ndjson` under `<tempRoot>/run-<run>/`. When the
15
+ * run directory is missing, the iterator yields nothing (consumers
16
16
  * treat absence as "no signals yet").
17
17
  *
18
18
  * ## Filter semantics
@@ -21,7 +21,7 @@
21
21
  * argument are yielded. Filtering happens after the per-line JSON
22
22
  * parse + envelope guard (see `lib/signals/schema.js`).
23
23
  * - `story` — narrows to a single Story's stream; otherwise we walk
24
- * every Story directory under the Epic.
24
+ * every Story directory under the run.
25
25
  *
26
26
  * ## Warn-once policy (AC #3)
27
27
  *
@@ -46,8 +46,8 @@ import fs from 'node:fs/promises';
46
46
  import path from 'node:path';
47
47
  import { createInterface } from 'node:readline';
48
48
  import {
49
- epicArtifactPath,
50
- epicTempDir,
49
+ runArtifactPath,
50
+ runTempDir,
51
51
  signalsFile,
52
52
  storyTempDir,
53
53
  } from '../config/temp-paths.js';
@@ -145,43 +145,43 @@ async function* streamFile(target, kindFilter) {
145
145
  }
146
146
 
147
147
  /**
148
- * List every `stories/story-<id>/signals.ndjson` path under the Epic's
149
- * `<tempRoot>/epic-<epic>/` directory. Returns an empty array when the
150
- * Epic directory is missing.
148
+ * List every `stories/story-<id>/signals.ndjson` path under the run's
149
+ * `<tempRoot>/run-<run>/` directory. Returns an empty array when the
150
+ * run directory is missing.
151
151
  *
152
152
  * The returned paths are sorted by Story ID ascending so the iterator's
153
153
  * output is stable across runs.
154
154
  *
155
155
  * Story #2940 nested per-Story directories under a `stories/` segment.
156
- * Epic-level signals continue to live at the Epic root.
156
+ * Run-level signals continue to live at the run root.
157
157
  *
158
- * @param {number} epic
158
+ * @param {number} run
159
159
  * @param {object | undefined} config
160
160
  * @returns {Promise<string[]>}
161
161
  */
162
- async function listEpicStorySignalsFiles(epic, config) {
163
- const epicDir = epicTempDir(epic, config);
164
- let epicEntries;
162
+ async function listRunStorySignalsFiles(run, config) {
163
+ const runDir = runTempDir(run, config);
164
+ let runEntries;
165
165
  try {
166
- epicEntries = await fs.readdir(epicDir, { withFileTypes: true });
166
+ runEntries = await fs.readdir(runDir, { withFileTypes: true });
167
167
  } catch {
168
168
  return [];
169
169
  }
170
- let hasEpicLevelSignals = false;
170
+ let hasRunLevelSignals = false;
171
171
  let hasStoriesDir = false;
172
- for (const ent of epicEntries) {
172
+ for (const ent of runEntries) {
173
173
  if (ent.isDirectory() && ent.name === 'stories') {
174
174
  hasStoriesDir = true;
175
175
  } else if (ent.isFile() && ent.name === 'signals.ndjson') {
176
176
  // Story #1430 — wave-runner lifecycle signals land here.
177
- hasEpicLevelSignals = true;
177
+ hasRunLevelSignals = true;
178
178
  }
179
179
  }
180
180
  const storyIds = [];
181
181
  if (hasStoriesDir) {
182
182
  let storyEntries;
183
183
  try {
184
- storyEntries = await fs.readdir(path.join(epicDir, 'stories'), {
184
+ storyEntries = await fs.readdir(path.join(runDir, 'stories'), {
185
185
  withFileTypes: true,
186
186
  });
187
187
  } catch {
@@ -195,43 +195,43 @@ async function listEpicStorySignalsFiles(epic, config) {
195
195
  }
196
196
  }
197
197
  storyIds.sort((a, b) => a - b);
198
- // Yield epic-level signals first (wave-start precedes per-Story friction),
198
+ // Yield run-level signals first (wave-start precedes per-Story friction),
199
199
  // then walk the per-Story streams in ascending ID order.
200
- const targets = hasEpicLevelSignals
201
- ? [epicArtifactPath(epic, 'signals.ndjson', config)]
200
+ const targets = hasRunLevelSignals
201
+ ? [runArtifactPath(run, 'signals.ndjson', config)]
202
202
  : [];
203
203
  for (const sid of storyIds) {
204
- targets.push(path.join(storyTempDir(epic, sid, config), 'signals.ndjson'));
204
+ targets.push(path.join(storyTempDir(run, sid, config), 'signals.ndjson'));
205
205
  }
206
206
  return targets;
207
207
  }
208
208
 
209
209
  /**
210
- * Stream every event matching `{ epic, story?, kind? }` from the
211
- * configured `tempRoot`'s `epic-<epic>/[story-<story>/]signals.ndjson`
210
+ * Stream every event matching `{ run, story?, kind? }` from the
211
+ * configured `tempRoot`'s `run-<run>/[story-<story>/]signals.ndjson`
212
212
  * file(s).
213
213
  *
214
214
  * Returns an async iterable so callers can `for await` over it without
215
215
  * buffering. The reader is **streaming** — peak memory stays below the
216
216
  * file size for any input.
217
217
  *
218
- * @param {{ epic: number, story?: number, kind?: string, config?: object }} args
218
+ * @param {{ run: number, story?: number, kind?: string, config?: object }} args
219
219
  * @returns {AsyncGenerator<object>}
220
220
  *
221
221
  * @example
222
- * for await (const evt of read({ epic: 1181 })) { ... }
223
- * for await (const evt of read({ epic: 1181, story: 1438, kind: 'friction' })) { ... }
222
+ * for await (const evt of read({ run: 1181 })) { ... }
223
+ * for await (const evt of read({ run: 1181, story: 1438, kind: 'friction' })) { ... }
224
224
  */
225
225
  export async function* read(args) {
226
226
  if (args == null || typeof args !== 'object') {
227
227
  throw new TypeError(
228
- `signals/read: args must be an object with at minimum { epic }; got ${args}`,
228
+ `signals/read: args must be an object with at minimum { run }; got ${args}`,
229
229
  );
230
230
  }
231
- const { epic, story, kind, config } = args;
232
- if (!isPositiveInt(epic)) {
231
+ const { run, story, kind, config } = args;
232
+ if (!isPositiveInt(run)) {
233
233
  throw new RangeError(
234
- `signals/read: epic must be a positive integer (got ${epic})`,
234
+ `signals/read: run must be a positive integer (got ${run})`,
235
235
  );
236
236
  }
237
237
  if (story !== undefined && !isPositiveInt(story)) {
@@ -259,8 +259,8 @@ export async function* read(args) {
259
259
  const kindFilter = kind ?? null;
260
260
  const targets =
261
261
  story !== undefined
262
- ? [signalsFile(epic, story, config)]
263
- : await listEpicStorySignalsFiles(epic, config);
262
+ ? [signalsFile(run, story, config)]
263
+ : await listRunStorySignalsFiles(run, config);
264
264
 
265
265
  for (const target of targets) {
266
266
  yield* streamFile(target, kindFilter);
@@ -3,7 +3,7 @@
3
3
  * shape guards (Epic #1181 / Story #1438 / Task #1458).
4
4
  *
5
5
  * Both writers (`lib/observability/signals-writer.js` callers) and
6
- * readers (`lib/signals/read.js`, `lib/observability/perf-aggregator.js`)
6
+ * readers (`lib/signals/read.js`, the retro's gather-signals phase)
7
7
  * import their field names from this module so the on-disk NDJSON shape
8
8
  * stays under one schema.
9
9
  *
@@ -19,15 +19,12 @@
19
19
  *
20
20
  * Signals-writer `appendSignal` call sites:
21
21
  * - `friction` — quality-gate / runtime friction (diagnose-friction.js,
22
- * lib/gates/friction.js, auto-refresh-runner.js,
23
- * worktree-reap.js, lifecycle-emit.js)
22
+ * lib/gates/friction.js)
24
23
  * - `acceptance-eval` — acceptance-eval.js per-criterion terminus signal.
25
- * - `wave-start` / `wave-complete` — `lib/wave-runner/tick.js` (read by
26
- * perf-aggregator's `waveParallelism` report;
27
- * `wave-start` also anchors span-tree Story spans)
24
+ * - `wave-start` / `wave-complete` — wave-window anchors; `wave-start` also
25
+ * anchors span-tree Story spans
28
26
  * - `wave-end` — span-tree pairing anchor (retained for span-tree)
29
- * - `state-transition` — notification-derived window anchor read by the
30
- * waveParallelism bucketer.
27
+ * - `state-transition` — notification-derived window anchor.
31
28
  *
32
29
  * The `dispatched` kind was deleted in the Epic #4406 signal-contract
33
30
  * cutover — it had no live emitter and no consumer.
@@ -80,9 +77,9 @@ export const EVENT_KINDS = Object.freeze({
80
77
  FRICTION: 'friction',
81
78
  TRACE: 'trace',
82
79
  // Wave-window forensics signals: `wave-start` / `wave-end` anchor the
83
- // span-tree's Story spans, and the perf-aggregator brackets each wave's
84
- // wall-clock from `wave-start` → `wave-complete` (the `waveParallelism`
85
- // report). These are READ they survive.
80
+ // span-tree's Story spans. Story #4545 deleted the `waveParallelism`
81
+ // consumer (perf-aggregator) with the execution-analysis surface; these
82
+ // kinds stay as the span-tree's anchors.
86
83
  WAVE_START: 'wave-start',
87
84
  WAVE_END: 'wave-end',
88
85
  WAVE_COMPLETE: 'wave-complete',
@@ -192,15 +192,15 @@ function bootstrapStoryWindow(storyNode, ts) {
192
192
  * from `buildSpanTree` so the outer function's CRAP score stays under
193
193
  * the new-method ceiling.
194
194
  *
195
- * @param {object} state — mutable accumulator (stories, tasksByStory, epic)
195
+ * @param {object} state — mutable accumulator (stories, tasksByStory, run)
196
196
  * @param {unknown} evt
197
197
  * @returns {void}
198
198
  */
199
199
  function ingestEvent(state, evt) {
200
200
  if (evt == null || typeof evt !== 'object') return;
201
201
 
202
- const eEpic = epicOf(evt);
203
- if (state.epic == null && eEpic != null) state.epic = eEpic;
202
+ const eRun = epicOf(evt);
203
+ if (state.run == null && eRun != null) state.run = eRun;
204
204
 
205
205
  const ts = tsOf(evt);
206
206
  const sid = storyOf(evt);
@@ -258,11 +258,11 @@ function finalizeStory(state, story) {
258
258
  * Build the span tree from an async iterable of signal events.
259
259
  *
260
260
  * @param {AsyncIterable<object> | Iterable<object>} iter
261
- * @returns {Promise<{ epic: number | null, stories: Array<object> }>}
261
+ * @returns {Promise<{ run: number | null, stories: Array<object> }>}
262
262
  *
263
263
  * @example
264
264
  * import { read, buildSpanTree } from './lib/signals/index.js';
265
- * const tree = await buildSpanTree(read({ epic: 1181 }));
265
+ * const tree = await buildSpanTree(read({ run: 1181 }));
266
266
  */
267
267
  export async function buildSpanTree(iter) {
268
268
  if (iter == null || typeof iter !== 'object') {
@@ -274,7 +274,7 @@ export async function buildSpanTree(iter) {
274
274
  // Accumulator state. `stories` keys are `storyKeyOf(sid)` strings;
275
275
  // `tasksByStory` mirrors that and holds per-task sub-maps.
276
276
  const state = {
277
- epic: null,
277
+ run: null,
278
278
  stories: new Map(),
279
279
  tasksByStory: new Map(),
280
280
  };
@@ -287,5 +287,5 @@ export async function buildSpanTree(iter) {
287
287
  storyEntries.sort(compareNodeIds);
288
288
  for (const story of storyEntries) finalizeStory(state, story);
289
289
 
290
- return { epic: state.epic, stories: storyEntries };
290
+ return { run: state.run, stories: storyEntries };
291
291
  }
@@ -12,7 +12,6 @@
12
12
  */
13
13
 
14
14
  export {
15
- appendEpicSignal,
16
15
  appendSignal,
17
16
  appendTrace,
18
17
  forEachLine,