@shrkcrft/cli 0.1.0-alpha.29 → 0.1.0-alpha.31
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/dist/asset-preview/apply-asset-preview.js +1 -1
- package/dist/authoring/authoring-kit.d.ts +9 -1
- package/dist/authoring/authoring-kit.d.ts.map +1 -1
- package/dist/authoring/authoring-kit.js +26 -13
- package/dist/bootstrap/unlinked-workspace-dependency.d.ts +11 -0
- package/dist/bootstrap/unlinked-workspace-dependency.d.ts.map +1 -0
- package/dist/bootstrap/unlinked-workspace-dependency.js +99 -0
- package/dist/command-registry.d.ts +100 -1
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +130 -3
- package/dist/commands/api-diff.command.d.ts.map +1 -1
- package/dist/commands/api-diff.command.js +12 -0
- package/dist/commands/apply.command.d.ts.map +1 -1
- package/dist/commands/apply.command.js +3 -0
- package/dist/commands/arch.command.d.ts.map +1 -1
- package/dist/commands/arch.command.js +26 -7
- package/dist/commands/architecture.command.d.ts.map +1 -1
- package/dist/commands/architecture.command.js +8 -2
- package/dist/commands/baseline.command.d.ts +76 -0
- package/dist/commands/baseline.command.d.ts.map +1 -1
- package/dist/commands/baseline.command.js +599 -75
- package/dist/commands/biome.command.d.ts.map +1 -1
- package/dist/commands/biome.command.js +20 -0
- package/dist/commands/boundaries.command.d.ts +9 -1
- package/dist/commands/boundaries.command.d.ts.map +1 -1
- package/dist/commands/boundaries.command.js +88 -20
- package/dist/commands/bundle.command.d.ts.map +1 -1
- package/dist/commands/bundle.command.js +57 -11
- package/dist/commands/cache-align.command.d.ts.map +1 -1
- package/dist/commands/cache-align.command.js +3 -1
- package/dist/commands/changelog-data.d.ts.map +1 -1
- package/dist/commands/changelog-data.js +164 -0
- package/dist/commands/changes.command.d.ts.map +1 -1
- package/dist/commands/changes.command.js +15 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +1260 -324
- package/dist/commands/checks.command.d.ts.map +1 -1
- package/dist/commands/checks.command.js +188 -40
- package/dist/commands/ci.command.d.ts.map +1 -1
- package/dist/commands/ci.command.js +35 -2
- package/dist/commands/code-intel.command.d.ts.map +1 -1
- package/dist/commands/code-intel.command.js +8 -1
- package/dist/commands/command-catalog.d.ts +23 -1
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +152 -35
- package/dist/commands/commands.command.d.ts +21 -3
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +172 -27
- package/dist/commands/compress.command.d.ts.map +1 -1
- package/dist/commands/compress.command.js +4 -0
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +50 -12
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +40 -5
- package/dist/commands/contract-templates.command.d.ts.map +1 -1
- package/dist/commands/contract-templates.command.js +9 -1
- package/dist/commands/contract.command.d.ts.map +1 -1
- package/dist/commands/contract.command.js +35 -0
- package/dist/commands/conventions.command.d.ts.map +1 -1
- package/dist/commands/conventions.command.js +189 -29
- package/dist/commands/coverage.command.d.ts.map +1 -1
- package/dist/commands/coverage.command.js +9 -0
- package/dist/commands/daily.commands.d.ts +0 -1
- package/dist/commands/daily.commands.d.ts.map +1 -1
- package/dist/commands/daily.commands.js +12 -76
- package/dist/commands/dashboard.command.d.ts.map +1 -1
- package/dist/commands/dashboard.command.js +10 -0
- package/dist/commands/dev.command.d.ts.map +1 -1
- package/dist/commands/dev.command.js +45 -2
- package/dist/commands/diagnostics.command.d.ts +0 -2
- package/dist/commands/diagnostics.command.d.ts.map +1 -1
- package/dist/commands/diagnostics.command.js +5 -78
- package/dist/commands/diff-check.command.d.ts +6 -4
- package/dist/commands/diff-check.command.d.ts.map +1 -1
- package/dist/commands/diff-check.command.js +190 -103
- package/dist/commands/docs-references.command.d.ts +20 -0
- package/dist/commands/docs-references.command.d.ts.map +1 -0
- package/dist/commands/docs-references.command.js +520 -0
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +60 -5
- package/dist/commands/drift.command.d.ts.map +1 -1
- package/dist/commands/drift.command.js +17 -4
- package/dist/commands/eslint.command.d.ts.map +1 -1
- package/dist/commands/eslint.command.js +20 -0
- package/dist/commands/export.command.d.ts.map +1 -1
- package/dist/commands/export.command.js +23 -1
- package/dist/commands/feedback-dispatch.command.d.ts.map +1 -1
- package/dist/commands/feedback-dispatch.command.js +29 -0
- package/dist/commands/feedback.command.d.ts.map +1 -1
- package/dist/commands/feedback.command.js +6 -1
- package/dist/commands/finish.command.d.ts.map +1 -1
- package/dist/commands/finish.command.js +32 -8
- package/dist/commands/fix.command.d.ts.map +1 -1
- package/dist/commands/fix.command.js +19 -0
- package/dist/commands/gate.command.d.ts +6 -1
- package/dist/commands/gate.command.d.ts.map +1 -1
- package/dist/commands/gate.command.js +50 -124
- package/dist/commands/gates.command.d.ts +54 -0
- package/dist/commands/gates.command.d.ts.map +1 -1
- package/dist/commands/gates.command.js +1314 -64
- package/dist/commands/gen.command.d.ts.map +1 -1
- package/dist/commands/gen.command.js +4 -0
- package/dist/commands/generated.command.d.ts +62 -0
- package/dist/commands/generated.command.d.ts.map +1 -1
- package/dist/commands/generated.command.js +639 -156
- package/dist/commands/graph-code-subverbs.d.ts +15 -0
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
- package/dist/commands/graph-code-subverbs.js +203 -25
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +79 -37
- package/dist/commands/help.command.d.ts +30 -6
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +323 -199
- package/dist/commands/helper.command.d.ts +1 -0
- package/dist/commands/helper.command.d.ts.map +1 -1
- package/dist/commands/helper.command.js +195 -56
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +115 -12
- package/dist/commands/infer.command.d.ts.map +1 -1
- package/dist/commands/infer.command.js +20 -45
- package/dist/commands/ingest.command.d.ts +0 -1
- package/dist/commands/ingest.command.d.ts.map +1 -1
- package/dist/commands/ingest.command.js +25 -41
- package/dist/commands/knowledge-author.command.d.ts +0 -3
- package/dist/commands/knowledge-author.command.d.ts.map +1 -1
- package/dist/commands/knowledge-author.command.js +10 -23
- package/dist/commands/knowledge.command.d.ts.map +1 -1
- package/dist/commands/knowledge.command.js +469 -129
- package/dist/commands/languages.command.d.ts.map +1 -1
- package/dist/commands/languages.command.js +27 -0
- package/dist/commands/lint.command.d.ts.map +1 -1
- package/dist/commands/lint.command.js +3 -0
- package/dist/commands/mcp.command.d.ts +18 -0
- package/dist/commands/mcp.command.d.ts.map +1 -1
- package/dist/commands/mcp.command.js +33 -8
- package/dist/commands/onboard.command.d.ts.map +1 -1
- package/dist/commands/onboard.command.js +20 -0
- package/dist/commands/owners.command.d.ts.map +1 -1
- package/dist/commands/owners.command.js +14 -2
- package/dist/commands/packs-new.d.ts +13 -1
- package/dist/commands/packs-new.d.ts.map +1 -1
- package/dist/commands/packs-new.js +500 -335
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +378 -36
- package/dist/commands/paths.command.d.ts.map +1 -1
- package/dist/commands/paths.command.js +9 -1
- package/dist/commands/pipelines.command.d.ts.map +1 -1
- package/dist/commands/pipelines.command.js +7 -1
- package/dist/commands/plan-check.command.d.ts.map +1 -1
- package/dist/commands/plan-check.command.js +3 -0
- package/dist/commands/plan.command.d.ts.map +1 -1
- package/dist/commands/plan.command.js +3 -0
- package/dist/commands/playbooks.command.d.ts.map +1 -1
- package/dist/commands/playbooks.command.js +7 -2
- package/dist/commands/policy-lint.command.d.ts +19 -2
- package/dist/commands/policy-lint.command.d.ts.map +1 -1
- package/dist/commands/policy-lint.command.js +224 -65
- package/dist/commands/policy.command.d.ts.map +1 -1
- package/dist/commands/policy.command.js +6 -1
- package/dist/commands/pr.command.d.ts.map +1 -1
- package/dist/commands/pr.command.js +14 -0
- package/dist/commands/presets.command.d.ts.map +1 -1
- package/dist/commands/presets.command.js +12 -27
- package/dist/commands/profiles.command.d.ts.map +1 -1
- package/dist/commands/profiles.command.js +94 -26
- package/dist/commands/provenance.command.js +1 -1
- package/dist/commands/quality.command.d.ts.map +1 -1
- package/dist/commands/quality.command.js +129 -33
- package/dist/commands/recommend.command.d.ts +20 -4
- package/dist/commands/recommend.command.d.ts.map +1 -1
- package/dist/commands/recommend.command.js +172 -278
- package/dist/commands/registrations.command.d.ts.map +1 -1
- package/dist/commands/registrations.command.js +138 -13
- package/dist/commands/registry-lifecycle-run.d.ts +14 -0
- package/dist/commands/registry-lifecycle-run.d.ts.map +1 -0
- package/dist/commands/registry-lifecycle-run.js +336 -0
- package/dist/commands/registry.command.d.ts.map +1 -1
- package/dist/commands/registry.command.js +235 -62
- package/dist/commands/release.command.d.ts.map +1 -1
- package/dist/commands/release.command.js +99 -20
- package/dist/commands/report.command.d.ts.map +1 -1
- package/dist/commands/report.command.js +38 -2
- package/dist/commands/reuse-coverage.command.d.ts +23 -0
- package/dist/commands/reuse-coverage.command.d.ts.map +1 -0
- package/dist/commands/reuse-coverage.command.js +536 -0
- package/dist/commands/reuse.command.d.ts +3 -18
- package/dist/commands/reuse.command.d.ts.map +1 -1
- package/dist/commands/reuse.command.js +395 -244
- package/dist/commands/review.command.d.ts.map +1 -1
- package/dist/commands/review.command.js +33 -1
- package/dist/commands/rounds.command.d.ts.map +1 -1
- package/dist/commands/rounds.command.js +5 -0
- package/dist/commands/safety.command.d.ts.map +1 -1
- package/dist/commands/safety.command.js +9 -0
- package/dist/commands/scaffolds.command.d.ts.map +1 -1
- package/dist/commands/scaffolds.command.js +97 -25
- package/dist/commands/search.command.d.ts +0 -8
- package/dist/commands/search.command.d.ts.map +1 -1
- package/dist/commands/search.command.js +136 -28
- package/dist/commands/self-config-xrefs.command.d.ts +24 -0
- package/dist/commands/self-config-xrefs.command.d.ts.map +1 -0
- package/dist/commands/self-config-xrefs.command.js +170 -0
- package/dist/commands/self-config.command.d.ts.map +1 -1
- package/dist/commands/self-config.command.js +261 -65
- package/dist/commands/smart-context.command.d.ts.map +1 -1
- package/dist/commands/smart-context.command.js +39 -3
- package/dist/commands/spec.command.d.ts.map +1 -1
- package/dist/commands/spec.command.js +3 -0
- package/dist/commands/stats.command.d.ts.map +1 -1
- package/dist/commands/stats.command.js +3 -0
- package/dist/commands/surface.command.d.ts +6 -1
- package/dist/commands/surface.command.d.ts.map +1 -1
- package/dist/commands/surface.command.js +256 -54
- package/dist/commands/task-context.command.js +1 -1
- package/dist/commands/task.command.d.ts.map +1 -1
- package/dist/commands/task.command.js +22 -1
- package/dist/commands/templates.command.d.ts +4 -0
- package/dist/commands/templates.command.d.ts.map +1 -1
- package/dist/commands/templates.command.js +109 -22
- package/dist/commands/test.command.d.ts.map +1 -1
- package/dist/commands/test.command.js +161 -33
- package/dist/commands/tests.command.d.ts.map +1 -1
- package/dist/commands/tests.command.js +29 -3
- package/dist/commands/trace.command.d.ts.map +1 -1
- package/dist/commands/trace.command.js +29 -4
- package/dist/commands/watch.command.d.ts.map +1 -1
- package/dist/commands/watch.command.js +3 -0
- package/dist/commands/why.command.d.ts.map +1 -1
- package/dist/commands/why.command.js +3 -0
- package/dist/commands/wiring.command.d.ts +18 -4
- package/dist/commands/wiring.command.d.ts.map +1 -1
- package/dist/commands/wiring.command.js +362 -74
- package/dist/dashboard/code-intelligence-data.js +4 -4
- package/dist/diff/deleted-orphan-coverage.d.ts +32 -0
- package/dist/diff/deleted-orphan-coverage.d.ts.map +1 -0
- package/dist/diff/deleted-orphan-coverage.js +107 -0
- package/dist/diff/deleted-orphan-scope-notes.d.ts +23 -0
- package/dist/diff/deleted-orphan-scope-notes.d.ts.map +1 -0
- package/dist/diff/deleted-orphan-scope-notes.js +49 -0
- package/dist/diff/deleted-orphans.d.ts +32 -0
- package/dist/diff/deleted-orphans.d.ts.map +1 -1
- package/dist/diff/deleted-orphans.js +42 -2
- package/dist/dispatch/closest-match.d.ts +15 -0
- package/dist/dispatch/closest-match.d.ts.map +1 -0
- package/dist/dispatch/closest-match.js +47 -0
- package/dist/dispatch/global-flags.d.ts +94 -0
- package/dist/dispatch/global-flags.d.ts.map +1 -0
- package/dist/dispatch/global-flags.js +123 -0
- package/dist/dispatch/guard-invocation-input.d.ts +24 -0
- package/dist/dispatch/guard-invocation-input.d.ts.map +1 -0
- package/dist/dispatch/guard-invocation-input.js +1 -0
- package/dist/dispatch/guard-invocation.d.ts +33 -0
- package/dist/dispatch/guard-invocation.d.ts.map +1 -0
- package/dist/dispatch/guard-invocation.js +183 -0
- package/dist/dispatch/help-intercept.d.ts +22 -0
- package/dist/dispatch/help-intercept.d.ts.map +1 -0
- package/dist/dispatch/help-intercept.js +42 -0
- package/dist/dispatch/i-declared-invocation.d.ts +17 -0
- package/dist/dispatch/i-declared-invocation.d.ts.map +1 -0
- package/dist/dispatch/i-declared-invocation.js +1 -0
- package/dist/dispatch/i-unknown-flag-refusal-input.d.ts +28 -0
- package/dist/dispatch/i-unknown-flag-refusal-input.d.ts.map +1 -0
- package/dist/dispatch/i-unknown-flag-refusal-input.js +1 -0
- package/dist/dispatch/invocation-rejection-kind.d.ts +15 -0
- package/dist/dispatch/invocation-rejection-kind.d.ts.map +1 -0
- package/dist/dispatch/invocation-rejection-kind.js +15 -0
- package/dist/dispatch/invocation-rejection.d.ts +18 -0
- package/dist/dispatch/invocation-rejection.d.ts.map +1 -0
- package/dist/dispatch/invocation-rejection.js +1 -0
- package/dist/dispatch/judge-invocation.d.ts +24 -0
- package/dist/dispatch/judge-invocation.d.ts.map +1 -0
- package/dist/dispatch/judge-invocation.js +50 -0
- package/dist/dispatch/positional-mode.d.ts +18 -0
- package/dist/dispatch/positional-mode.d.ts.map +1 -0
- package/dist/dispatch/positional-mode.js +18 -0
- package/dist/dispatch/read-tracking-map.d.ts +28 -0
- package/dist/dispatch/read-tracking-map.d.ts.map +1 -0
- package/dist/dispatch/read-tracking-map.js +58 -0
- package/dist/dispatch/refuse-sibling-valve-flags.d.ts +30 -0
- package/dist/dispatch/refuse-sibling-valve-flags.d.ts.map +1 -0
- package/dist/dispatch/refuse-sibling-valve-flags.js +65 -0
- package/dist/dispatch/subverb-spec.d.ts +26 -0
- package/dist/dispatch/subverb-spec.d.ts.map +1 -0
- package/dist/dispatch/subverb-spec.js +1 -0
- package/dist/dispatch/undocumented-flag-reads.d.ts +22 -0
- package/dist/dispatch/undocumented-flag-reads.d.ts.map +1 -0
- package/dist/dispatch/undocumented-flag-reads.js +283 -0
- package/dist/dispatch/unknown-flag-refusal-mode.d.ts +13 -0
- package/dist/dispatch/unknown-flag-refusal-mode.d.ts.map +1 -0
- package/dist/dispatch/unknown-flag-refusal-mode.js +13 -0
- package/dist/dispatch/unknown-flag-refusal.d.ts +22 -0
- package/dist/dispatch/unknown-flag-refusal.d.ts.map +1 -0
- package/dist/dispatch/unknown-flag-refusal.js +57 -0
- package/dist/dispatch/unread-flags.d.ts +152 -0
- package/dist/dispatch/unread-flags.d.ts.map +1 -0
- package/dist/dispatch/unread-flags.js +295 -0
- package/dist/dispatch/verdict-valve-flag.d.ts +21 -0
- package/dist/dispatch/verdict-valve-flag.d.ts.map +1 -0
- package/dist/dispatch/verdict-valve-flag.js +21 -0
- package/dist/dispatch/walk-declared-subverbs.d.ts +14 -0
- package/dist/dispatch/walk-declared-subverbs.d.ts.map +1 -0
- package/dist/dispatch/walk-declared-subverbs.js +28 -0
- package/dist/exit-codes.d.ts +41 -3
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +153 -14
- package/dist/finish/run-finish.d.ts +72 -5
- package/dist/finish/run-finish.d.ts.map +1 -1
- package/dist/finish/run-finish.js +453 -104
- package/dist/gates/accepted-empty-note.d.ts +10 -0
- package/dist/gates/accepted-empty-note.d.ts.map +1 -0
- package/dist/gates/accepted-empty-note.js +11 -0
- package/dist/gates/allow-empty.d.ts +25 -0
- package/dist/gates/allow-empty.d.ts.map +1 -0
- package/dist/gates/allow-empty.js +27 -0
- package/dist/gates/asset-doctor-failing-units.d.ts +16 -0
- package/dist/gates/asset-doctor-failing-units.d.ts.map +1 -0
- package/dist/gates/asset-doctor-failing-units.js +14 -0
- package/dist/gates/asset-doctor-failure-line.d.ts +16 -0
- package/dist/gates/asset-doctor-failure-line.d.ts.map +1 -0
- package/dist/gates/asset-doctor-failure-line.js +28 -0
- package/dist/gates/empty-rule-advice-lines.d.ts +15 -0
- package/dist/gates/empty-rule-advice-lines.d.ts.map +1 -0
- package/dist/gates/empty-rule-advice-lines.js +20 -0
- package/dist/gates/gate-envelope.d.ts +85 -8
- package/dist/gates/gate-envelope.d.ts.map +1 -1
- package/dist/gates/gate-envelope.js +56 -7
- package/dist/gates/gate-rule-globs.d.ts +67 -0
- package/dist/gates/gate-rule-globs.d.ts.map +1 -0
- package/dist/gates/gate-rule-globs.js +139 -0
- package/dist/gates/gate-rule-view.d.ts +25 -6
- package/dist/gates/gate-rule-view.d.ts.map +1 -1
- package/dist/gates/gate-rule-view.js +105 -77
- package/dist/gates/i-coverage-dead-glob.d.ts +19 -0
- package/dist/gates/i-coverage-dead-glob.d.ts.map +1 -0
- package/dist/gates/i-coverage-dead-glob.js +1 -0
- package/dist/gates/i-coverage-negation.d.ts +13 -0
- package/dist/gates/i-coverage-negation.d.ts.map +1 -0
- package/dist/gates/i-coverage-negation.js +1 -0
- package/dist/gates/i-registration-query-settle-input.d.ts +18 -0
- package/dist/gates/i-registration-query-settle-input.d.ts.map +1 -0
- package/dist/gates/i-registration-query-settle-input.js +1 -0
- package/dist/gates/i-unit-state-note-row.d.ts +16 -0
- package/dist/gates/i-unit-state-note-row.d.ts.map +1 -0
- package/dist/gates/i-unit-state-note-row.js +1 -0
- package/dist/gates/i-unit-state-notes.d.ts +32 -0
- package/dist/gates/i-unit-state-notes.d.ts.map +1 -0
- package/dist/gates/i-unit-state-notes.js +1 -0
- package/dist/gates/incomplete-registry-inventory.d.ts +19 -0
- package/dist/gates/incomplete-registry-inventory.d.ts.map +1 -0
- package/dist/gates/incomplete-registry-inventory.js +63 -0
- package/dist/gates/measure-registration-roles.d.ts +10 -0
- package/dist/gates/measure-registration-roles.d.ts.map +1 -0
- package/dist/gates/measure-registration-roles.js +9 -0
- package/dist/gates/plane-verdict.d.ts +14 -0
- package/dist/gates/plane-verdict.d.ts.map +1 -0
- package/dist/gates/plane-verdict.js +22 -0
- package/dist/gates/qualify-clean-for-units.d.ts +12 -0
- package/dist/gates/qualify-clean-for-units.d.ts.map +1 -0
- package/dist/gates/qualify-clean-for-units.js +18 -0
- package/dist/gates/registration-graph-verdict.d.ts +34 -0
- package/dist/gates/registration-graph-verdict.d.ts.map +1 -0
- package/dist/gates/registration-graph-verdict.js +70 -0
- package/dist/gates/registration-roles.d.ts +9 -0
- package/dist/gates/registration-roles.d.ts.map +1 -0
- package/dist/gates/registration-roles.js +1 -0
- package/dist/gates/registry-liveness.d.ts +11 -0
- package/dist/gates/registry-liveness.d.ts.map +1 -0
- package/dist/gates/registry-liveness.js +13 -0
- package/dist/gates/rule-coverage.d.ts +225 -4
- package/dist/gates/rule-coverage.d.ts.map +1 -1
- package/dist/gates/rule-coverage.js +610 -43
- package/dist/gates/run-gate-planes.d.ts +58 -0
- package/dist/gates/run-gate-planes.d.ts.map +1 -0
- package/dist/gates/run-gate-planes.js +403 -0
- package/dist/gates/scaffold-selftest.d.ts +56 -0
- package/dist/gates/scaffold-selftest.d.ts.map +1 -0
- package/dist/gates/scaffold-selftest.js +119 -0
- package/dist/gates/seam-rejected-rules.d.ts +28 -0
- package/dist/gates/seam-rejected-rules.d.ts.map +1 -0
- package/dist/gates/seam-rejected-rules.js +95 -0
- package/dist/gates/self-test-check.d.ts +29 -0
- package/dist/gates/self-test-check.d.ts.map +1 -0
- package/dist/gates/self-test-check.js +1 -0
- package/dist/gates/self-test-subject.d.ts +28 -0
- package/dist/gates/self-test-subject.d.ts.map +1 -0
- package/dist/gates/self-test-subject.js +1 -0
- package/dist/gates/self-test.d.ts +20 -0
- package/dist/gates/self-test.d.ts.map +1 -0
- package/dist/gates/self-test.js +82 -0
- package/dist/gates/settle-verdict.d.ts +14 -0
- package/dist/gates/settle-verdict.d.ts.map +1 -0
- package/dist/gates/settle-verdict.js +13 -0
- package/dist/gates/settled-verdict.d.ts +8 -0
- package/dist/gates/settled-verdict.d.ts.map +1 -0
- package/dist/gates/settled-verdict.js +1 -0
- package/dist/gates/template-registry-coverage.d.ts +18 -0
- package/dist/gates/template-registry-coverage.d.ts.map +1 -0
- package/dist/gates/template-registry-coverage.js +24 -0
- package/dist/gates/unit-state-notes.d.ts +40 -0
- package/dist/gates/unit-state-notes.d.ts.map +1 -0
- package/dist/gates/unit-state-notes.js +71 -0
- package/dist/gates/verdict-line.d.ts +18 -0
- package/dist/gates/verdict-line.d.ts.map +1 -0
- package/dist/gates/verdict-line.js +40 -0
- package/dist/graph/graph-reuse-lookup.d.ts +10 -0
- package/dist/graph/graph-reuse-lookup.d.ts.map +1 -0
- package/dist/graph/graph-reuse-lookup.js +41 -0
- package/dist/graph/index-behind-hint.d.ts +11 -0
- package/dist/graph/index-behind-hint.d.ts.map +1 -0
- package/dist/graph/index-behind-hint.js +27 -0
- package/dist/knowledge/knowledge-stale-gate-flags.d.ts +3 -0
- package/dist/knowledge/knowledge-stale-gate-flags.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate-flags.js +1 -0
- package/dist/knowledge/knowledge-stale-gate-input.d.ts +3 -0
- package/dist/knowledge/knowledge-stale-gate-input.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate-input.js +1 -0
- package/dist/knowledge/knowledge-stale-gate-result.d.ts +3 -0
- package/dist/knowledge/knowledge-stale-gate-result.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate-result.js +1 -0
- package/dist/knowledge/knowledge-stale-gate.d.ts +10 -0
- package/dist/knowledge/knowledge-stale-gate.d.ts.map +1 -0
- package/dist/knowledge/knowledge-stale-gate.js +9 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +204 -83
- package/dist/output/failure-hints.d.ts.map +1 -1
- package/dist/output/failure-hints.js +11 -9
- package/dist/output/rejected-entries-note.d.ts +57 -0
- package/dist/output/rejected-entries-note.d.ts.map +1 -0
- package/dist/output/rejected-entries-note.js +103 -0
- package/dist/output/stdout-is-pipe.d.ts +13 -0
- package/dist/output/stdout-is-pipe.d.ts.map +1 -0
- package/dist/output/stdout-is-pipe.js +21 -0
- package/dist/packs/stale-build-warning.d.ts +11 -0
- package/dist/packs/stale-build-warning.d.ts.map +1 -0
- package/dist/packs/stale-build-warning.js +23 -0
- package/dist/quality/run-quality.d.ts +123 -0
- package/dist/quality/run-quality.d.ts.map +1 -0
- package/dist/quality/run-quality.js +442 -0
- package/dist/schemas/json-schemas.d.ts +184 -3
- package/dist/schemas/json-schemas.d.ts.map +1 -1
- package/dist/schemas/json-schemas.js +113 -2
- package/dist/shrk.d.ts +3 -0
- package/dist/shrk.d.ts.map +1 -0
- package/dist/shrk.js +30 -0
- package/dist/surface/audience-applicability.d.ts +26 -0
- package/dist/surface/audience-applicability.d.ts.map +1 -0
- package/dist/surface/audience-applicability.js +60 -0
- package/dist/surface/catalog-command-safety.d.ts +18 -0
- package/dist/surface/catalog-command-safety.d.ts.map +1 -0
- package/dist/surface/catalog-command-safety.js +62 -0
- package/dist/surface/cli-command-resolver.d.ts +21 -0
- package/dist/surface/cli-command-resolver.d.ts.map +1 -0
- package/dist/surface/cli-command-resolver.js +87 -0
- package/dist/surface/command-dispatch-kind.d.ts +20 -0
- package/dist/surface/command-dispatch-kind.d.ts.map +1 -0
- package/dist/surface/command-dispatch-kind.js +20 -0
- package/dist/surface/command-index-entry.d.ts +40 -0
- package/dist/surface/command-index-entry.d.ts.map +1 -0
- package/dist/surface/command-index-entry.js +1 -0
- package/dist/surface/command-index.d.ts +77 -0
- package/dist/surface/command-index.d.ts.map +1 -0
- package/dist/surface/command-index.js +323 -0
- package/dist/surface/i-command-index.d.ts +22 -0
- package/dist/surface/i-command-index.d.ts.map +1 -0
- package/dist/surface/i-command-index.js +1 -0
- package/dist/surface/i-command-string-context.d.ts +20 -0
- package/dist/surface/i-command-string-context.d.ts.map +1 -0
- package/dist/surface/i-command-string-context.js +1 -0
- package/dist/surface/i-surface-denial.d.ts +13 -0
- package/dist/surface/i-surface-denial.d.ts.map +1 -0
- package/dist/surface/i-surface-denial.js +1 -0
- package/dist/surface/load-surface-context.d.ts +17 -1
- package/dist/surface/load-surface-context.d.ts.map +1 -1
- package/dist/surface/load-surface-context.js +48 -10
- package/dist/surface/not-enabled-error.d.ts +38 -4
- package/dist/surface/not-enabled-error.d.ts.map +1 -1
- package/dist/surface/not-enabled-error.js +96 -6
- package/dist/surface/profiles.d.ts +7 -0
- package/dist/surface/profiles.d.ts.map +1 -1
- package/dist/surface/resolve-command-string.d.ts +10 -0
- package/dist/surface/resolve-command-string.d.ts.map +1 -0
- package/dist/surface/resolve-command-string.js +572 -0
- package/dist/surface/surface-config-writer.d.ts +7 -2
- package/dist/surface/surface-config-writer.d.ts.map +1 -1
- package/dist/surface/surface-config-writer.js +25 -6
- package/dist/surface/surface-layer.d.ts +11 -0
- package/dist/surface/surface-layer.d.ts.map +1 -0
- package/dist/surface/surface-layer.js +11 -0
- package/dist/surface/surface-refusal-reason.d.ts +16 -0
- package/dist/surface/surface-refusal-reason.d.ts.map +1 -0
- package/dist/surface/surface-refusal-reason.js +16 -0
- package/dist/surface/surface-selector.d.ts +26 -0
- package/dist/surface/surface-selector.d.ts.map +1 -0
- package/dist/surface/surface-selector.js +42 -0
- package/dist/surface/surface-summary.d.ts +61 -18
- package/dist/surface/surface-summary.d.ts.map +1 -1
- package/dist/surface/surface-summary.js +147 -60
- package/dist/surface/tier.d.ts +85 -22
- package/dist/surface/tier.d.ts.map +1 -1
- package/dist/surface/tier.js +132 -30
- package/dist/usage/usage-log.d.ts +6 -1
- package/dist/usage/usage-log.d.ts.map +1 -1
- package/dist/usage/usage-log.js +7 -2
- package/dist/validation/run-validation-loop.d.ts +9 -0
- package/dist/validation/run-validation-loop.d.ts.map +1 -1
- package/dist/validation/run-validation-loop.js +14 -11
- package/dist/validation/typecheck-emitted.d.ts +8 -9
- package/dist/validation/typecheck-emitted.d.ts.map +1 -1
- package/dist/validation/typecheck-emitted.js +17 -97
- package/package.json +34 -34
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ISharkcraftInspection } from '@shrkcrft/inspector';
|
|
2
|
+
import type { IGateRuleResult } from './gate-envelope.js';
|
|
3
|
+
import type { IUnitStateNoteRow } from './i-unit-state-note-row.js';
|
|
4
|
+
import type { IGateRuleView } from './gate-rule-view.js';
|
|
5
|
+
/**
|
|
6
|
+
* Run every data-defined plane's VIOLATION check and normalize the results.
|
|
7
|
+
*
|
|
8
|
+
* Each plane already has a verb, but gating a change meant running five of them
|
|
9
|
+
* and `&&`-chaining five exit codes — so in practice people ran one or two. The
|
|
10
|
+
* value here is that one call answers "does this change violate any declared
|
|
11
|
+
* rule?" with one exit code, and it does so by calling the SAME evaluators the
|
|
12
|
+
* per-plane verbs call. A second implementation of any plane's check would
|
|
13
|
+
* eventually disagree with the verb, and the aggregate — the one wired into CI
|
|
14
|
+
* — would be the copy nobody notices is wrong.
|
|
15
|
+
*
|
|
16
|
+
* The same holds for COVERAGE: each rule's `coverage` comes from the plane's
|
|
17
|
+
* own coverage function (the one its verb uses), so `gates check` and the
|
|
18
|
+
* per-plane verb can never disagree about how much of a rule's scope was
|
|
19
|
+
* examined. Round 13: so does each rule's `expectEmpty` acceptance
|
|
20
|
+
* (`unitAcceptance`, folded into the envelope's one settle) and its unit lines.
|
|
21
|
+
*/
|
|
22
|
+
export interface IRunGatePlanesOptions {
|
|
23
|
+
readonly cwd: string;
|
|
24
|
+
readonly excludeDirs: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* Restrict to rules whose footprint intersects these changed files. Scoping
|
|
27
|
+
* is applied by the CALLER (it owns the git diff); this is passed through to
|
|
28
|
+
* the plane engines that scope internally.
|
|
29
|
+
*/
|
|
30
|
+
readonly changedFiles?: readonly string[];
|
|
31
|
+
/**
|
|
32
|
+
* Skip every check that would SPAWN a shell (a `command` baseline compute, a
|
|
33
|
+
* generated `regen`). The header/classification halves still run, so the
|
|
34
|
+
* result stays honest — a skipped drift check is reported as skipped, never
|
|
35
|
+
* folded into a pass.
|
|
36
|
+
*/
|
|
37
|
+
readonly noSpawn?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Registries for the doc-reference plane. Built lazily by the caller — a
|
|
40
|
+
* repo without that plane never loads them.
|
|
41
|
+
*/
|
|
42
|
+
readonly inspection?: ISharkcraftInspection;
|
|
43
|
+
}
|
|
44
|
+
/** One plane's contribution, plus any engine diagnostics it produced. */
|
|
45
|
+
export interface IGatePlaneRun {
|
|
46
|
+
readonly results: readonly IGateRuleResult[];
|
|
47
|
+
readonly diagnostics: readonly string[];
|
|
48
|
+
/**
|
|
49
|
+
* Each rule's settled non-live selector units (round 13, K2) — what THE
|
|
50
|
+
* shared unit-state renderer (`unitStateNotes`) reads, so `gates check` lists
|
|
51
|
+
* a dead unit or a went-live marker exactly as the plane verb does. Kept off
|
|
52
|
+
* the envelope rows, which carry the `units` lines only.
|
|
53
|
+
*/
|
|
54
|
+
readonly noteRows?: readonly IUnitStateNoteRow[];
|
|
55
|
+
}
|
|
56
|
+
/** Evaluate the given rules, dispatching each to its plane's real engine. */
|
|
57
|
+
export declare function runGatePlanes(rules: readonly IGateRuleView[], options: IRunGatePlanesOptions): IGatePlaneRun;
|
|
58
|
+
//# sourceMappingURL=run-gate-planes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-gate-planes.d.ts","sourceRoot":"","sources":["../../src/gates/run-gate-planes.ts"],"names":[],"mappings":"AAYA,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AA+BrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIpE;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,qBAAqB,CAAC;CAC7C;AAED,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAClD;AAqCD,6EAA6E;AAC7E,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,OAAO,EAAE,qBAAqB,GAC7B,aAAa,CA2Bf"}
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { failsWhenEmpty, resolveSourceGlobs, RuleEmptiness, settleRuleEmptiness, unitStateLists, UnitLivenessState, } from '@shrkcrft/core';
|
|
2
|
+
import { checkDocReferences } from '@shrkcrft/inspector';
|
|
3
|
+
import { buildRegistrationGraph, globListSelects, readScopeCoverage, readScopeHasUnread, registrationOrphans, registrationUnprovided, registryDuplicates, runPolicyLint, runWiring, scanRegistry, settleGlobLists, } from '@shrkcrft/boundaries';
|
|
4
|
+
import { baselineCoverage, evaluateBaselineRule } from "../commands/baseline.command.js";
|
|
5
|
+
import { evaluateGeneratedRule, generatedCoverage, generatedHintFor, } from "../commands/generated.command.js";
|
|
6
|
+
import { docReferenceCoverage } from "../commands/docs-references.command.js";
|
|
7
|
+
import { policyRuleCoverage } from "../commands/policy-lint.command.js";
|
|
8
|
+
import { measureRegistrationRoles } from "./measure-registration-roles.js";
|
|
9
|
+
import { registryLiveness } from "./registry-liveness.js";
|
|
10
|
+
/** The unit fields a plane engine put on its result, passed through to the envelope rule (round 13). */
|
|
11
|
+
function unitFields(r) {
|
|
12
|
+
return {
|
|
13
|
+
...(r.unitAcceptance !== undefined ? { unitAcceptance: r.unitAcceptance } : {}),
|
|
14
|
+
...(r.units !== undefined ? { units: r.units } : {}),
|
|
15
|
+
...(r.unitLiveness !== undefined && r.unitLiveness.length > 0 ? { unitLiveness: r.unitLiveness } : {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** The same, from a settle this module ran itself. */
|
|
19
|
+
function livenessFields(l) {
|
|
20
|
+
if (l === undefined)
|
|
21
|
+
return {};
|
|
22
|
+
const nonLive = l.units.filter((u) => u.state !== UnitLivenessState.Live);
|
|
23
|
+
return {
|
|
24
|
+
...(l.acceptance !== undefined ? { unitAcceptance: l.acceptance } : {}),
|
|
25
|
+
...(l.dead.length + l.intendedEmpty.length + l.wentLive.length > 0 ? { units: unitStateLists(l) } : {}),
|
|
26
|
+
...(nonLive.length > 0 ? { unitLiveness: nonLive } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Evaluate the given rules, dispatching each to its plane's real engine. */
|
|
30
|
+
export function runGatePlanes(rules, options) {
|
|
31
|
+
const results = [];
|
|
32
|
+
const diagnostics = [];
|
|
33
|
+
const byPlane = (plane) => rules.filter((r) => r.plane === plane).map((r) => r.raw);
|
|
34
|
+
const planeResults = [
|
|
35
|
+
...runWiringPlane(byPlane('wiring'), options, diagnostics),
|
|
36
|
+
...runPolicyPlane(byPlane('policy'), options),
|
|
37
|
+
...runRegistryPlane(byPlane('registry'), options),
|
|
38
|
+
...runRegistrationPlane(byPlane('registration'), options),
|
|
39
|
+
...runBaselinePlane(byPlane('baseline'), options),
|
|
40
|
+
...runGeneratedPlane(byPlane('generated'), options),
|
|
41
|
+
...runDocReferencePlane(byPlane('doc-reference'), options),
|
|
42
|
+
];
|
|
43
|
+
// Each rule's settled units leave its envelope row (the envelope carries the
|
|
44
|
+
// `units` lines) for the note rows THE shared unit-state renderer reads (K2).
|
|
45
|
+
const noteRows = [];
|
|
46
|
+
for (const r of planeResults) {
|
|
47
|
+
const { unitLiveness, ...row } = r;
|
|
48
|
+
results.push(row);
|
|
49
|
+
if (unitLiveness !== undefined) {
|
|
50
|
+
noteRows.push({ id: r.id, unitLiveness, reportedEmpty: r.status === 'skipped' || r.skipReason !== undefined });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { results, diagnostics, noteRows };
|
|
54
|
+
}
|
|
55
|
+
function runWiringPlane(rules, options, diagnostics) {
|
|
56
|
+
if (rules.length === 0)
|
|
57
|
+
return [];
|
|
58
|
+
const report = runWiring(options.cwd, rules, { excludeDirs: options.excludeDirs });
|
|
59
|
+
diagnostics.push(...report.diagnostics);
|
|
60
|
+
return report.rules.map((r) => {
|
|
61
|
+
const skip = report.skipped.find((s) => s.ruleId === r.ruleId);
|
|
62
|
+
return {
|
|
63
|
+
id: r.ruleId,
|
|
64
|
+
type: 'wiring',
|
|
65
|
+
status: r.status,
|
|
66
|
+
severity: r.severity,
|
|
67
|
+
counts: { declared: r.declaredCount, registered: r.registeredCount },
|
|
68
|
+
violations: r.violations.map((v) => ({
|
|
69
|
+
id: v.token,
|
|
70
|
+
...(v.file ? { file: v.file } : {}),
|
|
71
|
+
...(v.line !== undefined ? { line: v.line } : {}),
|
|
72
|
+
...(v.message ? { message: v.message } : {}),
|
|
73
|
+
...(v.hint ? { hint: v.hint } : {}),
|
|
74
|
+
})),
|
|
75
|
+
...(skip ? { skipReason: skip.reason } : {}),
|
|
76
|
+
...(r.error ? { error: r.error } : {}),
|
|
77
|
+
// The engine's own coverage — a subset rule's registered-only tokens
|
|
78
|
+
// arrive here as unexamined, which is what makes it `partial`.
|
|
79
|
+
coverage: r.coverage,
|
|
80
|
+
...unitFields(r),
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function runPolicyPlane(rules, options) {
|
|
85
|
+
if (rules.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
const report = runPolicyLint(options.cwd, rules, { excludeDirs: options.excludeDirs });
|
|
88
|
+
return report.rules.map((r) => {
|
|
89
|
+
const rule = rules.find((x) => x.id === r.ruleId);
|
|
90
|
+
const findings = report.findings.filter((f) => f.ruleId === r.ruleId);
|
|
91
|
+
const skip = report.skipped.find((s) => s.ruleId === r.ruleId);
|
|
92
|
+
return {
|
|
93
|
+
id: r.ruleId,
|
|
94
|
+
type: 'policy',
|
|
95
|
+
status: r.status,
|
|
96
|
+
severity: r.severity,
|
|
97
|
+
counts: { units: r.unitsScanned, findings: r.findingCount, suppressed: r.suppressedCount },
|
|
98
|
+
violations: findings.map((f) => ({
|
|
99
|
+
id: f.file,
|
|
100
|
+
file: f.file,
|
|
101
|
+
line: f.line,
|
|
102
|
+
message: f.message,
|
|
103
|
+
...(f.suggest ?? rule?.suggest ? { hint: f.suggest ?? rule.suggest } : {}),
|
|
104
|
+
})),
|
|
105
|
+
...(skip ? { skipReason: skip.reason } : {}),
|
|
106
|
+
...(r.error ? { error: r.error } : {}),
|
|
107
|
+
coverage: policyRuleCoverage(r),
|
|
108
|
+
...unitFields(r),
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The registry plane's violation check is DUPLICATES: two roots claiming one id
|
|
114
|
+
* compile fine, and whichever registration wins at runtime is an accident of
|
|
115
|
+
* load order. (Presence queries are lookups, not gates, so they are not run.)
|
|
116
|
+
*/
|
|
117
|
+
function runRegistryPlane(decls, options) {
|
|
118
|
+
return decls.map((decl) => {
|
|
119
|
+
const inventory = scanRegistry(options.cwd, decl, { excludeDirs: options.excludeDirs });
|
|
120
|
+
const dupes = registryDuplicates(inventory);
|
|
121
|
+
// A source file the reader could not read (over the read cap) makes the
|
|
122
|
+
// inventory incomplete. Zero ids over it is not "matched nothing" (never a
|
|
123
|
+
// failOnEmpty failure), and "no duplicates" over it is not a pass: its
|
|
124
|
+
// coverage is the file record, so the rule settles PARTIAL.
|
|
125
|
+
const empty = inventory.entries.length === 0 && !readScopeHasUnread(inventory.readScope);
|
|
126
|
+
// The registry's globs settled with their `expectEmpty` markers (round 13)
|
|
127
|
+
// — only when a unit is marked, or the inventory is empty (what it is
|
|
128
|
+
// decided from). The registry verbs read the same `registryLiveness`.
|
|
129
|
+
const marked = [decl.source, decl.consumer].some((s) => (s?.expectEmptyUnits?.length ?? 0) > 0);
|
|
130
|
+
const liveness = marked || empty ? registryLiveness(options.cwd, decl, options.excludeDirs) : undefined;
|
|
131
|
+
// `failOnEmpty` makes a dead inventory BLOCK, exactly as a failOnEmpty
|
|
132
|
+
// wiring rule does; without it an empty registry stays a loud skip. A
|
|
133
|
+
// registry has no severity of its own, so THE failOnEmpty authority answers
|
|
134
|
+
// for a warning-severity rule (default off; an explicit `true` promotes it).
|
|
135
|
+
const failOnEmpty = failsWhenEmpty({
|
|
136
|
+
...(decl.failOnEmpty !== undefined ? { failOnEmpty: decl.failOnEmpty } : {}),
|
|
137
|
+
severity: 'warning',
|
|
138
|
+
});
|
|
139
|
+
const staleReason = failOnEmpty
|
|
140
|
+
? 'matched 0 ids — failOnEmpty: the source selector is probably stale'
|
|
141
|
+
: 'matched 0 ids — the source selector is probably stale';
|
|
142
|
+
// THE rule-emptiness settle: every source inclusion glob marked
|
|
143
|
+
// `expectEmpty` and no source file matched → an inventory over a planned
|
|
144
|
+
// directory, accepted and printed; anything else is the loud skip.
|
|
145
|
+
const emptiness = empty && liveness !== undefined
|
|
146
|
+
? settleRuleEmptiness({
|
|
147
|
+
subject: decl.name,
|
|
148
|
+
unitLabel: 'ids',
|
|
149
|
+
filesMatched: inventory.readScope.read,
|
|
150
|
+
unitsMatched: 0,
|
|
151
|
+
unread: false,
|
|
152
|
+
liveness,
|
|
153
|
+
primaryLists: ['source.files'],
|
|
154
|
+
failOnEmpty,
|
|
155
|
+
noFilesReason: staleReason,
|
|
156
|
+
noUnitsReason: staleReason,
|
|
157
|
+
})
|
|
158
|
+
: undefined;
|
|
159
|
+
const intended = emptiness?.state === RuleEmptiness.IntendedEmpty && emptiness.coverage !== undefined;
|
|
160
|
+
const skipped = emptiness?.skipped === true;
|
|
161
|
+
const skipReason = emptiness?.skipReason ?? staleReason;
|
|
162
|
+
return {
|
|
163
|
+
id: decl.name,
|
|
164
|
+
type: 'registry',
|
|
165
|
+
status: skipped
|
|
166
|
+
? emptiness?.fails
|
|
167
|
+
? 'failed'
|
|
168
|
+
: 'skipped'
|
|
169
|
+
: dupes.length > 0
|
|
170
|
+
? 'failed'
|
|
171
|
+
: 'passed',
|
|
172
|
+
severity: emptiness?.fails ? 'error' : 'warning',
|
|
173
|
+
counts: { ids: inventory.entries.length, duplicates: dupes.length },
|
|
174
|
+
violations: dupes.map((d) => ({
|
|
175
|
+
id: d.id,
|
|
176
|
+
...(d.sites[0] ? { file: d.sites[0].file, line: d.sites[0].line } : {}),
|
|
177
|
+
message: `declared in ${d.sites.length} places: ${d.sites.map((s) => `${s.file}:${s.line}`).join(', ')}`,
|
|
178
|
+
hint: 'remove the duplicate declaration — which one wins is load-order roulette',
|
|
179
|
+
})),
|
|
180
|
+
...(skipped ? { skipReason } : {}),
|
|
181
|
+
coverage: intended && emptiness?.coverage !== undefined
|
|
182
|
+
? emptiness.coverage
|
|
183
|
+
: readScopeCoverage({
|
|
184
|
+
unit: 'ids',
|
|
185
|
+
expected: inventory.entries.length,
|
|
186
|
+
examined: inventory.entries.length,
|
|
187
|
+
...(skipped ? { reason: skipReason } : {}),
|
|
188
|
+
}, inventory.readScope),
|
|
189
|
+
...livenessFields(liveness),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The registration plane's violation check is UNPROVIDED: a token declared or
|
|
195
|
+
* consumed but provided by nothing resolves to nothing at runtime — invisible
|
|
196
|
+
* to the compiler, which is the entire reason this plane exists. Orphans (a
|
|
197
|
+
* provider nothing consumes) are COUNTED but do not fail: a registration ahead
|
|
198
|
+
* of its consumer is a normal intermediate state, not a defect.
|
|
199
|
+
*/
|
|
200
|
+
function runRegistrationPlane(idioms, options) {
|
|
201
|
+
if (idioms.length === 0)
|
|
202
|
+
return [];
|
|
203
|
+
// The graph is built from EVERY idiom at once (that is what makes a
|
|
204
|
+
// declared→provided→consumed chain traceable across them), so a finding has
|
|
205
|
+
// to be attributed back by matching its sites against each idiom's own globs.
|
|
206
|
+
// Dumping every finding on the first idiom would leave the others reading
|
|
207
|
+
// `passed` while their tokens resolve to nothing — a green that means nothing.
|
|
208
|
+
const graph = buildRegistrationGraph(options.cwd, idioms, { excludeDirs: options.excludeDirs });
|
|
209
|
+
const unprovided = registrationUnprovided(graph, options.changedFiles);
|
|
210
|
+
const orphans = registrationOrphans(graph, options.changedFiles);
|
|
211
|
+
// Per ROLE list: a role's `!x` subtracts from that role only, so it never
|
|
212
|
+
// disowns a file another role of the idiom reads.
|
|
213
|
+
const ownsFile = (idiom, file) => [idiom.declared, idiom.consumed, idiom.provided].some((role) => globListSelects(file, resolveSourceGlobs(role)));
|
|
214
|
+
return idioms.map((idiom) => {
|
|
215
|
+
const mine = unprovided.filter((t) => [...t.declared, ...t.consumed].some((site) => ownsFile(idiom, site.file)));
|
|
216
|
+
const myOrphans = orphans.filter((t) => t.provided.some((site) => ownsFile(idiom, site.file)));
|
|
217
|
+
// "Is this idiom empty?" and "which role examined nothing?" come from the
|
|
218
|
+
// ONE measurement `gates coverage` reads too. Keying emptiness on the
|
|
219
|
+
// graph's token union used to print a ✓ over an idiom whose declared glob
|
|
220
|
+
// had moved (its provided/consumed roles kept the union non-empty), while
|
|
221
|
+
// coverage FAILED the same idiom.
|
|
222
|
+
const roles = measureRegistrationRoles(options.cwd, idiom, options.excludeDirs);
|
|
223
|
+
const failOnEmpty = failsWhenEmpty({
|
|
224
|
+
...(idiom.failOnEmpty !== undefined ? { failOnEmpty: idiom.failOnEmpty } : {}),
|
|
225
|
+
severity: 'warning',
|
|
226
|
+
});
|
|
227
|
+
const staleReason = `the declared role extracted 0 tokens${failOnEmpty ? ' (failOnEmpty)' : ''}, ` +
|
|
228
|
+
'so the declared selector is probably stale';
|
|
229
|
+
// THE rule-emptiness settle (round 13) on the declared role — the idiom's
|
|
230
|
+
// primary selector: every declared inclusion glob marked `expectEmpty` and
|
|
231
|
+
// no declared file matched → a planned token space, accepted; 0 tokens out
|
|
232
|
+
// of live declared files is the loud skip it always was.
|
|
233
|
+
const emptiness = roles.error === undefined && roles.empty
|
|
234
|
+
? settleRuleEmptiness({
|
|
235
|
+
subject: idiom.name,
|
|
236
|
+
unitLabel: 'declared tokens',
|
|
237
|
+
filesMatched: roles.declared.filesScanned,
|
|
238
|
+
unitsMatched: 0,
|
|
239
|
+
unread: false,
|
|
240
|
+
liveness: roles.liveness ?? settleGlobLists({ subject: idiom.name, lists: [], marks: [] }),
|
|
241
|
+
primaryLists: ['declared.files'],
|
|
242
|
+
failOnEmpty,
|
|
243
|
+
noFilesReason: staleReason,
|
|
244
|
+
noUnitsReason: staleReason,
|
|
245
|
+
})
|
|
246
|
+
: undefined;
|
|
247
|
+
const skipped = emptiness?.skipped === true;
|
|
248
|
+
return {
|
|
249
|
+
id: idiom.name,
|
|
250
|
+
type: 'registration',
|
|
251
|
+
status: roles.error !== undefined
|
|
252
|
+
? 'error'
|
|
253
|
+
: skipped
|
|
254
|
+
? emptiness?.fails
|
|
255
|
+
? 'failed'
|
|
256
|
+
: 'skipped'
|
|
257
|
+
: mine.length > 0
|
|
258
|
+
? 'failed'
|
|
259
|
+
: 'passed',
|
|
260
|
+
severity: emptiness?.fails ? 'error' : 'warning',
|
|
261
|
+
counts: { tokens: graph.tokens.length, unprovided: mine.length, orphans: myOrphans.length },
|
|
262
|
+
violations: mine.map((t) => ({
|
|
263
|
+
id: t.token,
|
|
264
|
+
...(t.declared[0] ? { file: t.declared[0].file, line: t.declared[0].line } : {}),
|
|
265
|
+
message: 'declared/consumed but provided by nothing — resolves to nothing at runtime',
|
|
266
|
+
hint: `trace it with \`shrk wiring chain ${t.token}\``,
|
|
267
|
+
})),
|
|
268
|
+
...(skipped ? { skipReason: emptiness?.skipReason ?? staleReason } : {}),
|
|
269
|
+
...(roles.error !== undefined ? { error: roles.error } : {}),
|
|
270
|
+
// A dead role is an unexamined unit: the rule settles to `partial`, never
|
|
271
|
+
// ✓. An intended-empty role rides on the acceptance instead.
|
|
272
|
+
coverage: roles.coverage,
|
|
273
|
+
...livenessFields(roles.liveness),
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function runBaselinePlane(rules, options) {
|
|
278
|
+
return rules.map((rule) => {
|
|
279
|
+
// A `command` compute spawns a shell. Under --no-spawn it is reported as
|
|
280
|
+
// skipped WITH the reason, never silently treated as clean.
|
|
281
|
+
if (options.noSpawn && rule.compute.kind === 'command') {
|
|
282
|
+
return {
|
|
283
|
+
id: rule.id,
|
|
284
|
+
type: 'baseline',
|
|
285
|
+
status: 'skipped',
|
|
286
|
+
severity: rule.severity ?? 'error',
|
|
287
|
+
counts: { committed: 0, current: 0 },
|
|
288
|
+
violations: [],
|
|
289
|
+
skipReason: '`command` compute skipped by --no-spawn',
|
|
290
|
+
coverage: { unit: 'baseline computes', expected: 1, examined: 0, reason: 'skipped by --no-spawn' },
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const o = evaluateBaselineRule(options.cwd, rule, options.excludeDirs, options.changedFiles);
|
|
294
|
+
return {
|
|
295
|
+
id: o.rule.id,
|
|
296
|
+
type: 'baseline',
|
|
297
|
+
status: o.status,
|
|
298
|
+
severity: o.rule.severity ?? 'error',
|
|
299
|
+
counts: { committed: o.committedCount, current: o.currentCount },
|
|
300
|
+
violations: [
|
|
301
|
+
...(o.diff?.added ?? []).map((e) => ({ id: e, message: 'gained since the baseline was blessed' })),
|
|
302
|
+
...(o.diff?.removed ?? []).map((e) => ({ id: e, message: 'LOST since the baseline was blessed' })),
|
|
303
|
+
].map((v) => ({ ...v, hint: rule.hint ?? `bless with \`shrk baseline update --id ${rule.id}\`` })),
|
|
304
|
+
...(o.skipReason ? { skipReason: o.skipReason } : {}),
|
|
305
|
+
...(o.error ? { error: o.error } : {}),
|
|
306
|
+
coverage: baselineCoverage(o),
|
|
307
|
+
...unitFields(o),
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* The doc-reference plane's violation check: an id cited in prose that resolves
|
|
313
|
+
* to nothing. Skipped honestly when the registries were not loaded — claiming a
|
|
314
|
+
* pass without having checked is the failure mode this whole surface exists to
|
|
315
|
+
* prevent.
|
|
316
|
+
*/
|
|
317
|
+
function runDocReferencePlane(rules, options) {
|
|
318
|
+
if (rules.length === 0)
|
|
319
|
+
return [];
|
|
320
|
+
const inspection = options.inspection;
|
|
321
|
+
if (!inspection) {
|
|
322
|
+
const skipReason = 'registries not loaded — doc references were not resolved';
|
|
323
|
+
return rules.map((rule) => ({
|
|
324
|
+
id: rule.id,
|
|
325
|
+
type: 'doc-reference',
|
|
326
|
+
status: 'skipped',
|
|
327
|
+
severity: rule.severity ?? 'error',
|
|
328
|
+
counts: { files: 0, tokens: 0 },
|
|
329
|
+
violations: [],
|
|
330
|
+
skipReason,
|
|
331
|
+
coverage: { unit: 'references', expected: 0, examined: 0, reason: skipReason },
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
return rules.map((rule) => {
|
|
335
|
+
const res = checkDocReferences(options.cwd, rule, inspection, options.excludeDirs);
|
|
336
|
+
return {
|
|
337
|
+
id: res.ruleId,
|
|
338
|
+
type: 'doc-reference',
|
|
339
|
+
status: res.status,
|
|
340
|
+
severity: res.severity,
|
|
341
|
+
counts: { files: res.filesScanned, tokens: res.tokensChecked, skipped: res.tokensSkipped },
|
|
342
|
+
violations: res.findings.map((f) => ({
|
|
343
|
+
id: f.token,
|
|
344
|
+
file: f.file,
|
|
345
|
+
line: f.line,
|
|
346
|
+
message: f.message,
|
|
347
|
+
...(f.didYouMean.length > 0 ? { hint: `did you mean: ${f.didYouMean.join(', ')}` } : {}),
|
|
348
|
+
})),
|
|
349
|
+
...(res.skipReason ? { skipReason: res.skipReason } : {}),
|
|
350
|
+
...(res.error ? { error: res.error } : {}),
|
|
351
|
+
coverage: docReferenceCoverage(res),
|
|
352
|
+
...unitFields(res),
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function runGeneratedPlane(rules, options) {
|
|
357
|
+
return rules.map((rule) => {
|
|
358
|
+
// --no-spawn keeps the header + classification halves (pure reads) and
|
|
359
|
+
// drops only the regen diff, which is reported as a partial run.
|
|
360
|
+
const headersOnly = options.noSpawn === true;
|
|
361
|
+
const o = evaluateGeneratedRule(options.cwd, rule, options.excludeDirs, headersOnly);
|
|
362
|
+
const spawnSkipped = headersOnly && (rule.regen !== undefined || (rule.sources?.length ?? 0) > 0);
|
|
363
|
+
// A rule whose DRIFT half was skipped has not passed — it partially ran. A
|
|
364
|
+
// header contract holding proves nothing about whether the file was
|
|
365
|
+
// hand-edited, so reporting `passed` here would be the exact false green
|
|
366
|
+
// this plane exists to prevent. Findings still fail; the clean case
|
|
367
|
+
// downgrades to `skipped` so the aggregate exit is 2, not 0.
|
|
368
|
+
const status = spawnSkipped && o.status === 'passed' ? 'skipped' : o.status;
|
|
369
|
+
return {
|
|
370
|
+
id: o.rule.id,
|
|
371
|
+
type: 'generated',
|
|
372
|
+
status,
|
|
373
|
+
severity: o.rule.severity ?? 'error',
|
|
374
|
+
counts: {
|
|
375
|
+
files: o.committedCount,
|
|
376
|
+
differences: o.treeDiff?.differences.length ?? 0,
|
|
377
|
+
provenance: o.provenance.length,
|
|
378
|
+
},
|
|
379
|
+
violations: [
|
|
380
|
+
...(o.treeDiff?.differences ?? []).map((d) => ({
|
|
381
|
+
id: d.file,
|
|
382
|
+
file: d.file,
|
|
383
|
+
message: d.kind,
|
|
384
|
+
hint: generatedHintFor(o.rule),
|
|
385
|
+
})),
|
|
386
|
+
...o.provenance.map((f) => ({
|
|
387
|
+
id: f.file,
|
|
388
|
+
file: f.file,
|
|
389
|
+
message: f.message,
|
|
390
|
+
hint: generatedHintFor(o.rule),
|
|
391
|
+
})),
|
|
392
|
+
],
|
|
393
|
+
...(o.skipReason
|
|
394
|
+
? { skipReason: o.skipReason }
|
|
395
|
+
: spawnSkipped
|
|
396
|
+
? { skipReason: 'regen drift check skipped by --no-spawn (headers + classification still ran)' }
|
|
397
|
+
: {}),
|
|
398
|
+
...(o.error ? { error: o.error } : {}),
|
|
399
|
+
coverage: generatedCoverage(o, spawnSkipped),
|
|
400
|
+
...unitFields(o),
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { IGateCoverage } from './rule-coverage.js';
|
|
2
|
+
/**
|
|
3
|
+
* A scaffolded `selfTest`, ready to paste (or to insert with `--write`).
|
|
4
|
+
*
|
|
5
|
+
* The trust layer asks every rule to carry `failOnEmpty` + a `selfTest`, so a
|
|
6
|
+
* stale glob after a directory move fails LOUD instead of passing over an empty
|
|
7
|
+
* set. In practice the selfTest is the step that gets skipped — not from
|
|
8
|
+
* disagreement, but because authoring the fixture inline (which ids to pin,
|
|
9
|
+
* what floor to set) is a blank page at the exact moment the author just wants
|
|
10
|
+
* the rule to work. A rule with no selfTest is invisible to the stale-glob
|
|
11
|
+
* detector, so the friction converts directly into missing coverage.
|
|
12
|
+
*/
|
|
13
|
+
export interface IScaffoldedSelfTest {
|
|
14
|
+
readonly ruleId: string;
|
|
15
|
+
readonly plane: string;
|
|
16
|
+
/** What the rule matches right now — the number the floor is derived from. */
|
|
17
|
+
readonly currentCount: number;
|
|
18
|
+
readonly expectMatchesAtLeast: number;
|
|
19
|
+
readonly expectIds: readonly string[];
|
|
20
|
+
/** The percentage of headroom left below the current count. */
|
|
21
|
+
readonly marginPercent: number;
|
|
22
|
+
/** The literal block to paste into the rule. */
|
|
23
|
+
readonly snippet: string;
|
|
24
|
+
/**
|
|
25
|
+
* Why some extracted ids were NOT pinned, or why `expectIds` is empty. Today
|
|
26
|
+
* this applies to the policy plane: a live finding is debt, and pinning it
|
|
27
|
+
* would turn paying the debt into a gate failure.
|
|
28
|
+
*/
|
|
29
|
+
readonly note?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a ready-to-commit `selfTest` from what the rule matches TODAY.
|
|
33
|
+
*
|
|
34
|
+
* The floor is deliberately BELOW the current count: pinning the exact number
|
|
35
|
+
* would turn every legitimate addition into a failure, and a rule that cries
|
|
36
|
+
* wolf on normal work gets its expectations deleted rather than fixed. The
|
|
37
|
+
* margin is what keeps the assertion about "the selector still bites" rather
|
|
38
|
+
* than about "the set never changes".
|
|
39
|
+
*/
|
|
40
|
+
export declare function scaffoldSelfTest(coverage: IGateCoverage, marginPercent: number, sampleSize?: number): IScaffoldedSelfTest;
|
|
41
|
+
/** Outcome of inserting a scaffolded selfTest into the config text. */
|
|
42
|
+
export interface IInsertResult {
|
|
43
|
+
readonly ok: boolean;
|
|
44
|
+
readonly text?: string;
|
|
45
|
+
readonly error?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Insert the block into the rule whose `id` matches, in place.
|
|
49
|
+
*
|
|
50
|
+
* Refuses rather than guesses. A config is the file every gate reads, so a
|
|
51
|
+
* write that lands in the wrong rule — or in one of two rules sharing an id —
|
|
52
|
+
* would silently re-point an assertion at a different set, which is worse than
|
|
53
|
+
* making the author paste three lines by hand.
|
|
54
|
+
*/
|
|
55
|
+
export declare function insertSelfTest(configText: string, ruleId: string, snippet: string): IInsertResult;
|
|
56
|
+
//# sourceMappingURL=scaffold-selftest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-selftest.d.ts","sourceRoot":"","sources":["../../src/gates/scaffold-selftest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,EACvB,aAAa,EAAE,MAAM,EACrB,UAAU,SAAI,GACb,mBAAmB,CA6BrB;AAmBD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,aAAa,CA8Bf"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/** Markers that make an id a poor anchor: it is likely to be renamed or removed. */
|
|
2
|
+
const UNSTABLE = /(^|[^a-z])(tmp|temp|draft|wip|scratch|legacy|deprecated|old|new|test|fixture|sample|example|foo|bar|baz)([^a-z]|$)/i;
|
|
3
|
+
/**
|
|
4
|
+
* How good an anchor an id is, higher is better.
|
|
5
|
+
*
|
|
6
|
+
* A selfTest is only as useful as the ids it pins: anchoring on a name that was
|
|
7
|
+
* always going to be deleted produces a rule that fails for a reason unrelated
|
|
8
|
+
* to the selector, which trains people to delete the selfTest. So the obviously
|
|
9
|
+
* temporary and the obviously generated are ranked last, and everything else is
|
|
10
|
+
* ordered deterministically by name so the same tree always scaffolds the same
|
|
11
|
+
* fixture.
|
|
12
|
+
*/
|
|
13
|
+
function stability(id) {
|
|
14
|
+
let score = 100;
|
|
15
|
+
if (UNSTABLE.test(id))
|
|
16
|
+
score -= 60;
|
|
17
|
+
// A long digit or hex run reads as generated (a hash, a timestamp, an index).
|
|
18
|
+
if (/\d{4,}/.test(id))
|
|
19
|
+
score -= 40;
|
|
20
|
+
if (/[0-9a-f]{8,}/i.test(id))
|
|
21
|
+
score -= 40;
|
|
22
|
+
// Trailing counters (`handlerV2`, `route3`) churn as versions advance.
|
|
23
|
+
if (/\d$/.test(id))
|
|
24
|
+
score -= 15;
|
|
25
|
+
return score;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a ready-to-commit `selfTest` from what the rule matches TODAY.
|
|
29
|
+
*
|
|
30
|
+
* The floor is deliberately BELOW the current count: pinning the exact number
|
|
31
|
+
* would turn every legitimate addition into a failure, and a rule that cries
|
|
32
|
+
* wolf on normal work gets its expectations deleted rather than fixed. The
|
|
33
|
+
* margin is what keeps the assertion about "the selector still bites" rather
|
|
34
|
+
* than about "the set never changes".
|
|
35
|
+
*/
|
|
36
|
+
export function scaffoldSelfTest(coverage, marginPercent, sampleSize = 3) {
|
|
37
|
+
// A plane whose extracted set mixes stable anchors with ids that are meant to
|
|
38
|
+
// go away (the policy plane: a live finding is debt) says which ids are safe
|
|
39
|
+
// to pin. Only those are scaffolded. Pinning a live violation would make
|
|
40
|
+
// `gates coverage` fail on the day the author fixes the code the rule forbids.
|
|
41
|
+
const ids = coverage.pinIds ?? coverage.allIds ?? coverage.sampleIds;
|
|
42
|
+
const count = coverage.unitsMatched;
|
|
43
|
+
const floor = Math.max(1, Math.floor(count * (1 - marginPercent / 100)));
|
|
44
|
+
const expectIds = [...ids]
|
|
45
|
+
.sort((a, b) => stability(b) - stability(a) || a.localeCompare(b))
|
|
46
|
+
.slice(0, Math.min(sampleSize, ids.length));
|
|
47
|
+
const snippet = [
|
|
48
|
+
'selfTest: {',
|
|
49
|
+
` expectMatchesAtLeast: ${floor},`,
|
|
50
|
+
` expectIds: [${expectIds.map((i) => `'${i}'`).join(', ')}],`,
|
|
51
|
+
' expectNotIds: [],',
|
|
52
|
+
'},',
|
|
53
|
+
].join('\n');
|
|
54
|
+
const note = pinNote(coverage);
|
|
55
|
+
return {
|
|
56
|
+
ruleId: coverage.id,
|
|
57
|
+
plane: coverage.plane,
|
|
58
|
+
currentCount: count,
|
|
59
|
+
expectMatchesAtLeast: floor,
|
|
60
|
+
expectIds,
|
|
61
|
+
marginPercent,
|
|
62
|
+
snippet,
|
|
63
|
+
...(note ? { note } : {}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Why a plane's pinnable set is narrower than what it extracted, when it is. */
|
|
67
|
+
function pinNote(coverage) {
|
|
68
|
+
if (coverage.pinIds === undefined)
|
|
69
|
+
return undefined;
|
|
70
|
+
const pinnable = new Set(coverage.pinIds);
|
|
71
|
+
const live = (coverage.allIds ?? coverage.sampleIds).filter((id) => !pinnable.has(id));
|
|
72
|
+
const skipped = live.length > 0
|
|
73
|
+
? `${live.length} id(s) matched by a live finding were not pinned (${live.slice(0, 3).join(', ')}` +
|
|
74
|
+
`${live.length > 3 ? ', …' : ''}). A finding is debt, and pinning it would fail the gate once the debt is paid. `
|
|
75
|
+
: '';
|
|
76
|
+
if (coverage.pinIds.length > 0)
|
|
77
|
+
return skipped ? skipped.trimEnd() : undefined;
|
|
78
|
+
return (`${skipped}No exempted hit to pin, so expectIds is left empty. Add a fixture file under the rule's ` +
|
|
79
|
+
'`exemptFiles` that contains the forbidden pattern. expectIds can then prove the pattern still bites.');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Insert the block into the rule whose `id` matches, in place.
|
|
83
|
+
*
|
|
84
|
+
* Refuses rather than guesses. A config is the file every gate reads, so a
|
|
85
|
+
* write that lands in the wrong rule — or in one of two rules sharing an id —
|
|
86
|
+
* would silently re-point an assertion at a different set, which is worse than
|
|
87
|
+
* making the author paste three lines by hand.
|
|
88
|
+
*/
|
|
89
|
+
export function insertSelfTest(configText, ruleId, snippet) {
|
|
90
|
+
// A registry declaration is keyed by `name`, every other plane by `id`. Both
|
|
91
|
+
// are the rule's identity as `gates list` prints it, so both are accepted —
|
|
92
|
+
// an author should not have to know which plane spells it which way.
|
|
93
|
+
const idPattern = new RegExp(`^([ \\t]*)(?:id|name):\\s*['"\`]${escapeRegex(ruleId)}['"\`]\\s*,?\\s*$`, 'gm');
|
|
94
|
+
const matches = [...configText.matchAll(idPattern)];
|
|
95
|
+
if (matches.length === 0) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
error: `no \`id: '${ruleId}'\` (or \`name:\`) line found in the config — is the rule contributed by a pack?`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (matches.length > 1) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
error: `"${ruleId}" appears ${matches.length} times — refusing to guess which rule to edit`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const m = matches[0];
|
|
108
|
+
const indent = m[1] ?? ' ';
|
|
109
|
+
const lineEnd = configText.indexOf('\n', m.index);
|
|
110
|
+
const at = lineEnd === -1 ? configText.length : lineEnd + 1;
|
|
111
|
+
const block = snippet
|
|
112
|
+
.split('\n')
|
|
113
|
+
.map((l) => (l === '' ? l : indent + l))
|
|
114
|
+
.join('\n');
|
|
115
|
+
return { ok: true, text: configText.slice(0, at) + block + '\n' + configText.slice(at) };
|
|
116
|
+
}
|
|
117
|
+
function escapeRegex(s) {
|
|
118
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
119
|
+
}
|