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
@@ -0,0 +1,103 @@
1
+ /**
2
+ * lib/orchestration/change-set.js — the **one** Story change-set enumerator
3
+ * (Story #4593).
4
+ *
5
+ * ## Why this exists
6
+ *
7
+ * A single Story delivery used to enumerate `git diff --name-only
8
+ * <base>...<head>` three to four separate times: once for the ceremony
9
+ * derivation in `helpers/deliver-story`, once inside `runCodeReview` (to derive
10
+ * the review depth), once for the Story-scope local-lens roster, and once more
11
+ * per fresh acceptance critic. Every one of those consumers must agree about
12
+ * *what changed* — ceremony level and review depth both flow from
13
+ * `deriveChangeLevel`, so two enumerations straddling a commit could route the
14
+ * same Story two different ways. Computing the set once and injecting it closes
15
+ * that window and drops the redundant git calls.
16
+ *
17
+ * ## Contract
18
+ *
19
+ * {@link computeChangeSet} is **total**: it never throws. A diff it cannot
20
+ * enumerate (git failure, missing ref, spawn error) yields
21
+ * `{ files: null, enumerated: false }` — the neutral "diff unknown" signal that
22
+ * `deriveChangeLevel` / `resolveDepth` already fail safe on (`standard` depth, a
23
+ * `fresh` critic). `null` is deliberately distinct from `[]`: an empty array is
24
+ * the *fact* that nothing changed, whereas `null` is the *absence* of evidence,
25
+ * and only the latter must never buy a change less checking.
26
+ *
27
+ * The returned `files` are trimmed, de-duplicated, and sorted, so two consumers
28
+ * comparing the same change set compare byte-identical lists. The refs the set
29
+ * was computed against ride along on the envelope so a downstream consumer can
30
+ * report (or assert) its provenance rather than re-deriving it.
31
+ *
32
+ * @typedef {{
33
+ * baseRef: string,
34
+ * headRef: string,
35
+ * files: string[]|null,
36
+ * enumerated: boolean,
37
+ * }} ChangeSet
38
+ *
39
+ * @typedef {typeof gitSpawn} GitSpawnFn
40
+ */
41
+
42
+ import { gitSpawn } from '../git-utils.js';
43
+
44
+ /**
45
+ * Trim, drop empties, de-duplicate, and sort raw `git diff --name-only` output.
46
+ * Pure.
47
+ *
48
+ * @param {string} stdout
49
+ * @returns {string[]}
50
+ */
51
+ function normalizeFileList(stdout) {
52
+ const seen = new Set();
53
+ for (const line of stdout.split('\n')) {
54
+ const file = line.trim();
55
+ if (file.length > 0) seen.add(file);
56
+ }
57
+ return [...seen].sort();
58
+ }
59
+
60
+ /**
61
+ * Compute the change set for the `baseRef...headRef` diff **once**, for every
62
+ * consumer that needs to know what a Story touched.
63
+ *
64
+ * Total — never throws; see the module header for the `null` vs `[]` contract.
65
+ *
66
+ * @param {{
67
+ * baseRef: string,
68
+ * headRef: string,
69
+ * cwd?: string,
70
+ * gitSpawnFn?: typeof gitSpawn,
71
+ * }} args
72
+ * @returns {ChangeSet}
73
+ */
74
+ export function computeChangeSet({
75
+ baseRef,
76
+ headRef,
77
+ cwd = process.cwd(),
78
+ gitSpawnFn = gitSpawn,
79
+ } = {}) {
80
+ const unknown = { baseRef, headRef, files: null, enumerated: false };
81
+ if (typeof baseRef !== 'string' || baseRef.length === 0) return unknown;
82
+ if (typeof headRef !== 'string' || headRef.length === 0) return unknown;
83
+
84
+ try {
85
+ const result = gitSpawnFn(
86
+ cwd,
87
+ 'diff',
88
+ '--name-only',
89
+ `${baseRef}...${headRef}`,
90
+ );
91
+ if (!result || result.status !== 0 || typeof result.stdout !== 'string') {
92
+ return unknown;
93
+ }
94
+ return {
95
+ baseRef,
96
+ headRef,
97
+ files: normalizeFileList(result.stdout),
98
+ enumerated: true,
99
+ };
100
+ } catch {
101
+ return unknown;
102
+ }
103
+ }
@@ -11,12 +11,15 @@
11
11
  * adapter's `runReview()` to collect a `Finding[]`, render the
