mandrel 2.60.0 → 2.62.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 (662) hide show
  1. package/.agents/README.md +10 -10
  2. package/.agents/agents/story-worker.md +1 -1
  3. package/.agents/docs/agentrc-reference.json +4 -82
  4. package/.agents/docs/configuration.md +20 -87
  5. package/.agents/docs/workflows.md +1 -1
  6. package/.agents/instructions.md +1 -3
  7. package/.agents/rules/gherkin-standards.md +4 -0
  8. package/.agents/schemas/agentrc.schema.json +11 -486
  9. package/.agents/schemas/story-deliver-terminal.schema.json +10 -0
  10. package/.agents/schemas/validation-evidence.schema.json +2 -1
  11. package/.agents/scripts/README.md +14 -14
  12. package/.agents/scripts/acceptance-eval.js +32 -164
  13. package/.agents/scripts/agents-bootstrap-github.js +27 -129
  14. package/.agents/scripts/apply-quality-bootstrap.js +4 -40
  15. package/.agents/scripts/audit-baselines.js +6 -27
  16. package/.agents/scripts/audit-labels-bootstrap.js +5 -29
  17. package/.agents/scripts/audit-to-stories.js +91 -361
  18. package/.agents/scripts/boot-sweep.js +19 -72
  19. package/.agents/scripts/bootstrap.js +74 -395
  20. package/.agents/scripts/ceremony-derive.js +9 -45
  21. package/.agents/scripts/check-arch-cycles.js +14 -64
  22. package/.agents/scripts/check-baselines.js +8 -48
  23. package/.agents/scripts/check-context-budget.js +46 -172
  24. package/.agents/scripts/check-cyclomatic.js +12 -44
  25. package/.agents/scripts/check-dead-exports.js +13 -64
  26. package/.agents/scripts/check-doc-links.js +38 -186
  27. package/.agents/scripts/check-gherkin-corpus.js +24 -127
  28. package/.agents/scripts/check-test-temp-hygiene.js +28 -169
  29. package/.agents/scripts/coverage-capture.js +32 -99
  30. package/.agents/scripts/deliver-light.js +14 -101
  31. package/.agents/scripts/deliver-recover.js +6 -42
  32. package/.agents/scripts/deliver-run.js +36 -133
  33. package/.agents/scripts/diagnose-friction.js +24 -116
  34. package/.agents/scripts/drain-pending-cleanup.js +1 -1
  35. package/.agents/scripts/evidence-gate.js +23 -85
  36. package/.agents/scripts/file-ci-gap.js +10 -50
  37. package/.agents/scripts/generate-config-docs.js +30 -170
  38. package/.agents/scripts/generate-lens-checklists.js +8 -52
  39. package/.agents/scripts/generate-skills-index.js +14 -112
  40. package/.agents/scripts/generate-workflows-doc.js +10 -70
  41. package/.agents/scripts/git-cleanup.js +2 -34
  42. package/.agents/scripts/lib/Graph.js +24 -81
  43. package/.agents/scripts/lib/ITicketingProvider.js +47 -143
  44. package/.agents/scripts/lib/Logger.js +14 -76
  45. package/.agents/scripts/lib/audit-baselines/engine.js +11 -31
  46. package/.agents/scripts/lib/audit-baselines/gate-surface.js +3 -17
  47. package/.agents/scripts/lib/audit-baselines/headroom.js +4 -20
  48. package/.agents/scripts/lib/audit-baselines/hotspots.js +3 -15
  49. package/.agents/scripts/lib/audit-baselines/kinds.js +23 -89
  50. package/.agents/scripts/lib/audit-baselines/outliers.js +6 -23
  51. package/.agents/scripts/lib/audit-baselines/read.js +3 -16
  52. package/.agents/scripts/lib/audit-baselines/staleness.js +7 -26
  53. package/.agents/scripts/lib/audit-baselines/surface-entry.js +7 -28
  54. package/.agents/scripts/lib/audit-baselines/trend.js +7 -20
  55. package/.agents/scripts/lib/audit-baselines/weights.js +9 -37
  56. package/.agents/scripts/lib/audit-suite/audit-rules-reader.js +3 -18
  57. package/.agents/scripts/lib/audit-suite/checklist-threading.js +26 -124
  58. package/.agents/scripts/lib/audit-suite/dispatch-checklist.js +14 -45
  59. package/.agents/scripts/lib/audit-suite/findings.js +12 -52
  60. package/.agents/scripts/lib/audit-suite/frontmatter.js +5 -29
  61. package/.agents/scripts/lib/audit-suite/index.js +1 -15
  62. package/.agents/scripts/lib/audit-suite/lens-checklist.js +10 -45
  63. package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +11 -76
  64. package/.agents/scripts/lib/audit-suite/runner.js +13 -43
  65. package/.agents/scripts/lib/audit-suite/selector.js +49 -349
  66. package/.agents/scripts/lib/audit-suite/substitutions.js +12 -40
  67. package/.agents/scripts/lib/audit-suite/workflow-loader.js +3 -15
  68. package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +11 -73
  69. package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +8 -37
  70. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +25 -143
  71. package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +14 -80
  72. package/.agents/scripts/lib/audit-to-stories/epic-grouping-directive.js +4 -18
  73. package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +7 -40
  74. package/.agents/scripts/lib/audit-to-stories/group-findings.js +9 -39
  75. package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +10 -66
  76. package/.agents/scripts/lib/audit-to-stories/issue-index.js +6 -30
  77. package/.agents/scripts/lib/audit-to-stories/issues-file.js +6 -36
  78. package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +17 -69
  79. package/.agents/scripts/lib/audit-to-stories/ledger-pr.js +17 -85
  80. package/.agents/scripts/lib/audit-to-stories/ledger-record.js +10 -48
  81. package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +38 -146
  82. package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +7 -42
  83. package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +10 -57
  84. package/.agents/scripts/lib/baseline-loader.js +12 -51
  85. package/.agents/scripts/lib/baseline-schema-registry.js +5 -22
  86. package/.agents/scripts/lib/baselines/component-matcher.js +3 -12
  87. package/.agents/scripts/lib/baselines/components.js +10 -61
  88. package/.agents/scripts/lib/baselines/coverage-updater-cli.js +7 -36
  89. package/.agents/scripts/lib/baselines/crap-preview-incremental.js +5 -21
  90. package/.agents/scripts/lib/baselines/crap-preview-scan.js +19 -45
  91. package/.agents/scripts/lib/baselines/crap-updater-cli.js +16 -65
  92. package/.agents/scripts/lib/baselines/diff-scope-cli.js +5 -33
  93. package/.agents/scripts/lib/baselines/duplication-scanner.js +11 -60
  94. package/.agents/scripts/lib/baselines/env-overrides.js +12 -67
  95. package/.agents/scripts/lib/baselines/envelope.js +13 -129
  96. package/.agents/scripts/lib/baselines/exit-codes.js +6 -49
  97. package/.agents/scripts/lib/baselines/git-base.js +23 -142
  98. package/.agents/scripts/lib/baselines/kernel.js +11 -111
  99. package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +9 -47
  100. package/.agents/scripts/lib/baselines/kinds/_crap-read.js +8 -51
  101. package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +7 -62
  102. package/.agents/scripts/lib/baselines/kinds/bundle-size.js +8 -29
  103. package/.agents/scripts/lib/baselines/kinds/coverage.js +3 -15
  104. package/.agents/scripts/lib/baselines/kinds/crap.js +94 -415
  105. package/.agents/scripts/lib/baselines/kinds/duplication.js +4 -30
  106. package/.agents/scripts/lib/baselines/kinds/kind-factory.js +7 -53
  107. package/.agents/scripts/lib/baselines/kinds/maintainability.js +15 -80
  108. package/.agents/scripts/lib/baselines/kinds/mutation.js +12 -72
  109. package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +4 -11
  110. package/.agents/scripts/lib/baselines/merge-envelopes.js +30 -146
  111. package/.agents/scripts/lib/baselines/path-canon.js +19 -128
  112. package/.agents/scripts/lib/baselines/preview-gates.js +8 -37
  113. package/.agents/scripts/lib/baselines/reader.js +14 -107
  114. package/.agents/scripts/lib/baselines/refresh-service.js +35 -252
  115. package/.agents/scripts/lib/baselines/scope.js +11 -135
  116. package/.agents/scripts/lib/baselines/writer.js +16 -149
  117. package/.agents/scripts/lib/bdd-runner-detect.js +18 -113
  118. package/.agents/scripts/lib/bdd-scenario-budget.js +9 -36
  119. package/.agents/scripts/lib/bdd-scenario-scanner.js +11 -73
  120. package/.agents/scripts/lib/bdd-step-index.js +26 -100
  121. package/.agents/scripts/lib/bootstrap/baseline-merge-driver.js +23 -103
  122. package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -5
  123. package/.agents/scripts/lib/bootstrap/commit-push.js +12 -50
  124. package/.agents/scripts/lib/bootstrap/gh-preflight.js +14 -101
  125. package/.agents/scripts/lib/bootstrap/hitl-confirm.js +5 -30
  126. package/.agents/scripts/lib/bootstrap/install-ledger.js +17 -73
  127. package/.agents/scripts/lib/bootstrap/issue-forms-template.js +17 -121
  128. package/.agents/scripts/lib/bootstrap/manifest.js +15 -86
  129. package/.agents/scripts/lib/bootstrap/merge-methods.js +4 -33
  130. package/.agents/scripts/lib/bootstrap/preflight.js +12 -61
  131. package/.agents/scripts/lib/bootstrap/project-bootstrap.js +34 -235
  132. package/.agents/scripts/lib/bootstrap/prompt.js +21 -129
  133. package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +27 -141
  134. package/.agents/scripts/lib/bootstrap/summary.js +1 -8
  135. package/.agents/scripts/lib/bootstrap/workflow-audit.js +11 -84
  136. package/.agents/scripts/lib/branch-name-guard.js +4 -23
  137. package/.agents/scripts/lib/changed-files.js +31 -143
  138. package/.agents/scripts/lib/checks/core-bare-clean.js +4 -24
  139. package/.agents/scripts/lib/checks/index.js +18 -101
  140. package/.agents/scripts/lib/checks/loop-health.js +11 -53
  141. package/.agents/scripts/lib/checks/state.js +16 -91
  142. package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +10 -61
  143. package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +14 -83
  144. package/.agents/scripts/lib/child-exec.js +39 -108
  145. package/.agents/scripts/lib/cli/standard-args.js +9 -116
  146. package/.agents/scripts/lib/cli-args.js +31 -146
  147. package/.agents/scripts/lib/cli-usage.js +5 -27
  148. package/.agents/scripts/lib/cli-utils.js +2 -12
  149. package/.agents/scripts/lib/close-validation/commands.js +13 -76
  150. package/.agents/scripts/lib/close-validation/gates.js +92 -331
  151. package/.agents/scripts/lib/close-validation/process.js +121 -151
  152. package/.agents/scripts/lib/close-validation/projections/advisories.js +5 -31
  153. package/.agents/scripts/lib/close-validation/projections/crap.js +19 -67
  154. package/.agents/scripts/lib/close-validation/projections/head-sha.js +1 -17
  155. package/.agents/scripts/lib/close-validation/projections/inputs.js +4 -34
  156. package/.agents/scripts/lib/close-validation/projections/maintainability.js +11 -58
  157. package/.agents/scripts/lib/close-validation/runner.js +53 -110
  158. package/.agents/scripts/lib/command-header.js +6 -28
  159. package/.agents/scripts/lib/config/acceptance-eval.js +4 -30
  160. package/.agents/scripts/lib/config/baselines.js +3 -14
  161. package/.agents/scripts/lib/config/ci.js +8 -38
  162. package/.agents/scripts/lib/config/commands.js +1 -15
  163. package/.agents/scripts/lib/config/defaults.js +4 -33
  164. package/.agents/scripts/lib/config/delivery-routing.js +6 -35
  165. package/.agents/scripts/lib/config/explain.js +9 -80
  166. package/.agents/scripts/lib/config/gates/coverage.schema.js +0 -12
  167. package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +2 -30
  168. package/.agents/scripts/lib/config/gates/crap.schema.js +1 -34
  169. package/.agents/scripts/lib/config/gates/duplication.schema.js +1 -8
  170. package/.agents/scripts/lib/config/gates/index.js +2 -17
  171. package/.agents/scripts/lib/config/gates/maintainability.schema.js +1 -20
  172. package/.agents/scripts/lib/config/gates/mutation.schema.js +1 -7
  173. package/.agents/scripts/lib/config/gates/shared.js +4 -64
  174. package/.agents/scripts/lib/config/github.js +4 -22
  175. package/.agents/scripts/lib/config/limits.js +4 -57
  176. package/.agents/scripts/lib/config/paths.js +2 -21
  177. package/.agents/scripts/lib/config/qa.js +4 -38
  178. package/.agents/scripts/lib/config/quality.js +76 -432
  179. package/.agents/scripts/lib/config/runners.js +12 -56
  180. package/.agents/scripts/lib/config/runtime.js +13 -57
  181. package/.agents/scripts/lib/config/shared.js +2 -16
  182. package/.agents/scripts/lib/config/sync-agentrc.js +9 -50
  183. package/.agents/scripts/lib/config/temp-paths.js +40 -280
  184. package/.agents/scripts/lib/config/validate-orchestration.js +5 -17
  185. package/.agents/scripts/lib/config/worktree-isolation.js +9 -28
  186. package/.agents/scripts/lib/config-resolver.js +10 -52
  187. package/.agents/scripts/lib/config-schema-shared.js +1 -4
  188. package/.agents/scripts/lib/config-settings-schema-delivery.js +12 -294
  189. package/.agents/scripts/lib/config-settings-schema-quality.js +7 -219
  190. package/.agents/scripts/lib/config-settings-schema.js +37 -274
  191. package/.agents/scripts/lib/coverage-baseline.js +20 -110
  192. package/.agents/scripts/lib/coverage-capture-fullscope.js +12 -37
  193. package/.agents/scripts/lib/coverage-capture-incremental.js +12 -43
  194. package/.agents/scripts/lib/coverage-capture-usage.js +4 -24
  195. package/.agents/scripts/lib/coverage-capture.js +99 -224
  196. package/.agents/scripts/lib/coverage-utils.js +14 -79
  197. package/.agents/scripts/lib/cpu-pool.js +17 -123
  198. package/.agents/scripts/lib/crap-baseline-join.js +16 -88
  199. package/.agents/scripts/lib/crap-coordinates.js +6 -25
  200. package/.agents/scripts/lib/crap-engine.js +40 -153
  201. package/.agents/scripts/lib/crap-method-identity.js +16 -78
  202. package/.agents/scripts/lib/crap-utils.js +31 -155
  203. package/.agents/scripts/lib/cyclomatic-ceiling.js +14 -87
  204. package/.agents/scripts/lib/cyclomatic-scope.js +10 -50
  205. package/.agents/scripts/lib/dead-exports-knip.js +18 -56
  206. package/.agents/scripts/lib/dead-exports-mode.js +5 -24
  207. package/.agents/scripts/lib/degraded-mode.js +3 -26
  208. package/.agents/scripts/lib/dependency-parser.js +8 -40
  209. package/.agents/scripts/lib/dependency-version.js +13 -35
  210. package/.agents/scripts/lib/detect-package-manager.js +6 -36
  211. package/.agents/scripts/lib/doc-tiers.js +22 -126
  212. package/.agents/scripts/lib/duplicate-search.js +8 -72
  213. package/.agents/scripts/lib/env-loader.js +5 -27
  214. package/.agents/scripts/lib/error-redactor.js +6 -31
  215. package/.agents/scripts/lib/errors/index.js +2 -22
  216. package/.agents/scripts/lib/escomplex-ast-compat.js +22 -163
  217. package/.agents/scripts/lib/escomplex-kernel.js +21 -123
  218. package/.agents/scripts/lib/feedback-loop/graduator-core.js +93 -419
  219. package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +20 -98
  220. package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +27 -151
  221. package/.agents/scripts/lib/findings/audit-ledger.js +23 -97
  222. package/.agents/scripts/lib/findings/classify-finding.js +14 -69
  223. package/.agents/scripts/lib/findings/promote-finding.js +14 -112
  224. package/.agents/scripts/lib/findings/provenance-field.js +11 -52
  225. package/.agents/scripts/lib/findings/route-finding.js +37 -255
  226. package/.agents/scripts/lib/findings/semantic-issue-search.js +14 -59
  227. package/.agents/scripts/lib/findings/severity.js +18 -104
  228. package/.agents/scripts/lib/format-generated-json.js +12 -44
  229. package/.agents/scripts/lib/full-suite-lock.js +160 -429
  230. package/.agents/scripts/lib/full-suite-queue.js +213 -0
  231. package/.agents/scripts/lib/gates/baseline-store.js +5 -9
  232. package/.agents/scripts/lib/gates/friction.js +1 -3
  233. package/.agents/scripts/lib/generated/agentrc-validator.js +2 -2
  234. package/.agents/scripts/lib/gh-exec.js +31 -259
  235. package/.agents/scripts/lib/git/cached-fetch.js +9 -52
  236. package/.agents/scripts/lib/git/sync-from-base.js +15 -107
  237. package/.agents/scripts/lib/git-branch-cleanup.js +14 -61
  238. package/.agents/scripts/lib/git-branch-lifecycle.js +16 -87
  239. package/.agents/scripts/lib/git-utils.js +37 -161
  240. package/.agents/scripts/lib/github/framework-repo.js +8 -71
  241. package/.agents/scripts/lib/github-url.js +2 -17
  242. package/.agents/scripts/lib/import-graph.js +11 -38
  243. package/.agents/scripts/lib/install-cmd-parser.js +4 -13
  244. package/.agents/scripts/lib/json-utils.js +3 -18
  245. package/.agents/scripts/lib/label-constants.js +16 -111
  246. package/.agents/scripts/lib/label-taxonomy.js +4 -30
  247. package/.agents/scripts/lib/maintainability-engine.js +12 -80
  248. package/.agents/scripts/lib/maintainability-unscorable.js +5 -25
  249. package/.agents/scripts/lib/maintainability-utils.js +16 -92
  250. package/.agents/scripts/lib/mandrel-catalog.js +12 -70
  251. package/.agents/scripts/lib/notifications/notifier.js +13 -38
  252. package/.agents/scripts/lib/npm-scripts.js +4 -23
  253. package/.agents/scripts/lib/observability/metrics-ledger.js +16 -76
  254. package/.agents/scripts/lib/observability/runtime-friction.js +44 -286
  255. package/.agents/scripts/lib/observability/signal-validator.js +5 -37
  256. package/.agents/scripts/lib/observability/signals-writer.js +11 -121
  257. package/.agents/scripts/lib/observability/source-classifier.js +21 -203
  258. package/.agents/scripts/lib/observability/terse-result.js +14 -47
  259. package/.agents/scripts/lib/onboard/init-tail.js +13 -74
  260. package/.agents/scripts/lib/onboard/scaffold-docs.js +11 -34
  261. package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +25 -139
  262. package/.agents/scripts/lib/orchestration/auto-merge-cwd.js +11 -66
  263. package/.agents/scripts/lib/orchestration/behind-recovery.js +10 -55
  264. package/.agents/scripts/lib/orchestration/ceremony-routing.js +5 -75
  265. package/.agents/scripts/lib/orchestration/change-set.js +7 -36
  266. package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +9 -46
  267. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +11 -44
  268. package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +5 -24
  269. package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +3 -12
  270. package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +5 -38
  271. package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +3 -8
  272. package/.agents/scripts/lib/orchestration/check-baselines/phases/refresh-ack.js +28 -136
  273. package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +3 -13
  274. package/.agents/scripts/lib/orchestration/check-state.js +92 -0
  275. package/.agents/scripts/lib/orchestration/ci-gap-intake.js +37 -144
  276. package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +42 -189
  277. package/.agents/scripts/lib/orchestration/code-review.js +24 -128
  278. package/.agents/scripts/lib/orchestration/column-sync.js +23 -111
  279. package/.agents/scripts/lib/orchestration/complexity-gate.js +42 -214
  280. package/.agents/scripts/lib/orchestration/deliver-recover.js +36 -168
  281. package/.agents/scripts/lib/orchestration/dependency-analyzer.js +4 -35
  282. package/.agents/scripts/lib/orchestration/dependency-candidates.js +9 -41
  283. package/.agents/scripts/lib/orchestration/diff-magnitude.js +25 -115
  284. package/.agents/scripts/lib/orchestration/doc-reader.js +2 -6
  285. package/.agents/scripts/lib/orchestration/docs-digest.js +13 -51
  286. package/.agents/scripts/lib/orchestration/epic-candidates.js +13 -53
  287. package/.agents/scripts/lib/orchestration/epic-checklist.js +9 -33
  288. package/.agents/scripts/lib/orchestration/epic-container.js +41 -167
  289. package/.agents/scripts/lib/orchestration/epic-expansion.js +10 -40
  290. package/.agents/scripts/lib/orchestration/epic-rollup.js +55 -207
  291. package/.agents/scripts/lib/orchestration/file-assumption-enum.js +2 -22
  292. package/.agents/scripts/lib/orchestration/file-assumptions.js +45 -253
  293. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-detect.js +9 -46
  294. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +7 -57
  295. package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +21 -126
  296. package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +2 -6
  297. package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +1 -4
  298. package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +1 -5
  299. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +9 -34
  300. package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +41 -190
  301. package/.agents/scripts/lib/orchestration/git-cleanup/phases/merged-tip.js +9 -45
  302. package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +1 -4
  303. package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +19 -85
  304. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +3 -17
  305. package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +1 -5
  306. package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +16 -100
  307. package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +1 -4
  308. package/.agents/scripts/lib/orchestration/lease-guard-shared.js +16 -67
  309. package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +9 -33
  310. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +8 -29
  311. package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +15 -68
  312. package/.agents/scripts/lib/orchestration/light-backstop.js +11 -49
  313. package/.agents/scripts/lib/orchestration/light-escalation.js +21 -89
  314. package/.agents/scripts/lib/orchestration/light-suitability.js +26 -231
  315. package/.agents/scripts/lib/orchestration/merge-block-class.js +39 -216
  316. package/.agents/scripts/lib/orchestration/merge-poll.js +104 -377
  317. package/.agents/scripts/lib/orchestration/merge-queue.js +158 -0
  318. package/.agents/scripts/lib/orchestration/pinned-identifier-lint.js +17 -57
  319. package/.agents/scripts/lib/orchestration/plan-context.js +52 -303
  320. package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +22 -79
  321. package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +5 -31
  322. package/.agents/scripts/lib/orchestration/plan-metrics.js +29 -108
  323. package/.agents/scripts/lib/orchestration/plan-navigation.js +8 -27
  324. package/.agents/scripts/lib/orchestration/plan-persist/acceptance-handle-repair.js +12 -49
  325. package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +18 -73
  326. package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +14 -78
  327. package/.agents/scripts/lib/orchestration/plan-persist/cross-plan-links.js +6 -30
  328. package/.agents/scripts/lib/orchestration/plan-persist/epic-adoption.js +14 -65
  329. package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +21 -76
  330. package/.agents/scripts/lib/orchestration/plan-persist/external-deps.js +6 -43
  331. package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +14 -68
  332. package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +9 -41
  333. package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +49 -248
  334. package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +2 -12
  335. package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +85 -368
  336. package/.agents/scripts/lib/orchestration/plan-persist/summary.js +8 -38
  337. package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +30 -190
  338. package/.agents/scripts/lib/orchestration/plan-persist/wave-collision-gate.js +9 -49
  339. package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +7 -28
  340. package/.agents/scripts/lib/orchestration/plan-reachability.js +13 -42
  341. package/.agents/scripts/lib/orchestration/plan-run-labels/reap.js +16 -91
  342. package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +16 -54
  343. package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +10 -52
  344. package/.agents/scripts/lib/orchestration/planning/authoring-context.js +14 -109
  345. package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +11 -83
  346. package/.agents/scripts/lib/orchestration/pr-watch.js +48 -306
  347. package/.agents/scripts/lib/orchestration/project-meta-cache.js +15 -80
  348. package/.agents/scripts/lib/orchestration/project-meta-resolver.js +10 -51
  349. package/.agents/scripts/lib/orchestration/reassert-status-column.js +12 -76
  350. package/.agents/scripts/lib/orchestration/remote-verifier.js +12 -36
  351. package/.agents/scripts/lib/orchestration/resolve-stories.js +33 -164
  352. package/.agents/scripts/lib/orchestration/retro-proposals.js +66 -361
  353. package/.agents/scripts/lib/orchestration/review-base-ref.js +9 -43
  354. package/.agents/scripts/lib/orchestration/review-depth.js +12 -94
  355. package/.agents/scripts/lib/orchestration/review-providers/codex.js +10 -111
  356. package/.agents/scripts/lib/orchestration/review-providers/degraded-gates.js +13 -71
  357. package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +7 -51
  358. package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +8 -53
  359. package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -214
  360. package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +7 -52
  361. package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +4 -33
  362. package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +11 -71
  363. package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +19 -131
  364. package/.agents/scripts/lib/orchestration/review-providers/security-review.js +6 -89
  365. package/.agents/scripts/lib/orchestration/review-providers/types.js +26 -56
  366. package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +3 -46
  367. package/.agents/scripts/lib/orchestration/run-epilogue.js +40 -199
  368. package/.agents/scripts/lib/orchestration/run-scoped-config.js +13 -81
  369. package/.agents/scripts/lib/orchestration/single-story-close/close-note.js +7 -34
  370. package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +16 -89
  371. package/.agents/scripts/lib/orchestration/single-story-close/gate-log.js +18 -109
  372. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +190 -280
  373. package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +18 -80
  374. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +103 -97
  375. package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -64
  376. package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +183 -485
  377. package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +30 -134
  378. package/.agents/scripts/lib/orchestration/single-story-close/phases/graphql-preflight.js +9 -50
  379. package/.agents/scripts/lib/orchestration/single-story-close/phases/lock-wait-pending.js +49 -0
  380. package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +13 -86
  381. package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +20 -95
  382. package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +46 -203
  383. package/.agents/scripts/lib/orchestration/single-story-close/phases/pre-gate-steps.js +8 -50
  384. package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +12 -83
  385. package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -39
  386. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -6
  387. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-outcome.js +4 -28
  388. package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +6 -51
  389. package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +6 -34
  390. package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +23 -121
  391. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +110 -290
  392. package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +15 -69
  393. package/.agents/scripts/lib/orchestration/story-body-gate.js +4 -23
  394. package/.agents/scripts/lib/orchestration/story-close/baseline-upward-writeback.js +50 -179
  395. package/.agents/scripts/lib/orchestration/story-close/context-budget-writeback.js +11 -41
  396. package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +23 -311
  397. package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +22 -147
  398. package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +16 -67
  399. package/.agents/scripts/lib/orchestration/story-deliver-terminal-schema.js +12 -68
  400. package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +42 -201
  401. package/.agents/scripts/lib/orchestration/story-follow-ups.js +43 -245
  402. package/.agents/scripts/lib/orchestration/story-init-envelope.js +4 -23
  403. package/.agents/scripts/lib/orchestration/story-init-remote.js +2 -6
  404. package/.agents/scripts/lib/orchestration/story-reachability.js +3 -23
  405. package/.agents/scripts/lib/orchestration/task-body-validator.js +12 -126
  406. package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -131
  407. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +15 -149
  408. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +5 -33
  409. package/.agents/scripts/lib/orchestration/ticket-validator.js +39 -181
  410. package/.agents/scripts/lib/orchestration/ticketing/bulk.js +51 -244
  411. package/.agents/scripts/lib/orchestration/ticketing/reads.js +39 -250
  412. package/.agents/scripts/lib/orchestration/ticketing/state.js +14 -68
  413. package/.agents/scripts/lib/orchestration/ticketing/transition.js +46 -263
  414. package/.agents/scripts/lib/orchestration/ticketing.js +2 -26
  415. package/.agents/scripts/lib/orchestration/verify-credit.js +18 -79
  416. package/.agents/scripts/lib/orchestration/worktree-dirty.js +5 -30
  417. package/.agents/scripts/lib/path-security.js +3 -6
  418. package/.agents/scripts/lib/plan-phase-cleanup.js +9 -49
  419. package/.agents/scripts/lib/preflight-runner.js +13 -69
  420. package/.agents/scripts/lib/process-group.js +143 -0
  421. package/.agents/scripts/lib/project-root.js +2 -8
  422. package/.agents/scripts/lib/provider-factory.js +2 -25
  423. package/.agents/scripts/lib/qa/console-allowlist.js +10 -59
  424. package/.agents/scripts/lib/qa/qa-session.js +15 -69
  425. package/.agents/scripts/lib/qa/redact-evidence.js +18 -129
  426. package/.agents/scripts/lib/qa/resolve-qa-contract.js +26 -135
  427. package/.agents/scripts/lib/qa/resolve-selection.js +16 -84
  428. package/.agents/scripts/lib/reserved-test-ids.js +9 -47
  429. package/.agents/scripts/lib/runtime-deps/dep-resolution.js +13 -53
  430. package/.agents/scripts/lib/runtime-deps/ensure-installed.js +10 -44
  431. package/.agents/scripts/lib/runtime-deps/manifest.js +10 -30
  432. package/.agents/scripts/lib/runtime-deps/parser-major.js +15 -51
  433. package/.agents/scripts/lib/runtime-deps/preflight.js +4 -22
  434. package/.agents/scripts/lib/runtime-deps/scan-imports.js +11 -55
  435. package/.agents/scripts/lib/signals/detectors/common.js +12 -38
  436. package/.agents/scripts/lib/signals/index.js +2 -19
  437. package/.agents/scripts/lib/signals/schema.js +14 -109
  438. package/.agents/scripts/lib/signals/write.js +2 -11
  439. package/.agents/scripts/lib/single-story/confirm-merge.js +31 -72
  440. package/.agents/scripts/lib/single-story/story-merged-notify.js +8 -49
  441. package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +4 -26
  442. package/.agents/scripts/lib/single-story-sweep/protection.js +13 -100
  443. package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +60 -258
  444. package/.agents/scripts/lib/single-story-sweep.js +14 -97
  445. package/.agents/scripts/lib/skills/parse-skill.js +13 -59
  446. package/.agents/scripts/lib/skills/skills-index.js +8 -29
  447. package/.agents/scripts/lib/skills/walk-skill-files.js +17 -78
  448. package/.agents/scripts/lib/source-extensions.js +11 -42
  449. package/.agents/scripts/lib/source-text/strip-js-comments.js +8 -41
  450. package/.agents/scripts/lib/stdio-flush.js +9 -36
  451. package/.agents/scripts/lib/story-adjacency.js +8 -40
  452. package/.agents/scripts/lib/story-body/body-format-lints.js +22 -83
  453. package/.agents/scripts/lib/story-body/footer-block.js +11 -44
  454. package/.agents/scripts/lib/story-body/story-body.js +83 -358
  455. package/.agents/scripts/lib/temp-retention.js +42 -143
  456. package/.agents/scripts/lib/templates/decomposer-prompts.js +11 -65
  457. package/.agents/scripts/lib/test-env.js +12 -65
  458. package/.agents/scripts/lib/test-run-credit.js +10 -65
  459. package/.agents/scripts/lib/test-runner-contract.js +19 -74
  460. package/.agents/scripts/lib/test-temp.js +38 -184
  461. package/.agents/scripts/lib/test-tiers.js +12 -96
  462. package/.agents/scripts/lib/ticket-body-sections.js +21 -96
  463. package/.agents/scripts/lib/transpile.js +15 -74
  464. package/.agents/scripts/lib/util/concurrent-map.js +6 -25
  465. package/.agents/scripts/lib/util/parse-id-list.js +8 -34
  466. package/.agents/scripts/lib/util/poll-loop.js +8 -28
  467. package/.agents/scripts/lib/util/with-timeout.js +2 -11
  468. package/.agents/scripts/lib/validation-evidence.js +21 -96
  469. package/.agents/scripts/lib/wave-runner/footprint.js +17 -70
  470. package/.agents/scripts/lib/wave-runner/live-probe.js +53 -221
  471. package/.agents/scripts/lib/wave-runner/ready-set.js +72 -301
  472. package/.agents/scripts/lib/workers/crap-worker.js +11 -64
  473. package/.agents/scripts/lib/workers/maintainability-report-worker.js +8 -45
  474. package/.agents/scripts/lib/workers/maintainability-worker.js +5 -21
  475. package/.agents/scripts/lib/workers/serve-worker-messages.js +2 -18
  476. package/.agents/scripts/lib/workflow-closure.js +22 -105
  477. package/.agents/scripts/lib/workspace-provisioner.js +16 -54
  478. package/.agents/scripts/lib/worktree/git-hooks.js +15 -60
  479. package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +20 -60
  480. package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +10 -62
  481. package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +22 -86
  482. package/.agents/scripts/lib/worktree/lifecycle/reap.js +24 -126
  483. package/.agents/scripts/lib/worktree/lifecycle-manager.js +2 -20
  484. package/.agents/scripts/lib/worktree/node-modules-strategy.js +46 -180
  485. package/.agents/scripts/lib/worktree-manager.js +15 -40
  486. package/.agents/scripts/lint-issue-body.js +12 -68
  487. package/.agents/scripts/mandrel-update-preflight.js +10 -70
  488. package/.agents/scripts/merge-baseline.js +30 -104
  489. package/.agents/scripts/nav-registry-diff.js +22 -108
  490. package/.agents/scripts/notify.js +12 -69
  491. package/.agents/scripts/plan-context.js +27 -111
  492. package/.agents/scripts/plan-critics.js +12 -66
  493. package/.agents/scripts/plan-persist.js +31 -148
  494. package/.agents/scripts/plan-run-epilogue.js +11 -37
  495. package/.agents/scripts/pr-watch-with-update.js +76 -267
  496. package/.agents/scripts/providers/github/auth.js +3 -8
  497. package/.agents/scripts/providers/github/blocked-by-add.js +14 -61
  498. package/.agents/scripts/providers/github/board-add.js +5 -21
  499. package/.agents/scripts/providers/github/branch-protection.js +11 -43
  500. package/.agents/scripts/providers/github/cache.js +2 -14
  501. package/.agents/scripts/providers/github/comments.js +7 -40
  502. package/.agents/scripts/providers/github/compose.js +3 -19
  503. package/.agents/scripts/providers/github/errors.js +22 -125
  504. package/.agents/scripts/providers/github/issues.js +31 -123
  505. package/.agents/scripts/providers/github/labels.js +24 -124
  506. package/.agents/scripts/providers/github/mappers.js +5 -27
  507. package/.agents/scripts/providers/github/merge-methods.js +3 -22
  508. package/.agents/scripts/providers/github/project-board.js +2 -17
  509. package/.agents/scripts/providers/github/projects-v2-graphql.js +3 -6
  510. package/.agents/scripts/providers/github/request-helpers.js +5 -32
  511. package/.agents/scripts/providers/github/search-budget.js +9 -39
  512. package/.agents/scripts/providers/github/search-query.js +5 -26
  513. package/.agents/scripts/providers/github/sub-issue-add.js +15 -65
  514. package/.agents/scripts/providers/github/sub-issues.js +7 -36
  515. package/.agents/scripts/providers/github/tickets.js +32 -134
  516. package/.agents/scripts/providers/github.js +18 -59
  517. package/.agents/scripts/prune-plan-run-labels.js +12 -46
  518. package/.agents/scripts/quality-preview.js +35 -220
  519. package/.agents/scripts/resolve-doc-tiers.js +3 -26
  520. package/.agents/scripts/resolve-stories.js +16 -67
  521. package/.agents/scripts/resync-status-column.js +6 -25
  522. package/.agents/scripts/run-tests.js +29 -104
  523. package/.agents/scripts/single-story-close.js +23 -135
  524. package/.agents/scripts/single-story-confirm-merge.js +32 -139
  525. package/.agents/scripts/single-story-init.js +47 -209
  526. package/.agents/scripts/stories-wave-tick.js +102 -472
  527. package/.agents/scripts/sync-agentrc.js +3 -21
  528. package/.agents/scripts/sync-claude-agents.js +9 -55
  529. package/.agents/scripts/sync-claude-commands.js +20 -110
  530. package/.agents/scripts/test-wrapper.js +6 -43
  531. package/.agents/scripts/update-coverage-baseline.js +5 -22
  532. package/.agents/scripts/update-crap-baseline.js +7 -30
  533. package/.agents/scripts/update-duplication-baseline.js +16 -92
  534. package/.agents/scripts/update-maintainability-baseline.js +8 -62
  535. package/.agents/scripts/update-ticket-state.js +1 -7
  536. package/.agents/scripts/validate-skills.js +6 -26
  537. package/.agents/templates/agent-protocol.md +2 -2
  538. package/.agents/templates/docs/audit-sweep-runbook.md +3 -4
  539. package/.agents/workflows/audit-accessibility.md +4 -7
  540. package/.agents/workflows/audit-adrs.md +3 -6
  541. package/.agents/workflows/audit-architecture.md +3 -11
  542. package/.agents/workflows/audit-baselines.md +6 -9
  543. package/.agents/workflows/audit-clean-code.md +8 -12
  544. package/.agents/workflows/audit-data-model.md +3 -7
  545. package/.agents/workflows/audit-dependencies.md +4 -7
  546. package/.agents/workflows/audit-devops.md +4 -8
  547. package/.agents/workflows/audit-documentation.md +1 -4
  548. package/.agents/workflows/audit-mobile.md +4 -7
  549. package/.agents/workflows/audit-navigability.md +3 -6
  550. package/.agents/workflows/audit-performance.md +2 -4
  551. package/.agents/workflows/audit-privacy.md +4 -7
  552. package/.agents/workflows/audit-quality.md +4 -7
  553. package/.agents/workflows/audit-security.md +4 -7
  554. package/.agents/workflows/audit-seo.md +4 -7
  555. package/.agents/workflows/audit-sre.md +4 -7
  556. package/.agents/workflows/audit-to-stories.md +17 -30
  557. package/.agents/workflows/audit-ux-ui.md +4 -7
  558. package/.agents/workflows/helpers/audit-lens-core.md +1 -1
  559. package/.agents/workflows/helpers/code-quality-guardrails.md +12 -15
  560. package/.agents/workflows/helpers/deliver-digest.md +6 -4
  561. package/.agents/workflows/helpers/deliver-reference.md +114 -188
  562. package/.agents/workflows/helpers/deliver-story-reference.md +189 -533
  563. package/.agents/workflows/helpers/plan-reference.md +86 -139
  564. package/.agents/workflows/helpers/qa-core.md +12 -0
  565. package/.agents/workflows/memory-consolidate.md +1 -1
  566. package/.agents/workflows/qa-assist.md +27 -71
  567. package/.agents/workflows/qa-explore.md +19 -59
  568. package/.agents/workflows/qa-run.md +11 -33
  569. package/README.md +1 -1
  570. package/bin/mandrel.js +5 -46
  571. package/bin/postinstall.js +18 -107
  572. package/docs/CHANGELOG.md +32 -0
  573. package/lib/cli/doctor.js +11 -71
  574. package/lib/cli/init.js +26 -165
  575. package/lib/cli/migrate.js +5 -64
  576. package/lib/cli/registry.js +84 -467
  577. package/lib/cli/sync-agents.js +4 -64
  578. package/lib/cli/sync-commands.js +11 -64
  579. package/lib/cli/sync.js +34 -213
  580. package/lib/cli/uninstall.js +40 -219
  581. package/lib/cli/update.js +105 -661
  582. package/lib/cli/version-check.js +8 -72
  583. package/lib/cli/version-helpers.js +14 -87
  584. package/lib/migrations/helpers/retire-agentrc-key.js +11 -52
  585. package/lib/migrations/index.js +15 -92
  586. package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +2 -15
  587. package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +1 -13
  588. package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +2 -15
  589. package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +9 -38
  590. package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +1 -13
  591. package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +2 -27
  592. package/lib/migrations/steps/2.57.0-retire-delivery-limit-knobs.js +2 -23
  593. package/lib/migrations/steps/2.57.0-retire-planning-limit-knobs.js +2 -27
  594. package/lib/migrations/steps/2.60.0-retire-audit-results-autofile.js +2 -21
  595. package/lib/migrations/steps/strip-removed-agentrc-keys.js +333 -0
  596. package/package.json +16 -14
  597. package/.agents/docs/SDLC.md +0 -590
  598. package/.agents/docs/quality-gates.md +0 -1183
  599. package/.agents/rules/known-tooling-behavior.md +0 -200
  600. package/.agents/rules/orchestration-error-handling.md +0 -61
  601. package/.agents/rules/test-seams.md +0 -59
  602. package/.agents/schemas/baselines/lighthouse.schema.json +0 -59
  603. package/.agents/schemas/baselines/lint.schema.json +0 -47
  604. package/.agents/scripts/check-action-pinning.js +0 -260
  605. package/.agents/scripts/check-audit-attribution.js +0 -302
  606. package/.agents/scripts/check-baseline-drift.js +0 -211
  607. package/.agents/scripts/check-baseline-scope.js +0 -362
  608. package/.agents/scripts/check-generated-validator.js +0 -202
  609. package/.agents/scripts/check-knip-entries.js +0 -159
  610. package/.agents/scripts/check-lifecycle-lint.js +0 -294
  611. package/.agents/scripts/check-pinned-override-notes.js +0 -102
  612. package/.agents/scripts/check-schema-references.js +0 -368
  613. package/.agents/scripts/check-test-portability.js +0 -512
  614. package/.agents/scripts/check-workflow-citations.js +0 -218
  615. package/.agents/scripts/check-workflow-cli-lint.js +0 -299
  616. package/.agents/scripts/check-workflow-timeouts.js +0 -291
  617. package/.agents/scripts/install-matrix-assert.js +0 -326
  618. package/.agents/scripts/lib/audit-advisories.js +0 -195
  619. package/.agents/scripts/lib/audit-attribution.js +0 -134
  620. package/.agents/scripts/lib/baselines/drift-detector.js +0 -351
  621. package/.agents/scripts/lib/baselines/kinds/lighthouse.js +0 -87
  622. package/.agents/scripts/lib/baselines/kinds/lint.js +0 -184
  623. package/.agents/scripts/lib/baselines/orphan-pruner.js +0 -233
  624. package/.agents/scripts/lib/baselines/scope-assert.js +0 -223
  625. package/.agents/scripts/lib/baselines/scope-inventory.js +0 -314
  626. package/.agents/scripts/lib/c8-cli-path.js +0 -21
  627. package/.agents/scripts/lib/config/gates/lighthouse.schema.js +0 -51
  628. package/.agents/scripts/lib/config/gates/lint.schema.js +0 -18
  629. package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +0 -70
  630. package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +0 -284
  631. package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +0 -80
  632. package/.agents/scripts/lib/dynamic-workflow/degraded-coverage.js +0 -81
  633. package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +0 -87
  634. package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +0 -74
  635. package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +0 -90
  636. package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +0 -43
  637. package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +0 -83
  638. package/.agents/scripts/lib/fs-walk.js +0 -52
  639. package/.agents/scripts/lib/knip-config-resolver.js +0 -181
  640. package/.agents/scripts/lib/knip-entry-sync.js +0 -452
  641. package/.agents/scripts/lib/pinned-override-notes.js +0 -88
  642. package/.agents/scripts/lib/pinned-override-resolve.js +0 -212
  643. package/.agents/scripts/lib/test-isolate/cli-options.js +0 -93
  644. package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +0 -52
  645. package/.agents/scripts/lib/test-isolate/list-files.js +0 -90
  646. package/.agents/scripts/lib/test-isolate/parse-tap.js +0 -75
  647. package/.agents/scripts/lib/test-isolate/progress-log.js +0 -45
  648. package/.agents/scripts/lib/test-isolate/render-report.js +0 -97
  649. package/.agents/scripts/lib/test-isolate/run-isolate.js +0 -87
  650. package/.agents/scripts/lib/test-isolate/runner.js +0 -483
  651. package/.agents/scripts/lib/test-profile/parse-tap.js +0 -136
  652. package/.agents/scripts/lib/test-profile/render-report.js +0 -45
  653. package/.agents/scripts/lint-label-vocabulary.js +0 -214
  654. package/.agents/scripts/post-structured-comment.js +0 -127
  655. package/.agents/scripts/provision-git-hooks.js +0 -85
  656. package/.agents/scripts/prune-baseline-orphans.js +0 -181
  657. package/.agents/scripts/run-coverage.js +0 -197
  658. package/.agents/scripts/run-lint.js +0 -133
  659. package/.agents/scripts/run-test-profile.js +0 -129
  660. package/.agents/scripts/run-verify.js +0 -125
  661. package/.agents/scripts/test-isolate.js +0 -55
  662. package/.agents/scripts/update-dead-exports-baseline.js +0 -321
