mandrel 1.94.0 → 2.1.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 (560) hide show
  1. package/.agents/README.md +116 -99
  2. package/.agents/agents/acceptance-critic.md +9 -7
  3. package/.agents/agents/story-worker.md +45 -51
  4. package/.agents/audit-checklists/performance.md +1 -1
  5. package/.agents/docs/SDLC.md +498 -1287
  6. package/.agents/docs/agentrc-reference.json +185 -80
  7. package/.agents/docs/configuration.md +132 -190
  8. package/.agents/docs/execution-reference.md +51 -25
  9. package/.agents/docs/quality-gates.md +25 -33
  10. package/.agents/docs/workflows.md +8 -8
  11. package/.agents/instructions.md +113 -139
  12. package/.agents/rules/ci-remediation.md +11 -15
  13. package/.agents/rules/git-conventions-reference.md +48 -58
  14. package/.agents/rules/git-conventions.md +16 -22
  15. package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
  16. package/.agents/schemas/agentrc.schema.json +83 -254
  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/retro.end.schema.json +1 -1
  24. package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
  25. package/.agents/schemas/signal-event.schema.json +3 -3
  26. package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
  27. package/.agents/schemas/validation-evidence.schema.json +1 -1
  28. package/.agents/scripts/acceptance-eval.js +22 -66
  29. package/.agents/scripts/agents-bootstrap-github.js +1 -1
  30. package/.agents/scripts/audit-to-stories.js +7 -7
  31. package/.agents/scripts/boot-sweep.js +1 -1
  32. package/.agents/scripts/bootstrap.js +3 -3
  33. package/.agents/scripts/check-dead-exports.js +43 -104
  34. package/.agents/scripts/check-doc-links.js +2 -2
  35. package/.agents/scripts/check-lifecycle-lint.js +7 -10
  36. package/.agents/scripts/check-workflow-cli-lint.js +91 -0
  37. package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
  38. package/.agents/scripts/deliver-recover.js +122 -0
  39. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  40. package/.agents/scripts/evidence-gate.js +20 -50
  41. package/.agents/scripts/generate-skills-index.js +17 -1
  42. package/.agents/scripts/generate-workflows-doc.js +4 -4
  43. package/.agents/scripts/lib/ITicketingProvider.js +1 -19
  44. package/.agents/scripts/lib/Logger.js +6 -10
  45. package/.agents/scripts/lib/audit-suite/runner.js +2 -2
  46. package/.agents/scripts/lib/audit-suite/selector.js +328 -28
  47. package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
  48. package/.agents/scripts/lib/baselines/kernel.js +206 -18
  49. package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
  50. package/.agents/scripts/lib/baselines/reader.js +1 -6
  51. package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
  52. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
  53. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
  54. package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
  55. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
  56. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
  57. package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
  58. package/.agents/scripts/lib/checks/index.js +3 -2
  59. package/.agents/scripts/lib/checks/loop-health.js +12 -11
  60. package/.agents/scripts/lib/checks/state.js +17 -248
  61. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
  62. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
  63. package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
  64. package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
  65. package/.agents/scripts/lib/cli/standard-args.js +13 -22
  66. package/.agents/scripts/lib/cli-args.js +39 -9
  67. package/.agents/scripts/lib/close-validation/gates.js +15 -15
  68. package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
  69. package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
  70. package/.agents/scripts/lib/close-validation/runner.js +13 -21
  71. package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
  72. package/.agents/scripts/lib/config/ci.js +6 -31
  73. package/.agents/scripts/lib/config/delivery-routing.js +52 -35
  74. package/.agents/scripts/lib/config/explain.js +61 -48
  75. package/.agents/scripts/lib/config/github.js +7 -5
  76. package/.agents/scripts/lib/config/limits.js +29 -80
  77. package/.agents/scripts/lib/config/paths.js +0 -2
  78. package/.agents/scripts/lib/config/quality.js +12 -15
  79. package/.agents/scripts/lib/config/runners.js +20 -66
  80. package/.agents/scripts/lib/config/temp-paths.js +30 -63
  81. package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
  82. package/.agents/scripts/lib/config-resolver.js +2 -7
  83. package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
  84. package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
  85. package/.agents/scripts/lib/config-settings-schema.js +100 -60
  86. package/.agents/scripts/lib/dead-exports-knip.js +105 -0
  87. package/.agents/scripts/lib/dead-exports-mode.js +51 -0
  88. package/.agents/scripts/lib/dependency-parser.js +3 -2
  89. package/.agents/scripts/lib/doc-tiers.js +2 -2
  90. package/.agents/scripts/lib/duplicate-search.js +242 -41
  91. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
  92. package/.agents/scripts/lib/findings/promote-finding.js +23 -14
  93. package/.agents/scripts/lib/format-generated-json.js +97 -0
  94. package/.agents/scripts/lib/framework-version.js +19 -190
  95. package/.agents/scripts/lib/gh-exec.js +8 -0
  96. package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
  97. package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
  98. package/.agents/scripts/lib/git-utils.js +32 -20
  99. package/.agents/scripts/lib/github/framework-repo.js +6 -0
  100. package/.agents/scripts/lib/json-utils.js +1 -2
  101. package/.agents/scripts/lib/label-constants.js +10 -38
  102. package/.agents/scripts/lib/label-taxonomy.js +10 -55
  103. package/.agents/scripts/lib/observability/active-story-env.js +44 -165
  104. package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
  105. package/.agents/scripts/lib/observability/signal-validator.js +4 -4
  106. package/.agents/scripts/lib/observability/signals-writer.js +6 -82
  107. package/.agents/scripts/lib/observability/source-classifier.js +5 -5
  108. package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
  109. package/.agents/scripts/lib/onboard/init-tail.js +1 -3
  110. package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
  111. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
  112. package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
  113. package/.agents/scripts/lib/orchestration/code-review.js +78 -436
  114. package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
  115. package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
  116. package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
  117. package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
  118. package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
  119. package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
  120. package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
  121. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
  122. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
  123. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
  124. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
  125. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
  126. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
  127. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
  128. package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
  129. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
  130. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
  131. package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
  132. package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
  133. package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
  134. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
  135. package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
  136. package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
  137. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
  138. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
  139. package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
  140. package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
  141. package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
  142. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
  143. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
  144. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
  145. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
  146. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
  147. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
  148. package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
  149. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
  150. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
  151. package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
  152. package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
  153. package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
  154. package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
  155. package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
  156. package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
  157. package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
  158. package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
  159. package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
  160. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
  161. package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
  162. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
  163. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
  164. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
  165. package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
  166. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
  167. package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
  168. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
  169. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
  170. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
  171. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
  172. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
  173. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
  174. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
  175. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
  176. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
  177. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
  178. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
  179. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
  180. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
  181. package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
  182. package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
  183. package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
  184. package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
  185. package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
  186. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
  187. package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
  188. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
  189. package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
  190. package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
  191. package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
  192. package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
  193. package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
  194. package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
  195. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
  196. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
  197. package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
  198. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
  199. package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
  200. package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
  201. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  202. package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
  203. package/.agents/scripts/lib/planning-corpus.js +12 -281
  204. package/.agents/scripts/lib/preflight-runner.js +4 -4
  205. package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
  206. package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
  207. package/.agents/scripts/lib/signals/index.js +4 -17
  208. package/.agents/scripts/lib/signals/read.js +35 -35
  209. package/.agents/scripts/lib/signals/schema.js +8 -11
  210. package/.agents/scripts/lib/signals/span-tree.js +7 -7
  211. package/.agents/scripts/lib/signals/write.js +0 -1
  212. package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
  213. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
  214. package/.agents/scripts/lib/skills/parse-skill.js +16 -3
  215. package/.agents/scripts/lib/story-adjacency.js +17 -19
  216. package/.agents/scripts/lib/story-body/story-body.js +130 -75
  217. package/.agents/scripts/lib/story-plan.js +2 -4
  218. package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
  219. package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
  220. package/.agents/scripts/lib/test-env.js +14 -1
  221. package/.agents/scripts/lib/test-tiers.js +0 -3
  222. package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
  223. package/.agents/scripts/lib/validation-evidence.js +32 -60
  224. package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
  225. package/.agents/scripts/lib/workspace-provisioner.js +1 -1
  226. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
  227. package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
  228. package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
  229. package/.agents/scripts/lint-issue-body.js +71 -40
  230. package/.agents/scripts/mandrel-update-preflight.js +1 -1
  231. package/.agents/scripts/notify.js +4 -3
  232. package/.agents/scripts/plan-context.js +102 -80
  233. package/.agents/scripts/plan-persist.js +230 -279
  234. package/.agents/scripts/plan-run-epilogue.js +142 -0
  235. package/.agents/scripts/post-structured-comment.js +0 -38
  236. package/.agents/scripts/pr-watch-with-update.js +43 -22
  237. package/.agents/scripts/providers/github/compose.js +0 -1
  238. package/.agents/scripts/providers/github/errors.js +0 -19
  239. package/.agents/scripts/providers/github/issues.js +13 -39
  240. package/.agents/scripts/providers/github/mappers.js +5 -12
  241. package/.agents/scripts/providers/github/sub-issues.js +0 -47
  242. package/.agents/scripts/providers/github/tickets.js +33 -156
  243. package/.agents/scripts/providers/github.js +17 -6
  244. package/.agents/scripts/resolve-stories.js +236 -0
  245. package/.agents/scripts/run-coverage.js +4 -1
  246. package/.agents/scripts/run-lint.js +2 -2
  247. package/.agents/scripts/run-verify.js +31 -2
  248. package/.agents/scripts/signals-view.js +25 -21
  249. package/.agents/scripts/single-story-close.js +178 -26
  250. package/.agents/scripts/single-story-confirm-merge.js +313 -24
  251. package/.agents/scripts/single-story-init.js +35 -30
  252. package/.agents/scripts/stories-wave-tick.js +85 -10
  253. package/.agents/scripts/story-plan.js +28 -49
  254. package/.agents/scripts/update-ticket-state.js +14 -65
  255. package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
  256. package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
  257. package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
  258. package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
  259. package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
  260. package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
  261. package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
  262. package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
  263. package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
  264. package/.agents/skills/core/scope-triage/SKILL.md +31 -172
  265. package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
  266. package/.agents/skills/core/security-and-hardening/reference.md +375 -0
  267. package/.agents/skills/skills.index.json +3 -53
  268. package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
  269. package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
  270. package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
  271. package/.agents/starter-agentrc.json +0 -5
  272. package/.agents/templates/agent-protocol.md +9 -10
  273. package/.agents/workflows/audit-architecture.md +6 -7
  274. package/.agents/workflows/audit-clean-code.md +7 -7
  275. package/.agents/workflows/audit-dependencies.md +3 -3
  276. package/.agents/workflows/audit-devops.md +3 -3
  277. package/.agents/workflows/audit-documentation.md +9 -10
  278. package/.agents/workflows/audit-lighthouse.md +11 -3
  279. package/.agents/workflows/audit-navigability.md +13 -2
  280. package/.agents/workflows/audit-performance.md +5 -6
  281. package/.agents/workflows/audit-privacy.md +3 -3
  282. package/.agents/workflows/audit-quality.md +11 -12
  283. package/.agents/workflows/audit-security.md +4 -5
  284. package/.agents/workflows/audit-seo.md +13 -3
  285. package/.agents/workflows/audit-sre.md +3 -3
  286. package/.agents/workflows/audit-to-stories.md +20 -20
  287. package/.agents/workflows/audit-ux-ui.md +10 -3
  288. package/.agents/workflows/deliver.md +177 -176
  289. package/.agents/workflows/git-cleanup.md +5 -6
  290. package/.agents/workflows/git-deliver.md +1 -1
  291. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  292. package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
  293. package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
  294. package/.agents/workflows/helpers/code-review.md +75 -196
  295. package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
  296. package/.agents/workflows/helpers/deliver-story.md +606 -0
  297. package/.agents/workflows/helpers/diagnose.md +10 -10
  298. package/.agents/workflows/helpers/parallel-tooling.md +3 -3
  299. package/.agents/workflows/helpers/signals.md +16 -16
  300. package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
  301. package/.agents/workflows/mandrel-update.md +2 -1
  302. package/.agents/workflows/plan.md +277 -145
  303. package/.agents/workflows/qa-assist.md +27 -33
  304. package/.agents/workflows/qa-explore.md +29 -38
  305. package/.agents/workflows/qa-run.md +2 -6
  306. package/README.md +9 -8
  307. package/bin/mandrel.js +12 -1
  308. package/docs/CHANGELOG.md +70 -0
  309. package/lib/cli/registry.js +262 -19
  310. package/lib/cli/sync-agents.js +157 -0
  311. package/lib/cli/sync-commands.js +115 -6
  312. package/lib/cli/sync.js +168 -6
  313. package/lib/cli/update.js +105 -8
  314. package/lib/cli/version-helpers.js +131 -0
  315. package/lib/migrations/README.md +7 -5
  316. package/lib/migrations/index.js +12 -8
  317. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
  318. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
  319. package/package.json +2 -2
  320. package/.agents/agents/retro.md +0 -42
  321. package/.agents/personas/architect.md +0 -113
  322. package/.agents/personas/devops-engineer.md +0 -38
  323. package/.agents/personas/engineer.md +0 -33
  324. package/.agents/personas/project-manager.md +0 -114
  325. package/.agents/personas/qa-engineer.md +0 -95
  326. package/.agents/personas/security-engineer.md +0 -111
  327. package/.agents/personas/technical-writer.md +0 -101
  328. package/.agents/schemas/dispatch-manifest.json +0 -232
  329. package/.agents/schemas/epic-perf-report.schema.json +0 -89
  330. package/.agents/schemas/epic-spec.schema.json +0 -153
  331. package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
  332. package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
  333. package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
  334. package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
  335. package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
  336. package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
  337. package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
  338. package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
  339. package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
  340. package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
  341. package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
  342. package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
  343. package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
  344. package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
  345. package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
  346. package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
  347. package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
  348. package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
  349. package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
  350. package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
  351. package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
  352. package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
  353. package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
  354. package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
  355. package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
  356. package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
  357. package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
  358. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
  359. package/.agents/schemas/risk-verdict.schema.json +0 -66
  360. package/.agents/schemas/story-perf-summary.schema.json +0 -73
  361. package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
  362. package/.agents/scripts/analyze-execution.js +0 -444
  363. package/.agents/scripts/bookkeeping-reconcile.js +0 -117
  364. package/.agents/scripts/check-prepush-recovery.js +0 -90
  365. package/.agents/scripts/dispatcher.js +0 -295
  366. package/.agents/scripts/epic-audit-prepare.js +0 -497
  367. package/.agents/scripts/epic-audit-recheck.js +0 -274
  368. package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
  369. package/.agents/scripts/epic-deliver-preflight.js +0 -462
  370. package/.agents/scripts/epic-deliver-prepare.js +0 -852
  371. package/.agents/scripts/epic-execute-record-wave.js +0 -449
  372. package/.agents/scripts/epic-plan-clarity.js +0 -211
  373. package/.agents/scripts/epic-plan-healthcheck.js +0 -581
  374. package/.agents/scripts/epic-reconcile.js +0 -625
  375. package/.agents/scripts/lib/baseline-snapshot.js +0 -979
  376. package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
  377. package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
  378. package/.agents/scripts/lib/config/lifecycle.js +0 -40
  379. package/.agents/scripts/lib/config/preflight.js +0 -58
  380. package/.agents/scripts/lib/config/retro.js +0 -77
  381. package/.agents/scripts/lib/epic-merge-lock.js +0 -322
  382. package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
  383. package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
  384. package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
  385. package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
  386. package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
  387. package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
  388. package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
  389. package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
  390. package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
  391. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
  392. package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
  393. package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
  394. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
  395. package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
  396. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
  397. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
  398. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
  399. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
  400. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
  401. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
  402. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
  403. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
  404. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
  405. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
  406. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
  407. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
  408. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
  409. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
  410. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
  411. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
  412. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
  413. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
  414. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
  415. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
  416. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
  417. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
  418. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
  419. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
  420. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
  421. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
  422. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
  423. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
  424. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
  425. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
  426. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
  427. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
  428. package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
  429. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
  430. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
  431. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
  432. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
  433. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
  434. package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
  435. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
  436. package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
  437. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
  438. package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
  439. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
  440. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
  441. package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
  442. package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
  443. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
  444. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
  445. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
  446. package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
  447. package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
  448. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
  449. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
  450. package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
  451. package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
  452. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
  453. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
  454. package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
  455. package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
  456. package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
  457. package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
  458. package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
  459. package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
  460. package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
  461. package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
  462. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
  463. package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
  464. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
  465. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
  466. package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
  467. package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
  468. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
  469. package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
  470. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
  471. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
  472. package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
  473. package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
  474. package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
  475. package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
  476. package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
  477. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
  478. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
  479. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
  480. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
  481. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
  482. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
  483. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
  484. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
  485. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
  486. package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
  487. package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
  488. package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
  489. package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
  490. package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
  491. package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
  492. package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
  493. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
  494. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
  495. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
  496. package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
  497. package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
  498. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
  499. package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
  500. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
  501. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
  502. package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
  503. package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
  504. package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
  505. package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
  506. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
  507. package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
  508. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
  509. package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
  510. package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
  511. package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
  512. package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
  513. package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
  514. package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
  515. package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
  516. package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
  517. package/.agents/scripts/lib/push-epic-retry.js +0 -209
  518. package/.agents/scripts/lib/spec/index.js +0 -36
  519. package/.agents/scripts/lib/spec/loader.js +0 -425
  520. package/.agents/scripts/lib/spec/state.js +0 -208
  521. package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
  522. package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
  523. package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
  524. package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
  525. package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
  526. package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
  527. package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
  528. package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
  529. package/.agents/scripts/lib/wave-runner/tick.js +0 -754
  530. package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
  531. package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
  532. package/.agents/scripts/lifecycle-emit.js +0 -510
  533. package/.agents/scripts/retro-run.js +0 -218
  534. package/.agents/scripts/slice-phase.js +0 -361
  535. package/.agents/scripts/standalone-feedback-rollup.js +0 -188
  536. package/.agents/scripts/story-close.js +0 -294
  537. package/.agents/scripts/story-init.js +0 -599
  538. package/.agents/scripts/story-phase.js +0 -369
  539. package/.agents/scripts/wave-tick.js +0 -464
  540. package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
  541. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
  542. package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
  543. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
  544. package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
  545. package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
  546. package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
  547. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
  548. package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
  549. package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
  550. package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
  551. package/.agents/workflows/helpers/deliver-epic.md +0 -998
  552. package/.agents/workflows/helpers/deliver-stories.md +0 -450
  553. package/.agents/workflows/helpers/epic-audit.md +0 -189
  554. package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
  555. package/.agents/workflows/helpers/epic-testing.md +0 -125
  556. package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
  557. package/.agents/workflows/helpers/plan-epic.md +0 -353
  558. package/.agents/workflows/helpers/plan-story.md +0 -251
  559. package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
  560. package/.agents/workflows/helpers/single-story-deliver.md +0 -557
