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,195 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* audit-advisories.js — run `npm audit --json` and diff advisories across refs.
|
|
3
|
-
*
|
|
4
|
-
* The measuring half of `audit-attribution.js` next door. That module owns the
|
|
5
|
-
* verdict vocabulary and how a verdict is worded; this one owns what a verdict
|
|
6
|
-
* is computed FROM.
|
|
7
|
-
*
|
|
8
|
-
* The comparison is **per advisory**, not per exit code. Reading only "did the
|
|
9
|
-
* base audit fail too" collapses the case a busy repository meets most: a base
|
|
10
|
-
* that is already red for advisory A, and a diff that adds advisory B. That
|
|
11
|
-
* answered `pre-existing` — a true statement about A, and a misleading one
|
|
12
|
-
* about the branch, because the author was told their diff was innocent while
|
|
13
|
-
* it was carrying B. Diffing advisory ids at or above `high` reports both facts
|
|
14
|
-
* separately: B introduced, A pre-existing.
|
|
15
|
-
*
|
|
16
|
-
* The npm spawn is injectable (`.agents/rules/test-seams.md`), so the whole
|
|
17
|
-
* projection is reachable without a registry round-trip.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { spawnCapture } from './child-exec.js';
|
|
21
|
-
|
|
22
|
-
/** The levels the required SCA step fails on, so the levels attribution reads. */
|
|
23
|
-
const BLOCKING_SEVERITIES = new Set(['critical', 'high']);
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The stable identity of one advisory in an `npm audit --json` report.
|
|
27
|
-
*
|
|
28
|
-
* `source` is the advisory's own numeric id and is what makes two runs
|
|
29
|
-
* comparable; the URL and title are fallbacks for a registry that omits it. A
|
|
30
|
-
* `via` entry that is a bare string names another package rather than an
|
|
31
|
-
* advisory and is handled by the package-level fallback in
|
|
32
|
-
* {@link extractBlockingAdvisories}.
|
|
33
|
-
*
|
|
34
|
-
* @param {object} via
|
|
35
|
-
* @returns {string|null}
|
|
36
|
-
*/
|
|
37
|
-
function advisoryIdOf(via) {
|
|
38
|
-
if (via?.source !== undefined && via.source !== null) {
|
|
39
|
-
return `advisory:${via.source}`;
|
|
40
|
-
}
|
|
41
|
-
if (typeof via?.url === 'string' && via.url.length > 0) return via.url;
|
|
42
|
-
if (typeof via?.title === 'string' && via.title.length > 0) {
|
|
43
|
-
return `title:${via.title}`;
|
|
44
|
-
}
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Project an `npm audit --json` report onto the set of advisories at or above
|
|
50
|
-
* `high`, as `{ id, severity, title }` records sorted by id.
|
|
51
|
-
*
|
|
52
|
-
* A vulnerable package whose `via` list carries no advisory object at all — the
|
|
53
|
-
* transitive case, where `via` is a list of package names — still contributes a
|
|
54
|
-
* `package:<name>` entry. Dropping it would let a real blocking advisory go
|
|
55
|
-
* uncounted, and an attribution that under-counts the head is one that reports
|
|
56
|
-
* a genuine regression as `pre-existing`.
|
|
57
|
-
*
|
|
58
|
-
* @param {object|null} report — parsed `npm audit --json` output.
|
|
59
|
-
* @returns {Array<{ id: string, severity: string, title: string }>}
|
|
60
|
-
*/
|
|
61
|
-
export function extractBlockingAdvisories(report) {
|
|
62
|
-
const packages = report?.vulnerabilities;
|
|
63
|
-
if (!packages || typeof packages !== 'object') return [];
|
|
64
|
-
const found = new Map();
|
|
65
|
-
for (const [name, entry] of Object.entries(packages)) {
|
|
66
|
-
if (BLOCKING_SEVERITIES.has(String(entry?.severity ?? '').toLowerCase())) {
|
|
67
|
-
collectPackageAdvisories(name, entry, found);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return [...found.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Add one vulnerable package's blocking advisories to `found`, falling back to
|
|
75
|
-
* a `package:<name>` entry when its `via` list names packages rather than
|
|
76
|
-
* advisories.
|
|
77
|
-
*
|
|
78
|
-
* @param {string} name
|
|
79
|
-
* @param {object} entry
|
|
80
|
-
* @param {Map<string, object>} found
|
|
81
|
-
*/
|
|
82
|
-
function collectPackageAdvisories(name, entry, found) {
|
|
83
|
-
const before = found.size;
|
|
84
|
-
for (const via of Array.isArray(entry?.via) ? entry.via : []) {
|
|
85
|
-
const severity = String(via?.severity ?? entry.severity).toLowerCase();
|
|
86
|
-
const id = BLOCKING_SEVERITIES.has(severity) ? advisoryIdOf(via) : null;
|
|
87
|
-
if (id && !found.has(id)) {
|
|
88
|
-
found.set(id, { id, severity, title: via?.title ?? name });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (found.size === before) {
|
|
92
|
-
found.set(`package:${name}`, {
|
|
93
|
-
id: `package:${name}`,
|
|
94
|
-
severity: entry.severity,
|
|
95
|
-
title: name,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Split the head's blocking advisories into the ones this diff **introduced**
|
|
102
|
-
* (head minus base) and the ones the merge base already carried
|
|
103
|
-
* (the intersection).
|
|
104
|
-
*
|
|
105
|
-
* A `null` base is the degraded read: nothing can be attributed, so neither
|
|
106
|
-
* list is populated and the caller reports `unknown` rather than guessing.
|
|
107
|
-
*
|
|
108
|
-
* @param {{ head?: Array<object>, base?: Array<object>|null }} params
|
|
109
|
-
* @returns {{ introduced: Array<object>, preExisting: Array<object> }}
|
|
110
|
-
*/
|
|
111
|
-
export function diffAdvisories({ head = [], base = null }) {
|
|
112
|
-
if (!Array.isArray(base)) return { introduced: [], preExisting: [] };
|
|
113
|
-
const baseIds = new Set(base.map((a) => a.id));
|
|
114
|
-
return {
|
|
115
|
-
introduced: head.filter((a) => !baseIds.has(a.id)),
|
|
116
|
-
preExisting: head.filter((a) => baseIds.has(a.id)),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Run `npm audit --json` over a dependency manifest pair and project it.
|
|
122
|
-
*
|
|
123
|
-
* `--package-lock-only` audits the committed lockfile without installing, so
|
|
124
|
-
* the probe never touches the job's own `node_modules`: an attribution
|
|
125
|
-
* mechanism that could disturb the tree it is reporting on would be a worse
|
|
126
|
-
* defect than the one it explains. `--json` is what makes the two runs
|
|
127
|
-
* comparable at all — the human report says which advisories exist, but only
|
|
128
|
-
* as prose.
|
|
129
|
-
*
|
|
130
|
-
* @param {string} dir — directory holding package.json + package-lock.json.
|
|
131
|
-
* @param {{ spawn?: Function }} [deps]
|
|
132
|
-
* @returns {{ failed: boolean, advisories: Array<object> }}
|
|
133
|
-
* @throws {Error} when npm could not evaluate the tree at all.
|
|
134
|
-
*/
|
|
135
|
-
export function auditAdvisories(dir, { spawn = spawnCapture } = {}) {
|
|
136
|
-
const result = spawn(
|
|
137
|
-
'npm',
|
|
138
|
-
['audit', '--json', '--audit-level=high', '--package-lock-only'],
|
|
139
|
-
{ cwd: dir },
|
|
140
|
-
);
|
|
141
|
-
const report = parseAuditJson(result?.stdout);
|
|
142
|
-
// npm exits non-zero for "advisories found" and for "could not audit" alike.
|
|
143
|
-
// Only a real audit verdict carries a `vulnerabilities` map; anything else is
|
|
144
|
-
// a probe failure the caller must read as `unknown`.
|
|
145
|
-
if (!report) {
|
|
146
|
-
throw new Error(
|
|
147
|
-
`npm audit could not evaluate the tree: ${String(result?.stderr ?? '').slice(0, 200)}`,
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
const advisories = extractBlockingAdvisories(report);
|
|
151
|
-
return { failed: advisories.length > 0, advisories };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Parse `npm audit --json` stdout, returning `null` for anything that is not a
|
|
156
|
-
* real audit report. Never throws: a probe that crashed on its own output would
|
|
157
|
-
* be the second failure mode this module exists to avoid.
|
|
158
|
-
*
|
|
159
|
-
* @param {unknown} stdout
|
|
160
|
-
* @returns {object|null}
|
|
161
|
-
*/
|
|
162
|
-
function parseAuditJson(stdout) {
|
|
163
|
-
try {
|
|
164
|
-
const parsed = JSON.parse(String(stdout ?? ''));
|
|
165
|
-
return parsed?.vulnerabilities ? parsed : null;
|
|
166
|
-
} catch (_) {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Render the per-advisory detail lines that follow the verdict.
|
|
173
|
-
*
|
|
174
|
-
* Named lists, not counts: "3 introduced" sends the reader back to the raw
|
|
175
|
-
* audit output, which is the trip attribution exists to save.
|
|
176
|
-
*
|
|
177
|
-
* @param {{ introduced?: Array<object>, preExisting?: Array<object> }} input
|
|
178
|
-
* @returns {string[]}
|
|
179
|
-
*/
|
|
180
|
-
export function renderAdvisoryDetail({ introduced = [], preExisting = [] }) {
|
|
181
|
-
const list = (advisories) =>
|
|
182
|
-
advisories.map((a) => `${a.id} (${a.severity})`).join(', ');
|
|
183
|
-
const lines = [];
|
|
184
|
-
if (introduced.length > 0) {
|
|
185
|
-
lines.push(
|
|
186
|
-
`Introduced by this diff (${introduced.length}): ${list(introduced)}.`,
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
if (preExisting.length > 0) {
|
|
190
|
-
lines.push(
|
|
191
|
-
`Already on the merge base (${preExisting.length}): ${list(preExisting)}. Those are not this branch's to fix.`,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
return lines;
|
|
195
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* audit-attribution.js — decide whose defect a red advisory check is.
|
|
3
|
-
*
|
|
4
|
-
* `npm audit` reports the advisories present in a dependency tree; it says
|
|
5
|
-
* nothing about who introduced them. Advisories are published against
|
|
6
|
-
* packages that are ALREADY installed, so `main` goes red between pull
|
|
7
|
-
* requests without anybody's diff causing it, and the first PR opened
|
|
8
|
-
* afterwards becomes the discovery mechanism — its author debugging a
|
|
9
|
-
* supply-chain advisory from inside a failing log that has nothing to do
|
|
10
|
-
* with their change.
|
|
11
|
-
*
|
|
12
|
-
* The nightly sweep (`.github/workflows/dependency-audit-cron.yml`) shrinks
|
|
13
|
-
* how often that happens; it cannot make the failure legible when it does.
|
|
14
|
-
* An advisory published after one night's sweep and before the next lands on
|
|
15
|
-
* whoever opens a PR in between. This module supplies the missing sentence:
|
|
16
|
-
* the same audit, evaluated against the pull request's merge base.
|
|
17
|
-
*
|
|
18
|
-
* It buys legibility, never permission — see `attributionExitCode`.
|
|
19
|
-
*
|
|
20
|
-
* The comparison is **per advisory**, not per exit code. Reading only "did the
|
|
21
|
-
* base audit fail too" collapses the case a busy repository meets most: a base
|
|
22
|
-
* that is already red for advisory A, and a diff that adds advisory B. That
|
|
23
|
-
* answered `pre-existing` — a true statement about A, and a misleading one
|
|
24
|
-
* about the branch, because the author was told their diff was innocent while
|
|
25
|
-
* it was carrying B. Diffing advisory ids at or above `high` reports both
|
|
26
|
-
* facts separately: B introduced, A pre-existing.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
// The per-advisory machinery lives next door and is re-exported here, so this
|
|
30
|
-
// module stays the one import an attribution consumer needs while the audit
|
|
31
|
-
// runner, the projection and the diff keep their own file.
|
|
32
|
-
export {
|
|
33
|
-
auditAdvisories,
|
|
34
|
-
diffAdvisories,
|
|
35
|
-
renderAdvisoryDetail,
|
|
36
|
-
} from './audit-advisories.js';
|
|
37
|
-
|
|
38
|
-
// The verdict vocabulary. Only `UNKNOWN` is exported: the CLI branches on it
|
|
39
|
-
// to decide whether to audit the base at all, while the other two are read by
|
|
40
|
-
// callers out of the rendered report rather than compared as symbols. Their
|
|
41
|
-
// STRING values are the contract — they appear verbatim in the CI log — so
|
|
42
|
-
// the tests assert those literals rather than re-importing the constants,
|
|
43
|
-
// which would let a rename silently change what an operator reads.
|
|
44
|
-
/** The pull request's own diff introduced the advisory. */
|
|
45
|
-
const INTRODUCED = 'introduced-by-this-diff';
|
|
46
|
-
/** The merge base carries it too; the diff is innocent. */
|
|
47
|
-
const PRE_EXISTING = 'pre-existing';
|
|
48
|
-
/** The probe could not reach a verdict. Never an accusation. */
|
|
49
|
-
export const UNKNOWN = 'unknown';
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Decide the verdict from the two audit outcomes.
|
|
53
|
-
*
|
|
54
|
-
* Pure — the caller owns running the audits. `baseAudit` is `null` when the
|
|
55
|
-
* base could not be audited at all, which is the `unknown` path: an
|
|
56
|
-
* attribution mechanism must never guess, because the guess it would make
|
|
57
|
-
* (`introduced-by-this-diff`) is an accusation against the author reading it.
|
|
58
|
-
*
|
|
59
|
-
* `baseAudit.failed` is asked **per advisory** by the CLI: it passes
|
|
60
|
-
* `{ failed: <the base already carries every advisory the head has> }`, so a
|
|
61
|
-
* base that is red for its own advisory does not absolve a diff that added a
|
|
62
|
-
* different one.
|
|
63
|
-
*
|
|
64
|
-
* @param {{ headFailed: boolean, baseAudit: { failed: boolean } | null }} input
|
|
65
|
-
* @returns {string} one of INTRODUCED / PRE_EXISTING / UNKNOWN
|
|
66
|
-
*/
|
|
67
|
-
export function deriveVerdict({ headFailed, baseAudit }) {
|
|
68
|
-
if (!headFailed) return UNKNOWN;
|
|
69
|
-
if (!baseAudit || typeof baseAudit.failed !== 'boolean') return UNKNOWN;
|
|
70
|
-
return baseAudit.failed ? PRE_EXISTING : INTRODUCED;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The exit code a verdict earns.
|
|
75
|
-
*
|
|
76
|
-
* **Both real verdicts fail.** The advisory is genuine either way: a check
|
|
77
|
-
* that passed on `pre-existing` would let advisories accumulate on `main`
|
|
78
|
-
* unnoticed, which is the exact failure the nightly sweep exists to prevent.
|
|
79
|
-
* Attribution changes what the log says, never whether the branch may land.
|
|
80
|
-
*
|
|
81
|
-
* `unknown` exits 0 — the probe degraded, and the required `npm audit` step
|
|
82
|
-
* has already failed on its own account. A reporter that can turn a red into
|
|
83
|
-
* a second, unrelated red is worse than no reporter.
|
|
84
|
-
*
|
|
85
|
-
* @param {string} verdict
|
|
86
|
-
* @returns {number}
|
|
87
|
-
*/
|
|
88
|
-
export function attributionExitCode(verdict) {
|
|
89
|
-
return verdict === UNKNOWN ? 0 : 1;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Render the operator-facing report for a verdict.
|
|
94
|
-
*
|
|
95
|
-
* `trackingIssue` is the open `meta::dependency-advisory` issue number when
|
|
96
|
-
* the nightly sweep has already filed one, or `null`. Naming it is the point
|
|
97
|
-
* of the `pre-existing` branch: it turns "why is my unrelated PR red" into a
|
|
98
|
-
* link to the issue that already owns the fix.
|
|
99
|
-
*
|
|
100
|
-
* @param {{ verdict: string, baseRef?: string, trackingIssue?: number|null, reason?: string|null }} input
|
|
101
|
-
* @returns {string[]} lines, in emission order
|
|
102
|
-
*/
|
|
103
|
-
export function renderAttribution({
|
|
104
|
-
verdict,
|
|
105
|
-
baseRef = null,
|
|
106
|
-
trackingIssue = null,
|
|
107
|
-
reason = null,
|
|
108
|
-
}) {
|
|
109
|
-
const at = baseRef ? ` (merge base ${baseRef})` : '';
|
|
110
|
-
if (verdict === PRE_EXISTING) {
|
|
111
|
-
const lines = [
|
|
112
|
-
`::warning::Advisory attribution: PRE-EXISTING${at}. The same high-severity advisory is already present on the merge base, so this pull request's diff did not introduce it.`,
|
|
113
|
-
'This check still fails, and deliberately: the advisory is real, and letting it pass here would let advisories accumulate on `main` unnoticed.',
|
|
114
|
-
"Fix it as a standalone `fix(deps)` PR against `main` rather than spending this Story's scope on it. Check the `overrides` block in package.json first — a pinned floor one patch below the patched release is the usual cause.",
|
|
115
|
-
];
|
|
116
|
-
if (trackingIssue != null) {
|
|
117
|
-
lines.splice(
|
|
118
|
-
1,
|
|
119
|
-
0,
|
|
120
|
-
`The nightly sweep already filed the tracking issue: #${trackingIssue}.`,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
return lines;
|
|
124
|
-
}
|
|
125
|
-
if (verdict === INTRODUCED) {
|
|
126
|
-
return [
|
|
127
|
-
`::error::Advisory attribution: INTRODUCED BY THIS DIFF${at}. The merge base audits clean at the high-severity threshold, so a dependency change on this branch brought the advisory in.`,
|
|
128
|
-
"Read the advisory's own version range before accepting a fix: `npm audit fix --force` often proposes a needless semver-major when the parent package's declared range already admits a patched version.",
|
|
129
|
-
];
|
|
130
|
-
}
|
|
131
|
-
return [
|
|
132
|
-
`::warning::Advisory attribution: UNKNOWN${at}${reason ? ` — ${reason}` : ''}. The audit result above stands on its own; this probe adds nothing to it.`,
|
|
133
|
-
];
|
|
134
|
-
}
|
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
// .agents/scripts/lib/baselines/drift-detector.js
|
|
2
|
-
/**
|
|
3
|
-
* drift-detector.js — full-scope baseline drift detection (Story #4776).
|
|
4
|
-
*
|
|
5
|
-
* Every enforcement site for the maintainability and CRAP baselines is
|
|
6
|
-
* **diff-scoped**: close-validation, the pre-push hook and CI all compare
|
|
7
|
-
* the files a branch touched against their committed rows. That is the
|
|
8
|
-
* right per-PR trade — full-scope scoring on every push would be far too
|
|
9
|
-
* expensive — but it has a structural blind spot. A file that is never
|
|
10
|
-
* modified after its baseline row is written is never re-scored, so a
|
|
11
|
-
* regression introduced *indirectly* (a dependency getting more complex, a
|
|
12
|
-
* test deletion moving coverage underneath it) is invisible for as long as
|
|
13
|
-
* nobody happens to touch that file.
|
|
14
|
-
*
|
|
15
|
-
* This module closes that hole with the check that is too expensive to run
|
|
16
|
-
* per-PR and cheap enough to run on a schedule: re-score every target
|
|
17
|
-
* directory in full, and report every row whose current score has moved
|
|
18
|
-
* away from its baseline by more than the gate's tolerance — **in either
|
|
19
|
-
* direction**. Drift, not regression: a row that silently improved is
|
|
20
|
-
* equally strong evidence that the committed baseline no longer describes
|
|
21
|
-
* the tree, and leaving it stale means the ratchet is anchored to a number
|
|
22
|
-
* that no longer exists.
|
|
23
|
-
*
|
|
24
|
-
* Re-scoring routes through `refresh-service.resolveDefaultScorer` — the
|
|
25
|
-
* same scorer that writes the baseline — so the detector cannot report two
|
|
26
|
-
* implementations disagreeing with each other as drift in the tree.
|
|
27
|
-
*
|
|
28
|
-
* Pure-ish: the scorer and the baseline loader are both injectable, and the
|
|
29
|
-
* module never writes anything, exits, or emits friction.
|
|
30
|
-
*
|
|
31
|
-
* The public surface is deliberately the three symbols the CLI actually
|
|
32
|
-
* uses — `DRIFT_KINDS`, `detectBaselineDrift`, `formatDriftReport`. Every
|
|
33
|
-
* internal helper below stays module-local and is exercised through them
|
|
34
|
-
* (`detectBaselineDrift` forwards its `loadBaselineRows` / `scoreFullScope`
|
|
35
|
-
* seams straight down). Exporting the helpers so tests could reach them
|
|
36
|
-
* directly would ship five entry points nothing in production reaches —
|
|
37
|
-
* exactly the orphaning this Story exists to stop.
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
import { getQuality } from '../config/quality.js';
|
|
41
|
-
import { resolveConfig } from '../config-resolver.js';
|
|
42
|
-
import { getKindModule } from './kernel.js';
|
|
43
|
-
import { load as loadBaseline } from './reader.js';
|
|
44
|
-
import { resolveDefaultScorer } from './refresh-service.js';
|
|
45
|
-
|
|
46
|
-
/** The kinds whose rows carry a per-file/per-method score worth re-scoring. */
|
|
47
|
-
export const DRIFT_KINDS = Object.freeze(['maintainability', 'crap']);
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Per-kind identity, metric axis, and refresh remedy. `identity` must match
|
|
51
|
-
* the granularity the kind's baseline rows are keyed at, or unchanged rows
|
|
52
|
-
* masquerade as added/removed pairs.
|
|
53
|
-
*/
|
|
54
|
-
const KIND_SPECS = Object.freeze({
|
|
55
|
-
maintainability: Object.freeze({
|
|
56
|
-
metric: 'mi',
|
|
57
|
-
identity: (row) => row.path,
|
|
58
|
-
label: (row) => row.path,
|
|
59
|
-
refreshCommand: 'npm run maintainability:update -- --full-scope',
|
|
60
|
-
defaultTolerance: 0.5,
|
|
61
|
-
}),
|
|
62
|
-
crap: Object.freeze({
|
|
63
|
-
metric: 'crap',
|
|
64
|
-
identity: (row) => `${row.path}::${row.method}@${row.startLine}`,
|
|
65
|
-
label: (row) => `${row.path}::${row.method} (line ${row.startLine})`,
|
|
66
|
-
refreshCommand: 'npm run crap:update -- --full-scope',
|
|
67
|
-
defaultTolerance: 0.001,
|
|
68
|
-
}),
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Resolve the absolute drift tolerance for a kind: an explicit override
|
|
73
|
-
* wins, then the gate's configured `tolerance.value`, then the per-kind
|
|
74
|
-
* default.
|
|
75
|
-
*
|
|
76
|
-
* @param {string} kind
|
|
77
|
-
* @param {object|undefined} gate resolved `delivery.quality.gates.<kind>`
|
|
78
|
-
* @param {number|null|undefined} override
|
|
79
|
-
* @returns {number}
|
|
80
|
-
*/
|
|
81
|
-
function resolveTolerance(kind, gate, override) {
|
|
82
|
-
if (typeof override === 'number' && Number.isFinite(override)) {
|
|
83
|
-
return Math.abs(override);
|
|
84
|
-
}
|
|
85
|
-
const configured = gate?.tolerance;
|
|
86
|
-
if (configured?.kind === 'absolute') {
|
|
87
|
-
const value = Number(configured.value);
|
|
88
|
-
if (Number.isFinite(value)) return Math.abs(value);
|
|
89
|
-
}
|
|
90
|
-
return KIND_SPECS[kind].defaultTolerance;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Normalise a scorer's raw rows into the kind's canonical on-disk row
|
|
95
|
-
* shape, so scored rows and baseline rows are directly comparable. The
|
|
96
|
-
* per-kind `projectRow` is the same projection the writer applies, which is
|
|
97
|
-
* what makes the two sides comparable at all (it also reconciles the CRAP
|
|
98
|
-
* scorer's `file` key with the envelope's `path`).
|
|
99
|
-
*
|
|
100
|
-
* @param {string} kind
|
|
101
|
-
* @param {Array<object>} rows
|
|
102
|
-
* @returns {Array<object>}
|
|
103
|
-
*/
|
|
104
|
-
function projectScoredRows(kind, rows) {
|
|
105
|
-
const mod = getKindModule(kind);
|
|
106
|
-
const out = [];
|
|
107
|
-
for (const row of rows ?? []) {
|
|
108
|
-
try {
|
|
109
|
-
out.push(mod.projectRow(row));
|
|
110
|
-
} catch {
|
|
111
|
-
// A row the writer itself would refuse is not evidence of drift.
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return out;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Diff two row sets by the kind's identity, classifying each key as
|
|
119
|
-
* drifted / added / removed. Pure.
|
|
120
|
-
*
|
|
121
|
-
* @param {{ kind: string, baselineRows: Array<object>, currentRows: Array<object>, tolerance: number }} opts
|
|
122
|
-
* @returns {{ drifted: Array<object>, added: Array<object>, removed: Array<object> }}
|
|
123
|
-
*/
|
|
124
|
-
function diffRows({ kind, baselineRows, currentRows, tolerance }) {
|
|
125
|
-
const spec = KIND_SPECS[kind];
|
|
126
|
-
const baseByKey = new Map();
|
|
127
|
-
for (const row of baselineRows ?? []) baseByKey.set(spec.identity(row), row);
|
|
128
|
-
|
|
129
|
-
const drifted = [];
|
|
130
|
-
const added = [];
|
|
131
|
-
const seen = new Set();
|
|
132
|
-
|
|
133
|
-
for (const row of currentRows ?? []) {
|
|
134
|
-
const key = spec.identity(row);
|
|
135
|
-
seen.add(key);
|
|
136
|
-
const base = baseByKey.get(key);
|
|
137
|
-
if (!base) {
|
|
138
|
-
added.push({ key, label: spec.label(row), current: row[spec.metric] });
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const before = Number(base[spec.metric] ?? 0);
|
|
142
|
-
const after = Number(row[spec.metric] ?? 0);
|
|
143
|
-
const delta = after - before;
|
|
144
|
-
if (Math.abs(delta) <= tolerance) continue;
|
|
145
|
-
drifted.push({
|
|
146
|
-
key,
|
|
147
|
-
label: spec.label(row),
|
|
148
|
-
baseline: before,
|
|
149
|
-
current: after,
|
|
150
|
-
delta,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const removed = [];
|
|
155
|
-
for (const [key, row] of baseByKey) {
|
|
156
|
-
if (seen.has(key)) continue;
|
|
157
|
-
removed.push({ key, label: spec.label(row), baseline: row[spec.metric] });
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return { drifted, added, removed };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Re-score one kind full-scope and diff the result against its committed
|
|
165
|
-
* baseline.
|
|
166
|
-
*
|
|
167
|
-
* Returns `{ ok: true, skipped: '<reason>' }` when the kind cannot be
|
|
168
|
-
* checked (gate disabled, no baseline on disk, no scorer registered, the
|
|
169
|
-
* scorer produced nothing) — an unscorable kind is not drift, and a
|
|
170
|
-
* scheduled job must not go red because coverage happened to be absent.
|
|
171
|
-
*
|
|
172
|
-
* @param {{
|
|
173
|
-
* kind: string,
|
|
174
|
-
* cwd?: string,
|
|
175
|
-
* quality?: object,
|
|
176
|
-
* tolerance?: number|null,
|
|
177
|
-
* loadBaselineRows?: (kind: string, cwd: string) => Array<object>|null,
|
|
178
|
-
* scoreFullScope?: (kind: string, cwd: string) => Promise<Array<object>|null>|Array<object>|null,
|
|
179
|
-
* }} opts
|
|
180
|
-
* @returns {Promise<object>}
|
|
181
|
-
*/
|
|
182
|
-
async function detectKindDrift({
|
|
183
|
-
kind,
|
|
184
|
-
cwd = process.cwd(),
|
|
185
|
-
quality,
|
|
186
|
-
tolerance = null,
|
|
187
|
-
loadBaselineRows = defaultLoadBaselineRows,
|
|
188
|
-
scoreFullScope = defaultScoreFullScope,
|
|
189
|
-
}) {
|
|
190
|
-
if (!Object.hasOwn(KIND_SPECS, kind)) {
|
|
191
|
-
throw new Error(
|
|
192
|
-
`[drift] unknown kind "${kind}"; expected one of ${DRIFT_KINDS.join(', ')}`,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
const gate = quality?.[kind];
|
|
196
|
-
const spec = KIND_SPECS[kind];
|
|
197
|
-
const base = { kind, refreshCommand: spec.refreshCommand };
|
|
198
|
-
if (gate?.enabled === false) {
|
|
199
|
-
return { ...base, ok: true, skipped: 'gate-disabled' };
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const baselineRows = loadBaselineRows(kind, cwd);
|
|
203
|
-
if (!Array.isArray(baselineRows) || baselineRows.length === 0) {
|
|
204
|
-
return { ...base, ok: true, skipped: 'no-baseline' };
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const raw = await scoreFullScope(kind, cwd);
|
|
208
|
-
if (raw === null || raw === undefined) {
|
|
209
|
-
return { ...base, ok: true, skipped: 'no-scorer' };
|
|
210
|
-
}
|
|
211
|
-
const currentRows = projectScoredRows(kind, raw);
|
|
212
|
-
if (currentRows.length === 0) {
|
|
213
|
-
return { ...base, ok: true, skipped: 'no-scored-rows' };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const resolvedTolerance = resolveTolerance(kind, gate, tolerance);
|
|
217
|
-
const { drifted, added, removed } = diffRows({
|
|
218
|
-
kind,
|
|
219
|
-
baselineRows,
|
|
220
|
-
currentRows,
|
|
221
|
-
tolerance: resolvedTolerance,
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
...base,
|
|
226
|
-
ok: drifted.length === 0,
|
|
227
|
-
tolerance: resolvedTolerance,
|
|
228
|
-
scanned: currentRows.length,
|
|
229
|
-
baselineRows: baselineRows.length,
|
|
230
|
-
drifted,
|
|
231
|
-
added,
|
|
232
|
-
removed,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Run the drift check across several kinds.
|
|
238
|
-
*
|
|
239
|
-
* @param {{ kinds?: string[], cwd?: string, tolerance?: number|null, quality?: object }} opts
|
|
240
|
-
* @returns {Promise<{ ok: boolean, results: Array<object> }>}
|
|
241
|
-
*/
|
|
242
|
-
export async function detectBaselineDrift({
|
|
243
|
-
kinds = DRIFT_KINDS,
|
|
244
|
-
cwd = process.cwd(),
|
|
245
|
-
tolerance = null,
|
|
246
|
-
quality,
|
|
247
|
-
...seams
|
|
248
|
-
} = {}) {
|
|
249
|
-
let resolvedQuality = quality;
|
|
250
|
-
if (!resolvedQuality) {
|
|
251
|
-
try {
|
|
252
|
-
resolvedQuality = getQuality(resolveConfig({ cwd })) ?? {};
|
|
253
|
-
} catch {
|
|
254
|
-
resolvedQuality = {};
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const results = [];
|
|
258
|
-
for (const kind of kinds) {
|
|
259
|
-
results.push(
|
|
260
|
-
await detectKindDrift({
|
|
261
|
-
kind,
|
|
262
|
-
cwd,
|
|
263
|
-
tolerance,
|
|
264
|
-
quality: resolvedQuality,
|
|
265
|
-
...seams,
|
|
266
|
-
}),
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
return { ok: results.every((r) => r.ok), results };
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Default baseline loader — reads and schema-validates the committed
|
|
274
|
-
* envelope. Returns `null` when it cannot be read, which the caller maps to
|
|
275
|
-
* the `no-baseline` skip.
|
|
276
|
-
*/
|
|
277
|
-
function defaultLoadBaselineRows(kind, cwd) {
|
|
278
|
-
try {
|
|
279
|
-
return loadBaseline(kind, { cwd }).rows;
|
|
280
|
-
} catch {
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Default full-scope scorer — the same scorer `refreshBaseline` uses, run
|
|
287
|
-
* with `fullScope: true` so it walks every configured target directory
|
|
288
|
-
* rather than a diff-derived file list.
|
|
289
|
-
*/
|
|
290
|
-
async function defaultScoreFullScope(kind, cwd) {
|
|
291
|
-
const scorer = resolveDefaultScorer(kind, { cwd });
|
|
292
|
-
if (typeof scorer !== 'function') return null;
|
|
293
|
-
return await scorer(null, { fullScope: true, cwd });
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Render one kind's result as an operator-facing block: a per-row
|
|
298
|
-
* before/after table plus the refresh remedy. Returns a string always —
|
|
299
|
-
* a clean kind reports one line so a scheduled job's log shows it ran.
|
|
300
|
-
*
|
|
301
|
-
* @param {object} result
|
|
302
|
-
* @returns {string}
|
|
303
|
-
*/
|
|
304
|
-
function formatKindDrift(result) {
|
|
305
|
-
if (result.skipped) {
|
|
306
|
-
return `[drift] ⏭ ${result.kind}: skipped (${result.skipped})`;
|
|
307
|
-
}
|
|
308
|
-
if (result.ok) {
|
|
309
|
-
return `[drift] ✓ ${result.kind}: ${result.scanned} row(s) re-scored full-scope, no drift beyond ±${result.tolerance}`;
|
|
310
|
-
}
|
|
311
|
-
const width = Math.max(
|
|
312
|
-
12,
|
|
313
|
-
...result.drifted.map((d) => String(d.label).length),
|
|
314
|
-
);
|
|
315
|
-
const lines = [
|
|
316
|
-
`[drift] ✖ ${result.kind}: ${result.drifted.length} row(s) drifted beyond ±${result.tolerance} (${result.scanned} re-scored, ${result.baselineRows} in baseline)`,
|
|
317
|
-
` ${'ROW'.padEnd(width)} ${'BASELINE'.padStart(10)} ${'CURRENT'.padStart(10)} ${'DELTA'.padStart(10)}`,
|
|
318
|
-
];
|
|
319
|
-
for (const d of result.drifted) {
|
|
320
|
-
lines.push(
|
|
321
|
-
` ${String(d.label).padEnd(width)} ${d.baseline.toFixed(2).padStart(10)} ${d.current
|
|
322
|
-
.toFixed(2)
|
|
323
|
-
.padStart(
|
|
324
|
-
10,
|
|
325
|
-
)} ${(d.delta > 0 ? `+${d.delta.toFixed(2)}` : d.delta.toFixed(2)).padStart(10)}`,
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
if (result.added.length > 0 || result.removed.length > 0) {
|
|
329
|
-
lines.push(
|
|
330
|
-
` (${result.added.length} row(s) absent from baseline, ${result.removed.length} baseline row(s) absent from the tree)`,
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
lines.push(
|
|
334
|
-
` Remedy: run \`${result.refreshCommand}\` and commit the refreshed baseline with a \`baseline-refresh:\` tagged subject (non-empty body).`,
|
|
335
|
-
);
|
|
336
|
-
return lines.join('\n');
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Render the whole run.
|
|
341
|
-
*
|
|
342
|
-
* @param {{ ok: boolean, results: Array<object> }} run
|
|
343
|
-
* @returns {string}
|
|
344
|
-
*/
|
|
345
|
-
export function formatDriftReport(run) {
|
|
346
|
-
const body = run.results.map(formatKindDrift).join('\n');
|
|
347
|
-
const tail = run.ok
|
|
348
|
-
? '[drift] ✅ No baseline drift detected.'
|
|
349
|
-
: '[drift] ❌ Baseline drift detected — the committed baselines no longer describe the tree.';
|
|
350
|
-
return `${body}\n${tail}`;
|
|
351
|
-
}
|