@@ -1,1183 +0,0 @@
1
- # Quality Gates
2
-
3
- This is the consumer-facing reference for the quality gates the framework
4
- runs against your repo: the lint baseline ratchet, the maintainability
5
- ratchet, the CRAP per-method gate, the **absolute quality floors**
6
- (90/85/90 coverage, MI ≥ 70, CRAP ≤ 20), the anti-thrashing protocol,
7
- and the concurrent close-safety retry that protects Story-branch pushes
8
- when multiple Stories close in quick succession.
9
-
10
- The floor + ratchet duo is intentional: the ratchet protects against
11
- regressions on touched files; the floor enforces an absolute threshold
12
- on every in-scope file regardless of diff scope. See
13
- [§ Absolute quality floors (Epic #1184)](#absolute-quality-floors-epic-1184)
14
- below for the policy and [`docs/decisions.md`](../../docs/decisions.md) (ADR
15
- 20260512-coupling-stance) for the framework-wide stance that motivates
16
- the lift the floor gate represents.
17
-
18
- The configuration knobs that drive these gates live in
19
- [`.agents/docs/configuration.md`](../docs/configuration.md) under
20
- `delivery.quality.*`. This file is the runbook side — what the gate does,
21
- when it fires, and how to bootstrap or refresh it.
22
-
23
- The **baseline envelope, per-kind shapes, component model, writer/reader
24
- contract, and floor-override path** are documented in the
25
- [Baseline reference](#baseline-reference) section at the end of this
26
- document. Each per-gate section below cross-links to that section; consult
27
- it once and reuse the context as you read through any individual gate.
28
-
29
- > **Story-level gates.** Quality gates run against the Story branch
30
- > after the single Story-implementation phase completes. Friction
31
- > comments flip the Story to `agent::blocked` and post on the Story
32
- > ticket.
33
-
34
- ---
35
-
36
- ## Concurrent close safety
37
-
38
- `/mandrel-deliver` may close multiple Stories from separate branches in quick
39
- succession; each rebases onto the latest `main` in its own base-sync phase
40
- (`phases/base-sync.js`) before the push, so concurrent closes serialize
41
- through their own worktrees rather than racing one shared branch. The push
42
- does not retry — a rejected push or a real content conflict fails the close
43
- non-zero and leaves the tree clean for manual resolution. See
44
- [`SDLC.md` § Concurrent close](SDLC.md#concurrent-close).
45
-
46
- ---
47
-
48
- ## Test runner concurrency
49
-
50
- `npm test` (via [`.agents/scripts/run-tests.js`](../scripts/run-tests.js))
51
- derives `--test-concurrency` from `os.availableParallelism()` at startup,
52
- clamped into `[1, 16]` (`resolveTestConcurrency`). The clamp keeps the value
53
- sane at both extremes: on the GitHub Actions 2-vCPU runner the derived value
54
- matches the host, and on very-wide dev hosts the cap of 16 bounds the
55
- filesystem-race surface from shared FS fixtures (`memfs` mounts, `temp/`
56
- snapshot dirs, the `coverage/` artifact directory shared with the CRAP gate).
57
-
58
- The coverage run is the exception: `npm run test:coverage`
59
- ([`.agents/scripts/run-coverage.js`](../scripts/run-coverage.js)) pins
60
- `--test-concurrency=8` so coverage timings stay comparable across hosts. Any
61
- change to the clamp bounds or the coverage pin should be validated on both a
62
- Windows dev host and a GitHub Actions runner to confirm it doesn't reintroduce
63
- concurrency flakes.
64
-
65
- ---
66
-
67
- ## Coverage baseline gate
68
-
69
- > Baseline envelope, axes, and component model: see the
70
- > [Baseline reference](#baseline-reference) section below.
71
-
72
- `npm run test:coverage` drives
73
- [`.agents/scripts/run-coverage.js`](../scripts/run-coverage.js),
74
- which runs the unit-test suite with `NODE_V8_COVERAGE` set, post-processes
75
- the V8 dumps with `c8 report`, then delegates to
76
- [`.agents/scripts/check-baselines.js`](../scripts/check-baselines.js)
77
- for the gate decision. There is no global `lines/branches/functions`
78
- threshold — the gate compares **per-file** coverage in
79
- `coverage/coverage-final.json` against the floors recorded in
80
- [`baselines/coverage.json`](../../baselines/coverage.json) and fails on:
81
-
82
- - a regression on any axis (lines, branches, or functions) for any file
83
- whose coverage dropped more than `0.01` percentage points below its
84
- recorded floor;
85
- - an in-scope file with no baseline entry (a brand-new untested CLI
86
- shell would otherwise sail through with 0 % coverage and no recorded
87
- floor to drop below).
88
-
89
- Scope (include/exclude) and reporters are declared in
90
- [`.c8rc.cjs`](../../.c8rc.cjs); the gate reads the same file so `c8 report`
91
- and the per-file checker agree on what's in scope. Bootstrap or
92
- ratchet the baseline when an intentional scope change shifts coverage:
93
-
94
- ```bash
95
- npm run test:coverage # produces coverage/coverage-final.json (gate
96
- # warns + passes when no baseline exists yet)
97
- npm run coverage:update # writes baselines/coverage.json from the run
98
- ```
99
-
100
- `npm run coverage:check` runs the gate standalone against an existing
101
- `coverage-final.json` artifact (useful from CI hooks or close-validation
102
- runners that orchestrate coverage capture separately).
103
-
104
- The files-out-of-scope list is declared in [`.c8rc.cjs`](../../.c8rc.cjs) —
105
- thin CLI shells plus the larger Story #1702 carve-out of
106
- top-level/orchestration/git CLIs and `lib/*` glue. The `exclude[]` array is
107
- the **single** declaration: each entry carries its rationale as an inline
108
- comment on the line above it. Story #4922 removed the prose inventory the
109
- header used to duplicate — two copies of one list in one file, 27 files
110
- apart by the time it was measured. Do not reintroduce one. Every excluded
111
- file also carries `/* node:coverage ignore file */` at the top of its source
112
- as a second line of defence.
113
-
114
- `.c8rc.cjs`'s `include` globs and `delivery.quality.gates.coverage.targetDirs`
115
- in [`.agentrc.json`](../../.agentrc.json) MUST name the same roots — the gate
116
- scores what c8 measures. `tests/c8rc-scope.test.js` asserts both invariants.
117
-
118
- ---
119
-
120
- ## Absolute quality floors (Epic #1184)
121
-
122
- The per-file ratchet only protects against **regressions** — if a file
123
- has been sitting at 60 % coverage or MI = 58 since the v5 baseline, the
124
- ratchet is perfectly happy to keep it there forever. Epic #1184 layers
125
- an absolute-threshold gate on top of the ratchet that fails the build
126
- when any in-scope file is below floor, regardless of whether the diff
127
- touched it:
128
-
129
- | Metric | Floor | Scope |
130
- | --- | --- | --- |
131
- | Coverage — lines | ≥ 94 % | repo rollup |
132
- | Coverage — branches | ≥ 85 % | repo rollup |
133
- | Coverage — functions | ≥ 87 % | repo rollup |
134
- | Maintainability Index | ≥ 70 | repo rollup |
135
- | CRAP — methods above 20 | ≤ 13 | repo rollup |
136
-
137
- Floors are enforced against the baseline's `rollup` components — the
138
- `applyFloors` phase compares `rollup["*"]` (and any named component), never
139
- individual rows. Story #4922 corrected this table, which previously read
140
- "per file" and quoted 90/85/90 for coverage; those numbers came from the
141
- example in `.agents/docs/agentrc-reference.json`, which is validated only
142
- against itself, and the coverage gate was not configured at all.
143
-
144
- The live coverage floors are derived from the measurement in
145
- [`baselines/coverage.json`](../../baselines/coverage.json) — a full-tier run
146
- scored 95.65 / 86.16 / 88.52, and each floor sits ~1–1.7 points under its
147
- axis. Re-derive them, do not invent them, whenever the baseline is
148
- regenerated wholesale.
149
-
150
- The coverage gate deliberately declares **no `tolerance`**, so its
151
- head-vs-base ratchet arm reports regressions without failing the build (the
152
- same shape the `crap` gate uses). Story #4922's scope was making the
153
- instrument honest; arming the ratchet belongs with the debt burn-down that
154
- the widened measurement newly exposes.
155
-
156
- The floors are declared in [`.agentrc.json`](../../.agentrc.json) under
157
- `delivery.quality.gates.<gate>.floors.*` (defaults baked into the helper
158
- match the table above) and resolved at runtime by the shared
159
- helper [`lib/orchestration/check-baselines/phases/floors.js`](../scripts/lib/orchestration/check-baselines/phases/floors.js).
160
- All three gates run through `check-baselines.js` (coverage,
161
- maintainability, crap), which invokes the floors phase **after** the
162
- ratchet decision so a file that's below floor but matched the (stale)
163
- baseline still trips the gate.
164
-
165
- ### When the floor gate fires
166
-
167
- - **Pre-push** (`.husky/pre-push`): diff-scoped, fast path only —
168
- `coverage-capture.js` first, then
169
- `quality-preview.js --changed-since origin/main` (MI + CRAP preview)
170
- and `npm run crap:check` (unified dispatcher, diff-scoped via
171
- `delivery.quality.gateScoping`). Capture leads because the preview's
172
- CRAP half scores `coverage/coverage-final.json` off disk, so previewing
173
- first scores whatever artifact an earlier run happened to leave there
174
- (Story #5356). Full-repo
175
- lint, docs generation checks, and the complete test suite are **not**
176
- run on push; use `npm run verify` locally before a PR. CI enforces the
177
- authoritative full gate set on every PR.
178
- - **CI** (`.github/workflows/ci.yml`): the `validate` job runs
179
- **Lint and Format** (`npm run lint`) and **Run Tests with Coverage**
180
- (`npm run test:coverage`), uploading the `test-results` and
181
- `coverage-final` artifacts. A separate required **baselines** job runs
182
- the unified `node .agents/scripts/check-baselines.js --format text`,
183
- which enforces floors across every configured gate and is the only
184
- baseline gate on the per-change path. (Story #5004 removed a
185
- `Maintainability Check` step from `validate` that re-ran
186
- `check-baselines.js --gate maintainability` at the same scope; a later
187
- correction pass revisited its record of what the step's
188
- `BASELINE_SCOPE=full` branch did — see `docs/ci-contract.md`.)
189
- - **Nightly** (`.github/workflows/baseline-drift.yml`): the only
190
- automated **full-scope re-score**. See
191
- [`check-baseline-drift.js`](#check-baseline-driftjs--the-scheduled-full-scope-re-score)
192
- below.
193
-
194
- ### Opt-out
195
-
196
- There is no floor opt-out flag on the check path. The `*:update`
197
- baseline-snap scripts snapshot whatever the current numbers are without
198
- floor enforcement **by construction** — they are writers, not gates —
199
- so no disable switch exists or is needed (the floors phase at
200
- [`lib/orchestration/check-baselines/phases/floors.js`](../scripts/lib/orchestration/check-baselines/phases/floors.js)
201
- has no off switch).
202
-
203
- ### No silent excludes (`.c8rc.cjs` policy)
204
-
205
- The floor gate is only as strict as its scope, so the `exclude` list in
206
- [`.c8rc.cjs`](../../.c8rc.cjs) carries three hard requirements that are
207
- enforced by review (and partially by the audit suite):
208
-
209
- 1. **One-line rationale per entry.** Every file in `exclude[]` MUST carry
210
- an inline comment on the line(s) directly above it naming *why* it is
211
- excluded — typically "thin CLI shell, meaningful logic lives in
212
- `lib/<X>` and is unit-tested there." A bare path with no rationale is a
213
- review-block, and `tests/c8rc-scope.test.js` fails on one.
214
- 2. **`/* node:coverage ignore file */` pragma at source.** Every
215
- excluded file MUST carry the Node coverage pragma at the top of its
216
- own source. This is the second line of defence: when `c8 report` and
217
- the baseline checker disagree about scope (different cwd, different
218
- glob expansion, partial install), the pragma keeps the file out of
219
- the gate's numerator from the inside.
220
- 3. **Excluded file's callees clear the floor.** A CLI shell is only a
221
- legitimate exclude if the `lib/` module it wraps actually clears the
222
- floor (coverage 90/85/90, MI ≥ 70, CRAP ≤ 20). Excluding a shell
223
- that delegates to under-tested helpers re-introduces the very
224
- risk the floor gate exists to surface; the audit suite spot-checks
225
- the callee map at exclude-list churn time.
226
-
227
- ---
228
-
229
- ## Anti-thrashing protocol
230
-
231
- The qualitative anti-thrashing cues are owned by
232
- [`.agents/instructions.md`](../instructions.md) § 1.I. When they trip, the
233
- friction logger flips the Story to `agent::blocked` and posts a structured
234
- `friction` comment on the Story so the operator has the trace.
235
-
236
- ---
237
-
238
- ## Per-Story acceptance self-eval gate
239
-
240
- After a Story's implementation commits land and **before** it proceeds to
241
- close, delivery runs a bounded acceptance self-eval loop: a fresh-context
242
- critic scores the caller-injected change set against every inline
243
- `acceptance[]` item (using `verify[]` output as evidence) and yields
244
- **proceed** / **redraft** / **block**. This gate is complementary to the
245
- close-validation chain above — that chain proves the code is *healthy*, this
246
- loop proves it satisfies *this Story's* acceptance criteria. The per-round
247
- mechanic is owned by
248
- [`helpers/acceptance-self-eval`](../workflows/helpers/acceptance-self-eval.md)
249
- (Step 1a of [`helpers/deliver-story`](../workflows/helpers/deliver-story.md));
250
- the `delivery.acceptanceEval` field reference is in
251
- [`configuration.md`](../docs/configuration.md).
252
-
253
- ---
254
-
255
- ## Lint baseline ratchet
256
-
257
- > Baseline envelope, axes, and component model: see the
258
- > [Baseline reference](#baseline-reference) section below.
259
-
260
- The `lint` baseline kind enforces zero-deterioration during Story
261
- delivery: `check-baselines.js --gate lint` fails if new lint warnings are
262
- introduced, and the baseline tightens when the codebase improves.
263
-
264
- The canonical baseline file lives at `baselines/lint.json` (override via
265
- `delivery.quality.gates.lint.baselinePath`).
266
-
267
- **There is no framework capture CLI.** Story #5004 retired the
268
- `lint-baseline.js` shell that used to write this file: it spawned a
269
- configured lint command and parsed the linter's JSON, a shape only
270
- ESLint-style output satisfies, and this repo's own `npm run lint`
271
- (Biome + markdownlint fan-out) never produced it, so the gate was
272
- configured-but-unfed. A consumer that wants the kind writes
273
- `baselines/lint.json` from its own linter in the envelope shape documented
274
- under [Baseline reference](#baseline-reference); a consumer that does not is
275
- unaffected, because an absent baseline leaves the gate unconfigured.
276
-
277
- > **Upgrading?** The `project.commands.lintBaseline` key that fed the retired
278
- > shell is gone from the config schema, which is `additionalProperties: false`
279
- > — a `.agentrc.json` still carrying it now **fails validation** rather than
280
- > being silently ignored. Delete the key.
281
-
282
- Refresh commits should use a `baseline-refresh:` subject + non-empty body so
283
- the operator can spot baseline edits in review — same convention as the CRAP
284
- and maintainability ratchets. There is no CI guardrail enforcing the
285
- convention; the operator is the gate.
286
-
287
- ---
288
-
289
- ## Maintainability ratchet
290
-
291
- > Baseline envelope, axes, and component model: see the
292
- > [Baseline reference](#baseline-reference) section below.
293
-
294
- A per-file maintainability scoring engine computes composite scores based
295
- on cyclomatic complexity, file length, and dependency counts. The
296
- `baselines/maintainability.json` baseline prevents score degradation
297
- between Stories.
298
-
299
- Refresh with `npm run maintainability:update`.
300
-
301
- `delivery.quality.gates.maintainability.targetDirs` controls the scanned
302
- directories (see [`configuration.md`](../docs/configuration.md) for the
303
- default and the deep-merge extender form).
304
-
305
- ---
306
-
307
- ## Cyclomatic ceiling ratchet
308
-
309
- A fixed per-function complexity ceiling of `12` is enforced by
310
- `check-cyclomatic.js` (`lib/cyclomatic-ceiling.js#CYCLOMATIC_CEILING`; the
311
- `cyclomaticMustFix` config key was retired in Story #5313). It is a
312
- **standalone ratchet** — the same slot as `check-arch-cycles.js`,
313
- `check-dead-exports.js`, and `check-context-budget.js` — not a
314
- `delivery.quality.gates` kind, so it needs no gate block and no floor.
315
-
316
- ```bash
317
- node .agents/scripts/check-cyclomatic.js # the gate
318
- node .agents/scripts/check-cyclomatic.js --update # re-record the breaches
319
- ```
320
-
321
- `baselines/cyclomatic.json` records, per file, how many functions currently
322
- sit above the ceiling and how bad the worst one is. The gate fails when a
323
- file's over-ceiling count rises (including `0 → 1`, a brand-new breach) or
324
- when its worst function gets worse than recorded. Shrinking and disappearing
325
- are the success signals and never fail.
326
-
327
- Recording existing breaches is what makes the ceiling adoptable: a repository
328
- with dozens of over-ceiling functions can turn the gate on today and burn them
329
- down on its own schedule, instead of disabling a gate that fails on the first
330
- commit. Re-run `--update` after a deliberate refactor; that is the only motion
331
- allowed to raise a recorded count, and it shows up in review as a baseline
332
- diff.
333
-
334
- The scan reuses `delivery.quality.gates.maintainability.targetDirs` /
335
- `ignoreGlobs` — both instruments read the same coverage-free escomplex
336
- surface, so a separate scope declaration could only ever restate it.
337
-
338
- `cyclomaticFlag` (default `8`) is the one advisory knob: it is not gated, and
339
- names the ceiling `quality:preview` counts new methods against in its
340
- `new-method count over c=<flag>` column. The preview also lists every scanned
341
- method at cyclomatic 12 or above as an advisory and exits 0 on it.
342
-
343
- ---
344
-
345
- ## Gherkin corpus gate (opt-in)
346
-
347
- `check-gherkin-corpus.js` is a static gate over a project's `.feature` corpus.
348
- It runs inside `npm run lint` — the same required check as the arch-cycle
349
- ratchet — and it enforces two things:
350
-
351
- - **must-compile.** Every in-scope `.feature` is parsed with the real
352
- `@cucumber/gherkin` parser and a failure is reported at `file:line:column`.
353
- Re-implementing acceptance is the defect the gate exists to prevent: a
354
- hand-rolled reader skips what it does not recognise, so a corpus that cannot
355
- generate reads clean.
356
- - **must-bind.** Every active scenario's steps are resolved against the step
357
- definitions of **its own scope only**. A file that fails must-compile is
358
- excluded from must-bind — a broken file parses as an arbitrary subset of
359
- itself, and linting the remainder buries the one actionable finding.
360
-
361
- The gate is **opt-in**: with no `qa.gherkinLint` block in `.agentrc.json` it
362
- reports that it is not configured and exits 0, even when `.feature` files
363
- exist on disk. An upgrade must never redden the lint of a corpus the consumer
364
- never asked the framework to police. This repository does not configure it.
365
-
366
- ```jsonc
367
- "qa": {
368
- "gherkinLint": {
369
- "scopes": {
370
- "web": {
371
- "featureRoots": ["apps/web/tests/features"],
372
- "stepRoots": ["apps/web/tests/steps"]
373
- }
374
- },
375
- "exemptionTags": ["@skip"],
376
- "stepWaivers": []
377
- }
378
- }
379
- ```
380
-
381
- Inside the opt-in the gate fails **closed**. An unresolvable
382
- `@cucumber/gherkin`, or a scope resolving zero step definitions, exits 1
383
- naming the cause and the remedy — reporting every step as unbound would be the
384
- same blackout in a different costume. The parser is an optional peer
385
- dependency resolved from the consumer project's own module chain, so a
386
- consumer with no BDD tier gains nothing; install it with
387
- `npm install --save-dev @cucumber/gherkin` when enabling the gate.
388
-
389
- Two escapes exist because the step index is a source scan (heuristic) while
390
- the parser is exact: `exemptionTags` (default `["@skip"]`) drops a scenario
391
- from must-bind, and `stepWaivers` drops one exact step text. Neither is an
392
- escape from must-compile — a parse error in an exempt scenario's file still
393
- fails the run.
394
-
395
- ---
396
-
397
- ## CRAP gate — Consumer onboarding
398
-
399
- > Baseline envelope, axes, and component model: see the
400
- > [Baseline reference](#baseline-reference) section below.
401
-
402
- A sibling per-method gate alongside the maintainability ratchet. CRAP
403
- scores each JavaScript method via `c² · (1 − cov)³ + c`, combining
404
- kernel-derived cyclomatic complexity with per-method coverage from
405
- the `coverage/coverage-final.json` artifact your test runner already
406
- produces. No new runtime dependencies. Runs at three sites:
407
- `close-validation` (story close), `ci.yml` (push + PR), and
408
- `.husky/pre-push`.
409
-
410
- If you're a consumer repo that installed the framework via the
411
- `mandrel` npm package (`mandrel sync`), this is what you need to know.
412
-
413
- ### First-run behavior — bootstrap before the first push
414
-
415
- As of Story #791 the gate is hard-enforcing across all three firing sites
416
- (close-validation, pre-push, CI). With `crap.enabled: true` and no
417
- `baselines/crap.json` on disk, the CRAP gate (`npm run crap:check`)
418
- prints:
419
-
420
- ```text
421
- [CRAP] ❌ no baseline found — run the matching baseline-update command and commit with a 'baseline-refresh:' subject to bootstrap
422
- ```
423
-
424
- …and exits `1`. Bootstrap explicitly: run `npm run test:coverage` to
425
- produce `coverage/coverage-final.json`, then `npm run crap:update` to
426
- generate `baselines/crap.json`, and commit the file with a
427
- `baseline-refresh:` tagged subject + non-empty body so the
428
- refresh-guardrail accepts it on the next PR.
429
-
430
- If your test runner doesn't produce per-method coverage, see "Disabling the
431
- gate" below.
432
-
433
- ### Coverage freshness — what triggers a capture
434
-
435
- The CRAP scorer treats "no coverage" as "skip the method", so a missing or
436
- stale `coverage/coverage-final.json` silently weakens the gate.
437
- `coverage-capture.js` closes that hole by capturing coverage in-band, and
438
- decides whether it needs to by two rules (Story #5076):
439
-
440
- - **The source set is derived, not configured.** Freshness is measured over
441
- exactly the extensions the CRAP scanner walks — `.js`, `.mjs`, `.cjs`,
442
- `.ts`, `.tsx`, `.mts`, `.cts` — defined once in
443
- `.agents/scripts/lib/source-extensions.js`. There is deliberately no
444
- `.agentrc.json` key for this: a consumer-settable list would be a second
445
- way to mis-scope the same gate. Formats the engines cannot parse
446
- (`.astro`, `.vue`, `.svelte`) are not part of it — a project written in
447
- those still has its `.ts`/`.tsx` measured.
448
- - **Both freshness paths fail closed on an empty source set.** Finding no
449
- scorable source under `crap.targetDirs` means the check learned nothing,
450
- so it captures rather than assuming coverage is current, and warns naming
451
- the configured dirs. If you see that warning, `targetDirs` almost
452
- certainly does not point at your sources — fix it rather than living with
453
- a full capture on every run.
454
-
455
- **Upgrading from a version before this fix:** a TypeScript project's sources
456
- matched neither path, so the capture was skipped on every run and
457
- `crap:check` compared the committed baseline against itself. The first run
458
- after upgrading captures for real and measures your committed floors for the
459
- first time, which may surface breaches that were always there. That is a
460
- one-off re-baseline (`npm run crap:update`, committed with a
461
- `baseline-refresh:` subject), not a regression.
462
-
463
- ### Disabling the gate (single-flag opt-out)
464
-
465
- If your repo doesn't run coverage, set `enabled: false` in your
466
- `.agentrc.json`:
467
-
468
- ```jsonc
469
- {
470
- "delivery": {
471
- "quality": {
472
- "gates": {
473
- "crap": { "enabled": false }
474
- }
475
- }
476
- }
477
- }
478
- ```
479
-
480
- All three gate sites self-skip with `[CRAP] gate skipped (disabled)` — no
481
- source edits required. The maintainability ratchet keeps running.
482
-
483
- ### Extending `targetDirs` without re-listing framework defaults
484
-
485
- `targetDirs` (like the other list-valued gate keys) accepts the deep-merge
486
- extender form — `{ "append": [...] }` / `{ "prepend": [...] }` add to the
487
- framework default (`["src"]`), while a plain array replaces it entirely. The
488
- worked example and the general rule live once in
489
- [`configuration.md` § How to extend](../docs/configuration.md#how-to-extend).
490
-
491
- ### Interpreting the JSON report
492
-
493
- `npm run crap:check` runs the unified dispatcher
494
- (`check-baselines.js --gate crap`), which emits its structured report on
495
- **stdout** — `--format json` is the default (pass `--format text` for the
496
- human-readable summary). There is no file-writing flag; to capture a file
497
- artifact, redirect:
498
-
499
- ```bash
500
- npm run crap:check > temp/crap-report.json
501
- ```
502
-
503
- CI does **not** upload a `crap-report` artifact — `ci.yml` uploads only
504
- `test-results` (the test/coverage run log) and `coverage-final`
505
- (`coverage/coverage-final.json`).
506
-
507
- The JSON envelope is the unified check-baselines report (see
508
- [`lib/orchestration/check-baselines/phases/report.js`](../scripts/lib/orchestration/check-baselines/phases/report.js)):
509
- top-level totals (`totalBreaches`, `totalRegressions`,
510
- `kernelDriftCount`, `schemaErrors`) plus a `gates[]` array where each
511
- gate entry carries its `kind`, breach/regression counts,
512
- kernel-version match info, and per-`components[]` floor `violations[]`
513
- (`axis`, `value`, `floor`, `direction`).
514
-
515
- ### Refreshing the baseline (when the drift is justified)
516
-
517
- `npm run crap:update` regenerates `baselines/crap.json`. The refresh
518
- should land in a commit whose:
519
-
520
- 1. Subject starts with the configured `refreshTag` (default
521
- `baseline-refresh:`).
522
- 2. Body is non-empty and explains why the refresh is justified.
523
-
524
- There is no CI guardrail rejecting unlabeled baseline edits; the convention is
525
- preserved so the operator can grep refresh commits in a PR diff, but
526
- self-policing is the operator's job during `/mandrel-deliver`'s watch loop.
527
-
528
- ### The per-method coverage join (Story #4775)
529
-
530
- CRAP is the only gate that joins two independently-produced artifacts: the
531
- per-method complexity escomplex derives from the source, and the per-function
532
- coverage istanbul derives from the test run. Everything below exists because
533
- that join is silent when it fails — an unresolved method is simply absent from
534
- the baseline, so a broken join looks exactly like a small repo.
535
-
536
- **One coordinate system.** For a TS/TSX source, escomplex parses the
537
- *transpiled* output and reports each method's `lineStart` in transpiled
538
- coordinates, while `coverage-final.json` is keyed against the *original*
539
- source. The scorer therefore asks `transpileIfNeeded` for a source map
540
- (`{ withLineMap: true }`, backed by Node's built-in `SourceMap` — no extra
541
- runtime dependency) and remaps each method start into original coordinates
542
- before the lookup. JavaScript is a passthrough: its coordinates already are
543
- original coordinates, so no map is computed and nothing changes. The
544
- maintainability path never requests a map, and the emitted code is
545
- byte-identical either way, so MI scores are unaffected.
546
-
547
- **Tolerant matching.** Remapping alone is insufficient: escomplex's method
548
- start and istanbul's `decl.start.line` disagree by a line when a decorator, a
549
- leading `export`, or a wrapped parameter list sits between them. The lookup is
550
- exact-line first (so every already-resolving row keeps its exact prior value),
551
- then innermost containment, then nearest declaration within ±1.
552
-
553
- **`requireCoverage: false` means score it.** A method with no coverage entry
554
- scores as 0% covered — `crap = c² + c`, the formula's own treatment of
555
- untested code — and lands in the baseline. It used to be dropped individually
556
- regardless of the flag, which made the flag a no-op for baseline population.
557
- `requireCoverage: true` still skips and counts it.
558
-
559
- **The updater fails closed on a thin result.** `update-crap-baseline.js`
560
- reports `resolved/joinable` over files that *have* coverage and refuses to
561
- persist below `delivery.quality.gates.crap.minMethodResolutionRate` (default
562
- `0.75`), naming the worst unresolved files. The floor is not enforced below 25
563
- joinable methods, where a diff-scoped run's rate is noise. A healthy repo
564
- resolves ~98%; the 4–6% signature of a coordinate-system mismatch is far below
565
- the floor.
566
-
567
- **Re-derive your floors after adopting this — but do not re-pin `max`.** A
568
- `crap.floors` `max` ceiling pinned before the fix was computed over the
569
- minority of methods the join could see, so it is not a real ceiling — it is an
570
- artefact. The honest scan sees far more (in this repository, 2215 → 4058
571
- visible methods), and the newly-visible methods include the worst ones.
572
-
573
- The tempting response — raise `*.max` until the gate is green again — produces a
574
- floor fitted to the tree's current high-water mark, which **can never fire**:
575
- nothing breaches it until something becomes worse than the worst method already
576
- present. Prefer a *count* budget over a max ceiling:
577
-
578
- ```jsonc
579
- "crap": {
580
- // Number of methods allowed to score above 20. Ratchet this down; it
581
- // breaches the moment the count grows, which a `max` ceiling cannot do.
582
- "floors": { "*": { "methodsAbove20": 40 } }
583
- }
584
- ```
585
-
586
- `max` remains available and is the right instrument when you genuinely have a
587
- hard per-method ceiling to hold. It is the wrong instrument for absorbing
588
- pre-existing debt.
589
-
590
- Note that neither choice is what protects new code. `floors` is an absolute
591
- tree-wide comparison against the rollup; the forward pressure lives in
592
- `newMethodCeiling` (a *new* method scoring above it fails, default 30) and in
593
- `compareCrap`'s ratchet (an *existing* method fails when it regresses against
594
- its own baseline row). Both are unaffected by how much old debt the gate can
595
- now see, and neither consults `floors`.
596
-
597
- **Old baselines are invalidated explicitly.** Rows scored by the previous join
598
- are not comparable to rows scored by this one, and neither `kernelVersion` nor
599
- `escomplexVersion` moves (both track the same upstream package). The envelope
600
- therefore carries a `scoringSemantics` stamp; `check-baselines` fails closed on
601
- a mismatch with the exact re-baseline command rather than comparing across the
602
- boundary. Bump the stamp whenever the coverage join, the line coordinate
603
- system, the unresolved-method policy, or the method identity rule changes —
604
- Story #4969 bumped it for the last of these, replacing escomplex's positional
605
- `<anon method-N>` label with an enclosing-scope-path identity.
606
-
607
- ---
608
-
609
- ## Keeping a baseline fresh (Story #4776)
610
-
611
- Populating a baseline correctly is only half the loop. The other half is
612
- keeping it correct as the tree grows, and that half has two distinct holes —
613
- one at close time, one over the long run. Both are **advisory**:
614
- `check-baselines` already fails closed on a real regression, and duplicating
615
- that would double-gate the same defect.
616
-
617
- ### Pre-merge projections — the refresh nudge at close time
618
-
619
- Close-validation projects, after its gates pass, which committed baseline rows
620
- the post-merge tree would breach, and names the exact remedy while the operator
621
- still has the branch in hand:
622
-
623
- - `lib/close-validation/projections/maintainability.js` — per-file MI.
624
- - `lib/close-validation/projections/crap.js` — per-method CRAP, against each
625
- method's baseline row or, for methods with no row, `newMethodCeiling`.
626
-
627
- Both are wired through `projections/advisories.js`, which
628
- `close-validation/runner.js` calls once. Each self-skips — logging the reason,
629
- never erroring — when its gate is disabled, when no baseline exists, when the
630
- diff has no scorable files, or when the CRAP scorer finds no coverage
631
- artifact. A projected breach never changes the close verdict.
632
-
633
- > The maintainability projection shipped in v1 fully written and fully
634
- > unit-tested, and the v2 Epic-tier collapse removed its only caller. It sat
635
- > importable-but-unimported for the whole of v2, so its advisory never fired
636
- > once. `tests/lib/close-validation/runner-projections.test.js` now walks the
637
- > import graph and fails if **any** module under `projections/` is reachable
638
- > from nothing in production — the orphaning itself is the regression.
639
-
640
- ### `check-baseline-drift.js` — the scheduled full-scope re-score
641
-
642
- Every per-PR enforcement site (close-validation, pre-push, CI) is
643
- **diff-scoped**: it compares the files a branch touched against their baseline
644
- rows. A file nobody touches after its row is written is therefore never
645
- re-scored, so drift introduced *indirectly* — a dependency getting more
646
- complex, coverage moving underneath a method — stays invisible indefinitely.
647
- Full-scope scoring on every push is far too expensive to be the answer.
648
-
649
- ```bash
650
- node .agents/scripts/check-baseline-drift.js # both kinds
651
- node .agents/scripts/check-baseline-drift.js --gate crap # one kind
652
- node .agents/scripts/check-baseline-drift.js --tolerance 1 --json
653
- ```
654
-
655
- It re-scores full-scope through the *same* scorer that writes the baseline
656
- (`refresh-service.resolveDefaultScorer`) — scoring by a second implementation
657
- would report the two implementations' disagreement as drift — and prints a
658
- per-row before/after table for everything that moved beyond the gate's
659
- tolerance, **in either direction**. A row that silently improved is equally
660
- strong evidence the baseline no longer describes the tree.
661
-
662
- Exit codes: `0` no drift (or every kind skipped), `1` drift detected, `2` the
663
- check could not run.
664
-
665
- **`--require-scored`.** "Every kind skipped" mapping to `0` is a
666
- fail-open trap for the scheduled use this CLI was built for. Measured: with no
667
- `coverage/coverage-final.json` on disk, `check-baseline-drift.js --gate crap`
668
- prints `✅ No baseline drift detected` and exits `0` — a nightly job wired that
669
- way is green and inert. Pass `--require-scored` and any skipped kind exits `2`
670
- instead, naming the kind and the skip reason. Use it in every scheduled
671
- invocation.
672
-
673
- This repository schedules the maintainability kind in
674
- `.github/workflows/baseline-drift.yml` (framework repo only — that path is not
675
- part of the materialized `.agents/` payload) — nightly at 05:43 UTC plus
676
- `workflow_dispatch`; it files or updates one
677
- `meta::baseline-drift` issue with the report, closes it when the tree comes
678
- back clean, and fails the run. A consumer materializing `.agents/` still owns
679
- its own schedule.
680
-
681
- `crap` is deliberately **not** in that job. Its drift identity is
682
- `path::method@startLine`, so anything that shifts a method's line re-keys its
683
- row: measured on this tree with a real coverage artifact, 82 rows drifted but
684
- 1438 were reported added and 898 removed — and 853 of those removals are the
685
- same `path::method` reappearing at a different line. The added/removed axis is
686
- re-keying churn, not drift, and the remedy the report prints
687
- (`npm run crap:update -- --full-scope`) additionally re-measures, pulling in
688
- near-empty coverage entries minted by CLI-spawning tests. Fixing the identity
689
- is a prerequisite to scheduling the kind.
690
-
691
- ### `check-baseline-scope.js` — is this baseline still measuring the tree?
692
-
693
- Drift detection assumes the row set is right and asks whether its numbers
694
- moved. The prior question went unasked: **does this baseline still describe
695
- the tree at all?** A ratchet is perfectly capable of being green while
696
- measuring almost nothing — a row can point at a file deleted months ago, and
697
- an in-scope file can carry no row whatsoever, and every gate above stays
698
- green.
699
-
700
- The scope gate asserts the row set in **both directions**, recomputing each
701
- kind's in-scope file set from the gate's own configuration —
702
- `.c8rc.cjs` `include`/`exclude` for coverage,
703
- `delivery.quality.gates.<kind>.{targetDirs,ignoreGlobs}` for the rest —
704
- through the same helpers the refresh scorers use, so the gate and the
705
- producers cannot disagree about scope:
706
-
707
- ```bash
708
- npm run baselines:scope # every kind
709
- node .agents/scripts/check-baseline-scope.js --kind coverage --json
710
- node .agents/scripts/check-baseline-scope.js --strict # skip attribution
711
- ```
712
-
713
- Two design constraints are worth knowing before reading a report:
714
-
715
- - **Only dense kinds assert `missing`.** `coverage` and `maintainability`
716
- emit one row per in-scope file, so a file with no row is a real hole. `crap`
717
- (per-method, coverage-gated), `duplication` (rows only where clones exist),
718
- `lint` and `mutation` are sparse by construction — asserting `missing`
719
- against them yields hundreds of phantom findings on a healthy tree, so they
720
- assert `extra` only. `lighthouse` (`route`) and `bundle-size` (`bundle`) are
721
- not file-keyed and are excluded from both.
722
- - **A PR is blocked only for divergence it created.** Whole-tree equality
723
- would red every open PR the moment anyone lands an in-scope file, so the
724
- gate blocks on divergence attributable to `merge-base(base, HEAD)..HEAD` and
725
- warns about the inherited remainder. It fails towards **strict** — every
726
- finding fatal — when no base resolves, when HEAD is not ahead of it, or when
727
- the change set edits a baseline or the config defining its scope.
728
-
729
- Exit codes: `0` no fatal divergence, `1` fatal divergence, `2` the check could
730
- not run. It runs in the required `baselines` CI job.
731
-
732
- ### `prune-baseline-orphans.js` — the cheap remedy that makes the gate fair
733
-
734
- A hard gate is only defensible while clearing it costs a command. Re-deriving
735
- a whole baseline to express a *deletion* spends a coverage run or a full-tree
736
- MI pass, which is exactly why stale rows accumulate. The pruner is that
737
- deletion, done as arithmetic:
738
-
739
- ```bash
740
- npm run baselines:prune # write the prune
741
- node .agents/scripts/prune-baseline-orphans.js --check # report only, exit 1
742
- ```
743
-
744
- It removes exactly two provably-inert row classes across every file-keyed
745
- baseline — a row whose file is **absent** from disk, and a row for a file now
746
- **out-of-scope** under the gate's own `targetDirs`/`ignoreGlobs` — and it is
747
- **measurement-free by contract**: it never adds a row, never restamps
748
- `generatedAt` (a fresh stamp over rows nobody re-measured is the precise
749
- failure an age check exists to catch), and recomputes `rollup` through the
750
- kind's own arithmetic so the pruned envelope still validates against its
751
- schema. An unreadable scope config degrades to orphan-only pruning rather than
752
- reading unknown scope as empty scope, which would hand it the whole baseline.
753
-
754
- A **missing** row is the one thing the pruner will not fix: a file added
755
- without being measured needs its producer (`npm run coverage:update`,
756
- `npm run maintainability:update`), because inventing a row would be claiming a
757
- measurement nobody took.
758
-
759
- **CI does not run the pruner in either mode.** `--check` exits 1 on any stale
760
- row without asking which change set introduced it, so pairing it with
761
- `check-baseline-scope.js` in the required job cancelled that gate's merge-base
762
- attribution: a row inherited from `main` — say one PR deletes a file while a
763
- second, branched earlier, re-adds its row through a baseline refresh — reds
764
- every open PR on divergence its author did not create and cannot fix from
765
- their branch. The scope gate reports that row as an inherited warning; the
766
- pruner is the remedy an operator (or agent) runs with the branch in hand.
767
-
768
- ---
769
-
770
- ## Bundle-size ratchet — one-shot refresh/acknowledge (Story #151)
771
-
772
- > Baseline envelope, axes, and component model: see the
773
- > [Baseline reference](#baseline-reference) section below.
774
-
775
- `check-baselines --gate bundle-size` is a **strict** ratchet: it diffs the
776
- branch's committed `baselines/bundle-size.json` (head) against the base
777
- ref's copy (`origin/main` by default) using the gate's configured
778
- `tolerance`, and separately checks the head aggregate against `floors`.
779
- Unlike `coverage` / `crap` / `maintainability`, bundle-size has **no
780
- scorer of its own** — the measured `rawKb` / `gzippedKb` numbers come from
781
- whatever build step the consumer already runs, not a source-tree rescan —
782
- so there is no `refreshBaseline({ kind: 'bundle-size', ... })` path to
783
- regenerate a "corrected" baseline the way `npm run crap:update` does.
784
-
785
- This makes an **intentional** bundle-size growth (a framework major bump,
786
- a new dependency, an SSR runtime swap) impossible to land cleanly with the
787
- usual levers: permanently raising `tolerance` in `.agentrc.json` disables
788
- the ratchet for every *future* PR too, not just the one that legitimately
789
- grew.
790
-
791
- ### `BUNDLE_SIZE_REFRESH=1`
792
-
793
- Set the environment variable for the one CI/local run that needs to land
794
- the growth:
795
-
796
- ```bash
797
- BUNDLE_SIZE_REFRESH=1 npm run bundle-size:check
798
- # or, calling the dispatcher directly:
799
- BUNDLE_SIZE_REFRESH=1 node .agents/scripts/check-baselines.js --gate bundle-size
800
- ```
801
-
802
- When set (`1` or `true`, case-insensitive), every `bundle-size`
803
- head-vs-base regression is demoted to `unchanged` **for that invocation
804
- only** — the gate compares head-vs-head in effect, so it passes even
805
- though the committed baseline grew. **Floors still apply**: an
806
- acknowledged PR can still fail if the head aggregate breaches the
807
- configured `floors` budget, so a genuinely runaway regression isn't
808
- silently waved through under the guise of "intentional".
809
-
810
- Commit the regenerated `baselines/bundle-size.json` (reflecting the real,
811
- larger sizes) in the same PR so the new numbers become the base for the
812
- *next* PR's diff.
813
-
814
- ### The ratchet returns to full strength automatically
815
-
816
- `BUNDLE_SIZE_REFRESH` is read fresh on every invocation and is **never
817
- persisted** — no config write, no committed tag, no lingering state. The
818
- very next `check-baselines --gate bundle-size` run (i.e. the next PR),
819
- without the env var set, re-enforces the ratchet at full strength against
820
- the now-larger committed baseline. There is nothing to remember to reset.
821
-
822
- This mirrors the `CRAP_TOLERANCE` env-override precedent (see
823
- [CRAP gate — Consumer onboarding](#crap-gate--consumer-onboarding) above),
824
- but as a true one-shot acknowledgment rather than a run-scoped tolerance
825
- override: `CRAP_TOLERANCE` changes the *threshold*, `BUNDLE_SIZE_REFRESH`
826
- demotes the *outcome* of an already-flagged regression, which is the
827
- correct shape for a gate with no rescoring path of its own.
828
-
829
- ---
830
-
831
- ## HITL blocker escalation
832
-
833
- `risk::high` is planning/audit metadata only — it never pauses runtime. The
834
- sole runtime HITL pause point is `agent::blocked`. The full model is owned by
835
- [`.agents/instructions.md`](../instructions.md) § 1.J and
836
- [`SDLC.md` § HITL model](SDLC.md#hitl-human-in-the-loop-model).
837
-
838
- ---
839
-
840
- ## Baseline reference
841
-
842
- This is the authoritative reference for the canonical baseline shape used
843
- by every quality gate in the framework — `lint`, `coverage`, `crap`,
844
- `maintainability`, `mutation`, `lighthouse`, and `bundle-size`. It covers
845
- the envelope, the per-kind shapes, the component model, how paths are
846
- canonicalised, the writer/reader contract, how consumers override floors,
847
- and how kernel-version drift surfaces as friction. The runbook sections
848
- above describe the runtime behaviour of each gate (when it fires, what it
849
- asserts, how to refresh); this section is the data-shape contract those
850
- gates read and write.
851
-
852
- Cross-references:
853
-
854
- - [`.agents/docs/configuration.md`](../docs/configuration.md) — the `.agentrc.json`
855
- configuration surface that backs the gates.
856
- - [`.agents/README.md`](../README.md) — consumer onboarding.
857
-
858
- > `mutation` is a **registered baseline kind with no shipped runner**. The
859
- > envelope, schema, and floor config below describe a `baselines/mutation.json`
860
- > the framework can read and ratchet, but nothing in Mandrel invokes Stryker or
861
- > writes that file: the `update-mutation-baseline.js` refresh CLI was retired
862
- > in #4482 and the `lib/mutation/` snapshot machinery in #5008. Activating the
863
- > gate means shipping a runner first — treat the kind as a reserved slot, not a
864
- > dormant feature.
865
-
866
- ### Envelope
867
-
868
- Every baseline file under `baselines/<kind>.json` shares the same
869
- top-level envelope:
870
-
871
- ```json
872
- {
873
- "$schema": ".agents/schemas/baselines/<kind>.schema.json",
874
- "kernelVersion": "1.1.0",
875
- "generatedAt": "2026-05-15T19:30:00.000Z",
876
- "rollup": {
877
- "*": { "<axis>": <number>, "...": <number> }
878
- },
879
- "rows": [
880
- { "path": "<repo-relative-path>", "<axis>": <number>, "...": <number> }
881
- ]
882
- }
883
- ```
884
-
885
- | Field | Purpose |
886
- | --------------- | ----------------------------------------------------------------- |
887
- | `$schema` | Per-kind JSON Schema path. Drives validation in the shared AJV. |
888
- | `kernelVersion` | Version stamp of the writer that produced the file. See below. |
889
- | `generatedAt` | ISO 8601 timestamp; advisory — not load-bearing for gate logic. |
890
- | `rollup` | Per-component aggregate keyed by component name. `*` is required. |
891
- | `rows` | Sorted, canonicalised per-file (or per-route/per-bundle) entries. |
892
-
893
- The schemas live under [`.agents/schemas/baselines/`](../schemas/baselines/).
894
- The shared AJV instance is built by `buildBaselineSchemaAjv()` in
895
- [`.agents/scripts/lib/baseline-schema-registry.js`](../scripts/lib/baseline-schema-registry.js).
896
-
897
- ### Concurrent refreshes — the baseline merge driver
898
-
899
- `generatedAt` sits on line 4 of every envelope, so two branches that each
900
- refresh a baseline **always** differ there, even when they moved completely
901
- disjoint rows. Git merges JSON as text, and whether it can separate that hunk
902
- from the moved rows is an accident of proximity. Both outcomes are wrong:
903
-
904
- - it cannot → a conflict on work that never overlapped (the `coverage.json` /
905
- `maintainability.json` "always conflicts" pattern);
906
- - it can → it splices both sides' row lines into a row set **neither side
907
- scored** (the `crap.json` "silently auto-merges" pattern). The ratchet then
908
- guards a number no scorer ever produced.
909
-
910
- A baseline is a set of rows keyed by identity plus a rollup derived from them,
911
- so [`merge-baseline.js`](../scripts/merge-baseline.js) merges it as that. Per
912
- row identity the standard 3-way rule applies; only a genuine double move
913
- conflicts, and then markers wrap that row alone. The rollup is always
914
- **recomputed** from the merged rows — merging two rollups is the same splice
915
- hazard compressed into one number — and `generatedAt` resolves to the later of
916
- the two stamps rather than conflicting.
917
-
918
- Row identity comes from the kind module's `rowIdentity(row)`, which is
919
- deliberately not `keyField`: CRAP groups by file (`keyField: 'path'`) but
920
- ships one row per method, so keying on `keyField` would drop every method in a
921
- file but one. Any `baselines/*.json` whose `$schema` is not a known per-kind
922
- envelope — `arch-cycles`, `cyclomatic`, `dead-exports`, `audit-ledger`,
923
- `context-budget` — is handed straight back to
924
- `git merge-file`, so registering the driver cannot change their behaviour.
925
-
926
- Registration has two halves:
927
-
928
- ```bash
929
- # 1. tracked, installed by `node .agents/scripts/apply-quality-bootstrap.js`
930
- # → .gitattributes: baselines/*.json merge=mandrel-baseline
931
- # 2. per clone — git will not run a command chosen by whoever wrote the repo
932
- git config merge.mandrel-baseline.driver "node .agents/scripts/merge-baseline.js %O %A %B %P"
933
- ```
934
-
935
- Only the first ships with the repository, and a clone missing the second
936
- degrades **silently** back to the text merge. `mandrel doctor`'s
937
- `merge-driver` check is the guard: it prints the exact `git config` line
938
- above, and passes as skipped when `.gitattributes` does not declare the
939
- driver at all.
940
-
941
- `MANDREL_BASELINE_GENERATED_AT` pins the stamp for a reproducible build (see
942
- the environment table in [configuration.md](configuration.md)). It is no
943
- longer needed to dodge merge conflicts.
944
-
945
- ### Per-kind shapes
946
-
947
- Each kind contributes a `rows[]` schema and a `rollup` axis set. The
948
- authoritative declarations live in the per-kind modules at
949
- [`.agents/scripts/lib/baselines/kinds/`](../scripts/lib/baselines/kinds/):
950
-
951
- | Kind | Key field | Row axes | Rollup axes |
952
- | ----------------- | --------- | -------------------------------------------------------------- | ---------------------------------------- |
953
- | `lint` | `path` | `errorCount`, `warningCount` | `errorCount`, `warningCount` |
954
- | `coverage` | `path` | `lines`, `branches`, `functions`, `statements` | `lines`, `branches`, `functions` |
955
- | `crap` | `path` | `method`, `startLine`, `crap` | `max`, `p95`, `methodsAboveCeiling` |
956
- | `maintainability` | `path` | `maintainability` | `min`, `p50`, `p95` |
957
- | `mutation` | `path` | `score`, `killed`, `survived`, `noCoverage`, `timeout`, `total`| `score`, `survived`, `noCoverage` |
958
- | `lighthouse` | `route` | `route`, `performance`, `accessibility`, `bestPractices`, `seo`| per-category scores |
959
- | `bundle-size` | `bundle` | `bundle`, `bytes`, `gzippedBytes` | `bytes`, `gzippedBytes` |
960
-
961
- The `keyField` is the per-row identifier the writer canonicalises and the
962
- component grouper matches against (see below). Lighthouse keys rows on
963
- `route`; bundle-size keys on `bundle`; every other kind keys on `path`.
964
-
965
- ### Component model
966
-
967
- A component is a named bucket of rows that share a floor and a tolerance.
968
- Components let an operator slice a baseline so per-component floors can
969
- be evaluated independently (e.g. `api`, `worker`, `infra` each with its
970
- own coverage floor).
971
-
972
- Shape:
973
-
974
- ```json
975
- "components": {
976
- "<name>": ["<glob>", "<glob>", "..."]
977
- }
978
- ```
979
-
980
- Rules:
981
-
982
- - The component literally named `*` is the **whole-repo bucket** and
983
- captures every row regardless of declared globs. Every baseline emits
984
- `rollup['*']` for backwards compatibility with pre-component gates.
985
- - Glob matching uses
986
- [`minimatch`](https://github.com/isaacs/minimatch) with `dot: true`.
987
- - **Overlap is allowed by design** — a row matched by two components is
988
- reported under both.
989
- - When a gate omits `components`, the default is `{ "*": ["**"] }`. The
990
- resolver lives in
991
- [`.agents/scripts/lib/baselines/components.js`](../scripts/lib/baselines/components.js)
992
- (`resolveComponents` + `groupRows`).
993
-
994
- ### Path canonicalisation
995
-
996
- Every path-like field in a baseline (`rows[].path`, `rows[].route`,
997
- `rows[].bundle`) is canonicalised to a forward-slashed, repo-relative
998
- form before it is written:
999
-
1000
- - Windows backslashes are normalised to forward slashes.
1001
- - Leading `./` is stripped.
1002
- - A `.worktrees/<workspace>/` prefix — which would leak into a hand-edit
1003
- made inside a story worktree — is stripped.
1004
- - Absolute paths are rejected (the writer throws rather than silently
1005
- rewrite identity).
1006
-
1007
- The canonicaliser lives at
1008
- [`.agents/scripts/lib/baselines/path-canon.js`](../scripts/lib/baselines/path-canon.js).
1009
- The reader applies a defensive second pass (`canonicaliseRowPath`) when
1010
- loading so downstream consumers never have to special-case the worktree
1011
- prefix.
1012
-
1013
- ### Writer/reader contract
1014
-
1015
- The single funnel for **writing** a baseline is
1016
- [`.agents/scripts/lib/baselines/writer.js`](../scripts/lib/baselines/writer.js)
1017
- — `write({ kind, rows, components, kernelVersion?, generatedAt? })`:
1018
-
1019
- 1. Resolve the per-kind module from the kernel registry.
1020
- 2. Project every row through `projectRow` (which canonicalises the key
1021
- field and asserts the result with `assertCanonical`).
1022
- 3. Sort the rows deterministically for stable on-disk diffs.
1023
- 4. Compute the per-component rollup, always including `*`.
1024
- 5. Stamp `$schema`, `kernelVersion`, and `generatedAt` via
1025
- `buildEnvelope`.
1026
- 6. Validate the envelope against the per-kind schema via the shared AJV.
1027
- 7. Return the envelope. `writeFile(absPath, envelope)` is the separate
1028
- serialise + atomic-rename seam.
1029
-
1030
- The single funnel for **reading** a baseline is
1031
- [`.agents/scripts/lib/baselines/reader.js`](../scripts/lib/baselines/reader.js)
1032
- — `reader.load(kind, { cwd?, configPath? })`:
1033
-
1034
- 1. Resolve the on-disk path from `delivery.quality.gates.<kind>.baselinePath`,
1035
- falling back to the canonical default (`baselines/<kind>.json`).
1036
- 2. Read the file as UTF-8 JSON.
1037
- 3. Validate against the per-kind schema.
1038
- 4. Apply the defensive path canonicalisation pass to `rows[]`.
1039
- 5. Return `{ rollup, rows, kernelVersion, generatedAt }`.
1040
-
1041
- Every gate reads through this module — the unified
1042
- [`check-baselines.js`](../scripts/check-baselines.js) dispatcher
1043
- (whose per-kind gate logic lives in
1044
- [`.agents/scripts/lib/baselines/kinds/`](../scripts/lib/baselines/kinds/)
1045
- — `lint.js`, `coverage.js`, `crap.js`, `maintainability.js`,
1046
- `mutation.js`, etc.), the audit-suite delta emitter, and the
1047
- per-component drift signals. No gate opens
1048
- `JSON.parse(readFileSync(...))` of a baseline directly.
1049
-
1050
- `loadFile(absolutePath, { kind? })` is the same contract for ad-hoc
1051
- fixture paths; the kind is inferred from `$schema` when not supplied.
1052
-
1053
- ### Floor overrides
1054
-
1055
- Consumers override floors per gate in `.agentrc.json` under
1056
- `delivery.quality.gates.<kind>`:
1057
-
1058
- ```json
1059
- {
1060
- "delivery": {
1061
- "quality": {
1062
- "gates": {
1063
- "coverage": {
1064
- "floors": {
1065
- "*": { "lines": 90, "branches": 85, "functions": 90 },
1066
- "api": { "lines": 95, "branches": 90, "functions": 95 }
1067
- },
1068
- "components": {
1069
- "api": ["src/api/**", "src/server/**"]
1070
- }
1071
- }
1072
- }
1073
- }
1074
- }
1075
- }
1076
- ```
1077
-
1078
- Behaviour:
1079
-
1080
- - `floors['*']` is the whole-repo floor. Every gate falls back to `*`
1081
- when a component-scoped floor is not declared.
1082
- - A per-component floor overrides `*` for that component only. Other
1083
- components still inherit `*`.
1084
- - The `components` map is optional. When omitted, the default
1085
- `{ "*": ["**"] }` applies and only `*` rows are ever evaluated.
1086
- - The unified `check-baselines.js` reports breaches per component, with
1087
- `*` always present in the output. The shared baselines kernel
1088
- (`lib/baselines/kernel.js`, via the per-kind rollups in
1089
- `lib/baselines/kinds/`) groups rows by component and names the
1090
- breached component in its output so a `*` rollup is not falsely
1091
- implicated when only a component-scoped floor was crossed.
1092
-
1093
- #### Floor axes must match rollup axes
1094
-
1095
- A configured floor axis is only enforced when the rollup actually exposes
1096
- that axis — `check-baselines.js#compareToFloor` skips axes whose value is
1097
- missing from the rollup. As of Story #2193, the unified dispatcher
1098
- **fails closed** when a configured floor axis is absent from the rollup:
1099
- the gate exits non-zero with an actionable error naming the missing axis
1100
- and listing the available rollup keys (so a typo like
1101
- `{ maintainability: 70 }` against the maintainability rollup — which
1102
- exposes `min` / `p50` / `p95` — surfaces immediately instead of silently
1103
- passing).
1104
-
1105
- Match the floor axis names to the rollup axes documented in the [Per-kind
1106
- shapes](#per-kind-shapes) table above. For maintainability specifically:
1107
-
1108
- ```json
1109
- {
1110
- "delivery": {
1111
- "quality": {
1112
- "gates": {
1113
- "maintainability": {
1114
- "floors": {
1115
- "*": { "min": 70 }
1116
- }
1117
- }
1118
- }
1119
- }
1120
- }
1121
- }
1122
- ```
1123
-
1124
- The maintainability rollup exposes `min` (lowest per-file `mi`), `p50`
1125
- (median), and `p95` (95th percentile); a floor on `min` is the framework
1126
- default and enforces a hard lower bound on individual files. Floors keyed
1127
- on the legacy `maintainability` axis (which never appears in the rollup)
1128
- are rejected with an explanatory error.
1129
-
1130
- For the full configuration surface (every gate-level key with defaults
1131
- and types) see [`.agents/docs/configuration.md`](../docs/configuration.md) and the
1132
- `delivery.quality.*` section.
1133
-
1134
- #### Shipped surface vs follow-up
1135
-
1136
- The unified [`check-baselines.js`](../scripts/check-baselines.js)
1137
- ships **floor + tolerance + schema + kernel-mismatch** logic and is the
1138
- **only** baseline gate. Epic #1943 (Story #1981) absorbed the per-kind
1139
- regression / scope / git-base-ref logic and deleted the per-kind
1140
- `check-<kind>.js` CLIs (no `check-coverage.js`, `check-crap.js`, or
1141
- `check-maintainability.js` exists in `.agents/scripts/`; see the
1142
- `baselines` job comment in `.github/workflows/ci.yml` and the
1143
- Story #2210 note in
1144
- `.agents/scripts/lib/close-validation/gates.js`). Consumers wire only
1145
- the unified `baselines` status check into branch protection (see
1146
- `.agentrc.json` → `github.branchProtection.requiredChecks`).
1147
-
1148
- ### Kernel-version friction
1149
-
1150
- Every per-kind module exports a `kernelVersion()` function that returns
1151
- the writer's version of the analysis it produces. The writer stamps the
1152
- version on the envelope; the reader returns it; the unified gate
1153
- compares it against the running kernel.
1154
-
1155
- When `baseline.kernelVersion !== runningKernelVersion`, the gate emits a
1156
- `baseline-kernel-mismatch` friction signal (suppressed with
1157
- `--no-friction`) but does **not** change its exit code — kernel drift is
1158
- advisory. The friction record points the reviewer at the regenerate
1159
- workflow for the kind in question.
1160
-
1161
- Refresh paths:
1162
-
1163
- - `npm run test:coverage` then `npm run coverage:update` — rewrites
1164
- `baselines/coverage.json`.
1165
- - `node .agents/scripts/update-crap-baseline.js` — rewrites
1166
- `baselines/crap.json`.
1167
- - `node .agents/scripts/update-maintainability-baseline.js` — rewrites
1168
- `baselines/maintainability.json`.
1169
- - `baselines/lint.json` has no framework refresh CLI — see
1170
- [Lint baseline ratchet](#lint-baseline-ratchet).
1171
-
1172
- After a kernel bump, regenerate every baseline whose `kernelVersion`
1173
- drifted, then commit the refreshed files. The writer guarantees
1174
- deterministic ordering and canonical paths, so the diff is the kernel
1175
- delta and nothing else.
1176
-
1177
- ### Baseline source of truth
1178
-
1179
- - [`.agents/docs/configuration.md`](../docs/configuration.md) — full `.agentrc.json`
1180
- surface.
1181
- - [`.agents/scripts/lib/baselines/`](../scripts/lib/baselines/) —
1182
- source of truth for the writer, reader, kernel registry, components
1183
- resolver, envelope schemas, and per-kind modules.