mandrel 1.58.0 → 1.60.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 (319) hide show
  1. package/.agents/README.md +100 -98
  2. package/.agents/docs/SDLC.md +140 -141
  3. package/.agents/docs/configuration.md +16 -16
  4. package/.agents/docs/workflows.md +7 -8
  5. package/.agents/instructions.md +12 -11
  6. package/.agents/personas/architect.md +1 -1
  7. package/.agents/personas/product.md +1 -1
  8. package/.agents/personas/project-manager.md +14 -14
  9. package/.agents/personas/technical-writer.md +1 -1
  10. package/.agents/rules/changelog-style.md +5 -5
  11. package/.agents/rules/git-conventions.md +3 -3
  12. package/.agents/schemas/agentrc.schema.json +3 -3
  13. package/.agents/schemas/audit-rules.json +20 -0
  14. package/.agents/schemas/dispatch-manifest.json +4 -4
  15. package/.agents/schemas/epic-spec.schema.json +15 -45
  16. package/.agents/schemas/lifecycle/README.md +1 -1
  17. package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
  18. package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
  19. package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
  20. package/.agents/schemas/validation-evidence.schema.json +1 -1
  21. package/.agents/scripts/README.md +1 -1
  22. package/.agents/scripts/acceptance-eval.js +21 -4
  23. package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
  24. package/.agents/scripts/analyze-execution.js +2 -2
  25. package/.agents/scripts/assert-branch.js +1 -3
  26. package/.agents/scripts/audit-to-stories.js +1 -1
  27. package/.agents/scripts/bootstrap.js +1 -1
  28. package/.agents/scripts/check-arch-cycles.js +360 -0
  29. package/.agents/scripts/check-doc-links.js +2 -3
  30. package/.agents/scripts/coverage-capture.js +24 -3
  31. package/.agents/scripts/diagnose-friction.js +1 -1
  32. package/.agents/scripts/dispatcher.js +2 -2
  33. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  34. package/.agents/scripts/epic-audit-prepare.js +3 -3
  35. package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
  36. package/.agents/scripts/epic-deliver-preflight.js +11 -9
  37. package/.agents/scripts/epic-deliver-prepare.js +13 -5
  38. package/.agents/scripts/epic-execute-record-wave.js +5 -5
  39. package/.agents/scripts/epic-plan-healthcheck.js +6 -10
  40. package/.agents/scripts/epic-plan-spec-validate.js +1 -1
  41. package/.agents/scripts/epic-reconcile.js +11 -29
  42. package/.agents/scripts/evidence-gate.js +2 -2
  43. package/.agents/scripts/generate-workflows-doc.js +1 -1
  44. package/.agents/scripts/git-rebase-and-resolve.js +1 -1
  45. package/.agents/scripts/hierarchy-gate.js +40 -24
  46. package/.agents/scripts/lib/ITicketingProvider.js +1 -1
  47. package/.agents/scripts/lib/audit-suite/selector.js +1 -1
  48. package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
  49. package/.agents/scripts/lib/baseline-snapshot.js +7 -7
  50. package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
  51. package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
  52. package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
  53. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
  54. package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
  55. package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
  56. package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
  57. package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
  58. package/.agents/scripts/lib/baselines/writer.js +1 -1
  59. package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
  60. package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
  61. package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
  62. package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
  63. package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +1 -1
  64. package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
  65. package/.agents/scripts/lib/close-validation/commands.js +188 -0
  66. package/.agents/scripts/lib/close-validation/gates.js +235 -0
  67. package/.agents/scripts/lib/close-validation/process.js +101 -0
  68. package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
  69. package/.agents/scripts/lib/close-validation/runner.js +325 -0
  70. package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
  71. package/.agents/scripts/lib/codebase-snapshot.js +1 -1
  72. package/.agents/scripts/lib/config/explain.js +1 -1
  73. package/.agents/scripts/lib/config/quality.js +6 -6
  74. package/.agents/scripts/lib/config/runners.js +2 -2
  75. package/.agents/scripts/lib/config/runtime.js +1 -1
  76. package/.agents/scripts/lib/config/temp-paths.js +2 -2
  77. package/.agents/scripts/lib/config-resolver.js +2 -5
  78. package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
  79. package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
  80. package/.agents/scripts/lib/config-settings-schema.js +3 -3
  81. package/.agents/scripts/lib/coverage-capture.js +147 -4
  82. package/.agents/scripts/lib/cpu-pool.js +14 -0
  83. package/.agents/scripts/lib/crap-utils.js +6 -11
  84. package/.agents/scripts/lib/duplicate-search.js +1 -1
  85. package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
  86. package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
  87. package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
  88. package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
  89. package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
  90. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
  91. package/.agents/scripts/lib/findings/classify-finding.js +1 -1
  92. package/.agents/scripts/lib/findings/promote-finding.js +10 -10
  93. package/.agents/scripts/lib/git-utils.js +24 -22
  94. package/.agents/scripts/lib/label-constants.js +3 -4
  95. package/.agents/scripts/lib/label-taxonomy.js +3 -8
  96. package/.agents/scripts/lib/maintainability-engine.js +1 -1
  97. package/.agents/scripts/lib/maintainability-utils.js +4 -187
  98. package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
  99. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
  100. package/.agents/scripts/lib/orchestration/code-review.js +95 -82
  101. package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
  102. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
  103. package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
  104. package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
  105. package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +14 -37
  106. package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
  107. package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
  108. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
  109. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
  110. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
  111. package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
  112. package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
  113. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
  114. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
  115. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
  116. package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +28 -8
  117. package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
  118. package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
  119. package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
  120. package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
  121. package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +13 -41
  122. package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
  123. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
  124. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
  125. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
  126. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
  127. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
  128. package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
  129. package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
  130. package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
  131. package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
  132. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
  133. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
  134. package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
  135. package/.agents/scripts/lib/orchestration/file-assumptions.js +6 -5
  136. package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
  137. package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
  138. package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
  139. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
  140. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
  141. package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
  142. package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
  143. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
  144. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
  145. package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
  146. package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
  147. package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
  148. package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
  149. package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
  150. package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +8 -8
  151. package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
  152. package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
  153. package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
  154. package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
  155. package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
  156. package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
  157. package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
  158. package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
  159. package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
  160. package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
  161. package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
  162. package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
  163. package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
  164. package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
  165. package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
  166. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
  167. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
  168. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
  169. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
  170. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
  171. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
  172. package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
  173. package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
  174. package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
  175. package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
  176. package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
  177. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
  178. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
  179. package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
  180. package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
  181. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
  182. package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
  183. package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
  184. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
  185. package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
  186. package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
  187. package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
  188. package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
  189. package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
  190. package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
  191. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +4 -35
  192. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
  193. package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
  194. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +44 -73
  195. package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
  196. package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
  197. package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
  198. package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
  199. package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
  200. package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
  201. package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
  202. package/.agents/scripts/lib/preflight-runner.js +1 -1
  203. package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
  204. package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
  205. package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
  206. package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
  207. package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
  208. package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
  209. package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
  210. package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
  211. package/.agents/scripts/lib/project-root.js +17 -0
  212. package/.agents/scripts/lib/signals/schema.js +1 -1
  213. package/.agents/scripts/lib/spec/index.js +1 -1
  214. package/.agents/scripts/lib/spec/loader.js +2 -2
  215. package/.agents/scripts/lib/spec/state.js +7 -16
  216. package/.agents/scripts/lib/story-adjacency.js +76 -0
  217. package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
  218. package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
  219. package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
  220. package/.agents/scripts/lib/story-lifecycle.js +9 -9
  221. package/.agents/scripts/lib/story-plan.js +1 -1
  222. package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
  223. package/.agents/scripts/lib/transpile.js +93 -0
  224. package/.agents/scripts/lib/wave-runner/tick.js +4 -153
  225. package/.agents/scripts/lib/workers/crap-worker.js +1 -1
  226. package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
  227. package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
  228. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
  229. package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
  230. package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
  231. package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
  232. package/.agents/scripts/lifecycle-emit.js +1 -1
  233. package/.agents/scripts/providers/github/board-add.js +1 -1
  234. package/.agents/scripts/providers/github/errors.js +1 -1
  235. package/.agents/scripts/providers/github/mappers.js +2 -2
  236. package/.agents/scripts/providers/github/tickets.js +114 -10
  237. package/.agents/scripts/resync-status-column.js +1 -1
  238. package/.agents/scripts/retro-run.js +2 -2
  239. package/.agents/scripts/run-lint.js +10 -1
  240. package/.agents/scripts/run-tests.js +24 -4
  241. package/.agents/scripts/single-story-init.js +1 -1
  242. package/.agents/scripts/stories-wave-tick.js +13 -10
  243. package/.agents/scripts/story-close.js +1 -1
  244. package/.agents/scripts/story-init.js +162 -26
  245. package/.agents/scripts/story-phase.js +5 -5
  246. package/.agents/scripts/story-plan.js +3 -3
  247. package/.agents/scripts/sync-branch-from-base.js +2 -2
  248. package/.agents/scripts/validate-docs-freshness.js +1 -1
  249. package/.agents/scripts/wave-tick.js +1 -1
  250. package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
  251. package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
  252. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
  253. package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
  254. package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
  255. package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
  256. package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
  257. package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
  258. package/.agents/skills/core/scope-triage/SKILL.md +9 -10
  259. package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
  260. package/.agents/skills/skills.index.json +7 -7
  261. package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
  262. package/.agents/templates/agent-protocol.md +2 -2
  263. package/.agents/workflows/agents-update.md +2 -2
  264. package/.agents/workflows/audit-architecture.md +2 -2
  265. package/.agents/workflows/audit-clean-code.md +2 -2
  266. package/.agents/workflows/audit-dependencies.md +1 -1
  267. package/.agents/workflows/audit-devops.md +1 -1
  268. package/.agents/workflows/audit-documentation.md +226 -0
  269. package/.agents/workflows/audit-lighthouse.md +1 -1
  270. package/.agents/workflows/audit-performance.md +2 -2
  271. package/.agents/workflows/audit-privacy.md +1 -1
  272. package/.agents/workflows/audit-quality.md +2 -2
  273. package/.agents/workflows/audit-security.md +2 -2
  274. package/.agents/workflows/audit-seo.md +1 -1
  275. package/.agents/workflows/audit-sre.md +1 -1
  276. package/.agents/workflows/audit-to-stories.md +10 -10
  277. package/.agents/workflows/audit-ux-ui.md +1 -1
  278. package/.agents/workflows/deliver.md +85 -0
  279. package/.agents/workflows/explain.md +3 -3
  280. package/.agents/workflows/git-merge-pr.md +1 -1
  281. package/.agents/workflows/git-pr-all.md +13 -10
  282. package/.agents/workflows/git-push.md +6 -3
  283. package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
  284. package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
  285. package/.agents/workflows/helpers/code-review.md +5 -5
  286. package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
  287. package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
  288. package/.agents/workflows/helpers/diagnose.md +1 -1
  289. package/.agents/workflows/helpers/epic-audit.md +6 -6
  290. package/.agents/workflows/helpers/epic-deliver-story.md +28 -39
  291. package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
  292. package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
  293. package/.agents/workflows/helpers/epic-testing.md +3 -3
  294. package/.agents/workflows/helpers/parallel-tooling.md +1 -1
  295. package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
  296. package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
  297. package/.agents/workflows/helpers/signals.md +1 -1
  298. package/.agents/workflows/helpers/single-story-deliver.md +12 -11
  299. package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
  300. package/.agents/workflows/onboard.md +21 -20
  301. package/.agents/workflows/plan.md +89 -0
  302. package/.agents/workflows/qa-explore.md +1 -1
  303. package/.agents/workflows/qa-run-harness.md +1 -1
  304. package/README.md +17 -20
  305. package/docs/CHANGELOG.md +1149 -0
  306. package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
  307. package/lib/cli/__tests__/update-reexec.test.js +513 -0
  308. package/lib/cli/init.js +338 -0
  309. package/lib/cli/update.js +413 -52
  310. package/package.json +3 -1
  311. package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
  312. package/.agents/scripts/lib/close-validation.js +0 -897
  313. package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
  314. package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
  315. package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
  316. package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
  317. package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
  318. package/.agents/scripts/lib/task-utils.js +0 -26
  319. package/.agents/scripts/story-deliver-prepare.js +0 -267
