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