@@ -0,0 +1,105 @@
1
+ /**
2
+ * dead-exports-knip.js — the knip driver behind the dead-export ratchet.
3
+ *
4
+ * Owns everything about talking to knip and normalising what comes back:
5
+ * spawning it, reading a pre-captured report, and flattening its report into
6
+ * `{ file, symbol }` rows. `check-dead-exports.js` stays a thin CLI over this.
7
+ *
8
+ * @module lib/dead-exports-knip
9
+ */
10
+
11
+ import { spawnSync } from 'node:child_process';
12
+ import fs from 'node:fs';
13
+ import process from 'node:process';
14
+
15
+ /**
16
+ * Spawn `npx knip --reporter json --no-progress` and return the parsed
17
+ * envelope. Never throws — the caller logs the error and treats current rows as
18
+ * empty, which surfaces every baseline row as "removed": loud, but safe.
19
+ *
20
+ * `production` adds knip's `--production` flag, which restricts analysis to
21
+ * entry/project patterns carrying the `!` suffix in `knip.json`. The test globs
22
+ * deliberately lack that suffix, so production mode drops them as entry points
23
+ * and an export reachable only from a test reads as dead. Without those
24
+ * suffixes production mode has no entry patterns at all and reports nothing —
25
+ * `knip.json` and this flag are a matched pair.
26
+ *
27
+ * Exported as a hook so tests can stub the spawn without a working knip
28
+ * workspace.
29
+ *
30
+ * @param {{ cwd?: string, spawn?: typeof spawnSync, production?: boolean }} [opts]
31
+ * @returns {{ ok: true, envelope: unknown } | { ok: false, error: string }}
32
+ */
33
+ export function runKnip({
34
+ cwd = process.cwd(),
35
+ spawn = spawnSync,
36
+ production = false,
37
+ } = {}) {
38
+ const args = ['knip', '--reporter', 'json', '--no-progress'];
39
+ if (production) args.push('--production');
40
+ const result = spawn(process.platform === 'win32' ? 'npx.cmd' : 'npx', args, {
41
+ cwd,
42
+ stdio: ['ignore', 'pipe', 'pipe'],
43
+ encoding: 'utf-8',
44
+ shell: process.platform === 'win32',
45
+ });
46
+ if (result.error) {
47
+ return { ok: false, error: `spawn failed: ${result.error.message}` };
48
+ }
49
+ const stdout = typeof result.stdout === 'string' ? result.stdout : '';
50
+ if (stdout.trim().length === 0) {
51
+ return { ok: false, error: 'knip produced empty stdout' };
52
+ }
53
+ try {
54
+ return { ok: true, envelope: JSON.parse(stdout) };
55
+ } catch (err) {
56
+ return {
57
+ ok: false,
58
+ error: `knip JSON parse failed: ${err?.message ?? err}`,
59
+ };
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Read a pre-captured knip JSON envelope from disk (the `--knip-output` test
65
+ * seam). Returns the parsed envelope or `null` on failure.
66
+ *
67
+ * @param {string} filePath
68
+ * @returns {unknown}
69
+ */
70
+ export function readKnipOutput(filePath) {
71
+ try {
72
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Flatten knip's `--reporter json` output into `{ file, symbol }` rows. Knip
80
+ * emits `{ issues: [{ file, exports: [{ name, ... }], ... }, ...] }`. Only
81
+ * `exports` rows are mapped — the ratchet ignores file-, dependency- and
82
+ * duplicate-level issues, which knip surfaces under separate `rules` keys.
83
+ *
84
+ * @param {unknown} knipEnvelope The parsed knip JSON report.
85
+ * @returns {Array<{ file: string, symbol: string }>}
86
+ */
87
+ export function extractRowsFromKnip(knipEnvelope) {
88
+ const rows = [];
89
+ if (!knipEnvelope || typeof knipEnvelope !== 'object') return rows;
90
+ const issues = Array.isArray(knipEnvelope.issues) ? knipEnvelope.issues : [];
91
+ for (const issue of issues) {
92
+ const file = issue?.file;
93
+ if (typeof file !== 'string' || file.length === 0) continue;
94
+ const exports_ = Array.isArray(issue.exports) ? issue.exports : [];
95
+ for (const e of exports_) {
96
+ const symbol =
97
+ (e && typeof e.name === 'string' && e.name) ||
98
+ (e && typeof e.symbol === 'string' && e.symbol) ||
99
+ null;
100
+ if (!symbol) continue;
101
+ rows.push({ file, symbol });
102
+ }
103
+ }
104
+ return rows;
105
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * dead-exports-mode.js — the two passes of the dead-export ratchet.
3
+ *
4
+ * The gate runs twice with different reachability assumptions, and each pass
5
+ * needs a matched triple: which knip invocation to make, which baseline to
6
+ * ratchet against, and how to label its output. Resolving that triple in one
7
+ * place keeps the three from drifting apart — a pass that ran production knip
8
+ * against the default baseline would report every test-only export as newly
9
+ * dead and fail the build for no reason.
10
+ *
11
+ * - **default** — `tests/**` are knip entry points, so an export imported only
12
+ * by a test reads as used. This is the historical gate.
13
+ * - **production** — knip's `--production` drops the test entries, so an export
14
+ * no production code reaches reads as dead. Depends on the `!` pattern
15
+ * suffixes in `knip.json`; without them production mode has no entry points
16
+ * and silently reports nothing.
17
+ *
18
+ * The two ratchet against separate baselines on purpose. This repo sanctions
19
+ * exporting purely for a test (`.agents/rules/test-seams.md`), so the
20
+ * production row set is large and mostly intentional; merging it into the
21
+ * default baseline would destroy that baseline's meaning.
22
+ *
23
+ * @module lib/dead-exports-mode
24
+ */
25
+
26
+ import path from 'node:path';
27
+
28
+ /** Baseline for the default pass (test entry points included). */
29
+ const DEFAULT_BASELINE = path.join('baselines', 'dead-exports.json');
30
+
31
+ /** Baseline for the `--production` pass (test-only-importer discount). */
32
+ const PRODUCTION_BASELINE = path.join(
33
+ 'baselines',
34
+ 'dead-exports-production.json',
35
+ );
36
+
37
+ /**
38
+ * Resolve the baseline / label / mode-tag triple for a pass.
39
+ *
40
+ * @param {boolean} production
41
+ * @returns {{ mode: 'production'|'default', label: string, baseline: string }}
42
+ */
43
+ export function resolveDeadExportsMode(production) {
44
+ return production
45
+ ? {
46
+ mode: 'production',
47
+ label: 'dead-exports:production',
48
+ baseline: PRODUCTION_BASELINE,
49
+ }
50
+ : { mode: 'default', label: 'dead-exports', baseline: DEFAULT_BASELINE };
51
+ }
@@ -2,8 +2,9 @@
2
2
  * dependency-parser.js — Shared Dependency & Metadata Parsing Utilities
3
3
  *
4
4
  * Canonical implementation of dependency-related regex parsing and
5
- * ticket metadata extraction. Eliminates duplicated implementations
6
- * across dispatcher.js, verify-prereqs.js, and providers/github.js.
5
+ * ticket metadata extraction, shared by the ticketing provider and the
6
+ * orchestration layer (e.g. providers/github/tickets.js,
7
+ * lib/story-adjacency.js, lib/branch-name-guard.js).
7
8
  */
8
9
 
9
10
  /**
@@ -7,8 +7,8 @@
7
7
  *
8
8
  * - `alwaysLoaded` — `CLAUDE.md` plus the transitive closure of its
9
9
  * `@`-import references (`@AGENTS.md`,
10
- * `@.agents/instructions.md`, the persona, the always-on
11
- * rules, …). This is the context every session re-pays
10
+ * `@.agents/instructions.md`, the always-on rules, …).
11
+ * This is the context every session re-pays
12
12
  * on every subagent spawn (instructions.md § 4), so it is
13
13
  * the primary budget the context-budget ratchet gates.
14
14
  * - `mandatoryRead` — the resolved `project.docsContextFiles` set (prefixed
@@ -1,23 +1,36 @@
1
1
  /**
2
- * duplicate-search.js — Cross-Epic Duplicate Detection
2
+ * duplicate-search.js — Cross-Story Duplicate Detection
3
3
  *
4
- * Used by `/plan` Phase 2 (s-plan-ideation) to surface open Epics
5
- * whose scope overlaps with a sharpened one-pager before a new Epic is
6
- * created. Returns ranked candidates with an overlap score and URL so
7
- * the host LLM can pause for HITL confirmation.
4
+ * Used by `/plan` to surface open Stories whose scope overlaps with a
5
+ * seed / seed-file / tickets corpus before new Stories are created.
6
+ * Returns ranked candidates with an overlap score and URL so the host
7
+ * LLM can pause for HITL confirmation.
8
8
  *
9
9
  * Design notes:
10
- * - The provider abstraction (`ITicketingProvider#getEpics`) is the
11
- * only I/O surface; the scoring routine is pure and trivially
12
- * testable in isolation.
13
- * - Scoring is intentionally simple (token Jaccard over title +
14
- * structured one-pager sections). It is a triage signal, not a
15
- * semantic-search replacement.
16
- * - Provider errors propagate verbatim the caller is responsible
17
- * for translating them into a friction comment or operator-visible
18
- * failure.
10
+ * - Prefer `provider.searchIssues` to narrow open Stories server-side
11
+ * (`label:"type::story" state:open` + top seed tokens), capped at
12
+ * ~100 hits, then rank that set. Fall back to `listIssuesByLabel`
13
+ * when search errors, is unavailable, or returns **nothing** (same
14
+ * try/catch pattern as `TicketGateway.getTickets`).
15
+ * - The narrowing query is deliberately weak (Story #4541). GitHub ANDs
16
+ * free-text terms, so the former 8-token query demanded that a candidate
17
+ * contain all eight of a multi-sentence seed's longest words — which no
18
+ * real Story does. Gate #1 duplicate triage therefore reported "no
19
+ * candidates" on every realistic seed while looking perfectly healthy.
20
+ * Search is now a cheap best-case narrowing over a handful of tokens; an
21
+ * empty result is treated as "search did not help", not "no duplicates",
22
+ * and falls through to the label listing whose Jaccard ranker has real
23
+ * recall.
24
+ * - Scoring is intentionally simple (token Jaccard over title + body).
25
+ * It is a triage signal, not a semantic-search replacement.
26
+ * - Provider errors on the list fallback propagate verbatim — the
27
+ * caller is responsible for translating them into a friction comment
28
+ * or operator-visible failure.
19
29
  */
20
30
 
31
+ import { Logger } from './Logger.js';
32
+ import { TYPE_LABELS } from './label-constants.js';
33
+
21
34
  const STOPWORDS = new Set([
22
35
  'a',
23
36
  'an',
@@ -75,6 +88,19 @@ const STOPWORDS = new Set([
75
88
 
76
89
  const DEFAULT_MIN_SCORE = 0.15;
77
90
  const DEFAULT_MAX_RESULTS = 5;
91
+ /**
92
+ * How many seed tokens to pass as free-text search terms.
93
+ *
94
+ * GitHub ANDs these, so the cap is a precision/recall dial pointing the
95
+ * wrong way: every extra token shrinks the candidate set. Three is a
96
+ * best-case narrowing that can plausibly hit on a focused seed; anything
97
+ * beyond that reliably matched nothing on a multi-sentence one (Story
98
+ * #4541). Recall does not rest on this number — an empty result falls back
99
+ * to label-listing plus client-side ranking.
100
+ */
101
+ const DEFAULT_SEARCH_TOKEN_CAP = 3;
102
+ /** Hard cap on search hits ranked client-side (~one Search API page). */
103
+ const SEARCH_RESULT_CAP = 100;
78
104
 
79
105
  /**
80
106
  * Tokenize freeform text into a deduplicated set of meaningful words.
@@ -92,6 +118,33 @@ export function tokenize(text) {
92
118
  return new Set(tokens);
93
119
  }
94
120
 
121
+ /**
122
+ * Pick the highest-signal seed tokens for a Search API free-text query.
123
+ * Longer tokens first (specificity proxy), then alphabetical for stability.
124
+ *
125
+ * @param {string} seed
126
+ * @param {number} [maxTokens]
127
+ * @returns {string[]}
128
+ */
129
+ export function pickSearchTokens(seed, maxTokens = DEFAULT_SEARCH_TOKEN_CAP) {
130
+ const tokens = tokenize(seed);
131
+ return [...tokens]
132
+ .sort((a, b) => b.length - a.length || a.localeCompare(b))
133
+ .slice(0, maxTokens);
134
+ }
135
+
136
+ /**
137
+ * Build the `/search/issues` query for open Stories overlapping a seed.
138
+ * Repo scoping is left to `provider.searchIssues`.
139
+ *
140
+ * @param {string} seed
141
+ * @returns {string}
142
+ */
143
+ export function buildOpenStorySearchQuery(seed) {
144
+ const tokens = pickSearchTokens(seed);
145
+ return [`label:"${TYPE_LABELS.STORY}"`, 'state:open', ...tokens].join(' ');
146
+ }
147
+
95
148
  /**
96
149
  * Compute the Jaccard overlap between two token sets.
97
150
  *
@@ -108,14 +161,14 @@ export function overlapScore(a, b) {
108
161
  }
109
162
 
110
163
  /**
111
- * Build the Epic URL for an issue id. The candidate exposes the URL so
112
- * the HITL pause can render clickable links without a second round-trip.
164
+ * Build the issue URL for an id. The candidate exposes the URL so the
165
+ * HITL pause can render clickable links without a second round-trip.
113
166
  *
114
167
  * @param {number|string} id
115
168
  * @param {{ owner?: string, repo?: string }} [opts]
116
169
  * @returns {string}
117
170
  */
118
- function buildEpicUrl(id, opts = {}) {
171
+ function buildIssueUrl(id, opts = {}) {
119
172
  const owner = opts.owner || process.env.GITHUB_OWNER;
120
173
  const repo = opts.repo || process.env.GITHUB_REPO;
121
174
  if (owner && repo) {
@@ -125,54 +178,72 @@ function buildEpicUrl(id, opts = {}) {
125
178
  }
126
179
 
127
180
  /**
128
- * Find open Epics whose title + body overlap with the supplied
129
- * one-pager above a configurable threshold.
181
+ * Normalize a provider issue (list or search hit) into the ranking shape.
182
+ *
183
+ * @param {object} issue
184
+ * @returns {{ id: number, title: string, body: string, url?: string }}
185
+ */
186
+ function normalizeIssue(issue) {
187
+ return {
188
+ id: Number(issue.number ?? issue.id),
189
+ title: issue.title ?? '',
190
+ body: issue.body ?? '',
191
+ url: issue.html_url ?? issue.url ?? undefined,
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Rank a pre-fetched open-Story list against a seed corpus.
197
+ * Pure helper shared by the provider-backed search and tests.
130
198
  *
131
199
  * @param {{
132
- * onePager: string,
133
- * provider: import('./ITicketingProvider.js').ITicketingProvider,
200
+ * seed: string,
201
+ * openStories: Array<{ id:number, title?:string, body?:string, url?:string }>,
134
202
  * minScore?: number,
135
203
  * maxResults?: number,
136
204
  * owner?: string,
137
205
  * repo?: string,
206
+ * excludeIds?: Iterable<number|string>,
138
207
  * }} args
139
- * @returns {Promise<Array<{ id: number, title: string, score: number, url: string }>>}
208
+ * @returns {Array<{ id: number, title: string, score: number, url: string }>}
140
209
  */
141
- export async function findSimilarOpenEpics({
142
- onePager,
143
- provider,
210
+ export function rankOpenStoryDuplicates({
211
+ seed,
212
+ openStories,
144
213
  minScore = DEFAULT_MIN_SCORE,
145
214
  maxResults = DEFAULT_MAX_RESULTS,
146
215
  owner,
147
216
  repo,
217
+ excludeIds = [],
148
218
  }) {
149
- if (!onePager || typeof onePager !== 'string') {
150
- throw new Error(
151
- 'findSimilarOpenEpics: onePager must be a non-empty string',
152
- );
219
+ if (!seed || typeof seed !== 'string') {
220
+ throw new Error('rankOpenStoryDuplicates: seed must be a non-empty string');
153
221
  }
154
- if (!provider || typeof provider.getEpics !== 'function') {
155
- throw new Error('findSimilarOpenEpics: provider must implement getEpics()');
222
+ if (!Array.isArray(openStories)) {
223
+ throw new Error('rankOpenStoryDuplicates: openStories must be an array');
156
224
  }
157
225
 
158
- const seedTokens = tokenize(onePager);
226
+ const seedTokens = tokenize(seed);
159
227
  if (seedTokens.size === 0) return [];
160
228
 
161
- // Provider errors propagate verbatim — caller decides how to surface.
162
- const epics = await provider.getEpics({ state: 'open' });
163
- if (!Array.isArray(epics) || epics.length === 0) return [];
229
+ const excluded = new Set(
230
+ [...excludeIds].map((id) => Number(id)).filter((n) => Number.isFinite(n)),
231
+ );
164
232
 
165
233
  const ranked = [];
166
- for (const epic of epics) {
167
- const corpus = `${epic.title || ''}\n${epic.body || ''}`;
234
+ for (const story of openStories) {
235
+ const id = Number(story?.id ?? story?.number);
236
+ if (!Number.isFinite(id) || excluded.has(id)) continue;
237
+ const title = story.title || '';
238
+ const corpus = `${title}\n${story.body || ''}`;
168
239
  const candidateTokens = tokenize(corpus);
169
240
  const score = overlapScore(seedTokens, candidateTokens);
170
241
  if (score >= minScore) {
171
242
  ranked.push({
172
- id: epic.id,
173
- title: epic.title,
243
+ id,
244
+ title,
174
245
  score: Number(score.toFixed(4)),
175
- url: buildEpicUrl(epic.id, { owner, repo }),
246
+ url: story.url || buildIssueUrl(id, { owner, repo }),
176
247
  });
177
248
  }
178
249
  }
@@ -181,9 +252,139 @@ export async function findSimilarOpenEpics({
181
252
  return ranked.slice(0, maxResults);
182
253
  }
183
254
 
255
+ /**
256
+ * Server-narrowed open-Story fetch via `/search/issues`.
257
+ *
258
+ * @param {object} provider
259
+ * @param {string} seed
260
+ * @returns {Promise<Array<{ id:number, title:string, body:string, url?:string }>>}
261
+ */
262
+ async function fetchOpenStoriesViaSearch(provider, seed) {
263
+ const query = buildOpenStorySearchQuery(seed);
264
+ const hits = await provider.searchIssues({ query });
265
+ if (!Array.isArray(hits)) return [];
266
+ return hits.slice(0, SEARCH_RESULT_CAP).map(normalizeIssue);
267
+ }
268
+
269
+ /**
270
+ * Full open-Story backlog via label listing (fallback path).
271
+ *
272
+ * @param {object} provider
273
+ * @returns {Promise<Array<{ id:number, title:string, body:string, url?:string }>>}
274
+ */
275
+ async function fetchOpenStoriesViaList(provider) {
276
+ if (typeof provider.listIssuesByLabel !== 'function') {
277
+ throw new Error(
278
+ 'findSimilarOpenStories: provider must implement listIssuesByLabel()',
279
+ );
280
+ }
281
+ const issues = await provider.listIssuesByLabel({
282
+ state: 'open',
283
+ labels: TYPE_LABELS.STORY,
284
+ });
285
+ if (!Array.isArray(issues)) return [];
286
+ return issues.map(normalizeIssue);
287
+ }
288
+
289
+ /**
290
+ * Fetch open-Story candidates: try Search API narrowing first, fall back to
291
+ * `listIssuesByLabel` when search errors, is unavailable, or **returns no
292
+ * hits**.
293
+ *
294
+ * The empty-result fallback is the load-bearing one (Story #4541). The
295
+ * narrowing query ANDs seed tokens, so on any real multi-sentence seed it
296
+ * matches nothing — and treating that as an authoritative "no duplicates
297
+ * exist" is what made Gate #1 triage report a clean bill of health while
298
+ * never actually looking. An empty search is evidence about the *query*, not
299
+ * about the backlog.
300
+ *
301
+ * @param {object} provider
302
+ * @param {string} seed
303
+ * @returns {Promise<Array<{ id:number, title:string, body:string, url?:string }>>}
304
+ */
305
+ async function fetchOpenStoryCandidates(provider, seed) {
306
+ if (!provider || typeof provider !== 'object') {
307
+ throw new Error('findSimilarOpenStories: provider is required');
308
+ }
309
+
310
+ const hasSearch = typeof provider.searchIssues === 'function';
311
+ const hasList = typeof provider.listIssuesByLabel === 'function';
312
+ if (!hasSearch && !hasList) {
313
+ throw new Error(
314
+ 'findSimilarOpenStories: provider must implement searchIssues() or listIssuesByLabel()',
315
+ );
316
+ }
317
+
318
+ if (hasSearch) {
319
+ try {
320
+ const hits = await fetchOpenStoriesViaSearch(provider, seed);
321
+ if (hits.length > 0 || !hasList) return hits;
322
+ Logger.info(
323
+ '[duplicate-search] narrowed search matched no open Stories; ' +
324
+ 'falling back to listIssuesByLabel + client-side ranking',
325
+ );
326
+ } catch (err) {
327
+ if (!hasList) throw err;
328
+ const msg = typeof err?.message === 'string' ? err.message : String(err);
329
+ Logger.warn(
330
+ `[duplicate-search] search-based candidate fetch failed (${msg}); ` +
331
+ 'falling back to listIssuesByLabel',
332
+ );
333
+ }
334
+ }
335
+
336
+ return fetchOpenStoriesViaList(provider);
337
+ }
338
+
339
+ /**
340
+ * Find open Stories whose title + body overlap with the supplied seed
341
+ * above a configurable threshold.
342
+ *
343
+ * @param {{
344
+ * seed: string,
345
+ * provider: import('./ITicketingProvider.js').ITicketingProvider,
346
+ * minScore?: number,
347
+ * maxResults?: number,
348
+ * owner?: string,
349
+ * repo?: string,
350
+ * excludeIds?: Iterable<number|string>,
351
+ * }} args
352
+ * @returns {Promise<Array<{ id: number, title: string, score: number, url: string }>>}
353
+ */
354
+ export async function findSimilarOpenStories({
355
+ seed,
356
+ provider,
357
+ minScore = DEFAULT_MIN_SCORE,
358
+ maxResults = DEFAULT_MAX_RESULTS,
359
+ owner,
360
+ repo,
361
+ excludeIds = [],
362
+ }) {
363
+ if (!seed || typeof seed !== 'string') {
364
+ throw new Error('findSimilarOpenStories: seed must be a non-empty string');
365
+ }
366
+
367
+ // Avoid a network round-trip when the seed cannot produce a score.
368
+ if (tokenize(seed).size === 0) return [];
369
+
370
+ const openStories = await fetchOpenStoryCandidates(provider, seed);
371
+ return rankOpenStoryDuplicates({
372
+ seed,
373
+ openStories,
374
+ minScore,
375
+ maxResults,
376
+ owner,
377
+ repo,
378
+ excludeIds,
379
+ });
380
+ }
381
+
184
382
  export const __test = {
185
383
  STOPWORDS,
186
384
  DEFAULT_MIN_SCORE,
187
385
  DEFAULT_MAX_RESULTS,
188
- buildEpicUrl,
386
+ DEFAULT_SEARCH_TOKEN_CAP,
387
+ SEARCH_RESULT_CAP,
388
+ buildIssueUrl,
389
+ buildEpicUrl: buildIssueUrl,
189
390
  };
@@ -30,8 +30,8 @@
30
30
  * failure path is captured in `errors[]`.
31
31
  */
32
32
 
33
+ import { DEFAULT_FRAMEWORK_REPO } from '../github/framework-repo.js';
33
34
  import { META_LABELS } from '../label-constants.js';
34
- import { DEFAULT_FRAMEWORK_REPO } from '../orchestration/retro/phases/gather-signals.js';
35
35
  import {
36
36
  contentFingerprint,
37
37
  DEFAULT_MAX_FILINGS_PER_RUN,
@@ -6,7 +6,7 @@
6
6
  * session's ledger items (see `.agents/schemas/qa-ledger.schema.json` and
7
7
  * `lib/qa/qa-session.js`), the still-untriaged backlog is clustered and each
8
8
  * cluster is promoted to a follow-up ticket — a single Story (via `/plan`)
9
- * for a tight, one-deliverable cluster, or an Epic (via `/plan --idea`) for
9
+ * for a tight, one-deliverable cluster, or a multi-Story plan-seed (via `/plan --seed`) for
10
10
  * a broad cluster that spans multiple coverage surfaces. Each contributing
11
11
  * ledger item then has the resulting `routedTo` issue link written back onto it
12
12
  * so a resume run sees the item as filed rather than re-promoting it.
@@ -32,15 +32,24 @@ import { highestSeverity as highestSeverityOf } from './severity.js';
32
32
  /** Triaged dispositions, mirrored from the `disposition` enum in the schema. */
33
33
  const TRIAGED_DISPOSITIONS = Object.freeze(['file', 'defer', 'dismiss']);
34
34
 
35
- /** The two promotion targets a cluster routes to. */
35
+ /**
36
+ * The two promotion targets a cluster routes to.
37
+ *
38
+ * `PLAN_SEED` promotes a broad cluster by seeding a multi-Story `/plan`
39
+ * run (`/plan --seed`). Its persisted wire token is the legacy string
40
+ * `'epic'`: archived qa-ledger records (and the distributed
41
+ * `qa-ledger.schema.json` `routedTo.kind` enum) still carry `'epic'`, so
42
+ * the value is kept read-compatible while the write-side constant name is
43
+ * modernised.
44
+ */
36
45
  export const PROMOTION_TARGETS = Object.freeze({
37
46
  STORY: 'story',
38
- EPIC: 'epic',
47
+ PLAN_SEED: 'epic',
39
48
  });
40
49
 
41
50
  /**
42
51
  * A cluster of more than this many distinct coverage surfaces is broad enough
43
- * to warrant an Epic (`/plan --idea`) rather than a single Story
52
+ * to warrant a plan-seed (`/plan --seed`) rather than a single Story
44
53
  * (`/plan`). One or two surfaces is a tight, single-deliverable cluster.
45
54
  */
46
55
  const EPIC_COVERAGE_THRESHOLD = 2;
@@ -70,7 +79,7 @@ export function isPromotable(item) {
70
79
  * Stable cluster key for a ledger item: its `class`. Items sharing a class
71
80
  * describe the same kind of signal (a product bug, a tooling-DX gap, …) and
72
81
  * merge into one follow-up ticket. A class whose items span many distinct
73
- * coverage surfaces is broad enough to promote to an Epic (see
82
+ * coverage surfaces is broad enough to promote to a plan-seed (see
74
83
  * {@link targetForCluster}); a class confined to one or two surfaces is a
75
84
  * single-deliverable Story. Coverage is therefore a *secondary* signal that
76
85
  * sizes the cluster rather than splitting it.
@@ -159,7 +168,7 @@ export function clusterLedgerItems(items) {
159
168
  /**
160
169
  * Decide a cluster's promotion target. A cluster that spans more than
161
170
  * {@link EPIC_COVERAGE_THRESHOLD} distinct coverage surfaces is broad enough to
162
- * warrant an Epic (`/plan --idea`); otherwise it is a single-deliverable
171
+ * warrant a plan-seed (`/plan --seed`); otherwise it is a single-deliverable
163
172
  * Story (`/plan`).
164
173
  *
165
174
  * @param {{ coverages: string[] }} cluster
@@ -170,7 +179,7 @@ export function targetForCluster(cluster) {
170
179
  ? cluster.coverages.length
171
180
  : 0;
172
181
  return surfaces > EPIC_COVERAGE_THRESHOLD
173
- ? PROMOTION_TARGETS.EPIC
182
+ ? PROMOTION_TARGETS.PLAN_SEED
174
183
  : PROMOTION_TARGETS.STORY;
175
184
  }
176
185
 
@@ -224,7 +233,7 @@ function routedToLink(issue, kind) {
224
233
  }
225
234
 
226
235
  /**
227
- * Promote the clustered untriaged ledger items into Stories / Epics via the
236
+ * Promote the clustered untriaged ledger items into Stories / plan-seeds via the
228
237
  * shared findings/route logic, then write the resulting `routedTo` issue link
229
238
  * back onto each contributing ledger item.
230
239
  *
@@ -233,7 +242,7 @@ function routedToLink(issue, kind) {
233
242
  * shared `routeFinding` against existing Issues (via the injected search
234
243
  * port). This dedups against work already filed.
235
244
  * 2. On a `new` decision, open the follow-up ticket through the injected
236
- * `createStory` (`/plan`) or `createEpic` (`/plan --idea`) port,
245
+ * `createStory` (`/plan`) or `createPlanSeed` (`/plan --seed`) port,
237
246
  * chosen by {@link targetForCluster}. On any other decision, link back to
238
247
  * the matched Issue rather than creating a duplicate.
239
248
  * 3. Stamp the resolved `routedTo` link onto every contributing ledger item
@@ -251,8 +260,8 @@ function routedToLink(issue, kind) {
251
260
  * Optional semantic candidate search, forwarded to `routeFinding`.
252
261
  * @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createStory
253
262
  * Opens a single Story (`/plan`) for a tight cluster.
254
- * @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createEpic
255
- * Opens an Epic (`/plan --idea`) for a broad cluster.
263
+ * @param {(cluster: object) => Promise<{ number: number, url?: string }>} ports.createPlanSeed
264
+ * Opens a plan-seed (`/plan --seed`) run for a broad cluster.
256
265
  * @returns {Promise<{
257
266
  * promotions: Array<{
258
267
  * clusterKey: string,
@@ -268,7 +277,7 @@ function routedToLink(issue, kind) {
268
277
  * @throws {Error} when a required create port is missing for a routed cluster.
269
278
  */
270
279
  export async function promoteFindings(ledgerItems, ports = {}) {
271
- const { searchIssues, searchCandidates, createStory, createEpic } = ports;
280
+ const { searchIssues, searchCandidates, createStory, createPlanSeed } = ports;
272
281
  if (
273
282
  typeof searchCandidates !== 'function' &&
274
283
  typeof searchIssues !== 'function'
@@ -294,10 +303,10 @@ export async function promoteFindings(ledgerItems, ports = {}) {
294
303
 
295
304
  if (route.decision === 'new') {
296
305
  const createPort =
297
- target === PROMOTION_TARGETS.EPIC ? createEpic : createStory;
306
+ target === PROMOTION_TARGETS.PLAN_SEED ? createPlanSeed : createStory;
298
307
  if (typeof createPort !== 'function') {
299
308
  throw new Error(
300
- `promoteFindings: a ${target === PROMOTION_TARGETS.EPIC ? 'createEpic' : 'createStory'} port is required to promote cluster ${cluster.key}`,
309
+ `promoteFindings: a ${target === PROMOTION_TARGETS.PLAN_SEED ? 'createPlanSeed' : 'createStory'} port is required to promote cluster ${cluster.key}`,
301
310
  );
302
311
  }
303
312
  issue = await createPort(cluster);