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
@@ -1,238 +0,0 @@
1
- /**
2
- * progress-reporter/transport.js — outbound I/O for the
3
- * `/deliver` progress narrative.
4
- *
5
- * Extracted from the parent `progress-reporter.js` so the
6
- * GitHub-comment posting surface (in `composition.js`) and the
7
- * progress-signal aggregation (in `signals.js`) can be reasoned about
8
- * independently of the webhook fan-out.
9
- *
10
- * Each `emit*` helper dispatches a curated event-shaped payload through
11
- * the caller-supplied `notify` function. The `notify` contract is owned
12
- * by `lib/notifications/*` and ultimately reaches the configured webhook
13
- * via gh-exec (the retry/backoff loop lives there, not here — this
14
- * module's role is the boundary). Failures from `notify` are swallowed
15
- * by design: a flaky webhook URL must never crash the runner mid-wave.
16
- *
17
- * The webhook events emitted from this module are:
18
- *
19
- * - `epic-started` — fired once at /deliver kickoff
20
- * - `epic-progress` — fired at wave boundaries / blocker transitions
21
- * - `epic-blocked` — wave aggregated to blocked/failed outside halt path
22
- * - `epic-unblocked` — operator flipped back to executing
23
- *
24
- * Each fire passes `skipComment: true` so the operator-facing GitHub
25
- * comments stay owned by `composition.js` (`upsertEpicRunProgress` and
26
- * `ProgressReporter.fire()`) — there is exactly one comment writer and
27
- * exactly one webhook writer per event.
28
- */
29
-
30
- /**
31
- * Webhook event name for the curated epic-progress rollup. Distinct from
32
- * the `epic-run-progress` structured-comment kind in `signals.js` — the
33
- * comment is the operator-facing per-poll snapshot on the Epic ticket,
34
- * the webhook event is the coarse-grained rollup that fires at wave
35
- * boundaries and after blocker transitions.
36
- */
37
- export const EPIC_PROGRESS_EVENT = 'epic-progress';
38
-
39
- /**
40
- * Fire a curated `epic-progress` webhook event. Event-driven only — called
41
- * per recorder beat and after blocker raise/clear transitions. Carries the
42
- * rollup payload `{ pct, done, total, phase, openBlockers }`, which Slack
43
- * consumers and downstream subscribers use to track epic progress without
44
- * subscribing to per-story chatter.
45
- *
46
- * The dispatch passes `skipComment: true` — the operator-facing GitHub
47
- * comment is owned by `ProgressReporter.fire()` and `upsertEpicRunProgress`,
48
- * not by this webhook fire.
49
- *
50
- * Failures are swallowed by design: the runner must keep moving even if
51
- * the webhook URL is misconfigured or the network is flaky.
52
- *
53
- * Story #4155 — the Epic `/deliver` runtime cut over to the continuous
54
- * ready-set scheduler, which has **no wave index**. The wave segment was
55
- * dropped from the message and the rollup payload entirely (rather than
56
- * rendered as `Wave undefined/undefined`); the sole live caller
57
- * (`wave-record-notifications.js`) never supplied wave coordinates.
58
- *
59
- * @param {{
60
- * notify: Function|null,
61
- * epicId: number,
62
- * done: number,
63
- * total: number,
64
- * phase?: string,
65
- * openBlockers?: Array<{ reason: string, storyId?: number }>,
66
- * logger?: { warn?: Function },
67
- * }} args
68
- * @returns {Promise<{ payload: object } | null>}
69
- */
70
- export async function emitEpicProgress({
71
- notify,
72
- epicId,
73
- done,
74
- total,
75
- phase,
76
- openBlockers = [],
77
- logger,
78
- }) {
79
- if (typeof notify !== 'function') return null;
80
- const epicIdNum = Number(epicId);
81
- if (!Number.isInteger(epicIdNum) || epicIdNum <= 0) return null;
82
- const totalN = Math.max(0, Number(total) || 0);
83
- const doneN = Math.max(0, Math.min(totalN, Number(done) || 0));
84
- const pct = totalN === 0 ? 0 : Math.round((doneN / totalN) * 100);
85
- const blockerCount = Array.isArray(openBlockers) ? openBlockers.length : 0;
86
- const blockerSuffix =
87
- blockerCount > 0
88
- ? ` · 🚧 ${blockerCount} blocker${blockerCount === 1 ? '' : 's'}`
89
- : '';
90
- const message = `Epic #${epicIdNum} progress · ${doneN}/${totalN} stories done (${pct}%)${blockerSuffix}`;
91
-
92
- const payload = {
93
- severity: blockerCount > 0 ? 'high' : 'medium',
94
- message,
95
- event: EPIC_PROGRESS_EVENT,
96
- level: 'epic',
97
- epicId: epicIdNum,
98
- };
99
- if (phase) payload.phase = phase;
100
-
101
- try {
102
- await notify(epicIdNum, payload, { skipComment: true });
103
- } catch (err) {
104
- logger?.warn?.(
105
- `[emitEpicProgress] notify dispatch failed (swallowed): ${err?.message ?? err}`,
106
- );
107
- return null;
108
- }
109
- return {
110
- payload: {
111
- pct,
112
- done: doneN,
113
- total: totalN,
114
- phase,
115
- openBlockers: openBlockers ?? [],
116
- },
117
- };
118
- }
119
-
120
- /**
121
- * Fire a curated `epic-started` webhook event at /deliver kickoff.
122
- * The Slack consumer anchors the rest of the epic narrative to this fire.
123
- * Failures are swallowed.
124
- *
125
- * Story #4155 — the ready-set runtime has no wave count, so the wave
126
- * segment was dropped from the message (rather than rendered as
127
- * `undefined wave(s)`); the sole live caller never supplied one.
128
- */
129
- export async function emitEpicStarted({
130
- notify,
131
- epicId,
132
- totalStories,
133
- title,
134
- logger,
135
- }) {
136
- if (typeof notify !== 'function') return null;
137
- const epicIdNum = Number(epicId);
138
- if (!Number.isInteger(epicIdNum) || epicIdNum <= 0) return null;
139
- const message = `Epic #${epicIdNum} started · ${totalStories} stor${totalStories === 1 ? 'y' : 'ies'}${title ? ` — ${title}` : ''}`;
140
- try {
141
- await notify(
142
- epicIdNum,
143
- {
144
- severity: 'medium',
145
- message,
146
- event: 'epic-started',
147
- level: 'epic',
148
- epicId: epicIdNum,
149
- },
150
- { skipComment: true },
151
- );
152
- } catch (err) {
153
- logger?.warn?.(
154
- `[emitEpicStarted] notify dispatch failed (swallowed): ${err?.message ?? err}`,
155
- );
156
- }
157
- return null;
158
- }
159
-
160
- /**
161
- * Fire a curated `epic-blocked` webhook event when a wave aggregates to
162
- * `blocked` or `failed` outside the `BlockerHandler.halt` code path (the
163
- * /deliver host-LLM loop has no handler instance — it calls this
164
- * helper directly from `epic-execute-record-wave.js`). The payload shape
165
- * matches the inline emit in `BlockerHandler.halt` so downstream consumers
166
- * see one canonical envelope regardless of which entry point fired.
167
- * Failures are swallowed.
168
- */
169
- export async function emitEpicBlocked({
170
- notify,
171
- epicId,
172
- reason,
173
- storyId,
174
- logger,
175
- }) {
176
- if (typeof notify !== 'function') return null;
177
- const epicIdNum = Number(epicId);
178
- if (!Number.isInteger(epicIdNum) || epicIdNum <= 0) return null;
179
- const storyPart = storyId ? ` (story #${storyId})` : '';
180
- const message = `🚨 Action Required: Epic #${epicIdNum}${storyPart} blocked: ${reason}`;
181
- try {
182
- await notify(
183
- epicIdNum,
184
- {
185
- severity: 'high',
186
- message,
187
- event: 'epic-blocked',
188
- level: 'epic',
189
- epicId: epicIdNum,
190
- },
191
- { skipComment: true },
192
- );
193
- } catch (err) {
194
- logger?.warn?.(
195
- `[emitEpicBlocked] notify dispatch failed (swallowed): ${err?.message ?? err}`,
196
- );
197
- }
198
- return null;
199
- }
200
-
201
- /**
202
- * Fire a curated `epic-unblocked` webhook event after the operator flips
203
- * the Epic label back to `agent::executing`. Paired with `epic-blocked` so
204
- * downstream consumers can track open-blocker lifecycle. Failures are
205
- * swallowed.
206
- */
207
- export async function emitEpicUnblocked({
208
- notify,
209
- epicId,
210
- resolvedBlocker,
211
- logger,
212
- }) {
213
- if (typeof notify !== 'function') return null;
214
- const epicIdNum = Number(epicId);
215
- if (!Number.isInteger(epicIdNum) || epicIdNum <= 0) return null;
216
- const reasonPart = resolvedBlocker?.reason
217
- ? ` (${resolvedBlocker.reason})`
218
- : '';
219
- const message = `Epic #${epicIdNum} unblocked${reasonPart} · resuming.`;
220
- try {
221
- await notify(
222
- epicIdNum,
223
- {
224
- severity: 'medium',
225
- message,
226
- event: 'epic-unblocked',
227
- level: 'epic',
228
- epicId: epicIdNum,
229
- },
230
- { skipComment: true },
231
- );
232
- } catch (err) {
233
- logger?.warn?.(
234
- `[emitEpicUnblocked] notify dispatch failed (swallowed): ${err?.message ?? err}`,
235
- );
236
- }
237
- return null;
238
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Shared bullet-formatting helpers for progress-signal detectors.
3
- *
4
- * Both crap-drift and maintainability-drift produce component-level
5
- * regression bullets and need the same two pure utilities — hoisted here
6
- * so they are maintained in one place (Story #3631, refs #3631).
7
- */
8
-
9
- /**
10
- * Comparator that sorts the wildcard component `'*'` before all named
11
- * components, with named components in locale order.
12
- *
13
- * @param {string} a
14
- * @param {string} b
15
- * @returns {number}
16
- */
17
- export function componentOrder(a, b) {
18
- if (a === '*') return -1;
19
- if (b === '*') return 1;
20
- return a.localeCompare(b);
21
- }
22
-
23
- /**
24
- * Formats a numeric gate value for display: integers render without a
25
- * decimal point; non-integers are fixed to two decimal places.
26
- *
27
- * @param {number} value
28
- * @returns {string}
29
- */
30
- export function formatNumber(value) {
31
- return Number.isInteger(value) ? String(value) : value.toFixed(2);
32
- }
@@ -1,203 +0,0 @@
1
- import nodeFs from 'node:fs';
2
- import path from 'node:path';
3
- import { resolveComponents } from '../../../baselines/components.js';
4
- import { componentOrder } from './_bullet-format.js';
5
-
6
- /**
7
- * Shared component-regression walk for the progress-signal drift detectors
8
- * (Story #3984). `crap-drift.js` and `maintainability-drift.js` previously
9
- * duplicated this baseline-rollup → per-component compare → bullet-list
10
- * shape verbatim; the only divergence was the breach comparator (CRAP is
11
- * "lower is better", maintainability is "higher is better") and the bullet
12
- * text. Both detectors now parameterize this walk.
13
- *
14
- * Pure — the caller loads the baseline via `lib/baselines/reader.js#load(...)`
15
- * and passes the resulting `{ rollup }` plus the gate config block.
16
- *
17
- * Only components whose rollup breaches the configured floor surface — a
18
- * breach in a component-scoped floor does NOT report against `*` unless
19
- * `*` itself breaches. This keeps the rollout narrowly targeted: when an
20
- * operator wires up a per-component floor for `api`, regressing `api`
21
- * names `api` — not `*`.
22
- *
23
- * @param {{
24
- * rollup?: Record<string, Record<string, number>>,
25
- * gateConfig?: { floors?: Record<string, Record<string, number>> } & object,
26
- * }} params
27
- * @param {{
28
- * isBreach: (value: number, floor: number) => boolean,
29
- * formatBullet: (name: string, axis: string, value: number, floor: number) => string,
30
- * }} spec
31
- * @returns {string[]}
32
- */
33
- export function walkComponentRegressions(params = {}, spec) {
34
- const rollup = params.rollup ?? {};
35
- const gateConfig = params.gateConfig ?? {};
36
- const floors = gateConfig.floors ?? {};
37
- const components = resolveComponents(gateConfig);
38
- const names = new Set([
39
- ...Object.keys(components),
40
- ...Object.keys(floors),
41
- ...Object.keys(rollup),
42
- ]);
43
- const bullets = [];
44
- for (const name of [...names].sort(componentOrder)) {
45
- const aggregate = rollup[name];
46
- if (!aggregate || typeof aggregate !== 'object') continue;
47
- const floor = floors[name] ?? floors['*'];
48
- if (!floor || typeof floor !== 'object') continue;
49
- for (const axis of Object.keys(floor).sort()) {
50
- const target = floor[axis];
51
- const value = aggregate[axis];
52
- if (typeof target !== 'number' || !Number.isFinite(target)) continue;
53
- if (typeof value !== 'number' || !Number.isFinite(value)) continue;
54
- if (!spec.isBreach(value, target)) continue;
55
- bullets.push(spec.formatBullet(name, axis, value, target));
56
- }
57
- }
58
- return bullets;
59
- }
60
-
61
- /**
62
- * Shared wave-start snapshot persistence for the drift detectors. Both
63
- * detectors persist a `{ capturedAt, ...metadata, scores }` JSON document
64
- * under `<cwd>/<baselineDir>/<filename>` so a resumed epic run can re-read
65
- * the wave-start anchor rather than lose it, and both treat persistence as
66
- * best-effort (the in-memory baseline still works when a write fails).
67
- *
68
- * @param {{
69
- * fs: { readFileSync?: Function, writeFileSync?: Function, mkdirSync?: Function },
70
- * baselinePath: string,
71
- * metadata?: Record<string, unknown>,
72
- * }} opts
73
- */
74
- function createSnapshotStore({ fs, baselinePath, metadata = {} }) {
75
- return {
76
- persist(scores) {
77
- if (!fs.writeFileSync) return;
78
- try {
79
- fs.mkdirSync?.(path.dirname(baselinePath), { recursive: true });
80
- fs.writeFileSync(
81
- baselinePath,
82
- JSON.stringify(
83
- { capturedAt: new Date().toISOString(), ...metadata, scores },
84
- null,
85
- 2,
86
- ),
87
- );
88
- } catch {
89
- // persistence is best-effort; the in-memory baseline still works
90
- }
91
- },
92
-
93
- load() {
94
- if (!fs.readFileSync) return null;
95
- try {
96
- const raw = fs.readFileSync(baselinePath, 'utf-8');
97
- const parsed = JSON.parse(raw);
98
- return parsed?.scores ?? null;
99
- } catch {
100
- return null;
101
- }
102
- },
103
- };
104
- }
105
-
106
- /**
107
- * Shared per-file/per-method drift-detector skeleton for the progress-signal
108
- * detectors (Story #4076). `crap-drift.js` and `maintainability-drift.js`
109
- * previously duplicated the same `{ baselinePath, captureBaseline,
110
- * loadBaseline, detect }` shape — identical snapshot-store wiring, identical
111
- * "distinct from canonical baseline" framing, and identical
112
- * swallow-and-warn resilience. The only divergence is the per-axis scoring
113
- * (`scoreFile`), the snapshot value extracted from a score (`captureScore`),
114
- * and the per-detect comparison that emits bullets (`detect`). Both detectors
115
- * now supply those deltas to this factory, mirroring the "shared walker +
116
- * per-axis delta" pattern already established by `walkComponentRegressions`.
117
- *
118
- * The factory owns:
119
- *
120
- * - `fs` / `cwd` / `files` resolution from `opts`.
121
- * - The `<cwd>/<baselineDir>/<baselineFilename>` baseline path and the
122
- * `createSnapshotStore` wiring (with caller-supplied `metadata`).
123
- * - The in-memory `baseline` cache and the `captureBaseline` /
124
- * `loadBaseline` lifecycle.
125
- *
126
- * The caller owns:
127
- *
128
- * - `scoreFile(relPath, ctx)` — returns the per-file score (any shape) or
129
- * `null` when the file can't be scored. `ctx` is whatever
130
- * `beforeScore()` returned for this pass (e.g. a coverage map), or
131
- * `undefined` when `beforeScore` is omitted.
132
- * - `captureScore(score)` — maps a `scoreFile` result to the value
133
- * persisted in the snapshot (e.g. extract `crap` from each method row).
134
- * - `detect({ baseline, scoreFile })` — async; returns the bullet list.
135
- * Receives the loaded baseline and a `scoreFile(relPath)` bound to this
136
- * pass's score context so the per-axis compare stays free of plumbing.
137
- * - `beforeScore()` (optional) — runs once per `captureBaseline` / `detect`
138
- * pass and returns the score context threaded into `scoreFile`.
139
- *
140
- * @param {{
141
- * cwd?: string,
142
- * files?: string[],
143
- * fs?: { readFileSync?: Function, writeFileSync?: Function, mkdirSync?: Function, existsSync?: Function },
144
- * baselineDir?: string,
145
- * baselineFilename: string,
146
- * metadata?: Record<string, unknown>,
147
- * beforeScore?: () => unknown,
148
- * scoreFile: (relPath: string, ctx: unknown) => unknown,
149
- * captureScore: (score: unknown) => unknown,
150
- * detect: (args: { baseline: Record<string, unknown>, scoreFile: (relPath: string) => unknown }) => Promise<string[]>,
151
- * }} opts
152
- */
153
- export function createDriftDetector(opts = {}) {
154
- const fs = opts.fs ?? nodeFs;
155
- const cwd = opts.cwd ?? process.cwd();
156
- const files = Array.isArray(opts.files) ? [...opts.files] : [];
157
- const baselineDir = opts.baselineDir ?? '.agents/state';
158
- const baselinePath = path.join(cwd, baselineDir, opts.baselineFilename);
159
- const beforeScore = opts.beforeScore ?? (() => undefined);
160
- const scoreFile = opts.scoreFile;
161
- const captureScore = opts.captureScore;
162
- const runDetect = opts.detect;
163
- const store = createSnapshotStore({
164
- fs,
165
- baselinePath,
166
- metadata: opts.metadata ?? {},
167
- });
168
-
169
- let baseline = null;
170
-
171
- return {
172
- get baselinePath() {
173
- return baselinePath;
174
- },
175
-
176
- captureBaseline() {
177
- const ctx = beforeScore();
178
- const snapshot = {};
179
- for (const f of files) {
180
- const score = scoreFile(f, ctx);
181
- if (score == null) continue;
182
- snapshot[f] = captureScore(score);
183
- }
184
- baseline = snapshot;
185
- store.persist(snapshot);
186
- return snapshot;
187
- },
188
-
189
- loadBaseline() {
190
- baseline = store.load();
191
- return baseline;
192
- },
193
-
194
- async detect() {
195
- if (!baseline) return [];
196
- const ctx = beforeScore();
197
- return runDetect({
198
- baseline,
199
- scoreFile: (relPath) => scoreFile(relPath, ctx),
200
- });
201
- },
202
- };
203
- }
@@ -1,227 +0,0 @@
1
- import nodeFs from 'node:fs';
2
- import path from 'node:path';
3
- import { loadCoverage as defaultLoadCoverage } from '../../../coverage-utils.js';
4
- import { calculateCrapForSource } from '../../../crap-engine.js';
5
- import { formatNumber } from './_bullet-format.js';
6
- import {
7
- createDriftDetector,
8
- walkComponentRegressions,
9
- } from './component-drift.js';
10
-
11
- const DEFAULT_THRESHOLD = 5.0;
12
- const DEFAULT_CEILING = 30;
13
- // Distinct from the canonical ratchet baseline at `baselines/crap.json`
14
- // (Epic #730 Story 5.5) — this is a per-wave drift SNAPSHOT, not the
15
- // committed score baseline. Filename intentionally differs so a repo-wide
16
- // grep for the canonical baseline no longer hits the snapshot.
17
- const BASELINE_FILENAME = 'wave-crap-snapshot.json';
18
-
19
- /**
20
- * Normalise a repo-relative path for coverage-key lookup. Pure; exported
21
- * for unit tests so the underlying branches don't have to be exercised
22
- * through the closure-bound `findCoverageEntry`.
23
- */
24
- export function normaliseCoveragePath(relPath) {
25
- return String(relPath)
26
- .replace(/\\/g, '/')
27
- .replace(/^\.\/+/, '');
28
- }
29
-
30
- /**
31
- * True when a coverage map key matches a (normalised) relative path,
32
- * either by full equality or by suffix-with-slash. Pure.
33
- */
34
- export function coverageKeyMatches(key, suffix) {
35
- const norm = String(key).replace(/\\/g, '/');
36
- return norm === suffix || norm.endsWith(`/${suffix}`);
37
- }
38
-
39
- /**
40
- * Detect per-component CRAP regressions from a baseline rollup against the
41
- * gate's configured floors. Pure — the caller loads the baseline via
42
- * `lib/baselines/reader.js#load('crap')` and passes the resulting
43
- * `{ rollup }` plus the gate config block.
44
- *
45
- * Bullet shape (Task #1919, Epic #1786):
46
- *
47
- * 🧨 crap: <component> <axis> <value> > floor <floor>
48
- *
49
- * CRAP is a "lower is better" gate — every axis breach reports when
50
- * `value > floor`. Component-scoped breaches do NOT trigger a `*` bullet
51
- * unless `*` itself breaches. The walk itself is the shared
52
- * `component-drift.js` helper (Story #3984).
53
- *
54
- * @param {{
55
- * rollup?: Record<string, Record<string, number>>,
56
- * gateConfig?: { floors?: Record<string, Record<string, number>> } & object,
57
- * }} params
58
- * @returns {string[]}
59
- */
60
- export function detectComponentRegressions(params = {}) {
61
- return walkComponentRegressions(params, {
62
- isBreach: (value, cap) => value > cap,
63
- formatBullet: (name, axis, value, cap) =>
64
- `🧨 crap: ${name} ${axis} ${formatNumber(value)} > floor ${formatNumber(cap)}`,
65
- });
66
- }
67
-
68
- /**
69
- * Detects per-method CRAP drift versus a wave-start baseline.
70
- *
71
- * Mirrors the `maintainability-drift.js` contract but at per-method
72
- * granularity: baseline snapshots are `{ "<file>::<method>@<startLine>": crap }`
73
- * rather than a single score per file. A method surfaces as a Notable bullet
74
- * when either condition holds:
75
- *
76
- * - **Crossed the ceiling** — current CRAP ≥ `ceiling` and baseline was
77
- * either absent or below `ceiling`. Captures newly introduced hotspots
78
- * and methods that tipped over the budget.
79
- * - **Rose by ≥ threshold** — baseline present and `current - baseline ≥
80
- * threshold`. Captures silent regressions that stay under the ceiling
81
- * but are trending badly.
82
- *
83
- * Bullet shape:
84
- *
85
- * 🧨 CRAP drift: <file>::<method> <score> (ceiling <N>)
86
- *
87
- * Persistence: baseline is written to `<cwd>/<baselineDir>/wave-crap-snapshot.json`
88
- * so a resumed epic run can reuse the wave-start anchor rather than lose it.
89
- *
90
- * Resilience: read / scoring errors for an individual file are swallowed (and
91
- * optionally logged) so a single unscorable file cannot take the progress
92
- * reporter down.
93
- *
94
- * @param {{
95
- * cwd?: string,
96
- * files?: string[],
97
- * fs?: { readFileSync: Function, writeFileSync?: Function, mkdirSync?: Function, existsSync?: Function },
98
- * calculate?: (source: string, coverageForFile: object|null) => Array<{ method: string, startLine: number, crap: number|null }>,
99
- * loadCoverage?: (coveragePath: string) => object|null,
100
- * coveragePath?: string | null,
101
- * threshold?: number,
102
- * ceiling?: number,
103
- * baselineDir?: string,
104
- * logger?: { warn?: Function },
105
- * }} [opts]
106
- */
107
- export function createCrapDriftDetector(opts = {}) {
108
- const fs = opts.fs ?? nodeFs;
109
- const cwd = opts.cwd ?? process.cwd();
110
- const files = Array.isArray(opts.files) ? [...opts.files] : [];
111
- const calculate = opts.calculate ?? calculateCrapForSource;
112
- const loadCoverage = opts.loadCoverage ?? defaultLoadCoverage;
113
- const coveragePath = opts.coveragePath ?? null;
114
- const threshold = Number.isFinite(opts.threshold)
115
- ? opts.threshold
116
- : DEFAULT_THRESHOLD;
117
- const ceiling = Number.isFinite(opts.ceiling)
118
- ? opts.ceiling
119
- : DEFAULT_CEILING;
120
- const logger = opts.logger ?? null;
121
-
122
- function methodKey(method, startLine) {
123
- return `${method}@${startLine}`;
124
- }
125
-
126
- // Hoisted helpers — exported via `_internal` for unit coverage on each
127
- // branch. Keeping them outside the closure keeps the orchestration
128
- // method's complexity low.
129
- // (see normaliseCoveragePath / coverageKeyMatches at module top.)
130
- function findCoverageEntry(coverageMap, relPath) {
131
- if (!coverageMap || typeof coverageMap !== 'object') return null;
132
- const suffix = normaliseCoveragePath(relPath);
133
- if (!suffix) return null;
134
- for (const key of Object.keys(coverageMap)) {
135
- if (coverageKeyMatches(key, suffix)) {
136
- return coverageMap[key] ?? null;
137
- }
138
- }
139
- return null;
140
- }
141
-
142
- function scoreFile(relPath, coverageMap) {
143
- try {
144
- const abs = path.join(cwd, relPath);
145
- const src = fs.readFileSync(abs, 'utf-8');
146
- const entry = findCoverageEntry(coverageMap, relPath);
147
- const rows = calculate(src, entry) ?? [];
148
- const methods = {};
149
- for (const row of rows) {
150
- if (!row || typeof row.method !== 'string') continue;
151
- if (typeof row.startLine !== 'number') continue;
152
- if (typeof row.crap !== 'number' || !Number.isFinite(row.crap))
153
- continue;
154
- methods[methodKey(row.method, row.startLine)] = {
155
- method: row.method,
156
- startLine: row.startLine,
157
- crap: row.crap,
158
- };
159
- }
160
- return methods;
161
- } catch (err) {
162
- logger?.warn?.(
163
- `[crap-drift] scoring failed for ${relPath}: ${err?.message ?? err}`,
164
- );
165
- return null;
166
- }
167
- }
168
-
169
- function readCoverageMap() {
170
- if (!coveragePath) return null;
171
- try {
172
- return loadCoverage(coveragePath);
173
- } catch (err) {
174
- logger?.warn?.(
175
- `[crap-drift] coverage load failed (${coveragePath}): ${err?.message ?? err}`,
176
- );
177
- return null;
178
- }
179
- }
180
-
181
- return createDriftDetector({
182
- fs,
183
- cwd,
184
- files: opts.files,
185
- baselineDir: opts.baselineDir,
186
- baselineFilename: BASELINE_FILENAME,
187
- metadata: { ceiling, threshold },
188
- beforeScore: readCoverageMap,
189
- scoreFile,
190
- captureScore: (methods) => {
191
- const persisted = {};
192
- for (const [key, row] of Object.entries(methods)) {
193
- persisted[key] = row.crap;
194
- }
195
- return persisted;
196
- },
197
- async detect({ baseline, scoreFile: score }) {
198
- const bullets = [];
199
- // Union of files watched and files present in baseline, so methods that
200
- // appeared since the snapshot (new files) are still checked, and methods
201
- // that disappeared from baseline-only files don't get us stuck iterating
202
- // anything spurious.
203
- const fileSet = new Set([...files, ...Object.keys(baseline)]);
204
- for (const relPath of fileSet) {
205
- const currentMethods = score(relPath);
206
- if (!currentMethods) continue;
207
- const fileBaseline = baseline[relPath] ?? {};
208
- for (const [key, row] of Object.entries(currentMethods)) {
209
- const prev = fileBaseline[key];
210
- const crossed =
211
- row.crap >= ceiling &&
212
- (prev === undefined || prev === null || prev < ceiling);
213
- const rose =
214
- typeof prev === 'number' &&
215
- Number.isFinite(prev) &&
216
- row.crap - prev >= threshold;
217
- if (crossed || rose) {
218
- bullets.push(
219
- `🧨 CRAP drift: ${relPath}::${row.method} ${row.crap.toFixed(2)} (ceiling ${ceiling})`,
220
- );
221
- }
222
- }
223
- }
224
- return bullets;
225
- },
226
- });
227
- }