@@ -11,26 +11,25 @@
11
11
  * per-file percentages (which would over-weight small files).
12
12
  *
13
13
  * Lower duplication is better, so the gate's floor direction is `lte`
14
- * (see `check-baselines/phases/floors.js#axisDirection`).
14
+ * (see `check-baselines/phases/floors.js#axisDirection`). New paths land
15
+ * in the `additions` bucket (Story #2012); removed paths count as
16
+ * improvements when they carried any duplication — the file is gone, so
17
+ * its prior debt is gone too.
15
18
  *
16
19
  * `kernelVersion()` returns a static in-repo semver — the duplication
17
20
  * scorer is the in-repo scan + rollup contract (the jscpd output is
18
21
  * normalised before it reaches this module), so there is no upstream
19
22
  * library version to track the way CRAP/MI track `typhonjs-escomplex`.
20
- * Bump `KERNEL_VERSION` whenever the row shape or rollup math changes.
23
+ * Bump the version passed to `makeBaselineKind` whenever the row shape or
24
+ * rollup math changes. Scaffold is generated by `makeBaselineKind`
25
+ * (Story #3983).
21
26
  */
22
27
 
23
- import { componentMatches } from '../component-matcher.js';
24
28
  import { canonicalise } from '../path-canon.js';
25
- import { mergeRowsByScope } from '../scope.js';
29
+ import { makeBaselineKind } from './kind-factory.js';
26
30
 
27
31
  export const name = 'duplication';
28
32
  export const keyField = 'path';
29
- const KERNEL_VERSION = '1.0.0';
30
-
31
- export function kernelVersion() {
32
- return KERNEL_VERSION;
33
- }
34
33
 
35
34
  export function projectRow(row) {
36
35
  const duplicatedLines = Number(row.duplicatedLines ?? 0);
@@ -47,10 +46,6 @@ export function projectRow(row) {
47
46
  };
48
47
  }
49
48
 
50
- export function sortRows(rows) {
51
- return [...rows].sort((a, b) => a.path.localeCompare(b.path));
52
- }
53
-
54
49
  /**
55
50
  * Exact aggregate duplication ratio: total duplicated lines / total lines
56
51
  * across the row set, expressed as a percentage. Averaging per-file
@@ -92,106 +87,22 @@ function roundTo2(value) {
92
87
  return Number(value.toFixed(2));
93
88
  }
94
89
 
95
- export function rollup(rows, components = []) {
96
- const out = { '*': aggregate(rows) };
97
- for (const c of components ?? []) {
98
- const matched = (rows ?? []).filter((r) => componentMatches(c, r.path));
99
- out[c.name] = aggregate(matched);
100
- }
101
- return out;
102
- }
103
-
104
- /**
105
- * Pure compare(head, base) for the duplication kind. Diffs rows by `path`.
106
- *
107
- * Higher duplication = worse. A row regresses when its `percentage`
108
- * increases vs base; improves when it decreases; unchanged when equal.
109
- * New paths (head has a row that base lacks) land in the `additions`
110
- * bucket — absolute-floor enforcement is the unified `check-baselines`
111
- * gate's job and runs independently, so a Story that lands a new file
112
- * never fails through the regression arm (mirrors crap/maintainability,
113
- * Story #2012). Removed paths (base has a row that head dropped) count as
114
- * improvements when they carried any duplication; the file is gone, so its
115
- * prior debt is gone too.
116
- *
117
- * No I/O. No process exit. No friction emission.
118
- */
119
- export function compare(head, base) {
120
- const headRows = Array.isArray(head?.rows) ? head.rows : [];
121
- const baseRows = Array.isArray(base?.rows) ? base.rows : [];
122
- const baseByKey = new Map();
123
- for (const r of baseRows) baseByKey.set(r.path, r);
124
- const seen = new Set();
125
- const regressions = [];
126
- const improvements = [];
127
- const unchanged = [];
128
- const additions = [];
129
- for (const h of headRows) {
130
- seen.add(h.path);
131
- const b = baseByKey.get(h.path);
132
- if (!b) {
133
- additions.push({ key: h.path, head: h, base: null });
134
- continue;
135
- }
136
- const delta = (h.percentage ?? 0) - (b.percentage ?? 0);
137
- if (delta > 0) regressions.push({ key: h.path, head: h, base: b });
138
- else if (delta < 0) improvements.push({ key: h.path, head: h, base: b });
139
- else unchanged.push({ key: h.path, head: h, base: b });
140
- }
141
- for (const b of baseRows) {
142
- if (seen.has(b.path)) continue;
143
- if ((b.percentage ?? 0) > 0) {
144
- improvements.push({ key: b.path, head: null, base: b });
145
- } else {
146
- unchanged.push({ key: b.path, head: null, base: b });
147
- }
148
- }
149
- return { regressions, improvements, unchanged, additions };
150
- }
151
-
152
- /**
153
- * Pure stabilizer for s-stability-epsilon (Story #1964). Duplication rows
154
- * match by `path`. The metric is the absolute `percentage` delta. Sub-
155
- * epsilon deltas resolve to the prior bytes so env variance never rewrites
156
- * the on-disk baseline; missing-prior rows fall through.
157
- *
158
- * @param {Array<{path: string, percentage: number}>} prior
159
- * @param {Array<{path: string, percentage: number}>} regenerated
160
- * @param {number} epsilon non-negative absolute tolerance on percentage
161
- * @returns {Array<object>}
162
- */
163
- export function applyEpsilon(prior, regenerated, epsilon) {
164
- const priorRows = Array.isArray(prior) ? prior : [];
165
- const regenRows = Array.isArray(regenerated) ? regenerated : [];
166
- const eps = Number.isFinite(epsilon) && epsilon >= 0 ? epsilon : 0;
167
- const priorByKey = new Map();
168
- for (const r of priorRows) priorByKey.set(r.path, r);
169
- return regenRows.map((row) => {
170
- const p = priorByKey.get(row.path);
171
- if (!p) return row;
172
- return Math.abs((row.percentage ?? 0) - (p.percentage ?? 0)) <= eps
173
- ? p
174
- : row;
175
- });
176
- }
177
-
178
- /**
179
- * Pure scope-aware merge for s-diff-scoped-writes (Story #1974).
180
- * Duplication rows match by `path`. In diff mode, rows whose `path` is
181
- * OUTSIDE `scope.files` are preserved from `prior` verbatim; in-scope rows
182
- * come from `regenerated`. In full mode (or no scope), regenerated wins
183
- * everywhere.
184
- *
185
- * @param {Array<{path: string, percentage: number}>} prior
186
- * @param {Array<{path: string, percentage: number}>} regenerated
187
- * @param {{mode: 'full'|'diff', files: Set<string>}|null|undefined} scope
188
- * @returns {Array<object>}
189
- */
190
- export function mergeRows(prior, regenerated, scope) {
191
- return mergeRowsByScope({
192
- prior,
193
- regenerated,
194
- scope,
195
- scopeKey: (row) => row.path,
196
- });
197
- }
90
+ export const {
91
+ kernelVersion,
92
+ sortRows,
93
+ rollup,
94
+ compare,
95
+ applyEpsilon,
96
+ mergeRows,
97
+ } = makeBaselineKind({
98
+ keyField,
99
+ kernelVersion: '1.0.0',
100
+ axes: ['percentage'],
101
+ betterWhen: 'lower',
102
+ aggregate,
103
+ missingBasePolicy: 'addition',
104
+ removedRowPolicy: {
105
+ kind: 'improvement-when',
106
+ when: (b) => (b.percentage ?? 0) > 0,
107
+ },
108
+ });
@@ -0,0 +1,192 @@
1
+ /**
2
+ * kinds/kind-factory.js — shared scaffold factory for per-kind baseline
3
+ * modules (Story #3983).
4
+ *
5
+ * The five row-metric kinds (coverage, mutation, maintainability,
6
+ * lighthouse, duplication) used to hand-roll the same scaffold each:
7
+ * `kernelVersion`, `sortRows`, a component-aware `rollup`, a
8
+ * `compare(head, base)` that diffs rows by key into
9
+ * `{regressions, improvements, unchanged, additions}`, an
10
+ * `applyEpsilon` stabilizer (Story #1964), and a scope-aware
11
+ * `mergeRows` (Story #1974). Only the axis list, direction-of-better,
12
+ * aggregate math, and the missing/removed-row policies differ per kind.
13
+ *
14
+ * `makeBaselineKind` generates the scaffold once; each kind module stays
15
+ * a thin parameterization with a byte-identical exported surface. The
16
+ * Story #2012 class of fix ("new paths must land in `additions`, never
17
+ * the regression arm") lives here exactly once.
18
+ *
19
+ * All generated functions are pure: no I/O, no process exit, no
20
+ * friction emission.
21
+ */
22
+
23
+ import { componentMatches } from '../component-matcher.js';
24
+ import { mergeRowsByScope } from '../scope.js';
25
+
26
+ /**
27
+ * Build the shared scaffold for a per-kind baseline module.
28
+ *
29
+ * @param {{
30
+ * keyField: string,
31
+ * kernelVersion: string | (() => string),
32
+ * axes: string[],
33
+ * betterWhen: 'higher' | 'lower',
34
+ * aggregate: (rows: object[]) => Record<string, number>,
35
+ * missingBasePolicy?: 'addition' | 'perfect',
36
+ * removedRowPolicy?:
37
+ * | { kind: 'perfect-head' }
38
+ * | { kind: 'improvement-when', when: (row: object) => boolean },
39
+ * perfectRow?: (key: string) => object,
40
+ * }} opts
41
+ * - `keyField` — row identity property (`'path'` or `'route'`)
42
+ * - `kernelVersion` — static semver, or a thunk for kinds that pin
43
+ * to another kind's kernel (MI → CRAP)
44
+ * - `axes` — metric property names compared per row
45
+ * - `betterWhen` — `'higher'` (coverage, MI, …) or `'lower'`
46
+ * (duplication): decides which delta sign is a
47
+ * regression
48
+ * - `aggregate` — per-kind rollup math over a row set
49
+ * - `missingBasePolicy` — head row with no base row: `'addition'`
50
+ * (default; Story #2012 bucket) or `'perfect'`
51
+ * (classify against a perfect base — lighthouse,
52
+ * where a new route must meet the bar)
53
+ * - `removedRowPolicy` — base row with no head row: `'perfect-head'`
54
+ * classifies against a perfect head row;
55
+ * `'improvement-when'` pushes an improvement
56
+ * when `when(baseRow)` holds, else unchanged
57
+ * - `perfectRow` — builds the perfect row for the policies above
58
+ * @returns {{
59
+ * kernelVersion: () => string,
60
+ * sortRows: (rows: object[]) => object[],
61
+ * rollup: (rows: object[], components?: object[]) => Record<string, object>,
62
+ * compare: (head: object, base: object) => object,
63
+ * applyEpsilon: (prior: object[], regenerated: object[], epsilon: number) => object[],
64
+ * mergeRows: (prior: object[], regenerated: object[], scope: object) => object[],
65
+ * }}
66
+ */
67
+ export function makeBaselineKind({
68
+ keyField,
69
+ kernelVersion,
70
+ axes,
71
+ betterWhen,
72
+ aggregate,
73
+ missingBasePolicy = 'addition',
74
+ removedRowPolicy = { kind: 'improvement-when', when: () => false },
75
+ perfectRow = null,
76
+ }) {
77
+ const keyOf = (row) => row[keyField];
78
+ const kernelVersionFn =
79
+ typeof kernelVersion === 'function' ? kernelVersion : () => kernelVersion;
80
+
81
+ function sortRows(rows) {
82
+ return [...rows].sort((a, b) => keyOf(a).localeCompare(keyOf(b)));
83
+ }
84
+
85
+ function rollup(rows, components = []) {
86
+ const out = { '*': aggregate(rows) };
87
+ for (const c of components ?? []) {
88
+ const matched = (rows ?? []).filter((r) => componentMatches(c, keyOf(r)));
89
+ out[c.name] = aggregate(matched);
90
+ }
91
+ return out;
92
+ }
93
+
94
+ function classify(regressions, improvements, unchanged, key, head, base) {
95
+ let down = false;
96
+ let up = false;
97
+ for (const axis of axes) {
98
+ const delta = (head[axis] ?? 0) - (base[axis] ?? 0);
99
+ const worse = betterWhen === 'higher' ? delta < 0 : delta > 0;
100
+ const better = betterWhen === 'higher' ? delta > 0 : delta < 0;
101
+ if (worse) down = true;
102
+ else if (better) up = true;
103
+ }
104
+ if (down) regressions.push({ key, head, base });
105
+ else if (up) improvements.push({ key, head, base });
106
+ else unchanged.push({ key, head, base });
107
+ }
108
+
109
+ function compare(head, base) {
110
+ const headRows = Array.isArray(head?.rows) ? head.rows : [];
111
+ const baseRows = Array.isArray(base?.rows) ? base.rows : [];
112
+ const baseByKey = new Map();
113
+ for (const r of baseRows) baseByKey.set(keyOf(r), r);
114
+ const seen = new Set();
115
+ const regressions = [];
116
+ const improvements = [];
117
+ const unchanged = [];
118
+ const additions = [];
119
+ for (const h of headRows) {
120
+ const key = keyOf(h);
121
+ seen.add(key);
122
+ const b = baseByKey.get(key);
123
+ if (!b) {
124
+ if (missingBasePolicy === 'addition') {
125
+ additions.push({ key, head: h, base: null });
126
+ } else {
127
+ classify(
128
+ regressions,
129
+ improvements,
130
+ unchanged,
131
+ key,
132
+ h,
133
+ perfectRow(key),
134
+ );
135
+ }
136
+ continue;
137
+ }
138
+ classify(regressions, improvements, unchanged, key, h, b);
139
+ }
140
+ for (const b of baseRows) {
141
+ const key = keyOf(b);
142
+ if (seen.has(key)) continue;
143
+ if (removedRowPolicy.kind === 'perfect-head') {
144
+ classify(regressions, improvements, unchanged, key, perfectRow(key), b);
145
+ } else if (removedRowPolicy.when(b)) {
146
+ improvements.push({ key, head: null, base: b });
147
+ } else {
148
+ unchanged.push({ key, head: null, base: b });
149
+ }
150
+ }
151
+ if (missingBasePolicy === 'addition') {
152
+ return { regressions, improvements, unchanged, additions };
153
+ }
154
+ return { regressions, improvements, unchanged };
155
+ }
156
+
157
+ function applyEpsilon(prior, regenerated, epsilon) {
158
+ const priorRows = Array.isArray(prior) ? prior : [];
159
+ const regenRows = Array.isArray(regenerated) ? regenerated : [];
160
+ const eps = Number.isFinite(epsilon) && epsilon >= 0 ? epsilon : 0;
161
+ const priorByKey = new Map();
162
+ for (const r of priorRows) priorByKey.set(keyOf(r), r);
163
+ return regenRows.map((row) => {
164
+ const p = priorByKey.get(keyOf(row));
165
+ if (!p) return row;
166
+ let maxAxisDelta = 0;
167
+ for (const axis of axes) {
168
+ const d = Math.abs((row[axis] ?? 0) - (p[axis] ?? 0));
169
+ if (d > maxAxisDelta) maxAxisDelta = d;
170
+ }
171
+ return maxAxisDelta <= eps ? p : row;
172
+ });
173
+ }
174
+
175
+ function mergeRows(prior, regenerated, scope) {
176
+ return mergeRowsByScope({
177
+ prior,
178
+ regenerated,
179
+ scope,
180
+ scopeKey: keyOf,
181
+ });
182
+ }
183
+
184
+ return {
185
+ kernelVersion: kernelVersionFn,
186
+ sortRows,
187
+ rollup,
188
+ compare,
189
+ applyEpsilon,
190
+ mergeRows,
191
+ };
192
+ }
@@ -3,19 +3,22 @@
3
3
  * (Story #1891). Row shape: `{ route, performance, accessibility,
4
4
  * bestPractices, seo }`. The key field is `route` (a path-like string that
5
5
  * still passes the path-canon checks for absolute / `..` rejection).
6
+ *
7
+ * Higher score = better. New routes inherit a base of 100 for each axis
8
+ * (so lower scores register as regressions — a new route must meet the
9
+ * bar); dropped routes inherit a head of 100 (so a higher base registers
10
+ * as an improvement). Unlike the file-derived kinds, compare emits no
11
+ * `additions` bucket. Scaffold is generated by `makeBaselineKind`
12
+ * (Story #3983).
6
13
  */
7
14
 
8
- import { componentMatches } from '../component-matcher.js';
9
15
  import { canonicalise } from '../path-canon.js';
10
- import { mergeRowsByScope } from '../scope.js';
16
+ import { makeBaselineKind } from './kind-factory.js';
11
17
 
12
18
  export const name = 'lighthouse';
13
19
  export const keyField = 'route';
14
- const KERNEL_VERSION = '1.0.0';
15
20
 
16
- export function kernelVersion() {
17
- return KERNEL_VERSION;
18
- }
21
+ const LH_AXES = ['performance', 'accessibility', 'bestPractices', 'seo'];
19
22
 
20
23
  function canonRoute(route) {
21
24
  // Routes look like `/`, `/dashboard`, or `pricing` — strip the leading
@@ -36,75 +39,24 @@ export function projectRow(row) {
36
39
  };
37
40
  }
38
41
 
39
- export function sortRows(rows) {
40
- return [...rows].sort((a, b) => a.route.localeCompare(b.route));
42
+ function meanOf(rows, axis) {
43
+ let sum = 0;
44
+ for (const r of rows) sum += r[axis] ?? 0;
45
+ return Number((sum / rows.length).toFixed(2));
41
46
  }
42
47
 
43
48
  function aggregate(rows) {
44
49
  if (!rows || rows.length === 0) {
45
50
  return { performance: 0, accessibility: 0, bestPractices: 0, seo: 0 };
46
51
  }
47
- const sum = { performance: 0, accessibility: 0, bestPractices: 0, seo: 0 };
48
- for (const r of rows) {
49
- sum.performance += r.performance ?? 0;
50
- sum.accessibility += r.accessibility ?? 0;
51
- sum.bestPractices += r.bestPractices ?? 0;
52
- sum.seo += r.seo ?? 0;
53
- }
54
52
  return {
55
- performance: Number((sum.performance / rows.length).toFixed(2)),
56
- accessibility: Number((sum.accessibility / rows.length).toFixed(2)),
57
- bestPractices: Number((sum.bestPractices / rows.length).toFixed(2)),
58
- seo: Number((sum.seo / rows.length).toFixed(2)),
53
+ performance: meanOf(rows, 'performance'),
54
+ accessibility: meanOf(rows, 'accessibility'),
55
+ bestPractices: meanOf(rows, 'bestPractices'),
56
+ seo: meanOf(rows, 'seo'),
59
57
  };
60
58
  }
61
59
 
62
- export function rollup(rows, components = []) {
63
- const out = { '*': aggregate(rows) };
64
- for (const c of components ?? []) {
65
- const matched = (rows ?? []).filter((r) => componentMatches(c, r.route));
66
- out[c.name] = aggregate(matched);
67
- }
68
- return out;
69
- }
70
-
71
- /**
72
- * Pure compare(head, base) for the lighthouse kind. Diffs rows by `route`.
73
- *
74
- * Higher score = better. A row regresses when any of performance,
75
- * accessibility, bestPractices, or seo decreases vs the base. An
76
- * improvement requires at least one score to increase and none to
77
- * decrease. Otherwise the row is unchanged. New routes inherit a base of
78
- * 100 for each axis (so lower scores register as regressions); dropped
79
- * routes inherit a head of 100 (so a higher base registers as an
80
- * improvement).
81
- *
82
- * No I/O. No process exit. No friction emission.
83
- */
84
- const LH_AXES = ['performance', 'accessibility', 'bestPractices', 'seo'];
85
-
86
- export function compare(head, base) {
87
- const headRows = Array.isArray(head?.rows) ? head.rows : [];
88
- const baseRows = Array.isArray(base?.rows) ? base.rows : [];
89
- const baseByKey = new Map();
90
- for (const r of baseRows) baseByKey.set(r.route, r);
91
- const seen = new Set();
92
- const regressions = [];
93
- const improvements = [];
94
- const unchanged = [];
95
- for (const h of headRows) {
96
- seen.add(h.route);
97
- const b = baseByKey.get(h.route) ?? perfectLighthouseRow(h.route);
98
- classify(regressions, improvements, unchanged, h.route, h, b);
99
- }
100
- for (const b of baseRows) {
101
- if (seen.has(b.route)) continue;
102
- const h = perfectLighthouseRow(b.route);
103
- classify(regressions, improvements, unchanged, b.route, h, b);
104
- }
105
- return { regressions, improvements, unchanged };
106
- }
107
-
108
60
  function perfectLighthouseRow(route) {
109
61
  return {
110
62
  route,
@@ -115,71 +67,20 @@ function perfectLighthouseRow(route) {
115
67
  };
116
68
  }
117
69
 
118
- function classify(regressions, improvements, unchanged, key, head, base) {
119
- let down = false;
120
- let up = false;
121
- for (const axis of LH_AXES) {
122
- const delta = (head[axis] ?? 0) - (base[axis] ?? 0);
123
- if (delta < 0) down = true;
124
- else if (delta > 0) up = true;
125
- }
126
- if (down) regressions.push({ key, head, base });
127
- else if (up) improvements.push({ key, head, base });
128
- else unchanged.push({ key, head, base });
129
- }
130
-
131
- /**
132
- * Pure stabilizer for s-stability-epsilon (Story #1964). Lighthouse rows
133
- * match by `route`. The metric is the maximum absolute delta across the
134
- * four scoring axes. Sub-epsilon deltas resolve to the prior bytes;
135
- * missing-prior rows fall through.
136
- *
137
- * @param {Array<{route: string, performance: number, accessibility: number, bestPractices: number, seo: number}>} prior
138
- * @param {Array<{route: string, performance: number, accessibility: number, bestPractices: number, seo: number}>} regenerated
139
- * @param {number} epsilon non-negative absolute tolerance per axis
140
- * @returns {Array<object>}
141
- */
142
- export function applyEpsilon(prior, regenerated, epsilon) {
143
- const priorRows = Array.isArray(prior) ? prior : [];
144
- const regenRows = Array.isArray(regenerated) ? regenerated : [];
145
- const eps = Number.isFinite(epsilon) && epsilon >= 0 ? epsilon : 0;
146
- const priorByKey = new Map();
147
- for (const r of priorRows) priorByKey.set(r.route, r);
148
- return regenRows.map((row) => {
149
- const p = priorByKey.get(row.route);
150
- if (!p) return row;
151
- let maxAxisDelta = 0;
152
- for (const axis of LH_AXES) {
153
- const d = Math.abs((row[axis] ?? 0) - (p[axis] ?? 0));
154
- if (d > maxAxisDelta) maxAxisDelta = d;
155
- }
156
- return maxAxisDelta <= eps ? p : row;
157
- });
158
- }
159
-
160
- /**
161
- * Pure scope-aware merge for s-diff-scoped-writes (Story #1974). Lighthouse
162
- * rows match by `route`. In diff mode, rows whose `route` is OUTSIDE
163
- * `scope.files` are preserved from `prior` verbatim; in-scope rows come
164
- * from `regenerated`. In full mode (or no scope), regenerated wins
165
- * everywhere.
166
- *
167
- * Note: lighthouse routes are not file paths, so the scope filter only
168
- * narrows naturally when callers seed `scope.files` with route strings.
169
- * Auto-refresh callers using a Story file diff will see no in-scope rows
170
- * and therefore preserve every prior row — which is the safe default for
171
- * a baseline that is not file-derived.
172
- *
173
- * @param {Array<{route: string, performance: number, accessibility: number, bestPractices: number, seo: number}>} prior
174
- * @param {Array<{route: string, performance: number, accessibility: number, bestPractices: number, seo: number}>} regenerated
175
- * @param {{mode: 'full'|'diff', files: Set<string>}|null|undefined} scope
176
- * @returns {Array<object>}
177
- */
178
- export function mergeRows(prior, regenerated, scope) {
179
- return mergeRowsByScope({
180
- prior,
181
- regenerated,
182
- scope,
183
- scopeKey: (row) => row.route,
184
- });
185
- }
70
+ export const {
71
+ kernelVersion,
72
+ sortRows,
73
+ rollup,
74
+ compare,
75
+ applyEpsilon,
76
+ mergeRows,
77
+ } = makeBaselineKind({
78
+ keyField,
79
+ kernelVersion: '1.0.0',
80
+ axes: LH_AXES,
81
+ betterWhen: 'higher',
82
+ aggregate,
83
+ missingBasePolicy: 'perfect',
84
+ removedRowPolicy: { kind: 'perfect-head' },
85
+ perfectRow: perfectLighthouseRow,
86
+ });