12
12
  * structured-comment body via `findings-renderer`, and post the
13
13
  * comment through the GitHub provider here (the adapter is post-free
14
- * by design). The lifecycle events (`code-review.start`/`.end`)
15
- * preserve their previous payload shape so the ledger and listener
16
- * chain are unchanged.
14
+ * by design).
15
+ *
16
+ * v2.0.0 removed the Epic tier. The Epic-scope envelope and the
17
+ * Epic-scoped `code-review.start` / `.end` lifecycle emits (whose schema
18
+ * requires `epicId`) went with it; Story scope is the only scope and the
19
+ * module no longer touches the lifecycle bus.
17
20
  *
18
21
  * Public API:
19
- * - `runCodeReview({ scope, ticketId, provider, logger, bus, ... })` →
22
+ * - `runCodeReview({ ticketId, headRef, provider, logger, ... })` →
20
23
  * `{ status, severity, posted, report, halted, blockerReason }`.
21
24
  *
22
25
  * Behaviour:
@@ -24,7 +27,7 @@
24
27
  * a single `native` chain entry when `delivery.codeReview.providers`
25
28
  * is unset or empty.
26
29
  * - Always posts the unified `verification-results` structured comment on
27
- * the Epic issue (the adapter never posts; the orchestrator owns
30
+ * the target issue/PR (the adapter never posts; the orchestrator owns
28
31
  * persistence). Story #4411 (Epic #4405) unified the former
29
32
  * `code-review` and `audit-results` findings contracts into this one
30
33
  * `verification-results` marker.
@@ -38,8 +41,8 @@
38
41
 
39
42
  import { hasSurvivingCritical } from '../audit-suite/findings.js';
40
43
  import { resolveConfig } from '../config-resolver.js';
41
- import { gitSpawn } from '../git-utils.js';
42
- import { resolveDepth } from './review-depth.js';
44
+ import { computeChangeSet } from './change-set.js';
45
+ import { deriveChangeLevel, resolveDepth } from './review-depth.js';
43
46
  import {
44
47
  countBySeverity,
45
48
  renderFindings,
@@ -49,84 +52,19 @@ import { upsertStructuredComment } from './ticketing.js';
49
52
 
50
53
  /**
51
54
  * Review depth tiers, ordered light → standard → deep. The depth is resolved
52
- * by the shared {@link resolveDepth} resolver from the judged risk envelope's
53
- * `overallLevel` **and** the mechanical changed-file count of the diff under
54
- * review, then threaded into the review provider's `runReview` input so a
55
- * high-risk *or* wide-footprint change gets a deeper pass than a low-risk
56
- * small one. Depth is an **input** signal only it never changes the
55
+ * by the shared {@link resolveDepth} resolver from two observable properties of
56
+ * the diff under review — whether its changed files touch a registered
57
+ * sensitive path ({@link deriveChangeLevel}) and their mechanical count then
58
+ * threaded into the review provider's `runReview` input so a sensitive *or*
59
+ * wide-footprint change gets a deeper pass than a small, unremarkable one.
60
+ * Depth is an **input** signal only — it never changes the
57
61
  * `{ status, severity, posted, report, halted, blockerReason }` output
58
- * envelope nor the `code-review` structured-comment body (Story #3876,
59
- * extended by Story #3938).
62
+ * envelope nor the `verification-results` structured-comment body (Story #3876,
63
+ * extended by Story #3938; re-based off the diff by Story #4542).
60
64
  *
61
65
  * @typedef {import('./review-depth.js').ReviewDepth} ReviewDepth
62
66
  */
63
67
 
64
- /**
65
- * Count the files changed in the `baseRef...headRef` diff via
66
- * `git diff --name-only`. Returns the file count, or `null` when the diff
67
- * cannot be enumerated (git failure, missing ref). A `null` count is the
68
- * neutral "width unknown" signal {@link resolveDepth} tolerates without
69
- * downgrading or escalating the depth. Best-effort — never throws.
70
- *
71
- * @param {{ baseRef: string, headRef: string, gitSpawnFn?: typeof gitSpawn }} args
72
- * @returns {number|null}
73
- */
74
- function countChangedFiles({ baseRef, headRef, gitSpawnFn = gitSpawn }) {
75
- try {
76
- const result = gitSpawnFn(
77
- process.cwd(),
78
- 'diff',
79
- `${baseRef}...${headRef}`,
80
- '--name-only',
81
- );
82
- if (!result || result.status !== 0 || typeof result.stdout !== 'string') {
83
- return null;
84
- }
85
- return result.stdout
86
- .trim()
87
- .split('\n')
88
- .filter((line) => line.length > 0).length;
89
- } catch {
90
- return null;
91
- }
92
- }
93
-
94
- /**
95
- * Build the `code-review.end` payload from the normalized result envelope.
96
- * The runner result may carry a `report` field (full markdown body) which
97
- * is NOT included in the lifecycle payload: the schema's
98
- * `additionalProperties: false` forbids it, and the body can carry inline
99
- * severity counts that drift from the structured `severity` field. The
100
- * lifecycle ledger is the structured surface; the report is GitHub's
101
- * surface (posted via the structured comment).
102
- *
103
- * Story #2839: `epicId` is preserved verbatim in the lifecycle payload so
104
- * the `code-review.end` schema (additionalProperties: false, requires
105
- * `epicId`) stays unchanged. Story-scope invocations never reach this
106
- * helper — `runCodeReview` short-circuits the bus emit for `scope: 'story'`
107
- * because story-scope review sits outside the Epic lifecycle ledger.
108
- */
109
- function buildCodeReviewEndPayload({ epicId, result, durationMs }) {
110
- const payload = {
111
- epicId,
112
- status: result.status,
113
- };
114
- if (result.severity && typeof result.severity === 'object') {
115
- payload.severity = {
116
- critical: result.severity.critical ?? 0,
117
- high: result.severity.high ?? 0,
118
- medium: result.severity.medium ?? 0,
119
- suggestion: result.severity.suggestion ?? 0,
120
- };
121
- }
122
- if (typeof result.halted === 'boolean') payload.halted = result.halted;
123
- if (typeof result.posted === 'boolean') payload.posted = result.posted;
124
- if (Number.isFinite(durationMs) && durationMs >= 0) {
125
- payload.durationMs = Math.floor(durationMs);
126
- }
127
- return payload;
128
- }
129
-
130
68
  /**
131
69
  * Resolve the project base branch fallback used when a caller omits
132
70
  * `baseRef`.
@@ -152,7 +90,6 @@ function resolveCommentTargetId(commentTargetId, fallback) {
152
90
  * baseRef: string,
153
91
  * headRef: string,
154
92
  * commentTargetId: number,
155
- * epicIdForLedger: null,
156
93
  * }}
157
94
  */
158
95
  function resolveStoryScope(opts, config) {
@@ -175,53 +112,16 @@ function resolveStoryScope(opts, config) {
175
112
  opts.commentTargetId,
176
113
  opts.ticketId,
177
114
  ),
178
- epicIdForLedger: null,
179
- };
180
- }
181
-
182
- /**
183
- * Resolve the Epic-scope envelope from the parameterized
184
- * `{ scope: 'epic', ticketId, baseRef, headRef, commentTargetId }` shape.
185
- * `headRef` defaults to `epic/<ticketId>` and `baseRef` to the project
186
- * base branch.
187
- *
188
- * @returns {{
189
- * scope: 'epic',
190
- * ticketId: number,
191
- * baseRef: string,
192
- * headRef: string,
193
- * commentTargetId: number,
194
- * epicIdForLedger: number,
195
- * }}
196
- */
197
- function resolveEpicScope(opts, config) {
198
- if (!Number.isInteger(opts.ticketId) || opts.ticketId <= 0) {
199
- throw new TypeError(
200
- 'runCodeReview: ticketId is required (positive integer) when scope="epic".',
201
- );
202
- }
203
- return {
204
- scope: 'epic',
205
- ticketId: opts.ticketId,
206
- baseRef: opts.baseRef ?? resolveConfigBase(config),
207
- headRef: opts.headRef ?? `epic/${opts.ticketId}`,
208
- commentTargetId: resolveCommentTargetId(
209
- opts.commentTargetId,
210
- opts.ticketId,
211
- ),
212
- epicIdForLedger: opts.ticketId,
213
115
  };
214
116
  }
215
117
 
216
118
  /**
217
- * Dispatch the parameterized scope envelope
218
- * (`{ scope, ticketId, baseRef, headRef, commentTargetId }`) to the
219
- * matching pure resolver. `scope` defaults to `'epic'`.
119
+ * Resolve the scope envelope
120
+ * (`{ scope, ticketId, baseRef, headRef, commentTargetId }`). v2.0.0
121
+ * removed the Epic tier, so `'story'` is the only scope.
220
122
  */
221
123
  function resolveScopeEnvelope(opts, config) {
222
- return opts.scope === 'story'
223
- ? resolveStoryScope(opts, config)
224
- : resolveEpicScope(opts, config);
124
+ return resolveStoryScope(opts, config);
225
125
  }
226
126
 
227
127
  /**
@@ -239,38 +139,30 @@ function resolveScopeEnvelope(opts, config) {
239
139
  * derived from the `Finding[]` returned by the adapter (no separate
240
140
  * severity field on the runner result).
241
141
  *
242
- * Story #2839 (Epic #2815) — accepts a parameterized scope envelope
243
- * so the standalone Story closer can request a Story-scope review
244
- * against `main`, post the structured findings comment to the PR
245
- * (via `commentTargetId`), and surface critical findings to the
246
- * caller as `halted: true`. Lifecycle bus emits are confined to
247
- * `scope: 'epic'` because the `code-review.end` schema requires
248
- * `epicId` and the ledger only spans Epic lifecycles.
142
+ * Story #2839 (Epic #2815) — the Story closer requests a Story-scope
143
+ * review against `main`, posts the structured findings comment to the PR
144
+ * (via `commentTargetId`), and surfaces critical findings to the caller
145
+ * as `halted: true`.
249
146
  *
250
- * Argument shape (parameterized, Epic or Story):
251
- * `{ scope, ticketId, baseRef, headRef, [commentTargetId],
252
- * provider, bus }`
253
- * `scope` defaults to `'epic'`; `baseRef` defaults to the project base
254
- * branch and (Epic scope only) `headRef` defaults to `epic/<ticketId>`.
255
- * For `scope === 'story'`, `commentTargetId` overrides the post
256
- * target (e.g. PR number) while `ticketId` continues to label the
257
- * rendered header ("Story #N").
147
+ * Argument shape:
148
+ * `{ ticketId, baseRef, headRef, [commentTargetId], provider }`
149
+ * `baseRef` defaults to the project base branch; `headRef` is required.
150
+ * `commentTargetId` overrides the post target (e.g. PR number) while
151
+ * `ticketId` continues to label the rendered header ("Story #N").
258
152
  *
259
153
  * @param {{
260
- * scope?: 'epic'|'story',
154
+ * scope?: 'story',
261
155
  * ticketId: number,
262
156
  * baseRef?: string|null,
263
157
  * headRef?: string|null,
264
158
  * commentTargetId?: number|null,
265
159
  * provider: object,
266
160
  * logger?: { info?: Function, warn?: Function, error?: Function, fatal?: Function, createProgress?: Function },
267
- * planningRisk?: { overallLevel?: ('low'|'medium'|'high'), axes?: Array<{ axis?: string, level?: string }> }|null,
161
+ * changedFiles?: string[]|null,
268
162
  * changedFileCount?: number|null,
269
163
  * storyId?: number|null,
270
- * bus?: object|null,
271
- * now?: () => number,
272
164
  * reviewProvider?: { runReview: Function },
273
- * gitSpawnFn?: typeof gitSpawn,
165
+ * gitSpawnFn?: import('./change-set.js').GitSpawnFn,
274
166
  * resolveConfigFn?: typeof resolveConfig,
275
167
  * createReviewProviderFn?: typeof createReviewProvider,
276
168
  * upsertCommentFn?: typeof upsertStructuredComment,
@@ -307,24 +199,47 @@ function resolveProviderName(codeReviewConfig) {
307
199
  return 'native';
308
200
  }
309
201
 
202
+ /**
203
+ * Resolve the change set the depth derivation reads (Story #4593).
204
+ *
205
+ * `opts.changedFiles` is an injection with three distinct states,
206
+ * and the difference is load-bearing: an **array** is the change set to use
207
+ * verbatim; an explicit **null** is a caller (`runStoryReviewCore`) reporting
208
+ * that it already tried and the diff is unenumerable — re-running git here would
209
+ * only fail again, so it degrades straight to the fail-safe tier; **absent**
210
+ * means no caller enumerated at all, so the shared {@link computeChangeSet}
211
+ * enumerator runs as the fallback (standalone CLI use). On the close path the
212
+ * spine always injects, so the diff is enumerated exactly once per delivery and
213
+ * this pillar can never disagree with the lens pass about what changed.
214
+ */
215
+ function resolveInjectedChangedFiles({ opts, baseRef, headRef }) {
216
+ if (opts.changedFiles === undefined) {
217
+ return computeChangeSet({ baseRef, headRef, gitSpawnFn: opts.gitSpawnFn })
218
+ .files;
219
+ }
220
+ return Array.isArray(opts.changedFiles) ? opts.changedFiles : null;
221
+ }
222
+
310
223
  /**
311
224
  * Build the provider `runReview` input, resolving the review depth from the
312
- * judged risk envelope's `overallLevel` and the mechanical changed-file
313
- * count of the diff under review (Story #3876 / #3938). The depth is an
314
- * input-only signal (light → standard → deep) and never touches the output
315
- * envelope or the posted comment. Absent risk envelope + unknown width →
316
- * `standard`. Story #4075 — extracted from `runCodeReview`.
225
+ * diff under review: its changed files derive the change level (sensitive path
226
+ * touched or not Story #4542) and their count supplies the width. The depth
227
+ * is an input-only signal (light → standard → deep) and never touches the
228
+ * output envelope or the posted comment. An unenumerable diff `standard`.
229
+ * Story #4075 — extracted from `runCodeReview`.
317
230
  */
318
231
  function buildReviewInput({ opts, scope, ticketId, baseRef, headRef }) {
232
+ const changedFiles = resolveInjectedChangedFiles({ opts, baseRef, headRef });
319
233
  const changedFileCount =
320
234
  typeof opts.changedFileCount === 'number'
321
235
  ? opts.changedFileCount
322
- : countChangedFiles({ baseRef, headRef, gitSpawnFn: opts.gitSpawnFn });
236
+ : (changedFiles?.length ?? null);
323
237
  // v2 Stage 2: review depth uses DEFAULT_DIFF_WIDTH (mechanical file count
324
238
  // of the diff under review). It is deliberately decoupled from the
325
239
  // planning model-capacity advisory (`DEFAULT_MODEL_CAPACITY`).
240
+ const { level } = deriveChangeLevel({ changedFiles });
326
241
  const depth = resolveDepth({
327
- overallLevel: opts.planningRisk?.overallLevel,
242
+ derivedLevel: level,
328
243
  changedFileCount,
329
244
  });
330
245
  return {
@@ -418,7 +333,7 @@ async function executeReviewPipeline({ opts, config, envelope }) {
418
333
  injectedReviewProvider ?? createReviewProviderFn(codeReviewConfig);
419
334
 
420
335
  logger?.info?.(
421
- `[code-review] Running ${providerName} adapter for ${scope === 'epic' ? 'Epic' : 'Story'} #${ticketId} (${baseRef}...${headRef})...`,
336
+ `[code-review] Running ${providerName} adapter for Story #${ticketId} (${baseRef}...${headRef})...`,
422
337
  );
423
338
 
424
339
  const reviewInput = buildReviewInput({
@@ -477,49 +392,13 @@ async function executeReviewPipeline({ opts, config, envelope }) {
477
392
  }
478
393
 
479
394
  export async function runCodeReview(opts = {}) {
480
- const { bus, now = Date.now, resolveConfigFn = resolveConfig } = opts;
395
+ const { resolveConfigFn = resolveConfig } = opts;
481
396
 
482
397
  const config = resolveConfigFn();
483
398
  const envelope = resolveScopeEnvelope(opts, config);
484
- const { scope } = envelope;
485
-
486
- // Epic-scope lifecycle ledger requires `bus`; Story-scope sits outside
487
- // the Epic lifecycle so the bus is optional there. A caller without a
488
- // bus on the Story path still gets the full review semantics — only the
489
- // `code-review.start`/`.end` events are suppressed.
490
- if (scope === 'epic' && (!bus || typeof bus.emit !== 'function')) {
491
- throw new TypeError('runCodeReview: bus is required (object with emit()).');
492
- }
493
- const ledgerEnabled =
494
- scope === 'epic' && bus && typeof bus.emit === 'function';
495
-
496
- const startedAt = typeof now === 'function' ? now() : Date.now();
497
- // Emit the matched `code-review.end` boundary; the ledger must always
498
- // show a start/end pair (even on adapter throw, where `result` is the
499
- // canonical `{ status: 'invalid' }`).
500
- const emitEnd = async (result) => {
501
- if (!ledgerEnabled) return;
502
- const endedAt = typeof now === 'function' ? now() : Date.now();
503
- await bus.emit(
504
- 'code-review.end',
505
- buildCodeReviewEndPayload({
506
- epicId: envelope.epicIdForLedger,
507
- result,
508
- durationMs: Math.max(0, endedAt - startedAt),
509
- }),
510
- );
511
- };
512
-
513
- if (ledgerEnabled) {
514
- await bus.emit('code-review.start', { epicId: envelope.epicIdForLedger });
515
- }
516
399
 
517
- try {
518
- const result = await executeReviewPipeline({ opts, config, envelope });
519
- await emitEnd(result);
520
- return result;
521
- } catch (err) {
522
- await emitEnd({ status: 'invalid' });
523
- throw err;
524
- }
400
+ // No lifecycle-bus emit: the `code-review.start` / `.end` ledger pair was
401
+ // Epic-scoped (its schema requires `epicId`), and v2.0.0 removed the Epic
402
+ // tier. Story-scope review sits outside the lifecycle ledger entirely.
403
+ return executeReviewPipeline({ opts, config, envelope });
525
404
  }
@@ -12,7 +12,7 @@
12
12
  * reconcile — dispatching it is pure token spend for a no-op. This module
13
13
  * computes that decision **deterministically**, off the same two inputs the
14
14
  * critic itself reads (the draft array and the Epic body's Delivery Slicing
15
- * table), so `helpers/plan-epic.md` Phase 8.3 can skip the sub-agent
15
+ * table), so the planning workflow can skip the sub-agent
16
16
  * dispatch when it is provably safe to.
17
17
  *
18
18
  * **Fail-open by design.** Every ambiguous case — a missing or unparseable
@@ -123,7 +123,7 @@ export function parseDeliverySlicingTable(epicBody) {
123
123
 
124
124
  /**
125
125
  * True when `story` is the recognized wave-0 BDD scaffold Story — identified
126
- * by the literal `bdd-scaffold` goal token the `epic-plan-decompose-author`
126
+ * by the literal `bdd-scaffold` goal token the decomposer prompt
127
127
  * skill's WAVE-0 BDD SCAFFOLD STORY section requires. Scaffold Stories are
128
128
  * not a Delivery Slicing slice, so they are excluded from the count
129
129
  * comparison — BDD-adopting consumer repos still benefit from the
@@ -145,7 +145,7 @@ function isBddScaffoldStory(story) {
145
145
  *
146
146
  * @param {object} input
147
147
  * @param {object[]} input.draftStories - The draft `tickets.json` array
148
- * (`epic-plan-decompose-author`'s output) — raw Story ticket objects with
148
+ * (the decomposer's output) — raw Story ticket objects with
149
149
  * top-level `slug` / `depends_on` / `body` (serialized string).
150
150
  * @param {string} input.epicBody - The Epic body carrying the folded Tech
151
151
  * Spec sections (`## Delivery Slicing` onward).