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