@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
|
@@ -1,15 +1,52 @@
|
|
|
1
|
-
import { buildImportHygieneReport,
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { buildRegistrationGraph, evaluateBoundaries, loadTsconfigPaths, providedTokensFromEntries, registrationTouchesChanged, registrationUnprovided, runPolicyLint, runWiring, scanImports, } from '@shrkcrft/boundaries';
|
|
1
|
+
import { boundaryRuleSourceFiles, buildImportHygieneReport, describeBoundaryConfiguration, gitShowFile, importHygieneCoverage, importHygieneSubjects, inspectSharkcraft, isProjectConfigAbsent, resolveChangedFiles, resolveProjectConfig, runBoundaryCheck, } from '@shrkcrft/inspector';
|
|
2
|
+
import { coverageShortfall, settleRuleStatus, } from '@shrkcrft/core';
|
|
3
|
+
import { buildRegistrationGraph, measureIdiomRoleCoverage, planeScanExcludeDirs, providedTokensFromEntries, registrationTouchesChanged, registrationUnprovidedVerdict, runPolicyLint, runWiring, unreadDirectoryContains, } from '@shrkcrft/boundaries';
|
|
5
4
|
import { computeDeletedOrphans } from "../diff/deleted-orphans.js";
|
|
5
|
+
import { deletedOrphanCoverage } from "../diff/deleted-orphan-coverage.js";
|
|
6
6
|
import { ExitCode } from "../exit-codes.js";
|
|
7
|
+
import { buildGateEnvelope } from "../gates/gate-envelope.js";
|
|
8
|
+
import { seamRejectedRules } from "../gates/seam-rejected-rules.js";
|
|
9
|
+
import { unitStateNotes } from "../gates/unit-state-notes.js";
|
|
10
|
+
import { DEAD_SELECTOR_CAUSES } from '@shrkcrft/core';
|
|
7
11
|
export const FINISH_SCHEMA = 'sharkcraft.finish/v1';
|
|
8
12
|
/** Files in the boundary/import/wiring domain — a change outside it evaluates nothing there. */
|
|
9
13
|
const CODE_FILE = /\.(?:m|c)?[jt]sx?$/;
|
|
10
14
|
function codeFilesOf(files) {
|
|
11
15
|
return files.filter((f) => CODE_FILE.test(f));
|
|
12
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* A sub-gate's REPORTED status, settled against its coverage — the ONE place
|
|
19
|
+
* finish derives `partial`, through core's one rule (`settleRuleStatus`: a
|
|
20
|
+
* `passed` whose coverage has a shortfall is `partial`). Finish's sub-gates say
|
|
21
|
+
* `pass`, so the status is spoken in the envelope's vocabulary for the call.
|
|
22
|
+
* The fold, the text renderer and --json all read the settled gate; none
|
|
23
|
+
* re-derives it. (The gate envelope re-derives the same word through the same
|
|
24
|
+
* core function when a `partial` gate is handed to it as `passed`.)
|
|
25
|
+
*/
|
|
26
|
+
function settleFinishGate(g) {
|
|
27
|
+
const { shortfall: _producerShortfall, ...rest } = g;
|
|
28
|
+
// Every record the sub-gate settled — `coverage`, then `extraCoverage` —
|
|
29
|
+
// so a gap in any of them is `partial` and named (round 13 review).
|
|
30
|
+
const records = [...(g.coverage !== undefined ? [g.coverage] : []), ...(g.extraCoverage ?? [])];
|
|
31
|
+
// Over several records a gap names its subject (the idiom), or two dead
|
|
32
|
+
// roles would read as the same sentence twice.
|
|
33
|
+
const labelled = records.length > 1;
|
|
34
|
+
const gaps = records.flatMap((c) => {
|
|
35
|
+
const s = coverageShortfall(c);
|
|
36
|
+
if (s === undefined)
|
|
37
|
+
return [];
|
|
38
|
+
return [{ record: c, shortfall: labelled && c.subject !== undefined ? `${c.subject}: ${s}` : s }];
|
|
39
|
+
});
|
|
40
|
+
const first = gaps[0];
|
|
41
|
+
if (first === undefined)
|
|
42
|
+
return rest;
|
|
43
|
+
const settled = settleRuleStatus(g.status === 'pass' ? 'passed' : g.status, first.record);
|
|
44
|
+
return {
|
|
45
|
+
...rest,
|
|
46
|
+
...(settled === 'partial' ? { status: 'partial' } : {}),
|
|
47
|
+
shortfall: gaps.map((x) => x.shortfall).join('; '),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
13
50
|
/** The base ref the unprovided gate diffs against to spot removed providers. */
|
|
14
51
|
function baseRefFor(input) {
|
|
15
52
|
if (input.mode === 'files')
|
|
@@ -43,49 +80,30 @@ export async function runFinishGates(input) {
|
|
|
43
80
|
const { cwd } = input;
|
|
44
81
|
const changed = resolveChangedFiles(input.scope);
|
|
45
82
|
const changedFiles = changed.files;
|
|
46
|
-
// The boundary/import/wiring engines only reason about code files. A change to
|
|
47
|
-
// non-code files (docs, JSON, config) evaluates NOTHING in those gates, so they
|
|
48
|
-
// must SKIP, not trivially pass — otherwise a markdown-only change paints green
|
|
49
|
-
// ("evaluated nothing" must read as not-verified, never a `0`).
|
|
50
|
-
const codeChanged = codeFilesOf(changedFiles);
|
|
51
83
|
const gates = [];
|
|
52
|
-
// ── boundaries (changed-only)
|
|
84
|
+
// ── boundaries (changed-only, with rule escalation) ──────────────────
|
|
53
85
|
const inspection = await inspectSharkcraft({ cwd });
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const scan = scanImports({ projectRoot: cwd });
|
|
63
|
-
const tsconfigPaths = loadTsconfigPaths(cwd);
|
|
64
|
-
const evalResult = evaluateBoundaries(scan, boundaryRules, {
|
|
65
|
-
...(tsconfigPaths.aliases.size > 0 ? { tsconfigPaths } : {}),
|
|
66
|
-
});
|
|
67
|
-
const filtered = filterViolationsToChangedScope(evalResult.violations, input.scope);
|
|
68
|
-
const errors = filtered.includedViolations.filter((v) => v.severity === 'error');
|
|
69
|
-
const warnings = filtered.includedViolations.filter((v) => v.severity === 'warning');
|
|
70
|
-
gates.push({
|
|
71
|
-
name: 'boundaries',
|
|
72
|
-
status: errors.length > 0 ? 'fail' : 'pass',
|
|
73
|
-
detail: `${errors.length} error(s), ${warnings.length} warning(s) across ${changedFiles.length} changed file(s)`,
|
|
74
|
-
errors: errors.length,
|
|
75
|
-
warnings: warnings.length,
|
|
76
|
-
items: [...errors, ...warnings].map((v) => ({
|
|
77
|
-
file: v.file,
|
|
78
|
-
line: v.line,
|
|
79
|
-
message: `[${v.severity}] ${v.ruleId}: ${v.message}`,
|
|
80
|
-
})),
|
|
81
|
-
});
|
|
82
|
-
}
|
|
86
|
+
gates.push(boundariesGate(inspection, { mode: changed.mode, files: changedFiles }));
|
|
87
|
+
// The boundary/import engines only reason about code files, and a file that
|
|
88
|
+
// DEFINES rules (a boundaryFiles entry, the config) is a rule source, not a
|
|
89
|
+
// subject: a changed rule file escalates its rules above; it is never
|
|
90
|
+
// "scanned" as if it were the change under review. A change to non-code files
|
|
91
|
+
// evaluates NOTHING here, so these gates SKIP rather than trivially pass.
|
|
92
|
+
const ruleSources = new Set(boundaryRuleSourceFiles(inspection));
|
|
93
|
+
const codeChanged = codeFilesOf(changedFiles).filter((f) => !ruleSources.has(f));
|
|
83
94
|
// ── import hygiene (changed-only) ────────────────────────────────────
|
|
95
|
+
// Hygiene reads existing .ts/.tsx sources only (THE subject list the engine
|
|
96
|
+
// itself scans): a deleted file, a .js edit or a test fixture put nothing in
|
|
97
|
+
// front of it, and a pass over nothing is not a pass.
|
|
98
|
+
const hygieneFiles = importHygieneSubjects(cwd, codeChanged);
|
|
84
99
|
if (codeChanged.length === 0) {
|
|
85
100
|
gates.push(skip('imports', 'no code files in changed scope'));
|
|
86
101
|
}
|
|
102
|
+
else if (hygieneFiles.length === 0) {
|
|
103
|
+
gates.push(skip('imports', `no existing .ts/.tsx source in the changed scope (${codeChanged.length} changed code file(s))`));
|
|
104
|
+
}
|
|
87
105
|
else {
|
|
88
|
-
const report = buildImportHygieneReport(cwd, { files:
|
|
106
|
+
const report = buildImportHygieneReport(cwd, { files: hygieneFiles });
|
|
89
107
|
const errors = report.counts?.['error'] ?? (report.verdict === 'errors' ? report.findings.length : 0);
|
|
90
108
|
const warnings = report.counts?.['warning'] ?? (report.verdict === 'warnings' ? report.findings.length : 0);
|
|
91
109
|
// Only the findings that actually DRIVE the verdict become "failing items".
|
|
@@ -97,9 +115,12 @@ export async function runFinishGates(input) {
|
|
|
97
115
|
gates.push({
|
|
98
116
|
name: 'imports',
|
|
99
117
|
status: report.verdict === 'errors' ? 'fail' : 'pass',
|
|
100
|
-
detail: `verdict=${report.verdict} (${driving.length} of ${report.findings.length} finding(s) drive it)`,
|
|
118
|
+
detail: `verdict=${report.verdict} (${driving.length} of ${report.findings.length} finding(s) drive it) across ${hygieneFiles.length} changed source file(s)`,
|
|
101
119
|
errors,
|
|
102
120
|
warnings,
|
|
121
|
+
// THE hygiene coverage fold: an unreadable changed source was never
|
|
122
|
+
// checked, so a clean result over it settles this sub-gate `partial` (2).
|
|
123
|
+
coverage: importHygieneCoverage(report, 'changed source files'),
|
|
103
124
|
items: driving.map((f) => ({
|
|
104
125
|
file: f.file,
|
|
105
126
|
line: f.line,
|
|
@@ -114,7 +135,9 @@ export async function runFinishGates(input) {
|
|
|
114
135
|
// A MALFORMED config in a real sharkcraft project is a fail (the wiring/
|
|
115
136
|
// policy gates can't be trusted). The mere ABSENCE of a sharkcraft/ folder
|
|
116
137
|
// is not — those gates simply don't apply, so skip them without failing.
|
|
117
|
-
|
|
138
|
+
// THE "no config at all" answer (`isProjectConfigAbsent`: the loader found
|
|
139
|
+
// no sharkcraft/ folder) — the quality report's plane row reads it too.
|
|
140
|
+
const isSharkcraftProject = !isProjectConfigAbsent(loaded.error);
|
|
118
141
|
if (isSharkcraftProject)
|
|
119
142
|
configError = loaded.error.message;
|
|
120
143
|
const detail = isSharkcraftProject
|
|
@@ -125,7 +148,17 @@ export async function runFinishGates(input) {
|
|
|
125
148
|
gates.push(skip('policy', detail));
|
|
126
149
|
}
|
|
127
150
|
else {
|
|
128
|
-
|
|
151
|
+
// Every plane sub-gate walks THE plane scan scope (`planeScanExcludeDirs`),
|
|
152
|
+
// the same tree its verb (`check wiring`, `wiring unprovided`,
|
|
153
|
+
// `policy-lint`) walks for the same rule.
|
|
154
|
+
const excludeDirs = planeScanExcludeDirs(cwd, loaded.value.sharkcraftDir);
|
|
155
|
+
// A pack rule the merge seam rejected never runs, whatever changed: its
|
|
156
|
+
// sub-gate FAILS naming it, as `gates check` fails the same errored row
|
|
157
|
+
// (round 12 review, R12-X1) — never a "Safe to finish" over a rule that
|
|
158
|
+
// was silently dropped.
|
|
159
|
+
const rejected = seamRejectedRules(loaded.value, ['wiring', 'registration', 'policy']);
|
|
160
|
+
const rejectedOn = (plane) => rejected.filter((r) => r.type === plane);
|
|
161
|
+
gates.push(withSeamRejections(wiringGate(cwd, loaded.value.config.wiringRules ?? [], changedFiles, excludeDirs), rejectedOn('wiring')), withSeamRejections(unprovidedGate(cwd, loaded.value.config.registrationGraph ?? [], changedFiles, excludeDirs, baseRefFor(input)), rejectedOn('registration')), withSeamRejections(policyGate(cwd, loaded.value.config.policyRules ?? [], changedFiles, excludeDirs), rejectedOn('policy')));
|
|
129
162
|
}
|
|
130
163
|
// ── deleted-orphans (write-safety) ───────────────────────────────────
|
|
131
164
|
gates.push(await orphansGate(input));
|
|
@@ -133,46 +166,106 @@ export async function runFinishGates(input) {
|
|
|
133
166
|
gates.push(await archGate(cwd, changedFiles));
|
|
134
167
|
// ── impact summary (informational, best-effort) ──────────────────────
|
|
135
168
|
const impact = await impactSummary(cwd, changedFiles);
|
|
136
|
-
// ──
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
169
|
+
// ── settle each sub-gate's reported status, once ─────────────────────
|
|
170
|
+
// A `pass` whose coverage has a shortfall is `partial` — derived HERE, so the
|
|
171
|
+
// text renderer and --json show the same word and the fold below reads it.
|
|
172
|
+
const settledGates = gates.map(settleFinishGate);
|
|
173
|
+
// ── fold into one honest 0/1/2 verdict — through the shared envelope ──
|
|
174
|
+
// Only NON-advisory ("deciding") gates decide the verdict, and only the ones
|
|
175
|
+
// that evaluated something become rows: a skipped sub-gate is narrowing (its
|
|
176
|
+
// domain had nothing in the changeset), not a gap. The envelope settles the
|
|
177
|
+
// proposal on every row's coverage and on the run's — so a partial gate is 2,
|
|
178
|
+
// and a run where NO deciding gate evaluated anything (run coverage expected
|
|
179
|
+
// 0) is 2 unless `--allow-empty` accepted it. A deciding fail (or a config
|
|
180
|
+
// that could not load) is 1.
|
|
181
|
+
const deciding = settledGates.filter((g) => !g.advisory);
|
|
182
|
+
const evaluatedDeciding = deciding.filter((g) => g.status !== 'skipped');
|
|
143
183
|
const failed = deciding.filter((g) => g.status === 'fail');
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
184
|
+
const rows = evaluatedDeciding.flatMap((g) => [
|
|
185
|
+
{
|
|
186
|
+
id: g.name,
|
|
187
|
+
type: 'finish',
|
|
188
|
+
// A settled `partial` is handed over as `passed` with its coverage: the
|
|
189
|
+
// builder re-derives `partial` through the same core `settleRuleStatus`.
|
|
190
|
+
status: g.status === 'fail' ? 'failed' : 'passed',
|
|
191
|
+
severity: 'error',
|
|
192
|
+
counts: { errors: g.errors, warnings: g.warnings },
|
|
193
|
+
violations: g.items.slice(0, 50).map((i) => ({
|
|
194
|
+
id: i.message,
|
|
195
|
+
...(i.file ? { file: i.file } : {}),
|
|
196
|
+
...(i.line !== undefined ? { line: i.line } : {}),
|
|
197
|
+
})),
|
|
198
|
+
coverage: g.coverage ?? { unit: `${g.name} sub-gate runs`, expected: 1, examined: 1 },
|
|
199
|
+
},
|
|
200
|
+
// A sub-gate's further records (round 13 review: the unprovided sub-gate's
|
|
201
|
+
// other idioms' role records and acceptances) — each its own row, so the
|
|
202
|
+
// one settle names every gap and prints every acceptance.
|
|
203
|
+
...(g.extraCoverage ?? []).map((c, i) => ({
|
|
204
|
+
id: `${g.name}: ${c.subject ?? `record ${i + 2}`}${c.acceptedBy !== undefined ? ' (accepted)' : ''}`,
|
|
205
|
+
type: 'finish',
|
|
206
|
+
status: 'passed',
|
|
207
|
+
severity: 'error',
|
|
208
|
+
counts: {},
|
|
209
|
+
violations: [],
|
|
210
|
+
coverage: c,
|
|
211
|
+
})),
|
|
212
|
+
]);
|
|
213
|
+
if (configError) {
|
|
214
|
+
rows.push({
|
|
215
|
+
id: 'config',
|
|
216
|
+
type: 'finish',
|
|
217
|
+
status: 'error',
|
|
218
|
+
severity: 'error',
|
|
219
|
+
counts: {},
|
|
220
|
+
violations: [],
|
|
221
|
+
error: `sharkcraft.config.ts did not load: ${configError}`,
|
|
222
|
+
coverage: { unit: 'config files', expected: 1, examined: 0, reason: 'failed to load' },
|
|
223
|
+
});
|
|
159
224
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
225
|
+
const runCoverage = {
|
|
226
|
+
unit: 'deciding sub-gates',
|
|
227
|
+
expected: evaluatedDeciding.length,
|
|
228
|
+
examined: evaluatedDeciding.length,
|
|
229
|
+
...(evaluatedDeciding.length === 0
|
|
230
|
+
? {
|
|
231
|
+
reason: changedFiles.length === 0
|
|
232
|
+
? 'nothing changed'
|
|
233
|
+
: 'no deciding gate had anything in the changed scope to evaluate',
|
|
234
|
+
}
|
|
235
|
+
: {}),
|
|
236
|
+
...(input.emptyValve ? input.emptyValve(evaluatedDeciding.length) : {}),
|
|
237
|
+
};
|
|
238
|
+
const proposed = failed.length > 0 || configError ? ExitCode.Failure : ExitCode.VerifiedPass;
|
|
239
|
+
const gate = buildGateEnvelope('finish', proposed, rows, runCoverage);
|
|
240
|
+
const exit = gate.exit;
|
|
241
|
+
const verdict = gate.verdict === 'pass' ? 'pass' : gate.verdict === 'fail' ? 'fail' : 'not-verified';
|
|
242
|
+
const anyEvaluated = evaluatedDeciding.length > 0;
|
|
243
|
+
const warnings = settledGates.reduce((n, g) => n + g.warnings, 0);
|
|
244
|
+
const skipped = settledGates.filter((g) => g.status === 'skipped').map((g) => g.name);
|
|
245
|
+
const advisoryWarned = settledGates.filter((g) => g.advisory && g.warnings > 0).map((g) => g.name);
|
|
246
|
+
const gateShortfalls = gate.shortfalls;
|
|
162
247
|
const summary = verdict === 'fail'
|
|
163
248
|
? `Not safe to finish: ${configError ? 'config failed to load; ' : ''}${failed.map((g) => `${g.name} (${g.errors} error(s))`).join(', ') || 'see gates'}.`
|
|
164
|
-
: verdict === 'not-verified'
|
|
165
|
-
? `Not verified:
|
|
166
|
-
:
|
|
249
|
+
: verdict === 'not-verified' && anyEvaluated
|
|
250
|
+
? `Not verified: ${gateShortfalls.slice(0, 3).join('; ')}${gateShortfalls.length > 3 ? `; … (+${gateShortfalls.length - 3} more)` : ''} — this is NOT a pass.`
|
|
251
|
+
: verdict === 'not-verified'
|
|
252
|
+
? `Not verified: no gate evaluated the changed scope${changedFiles.length === 0 ? ' (nothing changed)' : ' (nothing in it is gate-relevant)'} — this is NOT a pass. Re-run over a scope with code changes, or gate explicitly.`
|
|
253
|
+
: !anyEvaluated
|
|
254
|
+
? 'Nothing to finish: no deciding gate had anything to evaluate — accepted explicitly.'
|
|
255
|
+
: `Safe to finish: every applicable gate passed${warnings > 0 ? ` (${warnings} non-blocking warning(s)${advisoryWarned.length > 0 ? ` — see ${advisoryWarned.join(', ')}` : ''})` : ''}${skipped.length > 0 ? `; skipped: ${skipped.join(', ')}` : ''}.`;
|
|
167
256
|
const nextAction = verdict === 'fail'
|
|
168
257
|
? 'Fix every failing gate item (each carries file:line), then re-run `shrk finish`.'
|
|
169
258
|
: verdict === 'not-verified'
|
|
170
|
-
?
|
|
171
|
-
|
|
259
|
+
? anyEvaluated
|
|
260
|
+
? 'Part of the changed scope was not verified (see "Not verified" above) — do not treat this as done.'
|
|
261
|
+
: 'Nothing was verified — do not treat this as done.'
|
|
262
|
+
: anyEvaluated
|
|
263
|
+
? 'Safe to declare done.'
|
|
264
|
+
: 'Nothing was gated — the empty changeset was accepted with --allow-empty.';
|
|
172
265
|
return {
|
|
173
266
|
schema: FINISH_SCHEMA,
|
|
174
267
|
scope: { mode: input.mode, files: changedFiles, fileCount: changedFiles.length },
|
|
175
|
-
gates,
|
|
268
|
+
gates: settledGates,
|
|
176
269
|
impact,
|
|
177
270
|
verdict,
|
|
178
271
|
exit,
|
|
@@ -180,6 +273,7 @@ export async function runFinishGates(input) {
|
|
|
180
273
|
...(configError ? { configError } : {}),
|
|
181
274
|
summary,
|
|
182
275
|
nextAction,
|
|
276
|
+
gate,
|
|
183
277
|
};
|
|
184
278
|
}
|
|
185
279
|
function skip(name, detail) {
|
|
@@ -189,12 +283,199 @@ function skip(name, detail) {
|
|
|
189
283
|
function advisorySkip(name, detail) {
|
|
190
284
|
return { name, status: 'skipped', detail, errors: 0, warnings: 0, items: [], advisory: true };
|
|
191
285
|
}
|
|
192
|
-
|
|
286
|
+
/**
|
|
287
|
+
* A plane sub-gate with the pack rules the merge seam REJECTED folded in: each
|
|
288
|
+
* is a failing item (it never ran), so the sub-gate fails — skipped or not —
|
|
289
|
+
* exactly as `gates check` fails the same errored rows (round 12 review,
|
|
290
|
+
* R12-X1). Unchanged when there are none.
|
|
291
|
+
*/
|
|
292
|
+
function withSeamRejections(gate, rejected) {
|
|
293
|
+
if (rejected.length === 0)
|
|
294
|
+
return gate;
|
|
295
|
+
const note = `${rejected.length} pack rule(s) rejected at the pack-plane merge seam — NOT evaluated`;
|
|
296
|
+
return {
|
|
297
|
+
...gate,
|
|
298
|
+
status: 'fail',
|
|
299
|
+
detail: gate.status === 'skipped' ? note : `${gate.detail}, ${note}`,
|
|
300
|
+
errors: gate.errors + rejected.length,
|
|
301
|
+
items: [
|
|
302
|
+
...gate.items,
|
|
303
|
+
...rejected.map((r) => ({ message: `rejected pack rule ${r.id}: ${r.error ?? 'failed validation'}` })),
|
|
304
|
+
],
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* One sub-gate's coverage over the rules its changeset SELECTED, folded from
|
|
309
|
+
* each rule's ENGINE coverage through core's `coverageShortfall` — the rule
|
|
310
|
+
* `check wiring --changed-only` / `policy-lint --changed-only` / `check
|
|
311
|
+
* boundaries --changed-only` settle their exit on — so a rule that passed over
|
|
312
|
+
* part of its scope, or examined nothing at all, is unexamined here exactly as
|
|
313
|
+
* those verbs count it. Returns the record and the ` — NOT VERIFIED: <rule>:
|
|
314
|
+
* <shortfall>` detail suffix. `diff-check` folds its boundaries row with it too.
|
|
315
|
+
*/
|
|
316
|
+
export function ruleSetCoverage(unit, rules, reason) {
|
|
317
|
+
const ruleShortfalls = rules.flatMap((r) => {
|
|
318
|
+
const s = coverageShortfall(r.coverage);
|
|
319
|
+
return s === undefined ? [] : [{ id: r.ruleId, shortfall: s }];
|
|
320
|
+
});
|
|
321
|
+
const coverage = {
|
|
322
|
+
unit,
|
|
323
|
+
expected: rules.length,
|
|
324
|
+
examined: rules.length - ruleShortfalls.length,
|
|
325
|
+
...(ruleShortfalls.length > 0
|
|
326
|
+
? {
|
|
327
|
+
unexamined: ruleShortfalls.slice(0, 20).map((r) => r.id),
|
|
328
|
+
unexaminedTotal: ruleShortfalls.length,
|
|
329
|
+
reason,
|
|
330
|
+
}
|
|
331
|
+
: {}),
|
|
332
|
+
};
|
|
333
|
+
const first = ruleShortfalls[0];
|
|
334
|
+
const notVerified = first
|
|
335
|
+
? ` — NOT VERIFIED: ${first.id}: ${first.shortfall}${ruleShortfalls.length > 1 ? ` (+${ruleShortfalls.length - 1} more)` : ''}`
|
|
336
|
+
: '';
|
|
337
|
+
return { coverage, notVerified };
|
|
338
|
+
}
|
|
339
|
+
/** A rule that matched nothing under `failOnEmpty`, as a sub-gate item. */
|
|
340
|
+
function emptyFailureItem(s) {
|
|
341
|
+
return { message: `[${s.severity}] ${s.ruleId}: matched nothing (failOnEmpty) — ${s.reason}` };
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* The boundaries sub-gate — through THE boundary orchestrator, the call `check
|
|
345
|
+
* boundaries --changed-only`, `diff-check` and the MCP tools make (round 11).
|
|
346
|
+
*
|
|
347
|
+
* It closes the two greens closing#d reproduced:
|
|
348
|
+
* - a rule EDIT (the changeset touches a rule source, the config or
|
|
349
|
+
* tsconfig) escalates those rules to a whole-tree evaluation: the
|
|
350
|
+
* violations the edit created in untouched files are this changeset's,
|
|
351
|
+
* never "legacy";
|
|
352
|
+
* - a change no rule GOVERNS evaluated nothing, so the gate SKIPS — it used to
|
|
353
|
+
* report `pass` from an error count of zero over files no rule looks at.
|
|
354
|
+
* Otherwise it carries the selected rules' coverage, so a rule whose scope glob
|
|
355
|
+
* went dead settles it `partial`.
|
|
356
|
+
*/
|
|
357
|
+
function boundariesGate(inspection, changed) {
|
|
358
|
+
const loadIssues = inspection.boundaryLoadIssues ?? [];
|
|
359
|
+
if (inspection.boundaryRegistry.size() === 0 && loadIssues.length === 0) {
|
|
360
|
+
const why = describeBoundaryConfiguration(inspection).diagnostics[0];
|
|
361
|
+
return skip('boundaries', `no boundary rules configured${why ? ` — ${why}` : ''}`);
|
|
362
|
+
}
|
|
363
|
+
if (changed.files.length === 0 && loadIssues.length === 0) {
|
|
364
|
+
return skip('boundaries', 'nothing in the changed scope');
|
|
365
|
+
}
|
|
366
|
+
const r = runBoundaryCheck(inspection, { changed });
|
|
367
|
+
if (r.selectedRuleIds.length === 0 && r.loadIssues.length === 0) {
|
|
368
|
+
return skip('boundaries', `no changed source file is governed by a boundary rule (${changed.files.length} changed file(s))`);
|
|
369
|
+
}
|
|
370
|
+
const errors = r.violations.filter((v) => v.severity === 'error');
|
|
371
|
+
const warnings = r.violations.filter((v) => v.severity === 'warning');
|
|
372
|
+
const failedOnEmpty = r.rules.filter((x) => x.failedOnEmpty === true);
|
|
373
|
+
const escalated = r.changed?.escalatedRuleIds ?? [];
|
|
374
|
+
const governed = r.changed?.governedFiles.length ?? 0;
|
|
375
|
+
const { coverage, notVerified } = ruleSetCoverage('boundary rules', r.rules.map((x) => ({ ruleId: x.ruleId, coverage: x.coverage })), 'passed over part of their scope or checked nothing');
|
|
376
|
+
const detailParts = [
|
|
377
|
+
`${errors.length} error(s), ${warnings.length} warning(s) across ${governed} governed changed file(s)`,
|
|
378
|
+
];
|
|
379
|
+
if (escalated.length > 0) {
|
|
380
|
+
const why = [...new Set(r.changed?.escalation.reasons.map((x) => x.file) ?? [])].join(', ');
|
|
381
|
+
detailParts.push(`${escalated.length} rule(s) escalated (${why} changed) — evaluated repo-wide`);
|
|
382
|
+
}
|
|
383
|
+
if (r.loadIssues.length > 0)
|
|
384
|
+
detailParts.push(`${r.loadIssues.length} errored rule(s)`);
|
|
385
|
+
if (r.staleExceptions.length > 0)
|
|
386
|
+
detailParts.push(`${r.staleExceptions.length} stale exception(s)`);
|
|
387
|
+
if (failedOnEmpty.length > 0)
|
|
388
|
+
detailParts.push(`${failedOnEmpty.length} matched nothing (failOnEmpty)`);
|
|
389
|
+
// Round 13 (lane B): dead selector units and went-live expectEmpty markers
|
|
390
|
+
// are ADVISORY on finish — named in the detail and listed in `notes`, never
|
|
391
|
+
// items (items are the envelope's violations) and never a failure: finish
|
|
392
|
+
// runs without --fail-on-dead-units. It used to report `pass` in silence
|
|
393
|
+
// while `check boundaries` withheld its ✓ over the same tree.
|
|
394
|
+
// A PACK marker that went live is INFO (round 13 review) — `check
|
|
395
|
+
// boundaries` prints it in its own INFO block; it never counts as advisory.
|
|
396
|
+
const advisory = [
|
|
397
|
+
...r.deadUnits.map((d) => `[advisory] dead selector unit [${d.unit}] ${d.ruleId}: ${d.selector} — ${d.reason}`),
|
|
398
|
+
...r.wentLive.map((u) => `${u.packageName !== undefined ? '[info]' : '[advisory]'} [${u.unit}] ${u.ruleId}: ${u.selector} — ${u.reason}`),
|
|
399
|
+
];
|
|
400
|
+
const localWentLive = r.wentLive.filter((u) => u.packageName === undefined).length;
|
|
401
|
+
const packWentLive = r.wentLive.length - localWentLive;
|
|
402
|
+
// Each rule's expectEmpty acceptance (settle record B) — its own envelope row
|
|
403
|
+
// through `extraCoverage`, so finish's ONE settle prints every acceptance at
|
|
404
|
+
// exit 0 (round 13 review: they were dropped in silence — the sub-gate's
|
|
405
|
+
// single `coverage` is the rule-set fold, which cannot carry them).
|
|
406
|
+
const acceptances = r.rules.flatMap((x) => x.detail.unitAcceptance !== undefined
|
|
407
|
+
? [{ ...x.detail.unitAcceptance, subject: x.detail.unitAcceptance.subject ?? x.ruleId }]
|
|
408
|
+
: []);
|
|
409
|
+
if (r.deadUnits.length > 0)
|
|
410
|
+
detailParts.push(`${r.deadUnits.length} dead selector unit(s) (advisory)`);
|
|
411
|
+
if (localWentLive > 0)
|
|
412
|
+
detailParts.push(`${localWentLive} expectEmpty marker(s) went live (advisory)`);
|
|
413
|
+
if (packWentLive > 0)
|
|
414
|
+
detailParts.push(`${packWentLive} pack expectEmpty marker(s) went live (INFO)`);
|
|
415
|
+
if (r.intendedEmpty.length > 0)
|
|
416
|
+
detailParts.push(`${r.intendedEmpty.length} expectEmpty unit(s) intended empty`);
|
|
417
|
+
return {
|
|
418
|
+
name: 'boundaries',
|
|
419
|
+
// The orchestrator's proposal is THE boundary verdict: 1 = something failed.
|
|
420
|
+
status: r.proposedExit === ExitCode.Failure ? 'fail' : 'pass',
|
|
421
|
+
detail: `${detailParts.join('; ')}${notVerified}`,
|
|
422
|
+
errors: errors.length + r.loadIssues.length + r.staleExceptions.length + failedOnEmpty.length,
|
|
423
|
+
warnings: warnings.length,
|
|
424
|
+
coverage,
|
|
425
|
+
...(acceptances.length > 0 ? { extraCoverage: acceptances } : {}),
|
|
426
|
+
...(advisory.length > 0 ? { notes: advisory } : {}),
|
|
427
|
+
items: [
|
|
428
|
+
...[...errors, ...warnings].map((v) => ({
|
|
429
|
+
file: v.file,
|
|
430
|
+
line: v.line,
|
|
431
|
+
message: `[${v.severity}] ${v.ruleId}: ${v.message}`,
|
|
432
|
+
})),
|
|
433
|
+
...r.loadIssues.map((i) => ({
|
|
434
|
+
file: i.file,
|
|
435
|
+
message: `[error] ${i.ruleId ?? i.kind}: ${i.issues.join('; ')} — NOT evaluated`,
|
|
436
|
+
})),
|
|
437
|
+
...r.staleExceptions.map((s) => ({ file: s.file, message: `[error] ${s.ruleId}: ${s.message}` })),
|
|
438
|
+
...failedOnEmpty.map((x) => emptyFailureItem({ ruleId: x.ruleId, reason: x.skipReason ?? 'no governed file', severity: x.severity })),
|
|
439
|
+
],
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* A plane sub-gate's per-rule expectEmpty acceptances and unit notes (round 13
|
|
444
|
+
* review): the acceptances ride beside the rule-set fold as `extraCoverage`
|
|
445
|
+
* (one envelope row each, as the boundaries sub-gate does), so finish's ONE
|
|
446
|
+
* settle prints every one at exit 0 — `check wiring` / `policy-lint` printed
|
|
447
|
+
* them while finish dropped them in silence. A dead glob of a rule that still
|
|
448
|
+
* matched and a LOCAL marker whose target appeared are ADVISORY notes; a pack
|
|
449
|
+
* marker is `[info]` — worded by THE shared unit-state renderer. Never a
|
|
450
|
+
* failure: finish runs without --fail-on-dead-units.
|
|
451
|
+
*/
|
|
452
|
+
function planeUnitFields(rules, emptyIds) {
|
|
453
|
+
const extra = rules.flatMap((r) => r.unitAcceptance !== undefined ? [{ ...r.unitAcceptance, subject: r.unitAcceptance.subject ?? r.ruleId }] : []);
|
|
454
|
+
const n = unitStateNotes(rules.map((r) => ({
|
|
455
|
+
id: r.ruleId,
|
|
456
|
+
...(r.unitLiveness !== undefined ? { unitLiveness: r.unitLiveness } : {}),
|
|
457
|
+
reportedEmpty: emptyIds.has(r.ruleId),
|
|
458
|
+
})));
|
|
459
|
+
const notes = [
|
|
460
|
+
...n.deadLines.map((l) => `[advisory] dead selector unit ${l} — ${DEAD_SELECTOR_CAUSES}`),
|
|
461
|
+
...n.localWentLiveLines.map((l) => `[advisory] ${l}`),
|
|
462
|
+
...n.packWentLiveLines.map((l) => `[info] ${l}`),
|
|
463
|
+
];
|
|
464
|
+
return { ...(extra.length > 0 ? { extraCoverage: extra } : {}), ...(notes.length > 0 ? { notes } : {}) };
|
|
465
|
+
}
|
|
466
|
+
function wiringGate(cwd, rules, changedFiles, excludeDirs) {
|
|
193
467
|
if (rules.length === 0)
|
|
194
468
|
return skip('wiring', 'no wiring rules configured');
|
|
195
|
-
const report = runWiring(cwd, rules, { changedOnly: true, changedFiles });
|
|
196
|
-
|
|
197
|
-
|
|
469
|
+
const report = runWiring(cwd, rules, { changedOnly: true, changedFiles, excludeDirs });
|
|
470
|
+
// Skip ONLY when the changeset selected no rule (no rule's footprint
|
|
471
|
+
// intersects it) — deliberate narrowing. A SELECTED rule that examined
|
|
472
|
+
// nothing (its source globs matched no files) is not narrowing: it falls
|
|
473
|
+
// through, carries its coverage, and settles this sub-gate `partial` (2) or
|
|
474
|
+
// `fail` (1, an error-severity failOnEmpty rule) — exactly where `check
|
|
475
|
+
// wiring --changed-only` reads 2 / 1. (Skipping on `evaluated === 0` read
|
|
476
|
+
// "Safe to finish", exit 0, over a rule that had checked nothing.)
|
|
477
|
+
if (report.rules.length === 0) {
|
|
478
|
+
return skip('wiring', `${rules.length} rule(s) configured, none in the changed scope`);
|
|
198
479
|
}
|
|
199
480
|
const errors = report.violations.filter((v) => v.severity === 'error');
|
|
200
481
|
const warnings = report.violations.filter((v) => v.severity === 'warning');
|
|
@@ -202,12 +483,23 @@ function wiringGate(cwd, rules, changedFiles) {
|
|
|
202
483
|
// rule-level error but NO violation — it must FAIL the gate, never read as a
|
|
203
484
|
// silent green ("loud, never silent green").
|
|
204
485
|
const diag = report.diagnostics;
|
|
486
|
+
// A rule that matched nothing under failOnEmpty carries no violation either:
|
|
487
|
+
// it is counted and listed so a failing sub-gate names what failed.
|
|
488
|
+
const emptyFailed = report.skipped.filter((s) => s.failed);
|
|
489
|
+
// Round 11: what this sub-gate examined — every selected rule's engine
|
|
490
|
+
// coverage through core's shortfall rule, so a rule that passed over part of
|
|
491
|
+
// its scope (a subset rule whose declared selector never produced some
|
|
492
|
+
// registered tokens) or checked nothing keeps this sub-gate from counting as
|
|
493
|
+
// a pass (the settle step reports it `partial` and the composite is 2).
|
|
494
|
+
const { coverage, notVerified } = ruleSetCoverage('wiring rules', report.rules, 'passed over part of their scope or checked nothing');
|
|
205
495
|
return {
|
|
496
|
+
...planeUnitFields(report.rules, new Set(report.skipped.map((s) => s.ruleId))),
|
|
206
497
|
name: 'wiring',
|
|
207
498
|
status: report.verdict === 'errors' || diag.length > 0 ? 'fail' : 'pass',
|
|
208
|
-
detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}`,
|
|
209
|
-
errors: errors.length + diag.length,
|
|
210
|
-
warnings: warnings.length,
|
|
499
|
+
detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}${emptyFailed.length > 0 ? `, ${emptyFailed.length} matched nothing (failOnEmpty)` : ''}${notVerified}`,
|
|
500
|
+
errors: errors.length + diag.length + emptyFailed.filter((s) => s.severity === 'error').length,
|
|
501
|
+
warnings: warnings.length + emptyFailed.filter((s) => s.severity === 'warning').length,
|
|
502
|
+
coverage,
|
|
211
503
|
items: [
|
|
212
504
|
...report.violations.map((v) => ({
|
|
213
505
|
file: v.file,
|
|
@@ -215,25 +507,40 @@ function wiringGate(cwd, rules, changedFiles) {
|
|
|
215
507
|
message: `[${v.severity}] ${v.ruleId}: "${v.token}" ${v.direction === 'registered-missing' ? 'registered but not declared' : 'declared but not registered'}`,
|
|
216
508
|
})),
|
|
217
509
|
...diag.map((d) => ({ message: `misconfigured rule: ${d}` })),
|
|
510
|
+
...emptyFailed.map(emptyFailureItem),
|
|
218
511
|
],
|
|
219
512
|
};
|
|
220
513
|
}
|
|
221
|
-
function policyGate(cwd, rules, changedFiles) {
|
|
514
|
+
function policyGate(cwd, rules, changedFiles, excludeDirs) {
|
|
222
515
|
if (rules.length === 0)
|
|
223
516
|
return skip('policy', 'no policy rules configured');
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
517
|
+
// The SharkCraft asset dir holds the rule definitions themselves (which can
|
|
518
|
+
// self-match): pruned through THE plane scan scope, exactly as `policy-lint`.
|
|
519
|
+
const report = runPolicyLint(cwd, rules, { changedOnly: true, changedFiles, excludeDirs });
|
|
520
|
+
// Same shape as the wiring sub-gate: skip ONLY when no rule is in the
|
|
521
|
+
// changed scope. The engine already narrows a rule out when the change put
|
|
522
|
+
// no content in front of it (only deleted files, or files with nothing on its
|
|
523
|
+
// surface), so a rule still SELECTED here that scanned nothing left real
|
|
524
|
+
// scope unexamined: it settles this sub-gate `partial` (2), or `fail` (1)
|
|
525
|
+
// under failOnEmpty — exactly where `policy-lint --changed-only` reads 2 / 1.
|
|
526
|
+
if (report.rules.length === 0) {
|
|
527
|
+
return skip('policy', `${rules.length} rule(s) configured, none with content in the changed scope`);
|
|
227
528
|
}
|
|
228
529
|
const errors = report.findings.filter((f) => f.severity === 'error');
|
|
229
530
|
const warnings = report.findings.filter((f) => f.severity === 'warning');
|
|
230
531
|
const diag = report.diagnostics;
|
|
532
|
+
// `policy-lint` fails on ANY failOnEmpty skip (its proposed exit is 1), so
|
|
533
|
+
// this sub-gate fails on one too, and counts it as an error.
|
|
534
|
+
const emptyFailed = report.skipped.filter((s) => s.failed);
|
|
535
|
+
const { coverage, notVerified } = ruleSetCoverage('policy rules', report.rules, 'examined only part of their scope, scanned nothing, or could not run');
|
|
231
536
|
return {
|
|
537
|
+
...planeUnitFields(report.rules, new Set(report.skipped.map((s) => s.ruleId))),
|
|
232
538
|
name: 'policy',
|
|
233
|
-
status: report.verdict === 'errors' || diag.length > 0 ? 'fail' : 'pass',
|
|
234
|
-
detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}`,
|
|
235
|
-
errors: errors.length + diag.length,
|
|
539
|
+
status: report.verdict === 'errors' || diag.length > 0 || emptyFailed.length > 0 ? 'fail' : 'pass',
|
|
540
|
+
detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}${emptyFailed.length > 0 ? `, ${emptyFailed.length} matched nothing (failOnEmpty)` : ''}${notVerified}`,
|
|
541
|
+
errors: errors.length + diag.length + emptyFailed.length,
|
|
236
542
|
warnings: warnings.length,
|
|
543
|
+
coverage,
|
|
237
544
|
items: [
|
|
238
545
|
...report.findings.map((f) => ({
|
|
239
546
|
file: f.file,
|
|
@@ -241,6 +548,7 @@ function policyGate(cwd, rules, changedFiles) {
|
|
|
241
548
|
message: `[${f.severity}] ${f.ruleId}: ${f.message ?? ''}`.trim(),
|
|
242
549
|
})),
|
|
243
550
|
...diag.map((d) => ({ message: `misconfigured rule: ${d}` })),
|
|
551
|
+
...emptyFailed.map(emptyFailureItem),
|
|
244
552
|
],
|
|
245
553
|
};
|
|
246
554
|
}
|
|
@@ -252,10 +560,10 @@ function policyGate(cwd, rules, changedFiles) {
|
|
|
252
560
|
* fails) when no idioms are configured or the change touched no wiring — so a
|
|
253
561
|
* repo that never modeled its DI is not penalized.
|
|
254
562
|
*/
|
|
255
|
-
function unprovidedGate(cwd, idioms, changedFiles, baseRef) {
|
|
563
|
+
function unprovidedGate(cwd, idioms, changedFiles, excludeDirs, baseRef) {
|
|
256
564
|
if (idioms.length === 0)
|
|
257
565
|
return skip('unprovided', 'no registration idioms configured');
|
|
258
|
-
const graph = buildRegistrationGraph(cwd, idioms);
|
|
566
|
+
const graph = buildRegistrationGraph(cwd, idioms, { excludeDirs });
|
|
259
567
|
const diag = graph.diagnostics;
|
|
260
568
|
// Two ways THIS change can leave a token unprovided:
|
|
261
569
|
// (1) a declared/injected site added in a changed file with no provider —
|
|
@@ -263,32 +571,65 @@ function unprovidedGate(cwd, idioms, changedFiles, baseRef) {
|
|
|
263
571
|
// (2) the last PROVIDER removed from a changed file — which leaves NO site in
|
|
264
572
|
// the changed file, so (1) structurally can't see it. Recover it by
|
|
265
573
|
// diffing the base content of the changed files (providerRegressions).
|
|
266
|
-
const scoped = registrationUnprovided(graph, changedFiles);
|
|
267
574
|
const regressions = baseRef ? providerRegressions(cwd, idioms, graph, changedFiles, baseRef) : [];
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
575
|
+
// Settled against what the graph READ, through the one boundaries helper
|
|
576
|
+
// `wiring unprovided` and MCP `get_wiring_graph` use. A file an idiom
|
|
577
|
+
// matched that the reader could not read (over the read cap) holds sites the
|
|
578
|
+
// graph never saw: a token whose provider could sit there is `unproven` —
|
|
579
|
+
// not a failure, and never a pass (its coverage names the token and file).
|
|
580
|
+
// Round 13 (P4): and against what every idiom's ROLES examined, from THE
|
|
581
|
+
// role authority `gates check` reads — a declared role that matched no file
|
|
582
|
+
// printed `✓ unprovided pass` here while `gates check` said NOT VERIFIED.
|
|
583
|
+
const verdict = registrationUnprovidedVerdict(graph, idioms, measureIdiomRoleCoverage(cwd, idioms, excludeDirs), changedFiles, regressions);
|
|
584
|
+
const unprovided = verdict.findings;
|
|
585
|
+
const candidates = unprovided.length + verdict.unproven.length;
|
|
586
|
+
// A CHANGED unread file is scope this change put in front of the gate, so it
|
|
587
|
+
// keeps the gate from skipping — so does a changed file beneath a directory
|
|
588
|
+
// the reader could not list.
|
|
589
|
+
const unreadChanged = (graph.readScope?.unread ?? []).filter((u) => changedFiles.includes(u.path) || changedFiles.some((f) => unreadDirectoryContains(u, f)));
|
|
273
590
|
// Skip (evaluated nothing) only when the change touched no registration site
|
|
274
591
|
// AND removed no provider AND has no misconfigured idiom — never a silent pass.
|
|
275
|
-
if (!registrationTouchesChanged(graph, changedFiles) &&
|
|
592
|
+
if (!registrationTouchesChanged(graph, changedFiles) &&
|
|
593
|
+
candidates === 0 &&
|
|
594
|
+
diag.length === 0 &&
|
|
595
|
+
unreadChanged.length === 0) {
|
|
276
596
|
return skip('unprovided', 'no registration sites in the changed scope');
|
|
277
597
|
}
|
|
598
|
+
// The most specific record: the demoted tokens (their reason names the
|
|
599
|
+
// unread file), else the first record with a gap — the unread idiom files,
|
|
600
|
+
// or an idiom role that examined nothing (round 13, P4) — else an
|
|
601
|
+
// expectEmpty acceptance (printed at 0), else the first. A full record
|
|
602
|
+
// picked over a gap would settle `pass` over a dead role.
|
|
603
|
+
const coverage = verdict.coverage.find((c) => c.subject === 'unprovided') ??
|
|
604
|
+
verdict.coverage.find((c) => coverageShortfall(c) !== undefined) ??
|
|
605
|
+
verdict.coverage.find((c) => c.acceptedBy !== undefined) ??
|
|
606
|
+
verdict.coverage[0];
|
|
607
|
+
// Every OTHER record rides beside it (round 13 review) — one per idiom: a
|
|
608
|
+
// second idiom's acceptance or dead role is never dropped.
|
|
609
|
+
const extraCoverage = verdict.coverage.filter((c) => c !== coverage);
|
|
610
|
+
const siteOf = (u) => {
|
|
611
|
+
const site = u.declared[0] ?? u.consumed[0];
|
|
612
|
+
return site ? { file: site.file, line: site.line } : {};
|
|
613
|
+
};
|
|
278
614
|
return {
|
|
279
615
|
name: 'unprovided',
|
|
280
616
|
status: unprovided.length > 0 || diag.length > 0 ? 'fail' : 'pass',
|
|
281
|
-
detail: `${unprovided.length} unprovided token(s) attributable to the change
|
|
617
|
+
detail: `${unprovided.length} unprovided token(s) attributable to the change` +
|
|
618
|
+
`${verdict.unproven.length > 0 ? `, ${verdict.unproven.length} not verified (a provider may sit in an unread file)` : ''}` +
|
|
619
|
+
`${diag.length > 0 ? `, ${diag.length} misconfigured idiom(s)` : ''}`,
|
|
282
620
|
errors: unprovided.length + diag.length,
|
|
283
621
|
warnings: 0,
|
|
622
|
+
...(coverage ? { coverage } : {}),
|
|
623
|
+
...(extraCoverage.length > 0 ? { extraCoverage } : {}),
|
|
284
624
|
items: [
|
|
285
|
-
...unprovided.map((u) => {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
625
|
+
...unprovided.map((u) => ({
|
|
626
|
+
...siteOf(u),
|
|
627
|
+
message: `"${u.token}" declared/injected but never provided (silent at runtime)`,
|
|
628
|
+
})),
|
|
629
|
+
...verdict.unproven.map((u) => ({
|
|
630
|
+
...siteOf(u),
|
|
631
|
+
message: `"${u.token}" has no provider among the files read — NOT VERIFIED (a provider may sit in an unread file)`,
|
|
632
|
+
})),
|
|
292
633
|
...diag.map((d) => ({ message: `misconfigured idiom: ${d}` })),
|
|
293
634
|
],
|
|
294
635
|
};
|
|
@@ -376,6 +717,13 @@ async function orphansGate(input) {
|
|
|
376
717
|
}
|
|
377
718
|
if (scan.deleted.length === 0)
|
|
378
719
|
return skip('orphans', `nothing deleted (vs ${scan.ref})`);
|
|
720
|
+
// What the orphan query examined — the ONE authority `check orphans` settles
|
|
721
|
+
// on: deleted files the index knows, over an index current for every
|
|
722
|
+
// surviving importer. A delete of nothing the graph indexes (a README, a
|
|
723
|
+
// `dist/*.js`) evaluates nothing here, so the sub-gate skips.
|
|
724
|
+
const coverage = deletedOrphanCoverage(scan);
|
|
725
|
+
if (coverage.expected === 0)
|
|
726
|
+
return skip('orphans', coverage.reason ?? 'no deleted code files');
|
|
379
727
|
const orphans = scan.report?.orphans ?? [];
|
|
380
728
|
return {
|
|
381
729
|
name: 'orphans',
|
|
@@ -383,6 +731,7 @@ async function orphansGate(input) {
|
|
|
383
731
|
detail: `${scan.deleted.length} deleted file(s) (vs ${scan.ref}) — ${orphans.length} surviving importer(s)`,
|
|
384
732
|
errors: orphans.length,
|
|
385
733
|
warnings: 0,
|
|
734
|
+
coverage,
|
|
386
735
|
items: orphans.map((o) => ({
|
|
387
736
|
file: o.path ?? o.id,
|
|
388
737
|
...(typeof o.line === 'number' ? { line: o.line } : {}),
|