@shrkcrft/inspector 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/acceptance-replay.js +1 -1
- package/dist/agent-contract-templates.js +6 -6
- package/dist/agent-contract.d.ts.map +1 -1
- package/dist/agent-contract.js +13 -8
- package/dist/agent-orchestration.js +2 -2
- package/dist/agent-task-prep.d.ts +16 -0
- package/dist/agent-task-prep.d.ts.map +1 -1
- package/dist/agent-task-prep.js +51 -4
- package/dist/ai-readiness.d.ts.map +1 -1
- package/dist/ai-readiness.js +21 -3
- package/dist/architecture-map.d.ts +16 -0
- package/dist/architecture-map.d.ts.map +1 -1
- package/dist/architecture-map.js +42 -26
- package/dist/area-explore.d.ts +2 -2
- package/dist/area-explore.d.ts.map +1 -1
- package/dist/area-explore.js +36 -16
- package/dist/area-map.d.ts +56 -11
- package/dist/area-map.d.ts.map +1 -1
- package/dist/area-map.js +106 -25
- package/dist/asset-doctor-proposed-exit.d.ts +21 -0
- package/dist/asset-doctor-proposed-exit.d.ts.map +1 -0
- package/dist/asset-doctor-proposed-exit.js +22 -0
- package/dist/asset-provenance.js +1 -1
- package/dist/boundaries-changed-only.d.ts +24 -1
- package/dist/boundaries-changed-only.d.ts.map +1 -1
- package/dist/boundaries-changed-only.js +12 -2
- package/dist/boundary-check-options.model.d.ts +40 -0
- package/dist/boundary-check-options.model.d.ts.map +1 -0
- package/dist/boundary-check-options.model.js +1 -0
- package/dist/boundary-check-result.model.d.ts +89 -0
- package/dist/boundary-check-result.model.d.ts.map +1 -0
- package/dist/boundary-check-result.model.js +1 -0
- package/dist/boundary-configuration-status.d.ts +38 -0
- package/dist/boundary-configuration-status.d.ts.map +1 -0
- package/dist/boundary-configuration-status.js +118 -0
- package/dist/boundary-configuration-status.model.d.ts +37 -0
- package/dist/boundary-configuration-status.model.d.ts.map +1 -0
- package/dist/boundary-configuration-status.model.js +1 -0
- package/dist/boundary-load-issue.model.d.ts +30 -0
- package/dist/boundary-load-issue.model.d.ts.map +1 -0
- package/dist/boundary-load-issue.model.js +1 -0
- package/dist/boundary-rule-check.model.d.ts +44 -0
- package/dist/boundary-rule-check.model.d.ts.map +1 -0
- package/dist/boundary-rule-check.model.js +1 -0
- package/dist/boundary-rule-invalidation.model.d.ts +31 -0
- package/dist/boundary-rule-invalidation.model.d.ts.map +1 -0
- package/dist/boundary-rule-invalidation.model.js +1 -0
- package/dist/boundary-rule-set-diff.model.d.ts +75 -0
- package/dist/boundary-rule-set-diff.model.d.ts.map +1 -0
- package/dist/boundary-rule-set-diff.model.js +1 -0
- package/dist/boundary-rule-sources.d.ts +28 -0
- package/dist/boundary-rule-sources.d.ts.map +1 -0
- package/dist/boundary-rule-sources.js +110 -0
- package/dist/boundary-stale-exception-finding.model.d.ts +12 -0
- package/dist/boundary-stale-exception-finding.model.d.ts.map +1 -0
- package/dist/boundary-stale-exception-finding.model.js +1 -0
- package/dist/boundary-unit-liveness.d.ts +35 -0
- package/dist/boundary-unit-liveness.d.ts.map +1 -0
- package/dist/boundary-unit-liveness.js +71 -0
- package/dist/change-intent.d.ts +3 -0
- package/dist/change-intent.d.ts.map +1 -1
- package/dist/change-intent.js +61 -27
- package/dist/changes-summary.d.ts.map +1 -1
- package/dist/changes-summary.js +19 -9
- package/dist/ci-integrity-report.js +1 -1
- package/dist/ci-predict.d.ts.map +1 -1
- package/dist/ci-predict.js +38 -5
- package/dist/code-intelligence-doctor.d.ts +40 -0
- package/dist/code-intelligence-doctor.d.ts.map +1 -1
- package/dist/code-intelligence-doctor.js +86 -12
- package/dist/command-recommender.d.ts +45 -2
- package/dist/command-recommender.d.ts.map +1 -1
- package/dist/command-recommender.js +41 -263
- package/dist/command-resolution-status.d.ts +44 -0
- package/dist/command-resolution-status.d.ts.map +1 -0
- package/dist/command-resolution-status.js +44 -0
- package/dist/command-safety-level.d.ts +15 -0
- package/dist/command-safety-level.d.ts.map +1 -0
- package/dist/command-safety-level.js +43 -0
- package/dist/command-taxonomy.js +1 -1
- package/dist/compliance-evidence.js +1 -1
- package/dist/compliance-profiles.js +1 -1
- package/dist/construct-registry.d.ts +40 -0
- package/dist/construct-registry.d.ts.map +1 -1
- package/dist/construct-registry.js +186 -82
- package/dist/context-tuning.d.ts +4 -0
- package/dist/context-tuning.d.ts.map +1 -1
- package/dist/context-tuning.js +10 -7
- package/dist/contract-template-registry.d.ts +10 -0
- package/dist/contract-template-registry.d.ts.map +1 -1
- package/dist/contract-template-registry.js +59 -33
- package/dist/contribution-kind.d.ts +52 -0
- package/dist/contribution-kind.d.ts.map +1 -0
- package/dist/contribution-kind.js +52 -0
- package/dist/contribution-load-failures.d.ts +106 -0
- package/dist/contribution-load-failures.d.ts.map +1 -0
- package/dist/contribution-load-failures.js +575 -0
- package/dist/contributions-report.d.ts +28 -0
- package/dist/contributions-report.d.ts.map +1 -0
- package/dist/contributions-report.js +242 -0
- package/dist/convention-registry.d.ts +22 -0
- package/dist/convention-registry.d.ts.map +1 -1
- package/dist/convention-registry.js +79 -39
- package/dist/coverage-report.d.ts.map +1 -1
- package/dist/coverage-report.js +6 -6
- package/dist/custom-checks.d.ts +60 -1
- package/dist/custom-checks.d.ts.map +1 -1
- package/dist/custom-checks.js +159 -6
- package/dist/dashboard/dashboard-data.d.ts +6 -0
- package/dist/dashboard/dashboard-data.d.ts.map +1 -1
- package/dist/dashboard/dashboard-data.js +99 -8
- package/dist/dashboard/dashboard-knowledge.d.ts.map +1 -1
- package/dist/dashboard/dashboard-knowledge.js +14 -2
- package/dist/decision-records.d.ts +20 -0
- package/dist/decision-records.d.ts.map +1 -1
- package/dist/decision-records.js +68 -20
- package/dist/declared-cross-references.d.ts +61 -0
- package/dist/declared-cross-references.d.ts.map +1 -0
- package/dist/declared-cross-references.js +550 -0
- package/dist/declared-gate-plane-rules.d.ts +24 -0
- package/dist/declared-gate-plane-rules.d.ts.map +1 -0
- package/dist/declared-gate-plane-rules.js +39 -0
- package/dist/declared-reference-coverage.d.ts +27 -0
- package/dist/declared-reference-coverage.d.ts.map +1 -0
- package/dist/declared-reference-coverage.js +50 -0
- package/dist/declared-xref-status.d.ts +19 -0
- package/dist/declared-xref-status.d.ts.map +1 -0
- package/dist/declared-xref-status.js +19 -0
- package/dist/delegate-pack-recipes.d.ts +13 -2
- package/dist/delegate-pack-recipes.d.ts.map +1 -1
- package/dist/delegate-pack-recipes.js +45 -14
- package/dist/demo-package.js +2 -2
- package/dist/demo-script.js +2 -2
- package/dist/demo-workflow.js +3 -3
- package/dist/dev-cycle.js +1 -1
- package/dist/dev-session.js +1 -1
- package/dist/doc-references.d.ts +80 -0
- package/dist/doc-references.d.ts.map +1 -0
- package/dist/doc-references.js +269 -0
- package/dist/doctor-result.d.ts +8 -0
- package/dist/doctor-result.d.ts.map +1 -1
- package/dist/doctor-verdict-kind.d.ts +19 -0
- package/dist/doctor-verdict-kind.d.ts.map +1 -0
- package/dist/doctor-verdict-kind.js +19 -0
- package/dist/doctor-verdict.d.ts +18 -0
- package/dist/doctor-verdict.d.ts.map +1 -0
- package/dist/doctor-verdict.js +28 -0
- package/dist/drift.d.ts +17 -0
- package/dist/drift.d.ts.map +1 -1
- package/dist/drift.js +40 -10
- package/dist/entrypoint-matrix.d.ts.map +1 -1
- package/dist/entrypoint-matrix.js +4 -1
- package/dist/feedback-ingestion.d.ts +28 -2
- package/dist/feedback-ingestion.d.ts.map +1 -1
- package/dist/feedback-ingestion.js +68 -19
- package/dist/fix-preview.d.ts.map +1 -1
- package/dist/fix-preview.js +9 -5
- package/dist/framework-extractor-outcomes.d.ts +27 -0
- package/dist/framework-extractor-outcomes.d.ts.map +1 -0
- package/dist/framework-extractor-outcomes.js +83 -0
- package/dist/fuzzy-impact.d.ts.map +1 -1
- package/dist/fuzzy-impact.js +12 -21
- package/dist/healing-plan.d.ts.map +1 -1
- package/dist/healing-plan.js +3 -4
- package/dist/helper-catalog.d.ts +25 -0
- package/dist/helper-catalog.d.ts.map +1 -0
- package/dist/helper-catalog.js +91 -0
- package/dist/helper-registry.d.ts.map +1 -1
- package/dist/helper-registry.js +17 -1
- package/dist/helper-view.d.ts +33 -0
- package/dist/helper-view.d.ts.map +1 -0
- package/dist/helper-view.js +1 -0
- package/dist/i-asset-doctor-flags.d.ts +16 -0
- package/dist/i-asset-doctor-flags.d.ts.map +1 -0
- package/dist/i-asset-doctor-flags.js +1 -0
- package/dist/i-command-resolution.d.ts +22 -0
- package/dist/i-command-resolution.d.ts.map +1 -0
- package/dist/i-command-resolution.js +1 -0
- package/dist/i-command-resolve-options.d.ts +24 -0
- package/dist/i-command-resolve-options.d.ts.map +1 -0
- package/dist/i-command-resolve-options.js +1 -0
- package/dist/i-command-safety.d.ts +13 -0
- package/dist/i-command-safety.d.ts.map +1 -0
- package/dist/i-command-safety.js +1 -0
- package/dist/i-contribution-accepted-entry.d.ts +18 -0
- package/dist/i-contribution-accepted-entry.d.ts.map +1 -0
- package/dist/i-contribution-accepted-entry.js +1 -0
- package/dist/i-contribution-entry-rejection.d.ts +21 -0
- package/dist/i-contribution-entry-rejection.d.ts.map +1 -0
- package/dist/i-contribution-entry-rejection.js +1 -0
- package/dist/i-contribution-file-issue.d.ts +18 -0
- package/dist/i-contribution-file-issue.d.ts.map +1 -0
- package/dist/i-contribution-file-issue.js +1 -0
- package/dist/i-contribution-file-report.d.ts +40 -0
- package/dist/i-contribution-file-report.d.ts.map +1 -0
- package/dist/i-contribution-file-report.js +1 -0
- package/dist/i-contribution-file-validation.d.ts +24 -0
- package/dist/i-contribution-file-validation.d.ts.map +1 -0
- package/dist/i-contribution-file-validation.js +1 -0
- package/dist/i-contributions-report.d.ts +31 -0
- package/dist/i-contributions-report.d.ts.map +1 -0
- package/dist/i-contributions-report.js +1 -0
- package/dist/i-declared-xref-field.d.ts +25 -0
- package/dist/i-declared-xref-field.d.ts.map +1 -0
- package/dist/i-declared-xref-field.js +1 -0
- package/dist/i-declared-xref-issue.d.ts +25 -0
- package/dist/i-declared-xref-issue.d.ts.map +1 -0
- package/dist/i-declared-xref-issue.js +1 -0
- package/dist/i-declared-xref-report.d.ts +48 -0
- package/dist/i-declared-xref-report.d.ts.map +1 -0
- package/dist/i-declared-xref-report.js +1 -0
- package/dist/i-declared-xref-row.d.ts +29 -0
- package/dist/i-declared-xref-row.d.ts.map +1 -0
- package/dist/i-declared-xref-row.js +1 -0
- package/dist/i-doctor-verdict.d.ts +10 -0
- package/dist/i-doctor-verdict.d.ts.map +1 -0
- package/dist/i-doctor-verdict.js +1 -0
- package/dist/i-kind-outcomes.d.ts +13 -0
- package/dist/i-kind-outcomes.d.ts.map +1 -0
- package/dist/i-kind-outcomes.js +1 -0
- package/dist/i-pack-doctor-verdict.d.ts +15 -0
- package/dist/i-pack-doctor-verdict.d.ts.map +1 -0
- package/dist/i-pack-doctor-verdict.js +1 -0
- package/dist/i-probed-id-field.d.ts +17 -0
- package/dist/i-probed-id-field.d.ts.map +1 -0
- package/dist/i-probed-id-field.js +1 -0
- package/dist/i-reference-kind-declaration.d.ts +38 -0
- package/dist/i-reference-kind-declaration.d.ts.map +1 -0
- package/dist/i-reference-kind-declaration.js +1 -0
- package/dist/i-reference-warm-options.d.ts +21 -0
- package/dist/i-reference-warm-options.d.ts.map +1 -0
- package/dist/i-reference-warm-options.js +1 -0
- package/dist/i-registry-outcomes.d.ts +16 -0
- package/dist/i-registry-outcomes.d.ts.map +1 -0
- package/dist/i-registry-outcomes.js +1 -0
- package/dist/i-scaffold-pattern-doctor-report.d.ts +35 -0
- package/dist/i-scaffold-pattern-doctor-report.d.ts.map +1 -0
- package/dist/i-scaffold-pattern-doctor-report.js +1 -0
- package/dist/i-search-tuning-key-probe.d.ts +30 -0
- package/dist/i-search-tuning-key-probe.d.ts.map +1 -0
- package/dist/i-search-tuning-key-probe.js +1 -0
- package/dist/i-search-tuning-lint-issue.d.ts +61 -0
- package/dist/i-search-tuning-lint-issue.d.ts.map +1 -0
- package/dist/i-search-tuning-lint-issue.js +1 -0
- package/dist/i-search-tuning-lint-report.d.ts +38 -0
- package/dist/i-search-tuning-lint-report.d.ts.map +1 -0
- package/dist/i-search-tuning-lint-report.js +1 -0
- package/dist/i-unresolvable-reference-scan.d.ts +14 -0
- package/dist/i-unresolvable-reference-scan.d.ts.map +1 -0
- package/dist/i-unresolvable-reference-scan.js +1 -0
- package/dist/i-unresolvable-reference.d.ts +29 -0
- package/dist/i-unresolvable-reference.d.ts.map +1 -0
- package/dist/i-unresolvable-reference.js +1 -0
- package/dist/i-workspace-profile-payload.d.ts +12 -0
- package/dist/i-workspace-profile-payload.d.ts.map +1 -0
- package/dist/i-workspace-profile-payload.js +1 -0
- package/dist/impact-analysis.d.ts +15 -0
- package/dist/impact-analysis.d.ts.map +1 -1
- package/dist/impact-analysis.js +58 -13
- package/dist/impact-render.d.ts.map +1 -1
- package/dist/impact-render.js +22 -0
- package/dist/import-hygiene.d.ts +35 -1
- package/dist/import-hygiene.d.ts.map +1 -1
- package/dist/import-hygiene.js +107 -13
- package/dist/index.d.ts +145 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +176 -0
- package/dist/inspection-discovery.d.ts +45 -0
- package/dist/inspection-discovery.d.ts.map +1 -0
- package/dist/inspection-discovery.js +70 -0
- package/dist/knowledge-advisory-code.d.ts +17 -0
- package/dist/knowledge-advisory-code.d.ts.map +1 -0
- package/dist/knowledge-advisory-code.js +17 -0
- package/dist/knowledge-aged-entry.d.ts +10 -0
- package/dist/knowledge-aged-entry.d.ts.map +1 -0
- package/dist/knowledge-aged-entry.js +1 -0
- package/dist/knowledge-authoring.d.ts +24 -1
- package/dist/knowledge-authoring.d.ts.map +1 -1
- package/dist/knowledge-authoring.js +46 -7
- package/dist/knowledge-entry-verdict-record.d.ts +22 -0
- package/dist/knowledge-entry-verdict-record.d.ts.map +1 -0
- package/dist/knowledge-entry-verdict-record.js +1 -0
- package/dist/knowledge-entry-verdict.d.ts +17 -0
- package/dist/knowledge-entry-verdict.d.ts.map +1 -0
- package/dist/knowledge-entry-verdict.js +17 -0
- package/dist/knowledge-graph.d.ts +6 -0
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +18 -7
- package/dist/knowledge-kind-bucket.d.ts +19 -0
- package/dist/knowledge-kind-bucket.d.ts.map +1 -0
- package/dist/knowledge-kind-bucket.js +1 -0
- package/dist/knowledge-load-failure.d.ts +23 -0
- package/dist/knowledge-load-failure.d.ts.map +1 -0
- package/dist/knowledge-load-failure.js +1 -0
- package/dist/knowledge-reference-kind-bucket.d.ts +11 -0
- package/dist/knowledge-reference-kind-bucket.d.ts.map +1 -0
- package/dist/knowledge-reference-kind-bucket.js +1 -0
- package/dist/knowledge-stale-advisory.d.ts +14 -0
- package/dist/knowledge-stale-advisory.d.ts.map +1 -0
- package/dist/knowledge-stale-advisory.js +1 -0
- package/dist/knowledge-stale-coverage.d.ts +19 -0
- package/dist/knowledge-stale-coverage.d.ts.map +1 -0
- package/dist/knowledge-stale-coverage.js +1 -0
- package/dist/knowledge-stale-gate-flags.d.ts +33 -0
- package/dist/knowledge-stale-gate-flags.d.ts.map +1 -0
- package/dist/knowledge-stale-gate-flags.js +1 -0
- package/dist/knowledge-stale-gate-input.d.ts +46 -0
- package/dist/knowledge-stale-gate-input.d.ts.map +1 -0
- package/dist/knowledge-stale-gate-input.js +1 -0
- package/dist/knowledge-stale-gate-result.d.ts +33 -0
- package/dist/knowledge-stale-gate-result.d.ts.map +1 -0
- package/dist/knowledge-stale-gate-result.js +1 -0
- package/dist/knowledge-stale-gate-rule.d.ts +33 -0
- package/dist/knowledge-stale-gate-rule.d.ts.map +1 -0
- package/dist/knowledge-stale-gate-rule.js +1 -0
- package/dist/knowledge-stale-gate-violation.d.ts +13 -0
- package/dist/knowledge-stale-gate-violation.d.ts.map +1 -0
- package/dist/knowledge-stale-gate-violation.js +1 -0
- package/dist/knowledge-stale-gate.d.ts +83 -0
- package/dist/knowledge-stale-gate.d.ts.map +1 -0
- package/dist/knowledge-stale-gate.js +429 -0
- package/dist/knowledge-stale-quality-gate.d.ts +26 -0
- package/dist/knowledge-stale-quality-gate.d.ts.map +1 -0
- package/dist/knowledge-stale-quality-gate.js +110 -0
- package/dist/knowledge-stale.d.ts +122 -9
- package/dist/knowledge-stale.d.ts.map +1 -1
- package/dist/knowledge-stale.js +1002 -316
- package/dist/knowledge-unverifiable-reason.d.ts +12 -0
- package/dist/knowledge-unverifiable-reason.d.ts.map +1 -0
- package/dist/knowledge-unverifiable-reason.js +12 -0
- package/dist/loader-diagnostics.d.ts +14 -2
- package/dist/loader-diagnostics.d.ts.map +1 -1
- package/dist/match-terms.d.ts +48 -0
- package/dist/match-terms.d.ts.map +1 -0
- package/dist/match-terms.js +155 -0
- package/dist/migration-profile-registry.d.ts +10 -0
- package/dist/migration-profile-registry.d.ts.map +1 -1
- package/dist/migration-profile-registry.js +57 -34
- package/dist/migration-readiness.d.ts.map +1 -1
- package/dist/migration-readiness.js +1 -3
- package/dist/nearest-id.d.ts +14 -0
- package/dist/nearest-id.d.ts.map +1 -0
- package/dist/nearest-id.js +12 -0
- package/dist/pack-asset-freshness.d.ts +65 -0
- package/dist/pack-asset-freshness.d.ts.map +1 -0
- package/dist/pack-asset-freshness.js +340 -0
- package/dist/pack-author-ux.d.ts +24 -3
- package/dist/pack-author-ux.d.ts.map +1 -1
- package/dist/pack-author-ux.js +87 -93
- package/dist/pack-contributions-inventory.d.ts +79 -39
- package/dist/pack-contributions-inventory.d.ts.map +1 -1
- package/dist/pack-contributions-inventory.js +443 -286
- package/dist/pack-doctor-verdict.d.ts +29 -0
- package/dist/pack-doctor-verdict.d.ts.map +1 -0
- package/dist/pack-doctor-verdict.js +45 -0
- package/dist/pack-doctor.d.ts +74 -1
- package/dist/pack-doctor.d.ts.map +1 -1
- package/dist/pack-doctor.js +281 -1
- package/dist/pack-helper-plan.d.ts +24 -0
- package/dist/pack-helper-plan.d.ts.map +1 -0
- package/dist/pack-helper-plan.js +77 -0
- package/dist/pack-helper-registry.d.ts +14 -0
- package/dist/pack-helper-registry.d.ts.map +1 -1
- package/dist/pack-helper-registry.js +76 -37
- package/dist/pack-manifest-reader.d.ts +8 -0
- package/dist/pack-manifest-reader.d.ts.map +1 -0
- package/dist/pack-manifest-reader.js +79 -0
- package/dist/pack-release-check.d.ts +14 -1
- package/dist/pack-release-check.d.ts.map +1 -1
- package/dist/pack-release-check.js +23 -1
- package/dist/pack-signature-status.d.ts +18 -6
- package/dist/pack-signature-status.d.ts.map +1 -1
- package/dist/pack-signature-status.js +59 -105
- package/dist/pack-test-runner.d.ts +27 -5
- package/dist/pack-test-runner.d.ts.map +1 -1
- package/dist/pack-test-runner.js +91 -35
- package/dist/pack-typecheck.d.ts +8 -0
- package/dist/pack-typecheck.d.ts.map +1 -0
- package/dist/pack-typecheck.js +76 -0
- package/dist/plan-review.d.ts +2 -0
- package/dist/plan-review.d.ts.map +1 -1
- package/dist/plan-review.js +17 -5
- package/dist/plan-simulation.d.ts.map +1 -1
- package/dist/plan-simulation.js +16 -5
- package/dist/playbook-load-issue.d.ts +16 -0
- package/dist/playbook-load-issue.d.ts.map +1 -0
- package/dist/playbook-load-issue.js +1 -0
- package/dist/playbook-registry.d.ts +32 -1
- package/dist/playbook-registry.d.ts.map +1 -1
- package/dist/playbook-registry.js +144 -55
- package/dist/policy-declaration.d.ts +18 -0
- package/dist/policy-declaration.d.ts.map +1 -0
- package/dist/policy-declaration.js +1 -0
- package/dist/policy-engine.d.ts +8 -0
- package/dist/policy-engine.d.ts.map +1 -1
- package/dist/policy-engine.js +26 -9
- package/dist/policy-overrides.d.ts.map +1 -1
- package/dist/policy-overrides.js +4 -4
- package/dist/policy-registry.d.ts +42 -0
- package/dist/policy-registry.d.ts.map +1 -0
- package/dist/policy-registry.js +131 -0
- package/dist/probed-id-fields.d.ts +21 -0
- package/dist/probed-id-fields.d.ts.map +1 -0
- package/dist/probed-id-fields.js +25 -0
- package/dist/probed-id-source.d.ts +11 -0
- package/dist/probed-id-source.d.ts.map +1 -0
- package/dist/probed-id-source.js +11 -0
- package/dist/profile-registry.d.ts +25 -2
- package/dist/profile-registry.d.ts.map +1 -1
- package/dist/profile-registry.js +59 -6
- package/dist/quality-gate-examination.d.ts +15 -0
- package/dist/quality-gate-examination.d.ts.map +1 -0
- package/dist/quality-gate-examination.js +15 -0
- package/dist/quality-gate-row-status.d.ts +19 -0
- package/dist/quality-gate-row-status.d.ts.map +1 -0
- package/dist/quality-gate-row-status.js +28 -0
- package/dist/quality-gate-status.d.ts +16 -0
- package/dist/quality-gate-status.d.ts.map +1 -0
- package/dist/quality-gate-status.js +16 -0
- package/dist/quality-html.d.ts.map +1 -1
- package/dist/quality-html.js +14 -3
- package/dist/quality-report-coverage.d.ts +26 -0
- package/dist/quality-report-coverage.d.ts.map +1 -0
- package/dist/quality-report-coverage.js +48 -0
- package/dist/quality-report.d.ts +28 -1
- package/dist/quality-report.d.ts.map +1 -1
- package/dist/quality-report.js +263 -35
- package/dist/query-intent-kind.d.ts +18 -0
- package/dist/query-intent-kind.d.ts.map +1 -0
- package/dist/query-intent-kind.js +18 -0
- package/dist/query-intent-result.d.ts +23 -0
- package/dist/query-intent-result.d.ts.map +1 -0
- package/dist/query-intent-result.js +1 -0
- package/dist/query-intent.d.ts +18 -0
- package/dist/query-intent.d.ts.map +1 -0
- package/dist/query-intent.js +336 -0
- package/dist/query-resolver.d.ts +18 -0
- package/dist/query-resolver.d.ts.map +1 -1
- package/dist/query-resolver.js +15 -12
- package/dist/ranked-recommendations.d.ts +36 -0
- package/dist/ranked-recommendations.d.ts.map +1 -0
- package/dist/ranked-recommendations.js +1 -0
- package/dist/ranker-explainability.d.ts.map +1 -1
- package/dist/ranker-explainability.js +38 -10
- package/dist/recommend-recipe.d.ts +17 -0
- package/dist/recommend-recipe.d.ts.map +1 -0
- package/dist/recommend-recipe.js +1 -0
- package/dist/recommend-recipes.d.ts +9 -0
- package/dist/recommend-recipes.d.ts.map +1 -0
- package/dist/recommend-recipes.js +106 -0
- package/dist/recommendation-candidate.d.ts +30 -0
- package/dist/recommendation-candidate.d.ts.map +1 -0
- package/dist/recommendation-candidate.js +1 -0
- package/dist/recommendation-confidence.d.ts +27 -0
- package/dist/recommendation-confidence.d.ts.map +1 -0
- package/dist/recommendation-confidence.js +1 -0
- package/dist/recommendation-ranking-options.d.ts +24 -0
- package/dist/recommendation-ranking-options.d.ts.map +1 -0
- package/dist/recommendation-ranking-options.js +1 -0
- package/dist/recommendation-ranking.d.ts +50 -0
- package/dist/recommendation-ranking.d.ts.map +1 -0
- package/dist/recommendation-ranking.js +527 -0
- package/dist/recommendation-source.d.ts +23 -0
- package/dist/recommendation-source.d.ts.map +1 -0
- package/dist/recommendation-source.js +23 -0
- package/dist/recommendation-suppression.d.ts +14 -0
- package/dist/recommendation-suppression.d.ts.map +1 -0
- package/dist/recommendation-suppression.js +14 -0
- package/dist/reference-asset-kind.d.ts +14 -0
- package/dist/reference-asset-kind.d.ts.map +1 -0
- package/dist/reference-asset-kind.js +14 -0
- package/dist/reference-content-check.d.ts +32 -0
- package/dist/reference-content-check.d.ts.map +1 -0
- package/dist/reference-content-check.js +143 -0
- package/dist/reference-failure.d.ts +31 -0
- package/dist/reference-failure.d.ts.map +1 -0
- package/dist/reference-failure.js +31 -0
- package/dist/reference-id-status.d.ts +15 -0
- package/dist/reference-id-status.d.ts.map +1 -0
- package/dist/reference-id-status.js +15 -0
- package/dist/reference-kind-declarations.d.ts +42 -0
- package/dist/reference-kind-declarations.d.ts.map +1 -0
- package/dist/reference-kind-declarations.js +142 -0
- package/dist/reference-lookup.d.ts +6 -0
- package/dist/reference-lookup.d.ts.map +1 -1
- package/dist/reference-lookup.js +13 -5
- package/dist/reference-registry.d.ts +210 -0
- package/dist/reference-registry.d.ts.map +1 -0
- package/dist/reference-registry.js +395 -0
- package/dist/reference-subject.d.ts +20 -0
- package/dist/reference-subject.d.ts.map +1 -0
- package/dist/reference-subject.js +1 -0
- package/dist/registration-hint-discovery-status.d.ts +25 -0
- package/dist/registration-hint-discovery-status.d.ts.map +1 -0
- package/dist/registration-hint-discovery-status.js +25 -0
- package/dist/registration-hint-registry.d.ts +80 -0
- package/dist/registration-hint-registry.d.ts.map +1 -1
- package/dist/registration-hint-registry.js +356 -138
- package/dist/registry-lifecycle.d.ts +200 -19
- package/dist/registry-lifecycle.d.ts.map +1 -1
- package/dist/registry-lifecycle.js +526 -136
- package/dist/release-readiness.d.ts.map +1 -1
- package/dist/release-readiness.js +10 -7
- package/dist/release-smoke.d.ts.map +1 -1
- package/dist/release-smoke.js +11 -7
- package/dist/report-site.d.ts.map +1 -1
- package/dist/report-site.js +25 -7
- package/dist/repository-intelligence.d.ts.map +1 -1
- package/dist/repository-intelligence.js +3 -2
- package/dist/repository-knowledge-model.d.ts.map +1 -1
- package/dist/repository-knowledge-model.js +18 -5
- package/dist/resolve-project-config.d.ts +64 -0
- package/dist/resolve-project-config.d.ts.map +1 -1
- package/dist/resolve-project-config.js +258 -32
- package/dist/reuse/compute-reuse-coverage.d.ts +29 -0
- package/dist/reuse/compute-reuse-coverage.d.ts.map +1 -0
- package/dist/reuse/compute-reuse-coverage.js +198 -0
- package/dist/reuse/rank-reuse-candidates.d.ts +38 -0
- package/dist/reuse/rank-reuse-candidates.d.ts.map +1 -0
- package/dist/reuse/rank-reuse-candidates.js +212 -0
- package/dist/reuse/rank-reuse-options.d.ts +20 -0
- package/dist/reuse/rank-reuse-options.d.ts.map +1 -0
- package/dist/reuse/rank-reuse-options.js +1 -0
- package/dist/reuse/rank-reuse-suggestions.d.ts +15 -0
- package/dist/reuse/rank-reuse-suggestions.d.ts.map +1 -0
- package/dist/reuse/rank-reuse-suggestions.js +31 -0
- package/dist/reuse/resolve-curated-reuse.d.ts +43 -0
- package/dist/reuse/resolve-curated-reuse.d.ts.map +1 -0
- package/dist/reuse/resolve-curated-reuse.js +115 -0
- package/dist/reuse/reuse-candidate.d.ts +29 -0
- package/dist/reuse/reuse-candidate.d.ts.map +1 -0
- package/dist/reuse/reuse-candidate.js +1 -0
- package/dist/reuse/reuse-coverage-report.d.ts +74 -0
- package/dist/reuse/reuse-coverage-report.d.ts.map +1 -0
- package/dist/reuse/reuse-coverage-report.js +1 -0
- package/dist/reuse/reuse-curated-coverage.d.ts +36 -0
- package/dist/reuse/reuse-curated-coverage.d.ts.map +1 -0
- package/dist/reuse/reuse-curated-coverage.js +1 -0
- package/dist/reuse/reuse-curated-resolution.d.ts +36 -0
- package/dist/reuse/reuse-curated-resolution.d.ts.map +1 -0
- package/dist/reuse/reuse-curated-resolution.js +1 -0
- package/dist/reuse/reuse-curated-status.d.ts +21 -0
- package/dist/reuse/reuse-curated-status.d.ts.map +1 -0
- package/dist/reuse/reuse-curated-status.js +21 -0
- package/dist/reuse/reuse-match-detail.d.ts +15 -0
- package/dist/reuse/reuse-match-detail.d.ts.map +1 -0
- package/dist/reuse/reuse-match-detail.js +1 -0
- package/dist/reuse/reuse-ranking.d.ts +37 -0
- package/dist/reuse/reuse-ranking.d.ts.map +1 -0
- package/dist/reuse/reuse-ranking.js +1 -0
- package/dist/reuse/reuse-suggestion.d.ts +19 -0
- package/dist/reuse/reuse-suggestion.d.ts.map +1 -0
- package/dist/reuse/reuse-suggestion.js +1 -0
- package/dist/reuse/reuse-symbol-lookup.d.ts +28 -0
- package/dist/reuse/reuse-symbol-lookup.d.ts.map +1 -0
- package/dist/reuse/reuse-symbol-lookup.js +1 -0
- package/dist/reuse/reuse-tokenize.d.ts +11 -0
- package/dist/reuse/reuse-tokenize.d.ts.map +1 -0
- package/dist/reuse/reuse-tokenize.js +26 -0
- package/dist/reuse/score-reuse-name.d.ts +16 -0
- package/dist/reuse/score-reuse-name.d.ts.map +1 -0
- package/dist/reuse/score-reuse-name.js +35 -0
- package/dist/reuse/score-reuse-primitive.d.ts +33 -0
- package/dist/reuse/score-reuse-primitive.d.ts.map +1 -0
- package/dist/reuse/score-reuse-primitive.js +92 -0
- package/dist/review-packet.d.ts +2 -0
- package/dist/review-packet.d.ts.map +1 -1
- package/dist/review-packet.js +13 -6
- package/dist/role-views.d.ts.map +1 -1
- package/dist/role-views.js +16 -16
- package/dist/routing-recommends-channels.d.ts +24 -0
- package/dist/routing-recommends-channels.d.ts.map +1 -0
- package/dist/routing-recommends-channels.js +19 -0
- package/dist/run-boundary-check.d.ts +118 -0
- package/dist/run-boundary-check.d.ts.map +1 -0
- package/dist/run-boundary-check.js +592 -0
- package/dist/safety-audit.js +1 -1
- package/dist/scaffold-coverage.js +1 -1
- package/dist/scaffold-pattern-doctor-report.d.ts +19 -0
- package/dist/scaffold-pattern-doctor-report.d.ts.map +1 -0
- package/dist/scaffold-pattern-doctor-report.js +54 -0
- package/dist/scaffold-patterns.d.ts +75 -1
- package/dist/scaffold-patterns.d.ts.map +1 -1
- package/dist/scaffold-patterns.js +375 -84
- package/dist/schema-inventory.d.ts.map +1 -1
- package/dist/schema-inventory.js +4 -3
- package/dist/search-document-id.d.ts +52 -0
- package/dist/search-document-id.d.ts.map +1 -0
- package/dist/search-document-id.js +72 -0
- package/dist/search-document-prefix.d.ts +7 -0
- package/dist/search-document-prefix.d.ts.map +1 -0
- package/dist/search-document-prefix.js +1 -0
- package/dist/search-index.d.ts.map +1 -1
- package/dist/search-index.js +20 -23
- package/dist/search-tuning-explain.d.ts +6 -0
- package/dist/search-tuning-explain.d.ts.map +1 -1
- package/dist/search-tuning-explain.js +19 -17
- package/dist/search-tuning-key-probes.d.ts +16 -0
- package/dist/search-tuning-key-probes.d.ts.map +1 -0
- package/dist/search-tuning-key-probes.js +65 -0
- package/dist/search-tuning-key-resolution.d.ts +25 -0
- package/dist/search-tuning-key-resolution.d.ts.map +1 -0
- package/dist/search-tuning-key-resolution.js +1 -0
- package/dist/search-tuning-key-resolver.d.ts +4 -0
- package/dist/search-tuning-key-resolver.d.ts.map +1 -0
- package/dist/search-tuning-key-resolver.js +107 -0
- package/dist/search-tuning-key-status.d.ts +24 -0
- package/dist/search-tuning-key-status.d.ts.map +1 -0
- package/dist/search-tuning-key-status.js +24 -0
- package/dist/search-tuning-lint.d.ts +9 -0
- package/dist/search-tuning-lint.d.ts.map +1 -0
- package/dist/search-tuning-lint.js +468 -0
- package/dist/search-tuning-registry.d.ts +43 -0
- package/dist/search-tuning-registry.d.ts.map +1 -1
- package/dist/search-tuning-registry.js +214 -49
- package/dist/self-audit.d.ts +12 -0
- package/dist/self-audit.d.ts.map +1 -1
- package/dist/self-audit.js +36 -10
- package/dist/self-config-doctor-v2.d.ts +145 -3
- package/dist/self-config-doctor-v2.d.ts.map +1 -1
- package/dist/self-config-doctor-v2.js +1329 -255
- package/dist/self-config-doctor.d.ts +20 -1
- package/dist/self-config-doctor.d.ts.map +1 -1
- package/dist/self-config-doctor.js +65 -309
- package/dist/settled-unit-states.d.ts +9 -0
- package/dist/settled-unit-states.d.ts.map +1 -0
- package/dist/settled-unit-states.js +14 -0
- package/dist/sharkcraft-inspector.d.ts +47 -1
- package/dist/sharkcraft-inspector.d.ts.map +1 -1
- package/dist/sharkcraft-inspector.js +278 -29
- package/dist/spec/spec-evidence.d.ts.map +1 -1
- package/dist/spec/spec-evidence.js +14 -20
- package/dist/split-identifier.d.ts +21 -0
- package/dist/split-identifier.d.ts.map +1 -0
- package/dist/split-identifier.js +27 -0
- package/dist/start-here.d.ts.map +1 -1
- package/dist/start-here.js +1 -2
- package/dist/substitute-placeholders.d.ts +8 -0
- package/dist/substitute-placeholders.d.ts.map +1 -0
- package/dist/substitute-placeholders.js +9 -0
- package/dist/symbol-index.d.ts +33 -0
- package/dist/symbol-index.d.ts.map +1 -1
- package/dist/symbol-index.js +241 -3
- package/dist/symbol-member-entry.d.ts +26 -0
- package/dist/symbol-member-entry.d.ts.map +1 -0
- package/dist/symbol-member-entry.js +1 -0
- package/dist/symbol-member-kind.d.ts +14 -0
- package/dist/symbol-member-kind.d.ts.map +1 -0
- package/dist/symbol-member-kind.js +14 -0
- package/dist/task-packet.d.ts.map +1 -1
- package/dist/task-packet.js +52 -9
- package/dist/task-ranker.d.ts +8 -0
- package/dist/task-ranker.d.ts.map +1 -1
- package/dist/task-ranker.js +25 -13
- package/dist/task-risk.js +1 -1
- package/dist/task-routing-hint-registry.d.ts +20 -1
- package/dist/task-routing-hint-registry.d.ts.map +1 -1
- package/dist/task-routing-hint-registry.js +186 -42
- package/dist/template-drift.d.ts.map +1 -1
- package/dist/template-drift.js +30 -9
- package/dist/template-lint.d.ts.map +1 -1
- package/dist/template-lint.js +160 -2
- package/dist/term-query.d.ts +10 -0
- package/dist/term-query.d.ts.map +1 -0
- package/dist/term-query.js +1 -0
- package/dist/test-definitions.d.ts +6 -3
- package/dist/test-definitions.d.ts.map +1 -1
- package/dist/test-runner.d.ts +95 -10
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +309 -318
- package/dist/tuning-query-tokens.d.ts +29 -0
- package/dist/tuning-query-tokens.d.ts.map +1 -0
- package/dist/tuning-query-tokens.js +51 -0
- package/dist/typecheck-files.d.ts +36 -0
- package/dist/typecheck-files.d.ts.map +1 -0
- package/dist/typecheck-files.js +158 -0
- package/dist/uncertainty.js +1 -1
- package/dist/universal-search.d.ts.map +1 -1
- package/dist/universal-search.js +5 -3
- package/dist/unregistered-exports.d.ts +19 -0
- package/dist/unregistered-exports.d.ts.map +1 -0
- package/dist/unregistered-exports.js +141 -0
- package/dist/unresolvable-reason.d.ts +11 -0
- package/dist/unresolvable-reason.d.ts.map +1 -0
- package/dist/unresolvable-reason.js +11 -0
- package/dist/upgrade-advisor.d.ts.map +1 -1
- package/dist/upgrade-advisor.js +16 -9
- package/dist/validate-contribution-file.d.ts +4 -0
- package/dist/validate-contribution-file.d.ts.map +1 -0
- package/dist/validate-contribution-file.js +228 -0
- package/dist/why-file.d.ts +3 -0
- package/dist/why-file.d.ts.map +1 -1
- package/dist/why-file.js +7 -3
- package/package.json +17 -17
|
@@ -17,16 +17,32 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { existsSync } from 'node:fs';
|
|
19
19
|
import * as nodePath from 'node:path';
|
|
20
|
+
import { coverageShortfall, DEAD_SELECTOR_CAUSES, formatCoverage, RejectionCause, settleUnitLiveness, UnitDeadCause, UnitDeadWeight, UnitLivenessState, } from '@shrkcrft/core';
|
|
21
|
+
import { searchTuningKeyProbes } from "./search-tuning-key-probes.js";
|
|
22
|
+
import { ContributionKind } from "./contribution-kind.js";
|
|
23
|
+
import { resolveEntryFile } from "./contribution-load-failures.js";
|
|
24
|
+
import { UnresolvableReason } from "./unresolvable-reason.js";
|
|
25
|
+
import { listTaskRoutingHintIssues, loadTaskRoutingHints, listTaskRoutingHints, routingHintCanMatch, } from "./task-routing-hint-registry.js";
|
|
26
|
+
import { buildRegistrationHintDoctorReport, listRegistrationHints } from "./registration-hint-registry.js";
|
|
27
|
+
import { listDecisions, loadTsDecisions } from "./decision-records.js";
|
|
28
|
+
import { COMMAND_INDEX_NOT_INJECTED, emptyReferenceKinds, hasCommandResolver, referenceIdExists, referenceIdsFor, referenceKindsOf, resolveCommandReference, resolveShrkCommandReference, warmReferenceRegistries, } from "./reference-registry.js";
|
|
29
|
+
import { nearestIds } from "./nearest-id.js";
|
|
30
|
+
import { formatReferenceKindDeclaration, isReferenceKindDeclarable, referenceKindListVerb, } from "./reference-kind-declarations.js";
|
|
31
|
+
import { PROBED_ID_FIELDS } from "./probed-id-fields.js";
|
|
32
|
+
import { ProbedIdSource } from "./probed-id-source.js";
|
|
20
33
|
import { listConventions } from "./convention-registry.js";
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
34
|
+
import { ROUTING_RECOMMENDS_CHANNEL_KEYS, ROUTING_RECOMMENDS_CHANNELS, } from "./routing-recommends-channels.js";
|
|
35
|
+
import { doctorScaffoldPatterns, enumerateScaffoldPatternCandidates, loadScaffoldPatternsFromInspection, scaffoldPatternCoverage, } from "./scaffold-patterns.js";
|
|
36
|
+
import { lintSearchTuning } from "./search-tuning-lint.js";
|
|
37
|
+
import { listSearchTuningIssues, loadSearchTuning } from "./search-tuning-registry.js";
|
|
38
|
+
import { resolveSearchTuningKey } from "./search-tuning-key-resolver.js";
|
|
39
|
+
import { SearchTuningKeyStatus } from "./search-tuning-key-status.js";
|
|
40
|
+
import { CommandResolutionStatus } from "./command-resolution-status.js";
|
|
41
|
+
import { collectDeclaredXrefs, declaredXrefCoverage, declaredXrefSummaryLine, } from "./declared-cross-references.js";
|
|
42
|
+
import { DeclaredXrefStatus } from "./declared-xref-status.js";
|
|
43
|
+
import { listConstructs } from "./construct-registry.js";
|
|
44
|
+
import { listPlaybooks, loadPlaybooksWithIssues } from "./playbook-registry.js";
|
|
45
|
+
import { buildPackContributionsInventoryAsync, } from "./pack-contributions-inventory.js";
|
|
30
46
|
export const SELF_CONFIG_DOCTOR_V2_SCHEMA = 'sharkcraft.self-config-doctor/v2';
|
|
31
47
|
export var SelfConfigSeverityV2;
|
|
32
48
|
(function (SelfConfigSeverityV2) {
|
|
@@ -35,86 +51,61 @@ export var SelfConfigSeverityV2;
|
|
|
35
51
|
SelfConfigSeverityV2["Error"] = "error";
|
|
36
52
|
})(SelfConfigSeverityV2 || (SelfConfigSeverityV2 = {}));
|
|
37
53
|
async function buildLookupsV2(inspection) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
]);
|
|
51
|
-
const routingHints = new Set((await listTaskRoutingHints(inspection)).map((e) => e.hint.id));
|
|
52
|
-
const registrationHints = new Set((await listRegistrationHints(inspection)).map((e) => e.hint.id));
|
|
53
|
-
let playbooks;
|
|
54
|
-
try {
|
|
55
|
-
const pb = await listPlaybooks(inspection);
|
|
56
|
-
playbooks = new Set(pb.map((p) => p.id));
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
playbooks = new Set();
|
|
60
|
-
}
|
|
61
|
-
// Policies surface through the pack contributions inventory; the policy
|
|
62
|
-
// engine itself runs side-effects we want to avoid here.
|
|
63
|
-
const policies = new Set();
|
|
64
|
-
try {
|
|
65
|
-
const inv = buildPackContributionsInventory(inspection);
|
|
66
|
-
for (const entry of inv.entriesByKind['policy'] ?? [])
|
|
67
|
-
policies.add(entry.id);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
// ignore
|
|
71
|
-
}
|
|
72
|
-
const decisions = new Set();
|
|
73
|
-
try {
|
|
74
|
-
for (const d of listDecisions(inspection))
|
|
75
|
-
decisions.add(d.id);
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// ignore
|
|
79
|
-
}
|
|
80
|
-
// Commands & MCP tools — taken from the catalog / repository commands
|
|
81
|
-
// surface. Best-effort; if registries are absent the sets stay empty and
|
|
82
|
-
// the corresponding checks degrade to info-level.
|
|
83
|
-
const commands = new Set();
|
|
84
|
-
const mcpTools = new Set();
|
|
85
|
-
try {
|
|
86
|
-
const repoCmds = inspection.repositoryCommands;
|
|
87
|
-
for (const c of repoCmds ?? [])
|
|
88
|
-
commands.add(c.id);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
// ignore
|
|
92
|
-
}
|
|
54
|
+
// Every set is a projection of the SHARED reference registry — the same
|
|
55
|
+
// module the prose linter and the structured-`references[]` validator use.
|
|
56
|
+
//
|
|
57
|
+
// These sets used to be built here from their own sources, and the doc claim
|
|
58
|
+
// "there is one definition of does-this-id-exist, not two" was true only by
|
|
59
|
+
// coincidence. It was not always true: `policies` came from the pack
|
|
60
|
+
// contributions inventory alone, so every LOCALLY declared policy read as
|
|
61
|
+
// unknown; `scaffoldPatterns` had no set at all; `commands` read a
|
|
62
|
+
// `repositoryCommands` property nothing assigns. Seven of shrk's own
|
|
63
|
+
// correctly-registered ids were reported missing.
|
|
64
|
+
await warmReferenceRegistries(inspection);
|
|
65
|
+
const ids = (kind) => new Set(referenceIdsFor(inspection, kind));
|
|
93
66
|
return {
|
|
94
|
-
knowledge,
|
|
95
|
-
rules,
|
|
96
|
-
paths,
|
|
97
|
-
templates,
|
|
98
|
-
pipelines,
|
|
99
|
-
policies,
|
|
100
|
-
playbooks,
|
|
101
|
-
conventions,
|
|
102
|
-
contractTemplates,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
commands
|
|
109
|
-
|
|
67
|
+
knowledge: ids('knowledge'),
|
|
68
|
+
rules: ids('rule'),
|
|
69
|
+
paths: ids('path-convention'),
|
|
70
|
+
templates: ids('template'),
|
|
71
|
+
pipelines: ids('pipeline'),
|
|
72
|
+
policies: ids('policy'),
|
|
73
|
+
playbooks: ids('playbook'),
|
|
74
|
+
conventions: ids('convention'),
|
|
75
|
+
contractTemplates: ids('contract-template'),
|
|
76
|
+
helpers: ids('helper'),
|
|
77
|
+
routingHints: ids('routing-hint'),
|
|
78
|
+
registrationHints: ids('registration-hint'),
|
|
79
|
+
decisions: ids('decision'),
|
|
80
|
+
scaffoldPatterns: ids('scaffold-pattern'),
|
|
81
|
+
// No `commands` set: it was initialised EMPTY (the command index lives in
|
|
82
|
+
// the CLI, above this layer), so every command in every hint and agent test
|
|
83
|
+
// was reported missing — correct ones included, and an agent test's correct
|
|
84
|
+
// `expectedCommands` failed the doctor at error severity. Commands resolve
|
|
85
|
+
// through the injected resolver instead — see `checkCommandStrings`.
|
|
110
86
|
};
|
|
111
87
|
}
|
|
112
88
|
function findingId(parts) {
|
|
113
89
|
return `${parts.sourceKind}:${parts.sourceId}|${parts.relation}|${parts.targetKind}:${parts.targetId}`;
|
|
114
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Append a finding — or MERGE it into an existing one with the same id and
|
|
93
|
+
* code (bumping `occurrences`), so the documented unique id is unique for
|
|
94
|
+
* every check. The same tuning key in three task hints used to be three
|
|
95
|
+
* identical warnings sharing one id. A same-id finding with a DIFFERENT code
|
|
96
|
+
* keeps its own entry, its id suffixed with `|<code>`.
|
|
97
|
+
*/
|
|
115
98
|
function pushFinding(out, input) {
|
|
99
|
+
const baseId = findingId(input);
|
|
100
|
+
const codedId = `${baseId}|${input.code}`;
|
|
101
|
+
const same = out.findIndex((f) => (f.id === baseId || f.id === codedId) && f.code === input.code);
|
|
102
|
+
if (same >= 0) {
|
|
103
|
+
const prev = out[same];
|
|
104
|
+
out[same] = { ...prev, occurrences: (prev.occurrences ?? 1) + (input.occurrences ?? 1) };
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
116
107
|
const entry = {
|
|
117
|
-
id:
|
|
108
|
+
id: out.some((f) => f.id === baseId) ? codedId : baseId,
|
|
118
109
|
severity: input.severity,
|
|
119
110
|
code: input.code,
|
|
120
111
|
sourceKind: input.sourceKind,
|
|
@@ -127,9 +118,231 @@ function pushFinding(out, input) {
|
|
|
127
118
|
...(input.file !== undefined ? { file: input.file } : {}),
|
|
128
119
|
...(input.suggestedFix !== undefined ? { suggestedFix: input.suggestedFix } : {}),
|
|
129
120
|
...(input.nextCommand !== undefined ? { nextCommand: input.nextCommand } : {}),
|
|
121
|
+
...(input.occurrences !== undefined && input.occurrences > 1 ? { occurrences: input.occurrences } : {}),
|
|
130
122
|
};
|
|
131
123
|
out.push(entry);
|
|
132
124
|
}
|
|
125
|
+
function severityV2(s) {
|
|
126
|
+
return s === 'error'
|
|
127
|
+
? SelfConfigSeverityV2.Error
|
|
128
|
+
: s === 'warning'
|
|
129
|
+
? SelfConfigSeverityV2.Warning
|
|
130
|
+
: SelfConfigSeverityV2.Info;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* THE reference-kind → finding-kind table: every kind maps onto ITSELF. The
|
|
134
|
+
* mapped type makes a kind added to `ReferenceKind` without a row here a
|
|
135
|
+
* compile error, and a row naming a kind `SelfConfigKind` lacks a compile error
|
|
136
|
+
* in `selfKindOf`. This was a `switch` whose default was `'unknown'`, so any
|
|
137
|
+
* kind it forgot rendered every resolvable id of that kind `unknown:`.
|
|
138
|
+
*/
|
|
139
|
+
const SELF_KIND_OF_REFERENCE_KIND = Object.freeze({
|
|
140
|
+
file: 'file',
|
|
141
|
+
directory: 'directory',
|
|
142
|
+
symbol: 'symbol',
|
|
143
|
+
command: 'command',
|
|
144
|
+
package: 'package',
|
|
145
|
+
url: 'url',
|
|
146
|
+
template: 'template',
|
|
147
|
+
pipeline: 'pipeline',
|
|
148
|
+
playbook: 'playbook',
|
|
149
|
+
policy: 'policy',
|
|
150
|
+
construct: 'construct',
|
|
151
|
+
helper: 'helper',
|
|
152
|
+
'boundary-rule': 'boundary-rule',
|
|
153
|
+
'path-convention': 'path-convention',
|
|
154
|
+
rule: 'rule',
|
|
155
|
+
knowledge: 'knowledge',
|
|
156
|
+
decision: 'decision',
|
|
157
|
+
convention: 'convention',
|
|
158
|
+
'contract-template': 'contract-template',
|
|
159
|
+
'migration-profile': 'migration-profile',
|
|
160
|
+
'workspace-profile': 'workspace-profile',
|
|
161
|
+
'routing-hint': 'routing-hint',
|
|
162
|
+
'registration-hint': 'registration-hint',
|
|
163
|
+
'scaffold-pattern': 'scaffold-pattern',
|
|
164
|
+
});
|
|
165
|
+
/** The ONE place a finding's `unknown` kind comes from: a target THE resolvers placed in no registry. */
|
|
166
|
+
const RESOLVED_NOWHERE = 'unknown';
|
|
167
|
+
/**
|
|
168
|
+
* A reference kind's finding kind. `undefined` — the caller's resolver placed
|
|
169
|
+
* the id in NO registry — is `unknown`, and nothing else is: a caller holding a
|
|
170
|
+
* resolvable id must pass the kind it resolved in (round 12, 12.4).
|
|
171
|
+
*/
|
|
172
|
+
function selfKindOf(kind) {
|
|
173
|
+
return kind === undefined ? RESOLVED_NOWHERE : SELF_KIND_OF_REFERENCE_KIND[kind];
|
|
174
|
+
}
|
|
175
|
+
/** The `<field>` a validator issue message (`<field>: <message>`) names. */
|
|
176
|
+
function validatorField(message) {
|
|
177
|
+
const at = message.indexOf(':');
|
|
178
|
+
return at > 0 ? message.slice(0, at) : message;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* THE contribution kind → rejection-finding table (round 12, 12.1): the
|
|
182
|
+
* finding kind a rejected entry is labelled with (also its code prefix,
|
|
183
|
+
* `<kind>-invalid` / `<kind>-duplicate-id`) and the doctor that shows the
|
|
184
|
+
* loader's own view of it. A mapped type over the enum: a contribution kind
|
|
185
|
+
* without a row is a compile error, so a new kind can never fall to a borrowed
|
|
186
|
+
* label (the `unknown:` alarm of 12.4). The routing / registration hint codes
|
|
187
|
+
* (`routing-hint-invalid`, `registration-hint-duplicate-id`, …) are the ones
|
|
188
|
+
* those families always emitted.
|
|
189
|
+
*/
|
|
190
|
+
const REJECTION_FINDING = Object.freeze({
|
|
191
|
+
[ContributionKind.Knowledge]: { kind: 'knowledge', doctor: 'shrk doctor' },
|
|
192
|
+
[ContributionKind.Rule]: { kind: 'rule', doctor: 'shrk doctor' },
|
|
193
|
+
[ContributionKind.Path]: { kind: 'path-convention', doctor: 'shrk doctor' },
|
|
194
|
+
[ContributionKind.PathConvention]: { kind: 'path-convention', doctor: 'shrk doctor' },
|
|
195
|
+
[ContributionKind.Docs]: { kind: 'docs', doctor: 'shrk doctor' },
|
|
196
|
+
[ContributionKind.Template]: { kind: 'template', doctor: 'shrk templates doctor' },
|
|
197
|
+
[ContributionKind.Pipeline]: { kind: 'pipeline', doctor: 'shrk pipelines list' },
|
|
198
|
+
[ContributionKind.Preset]: { kind: 'preset', doctor: 'shrk presets list' },
|
|
199
|
+
[ContributionKind.Boundary]: { kind: 'boundary-rule', doctor: 'shrk check boundaries' },
|
|
200
|
+
[ContributionKind.ScaffoldPattern]: { kind: 'scaffold-pattern', doctor: 'shrk scaffolds doctor' },
|
|
201
|
+
[ContributionKind.Policy]: { kind: 'policy', doctor: 'shrk policy list' },
|
|
202
|
+
[ContributionKind.Construct]: { kind: 'construct', doctor: 'shrk constructs list' },
|
|
203
|
+
[ContributionKind.ConstructFacet]: { kind: 'construct-facet', doctor: 'shrk constructs list' },
|
|
204
|
+
[ContributionKind.Playbook]: { kind: 'playbook', doctor: 'shrk playbooks list' },
|
|
205
|
+
[ContributionKind.SearchTuning]: { kind: 'search-tuning', doctor: 'shrk search tuning doctor' },
|
|
206
|
+
[ContributionKind.FeedbackRule]: { kind: 'feedback-rule', doctor: 'shrk packs contributions' },
|
|
207
|
+
[ContributionKind.Decision]: { kind: 'decision', doctor: 'shrk packs contributions' },
|
|
208
|
+
[ContributionKind.ContractTemplate]: { kind: 'contract-template', doctor: 'shrk contract template list' },
|
|
209
|
+
[ContributionKind.MigrationProfile]: { kind: 'migration-profile', doctor: 'shrk profiles doctor' },
|
|
210
|
+
[ContributionKind.ContextTest]: { kind: 'context-test', doctor: 'shrk test context' },
|
|
211
|
+
[ContributionKind.AgentTest]: { kind: 'agent-test', doctor: 'shrk test agent' },
|
|
212
|
+
[ContributionKind.Helper]: { kind: 'helper', doctor: 'shrk helper doctor' },
|
|
213
|
+
[ContributionKind.TaskRoutingHint]: { kind: 'routing-hint', doctor: 'shrk packs contributions' },
|
|
214
|
+
[ContributionKind.RegistrationHint]: { kind: 'registration-hint', doctor: 'shrk registrations doctor' },
|
|
215
|
+
[ContributionKind.Convention]: { kind: 'convention', doctor: 'shrk conventions doctor' },
|
|
216
|
+
[ContributionKind.DelegateRecipe]: { kind: 'delegate-recipe', doctor: 'shrk packs contributions' },
|
|
217
|
+
[ContributionKind.FrameworkExtractor]: { kind: 'framework-extractor', doctor: 'shrk packs contributions' },
|
|
218
|
+
[ContributionKind.WiringRule]: { kind: 'wiring-rule', doctor: 'shrk gates coverage' },
|
|
219
|
+
[ContributionKind.Registry]: { kind: 'registry', doctor: 'shrk gates coverage' },
|
|
220
|
+
[ContributionKind.RegistrationIdiom]: { kind: 'registration-idiom', doctor: 'shrk gates coverage' },
|
|
221
|
+
[ContributionKind.PolicyRule]: { kind: 'policy-rule', doctor: 'shrk gates coverage' },
|
|
222
|
+
[ContributionKind.ReusePrimitive]: { kind: 'reuse-primitive', doctor: 'shrk reuse coverage' },
|
|
223
|
+
[ContributionKind.Baseline]: { kind: 'baseline', doctor: 'shrk gates coverage' },
|
|
224
|
+
[ContributionKind.GeneratedArtifact]: { kind: 'generated-artifact', doctor: 'shrk gates coverage' },
|
|
225
|
+
[ContributionKind.DocReference]: { kind: 'doc-reference', doctor: 'shrk docs references check' },
|
|
226
|
+
});
|
|
227
|
+
/** A rejected entry's finding kind and code — THE table above, never a fallback. */
|
|
228
|
+
export function rejectionFindingOf(kind, cause) {
|
|
229
|
+
const row = REJECTION_FINDING[kind];
|
|
230
|
+
return {
|
|
231
|
+
kind: row.kind,
|
|
232
|
+
code: `${row.kind}-${cause === RejectionCause.DuplicateId ? 'duplicate-id' : 'invalid'}`,
|
|
233
|
+
doctor: row.doctor,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function newIdProbe() {
|
|
237
|
+
return {
|
|
238
|
+
probed: 0,
|
|
239
|
+
resolved: 0,
|
|
240
|
+
missing: 0,
|
|
241
|
+
unverified: 0,
|
|
242
|
+
unverifiedLabels: [],
|
|
243
|
+
unverifiedRefs: [],
|
|
244
|
+
emptyKinds: [],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* The loud-skip reason. It names how to fill each empty kind, from THE
|
|
249
|
+
* declaration table (`REFERENCE_KIND_DECLARATIONS`), so a NOT VERIFIED unit is
|
|
250
|
+
* actionable instead of a dead end: `… — declare migration-profile via pack
|
|
251
|
+
* key migrationProfileFiles · sharkcraft/migration-profiles.ts …`.
|
|
252
|
+
*/
|
|
253
|
+
function emptyRegistryReason(probe) {
|
|
254
|
+
const base = "could not be checked (their kind's registry is empty in this workspace";
|
|
255
|
+
if (probe.emptyKinds.length === 0)
|
|
256
|
+
return `${base})`;
|
|
257
|
+
const how = probe.emptyKinds.map((k) => `${k} via ${formatReferenceKindDeclaration(k)}`).join('; ');
|
|
258
|
+
return `${base} — declare ${how})`;
|
|
259
|
+
}
|
|
260
|
+
/** Read a dotted id-list field (`discovery.profileIds`) off an asset; anything but an array reads as absent. */
|
|
261
|
+
function readIdList(asset, field) {
|
|
262
|
+
let cur = asset;
|
|
263
|
+
for (const part of field.split('.')) {
|
|
264
|
+
if (!cur || typeof cur !== 'object')
|
|
265
|
+
return undefined;
|
|
266
|
+
cur = cur[part];
|
|
267
|
+
}
|
|
268
|
+
return Array.isArray(cur) ? cur : undefined;
|
|
269
|
+
}
|
|
270
|
+
/** How a `*-missing` message ends: a workspace profile id is a vocabulary word, not a registration. */
|
|
271
|
+
function notResolvedTail(kind) {
|
|
272
|
+
return kind === 'workspace-profile' ? ', which is not a WorkspaceProfile id' : ' but it is not registered';
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* THE "this finding reports a reference that resolved nowhere" predicate: a
|
|
276
|
+
* `*-missing` code (an id, or a referenced file), any severity — the family
|
|
277
|
+
* r73 reads as "unknown id". The CLI's exit-0 line counts the INFO-severity
|
|
278
|
+
* ones instead of certifying "every checked probe resolved" over them.
|
|
279
|
+
*/
|
|
280
|
+
export function isUnresolvedReferenceFinding(finding) {
|
|
281
|
+
return finding.code.endsWith('-missing');
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* THE "this reference resolved, but only as a kind its field does not accept"
|
|
285
|
+
* predicate: a `*-wrong-kind` code (`xref-wrong-kind`, a pipeline step naming a
|
|
286
|
+
* construct). Not unresolved — `unknown:` is reserved for that — and not a
|
|
287
|
+
* clean resolution either, so the CLI's exit-0 line counts an info-severity one
|
|
288
|
+
* instead of certifying "every checked probe resolved".
|
|
289
|
+
*/
|
|
290
|
+
export function isWrongKindReferenceFinding(finding) {
|
|
291
|
+
return finding.code.endsWith('-wrong-kind');
|
|
292
|
+
}
|
|
293
|
+
function probeIds(inspection, kind, ids, probe, label, onMissing, site) {
|
|
294
|
+
if (!Array.isArray(ids) || ids.length === 0)
|
|
295
|
+
return;
|
|
296
|
+
const empty = emptyReferenceKinds(inspection, [kind]).length > 0;
|
|
297
|
+
for (const raw of ids) {
|
|
298
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
299
|
+
continue;
|
|
300
|
+
probe.probed += 1;
|
|
301
|
+
if (empty) {
|
|
302
|
+
probe.unverified += 1;
|
|
303
|
+
probe.unverifiedLabels.push(label(raw));
|
|
304
|
+
if (site) {
|
|
305
|
+
probe.unverifiedRefs.push({
|
|
306
|
+
...(site.file ? { file: resolveEntryFile(inspection, site.file, site.packageName) } : {}),
|
|
307
|
+
...(site.packageName ? { packageName: site.packageName } : {}),
|
|
308
|
+
sourceKind: site.sourceKind,
|
|
309
|
+
sourceId: site.sourceId,
|
|
310
|
+
field: site.field,
|
|
311
|
+
kind,
|
|
312
|
+
id: raw,
|
|
313
|
+
// THE declarability authority: an empty registry nothing can EVER
|
|
314
|
+
// fill is a different fact from one this workspace has not filled.
|
|
315
|
+
reason: isReferenceKindDeclarable(kind) ? UnresolvableReason.RegistryEmpty : UnresolvableReason.UndeclarableKind,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (!probe.emptyKinds.includes(kind))
|
|
319
|
+
probe.emptyKinds.push(kind);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (referenceIdExists(inspection, kind, raw)) {
|
|
323
|
+
probe.resolved += 1;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
probe.missing += 1;
|
|
327
|
+
onMissing(raw, nearestIds(raw, referenceIdsFor(inspection, kind), 1)[0]?.id);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function idProbeCoverage(subject, unit, probe) {
|
|
331
|
+
if (probe.probed === 0)
|
|
332
|
+
return [];
|
|
333
|
+
return [
|
|
334
|
+
{
|
|
335
|
+
subject,
|
|
336
|
+
unit,
|
|
337
|
+
expected: probe.probed,
|
|
338
|
+
examined: probe.probed - probe.unverified,
|
|
339
|
+
...(probe.unverified > 0
|
|
340
|
+
? { unexamined: probe.unverifiedLabels.slice(0, 20), unexaminedTotal: probe.unverified }
|
|
341
|
+
: {}),
|
|
342
|
+
reason: emptyRegistryReason(probe),
|
|
343
|
+
},
|
|
344
|
+
];
|
|
345
|
+
}
|
|
133
346
|
// ─── 1. Knowledge → file references ───────────────────────────────────────
|
|
134
347
|
function checkKnowledgeFileRefs(inspection, findings) {
|
|
135
348
|
for (const k of inspection.knowledgeEntries) {
|
|
@@ -159,41 +372,182 @@ function checkKnowledgeFileRefs(inspection, findings) {
|
|
|
159
372
|
}
|
|
160
373
|
}
|
|
161
374
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
375
|
+
/**
|
|
376
|
+
* The search-tuning boost keys as a REFERENCE probe (round 13): the key probes
|
|
377
|
+
* THE lint judged (`searchTuningKeyProbes`), one per (entry, key), and every
|
|
378
|
+
* one THE key resolver could not check — its kind's registry is empty here
|
|
379
|
+
* (`registry-empty`), or its document kind has no id registry at all
|
|
380
|
+
* (`undeclarable-kind`, kind `search-document`). `packs contributions` reads
|
|
381
|
+
* this, so it and `search tuning doctor` can no longer disagree about the same
|
|
382
|
+
* contributed boost.
|
|
383
|
+
*/
|
|
384
|
+
function tuningReferenceProbe(inspection, probes) {
|
|
385
|
+
const probe = newIdProbe();
|
|
386
|
+
for (const p of probes) {
|
|
387
|
+
const r = p.resolution;
|
|
388
|
+
probe.probed += 1;
|
|
389
|
+
if (r.status === SearchTuningKeyStatus.Resolved)
|
|
390
|
+
probe.resolved += 1;
|
|
391
|
+
else if (r.status === SearchTuningKeyStatus.Missing)
|
|
392
|
+
probe.missing += 1;
|
|
393
|
+
if (r.status !== SearchTuningKeyStatus.Unverified)
|
|
394
|
+
continue;
|
|
395
|
+
const kind = r.referenceKind;
|
|
396
|
+
probe.unverified += 1;
|
|
397
|
+
probe.unverifiedLabels.push(`${p.tuningId} → ${kind ?? 'search-document'} ${kind !== undefined ? r.id : p.key}`);
|
|
398
|
+
probe.unverifiedRefs.push({
|
|
399
|
+
...(p.sourceFile ? { file: resolveEntryFile(inspection, p.sourceFile, p.packageName) } : {}),
|
|
400
|
+
...(p.packageName ? { packageName: p.packageName } : {}),
|
|
401
|
+
sourceKind: 'search-tuning',
|
|
402
|
+
sourceId: p.tuningId,
|
|
403
|
+
field: p.lists[0] ?? 'boostIds',
|
|
404
|
+
kind: kind ?? 'search-document',
|
|
405
|
+
id: kind !== undefined ? r.id : p.key,
|
|
406
|
+
reason: kind !== undefined && isReferenceKindDeclarable(kind)
|
|
407
|
+
? UnresolvableReason.RegistryEmpty
|
|
408
|
+
: UnresolvableReason.UndeclarableKind,
|
|
409
|
+
});
|
|
410
|
+
if (kind !== undefined && !probe.emptyKinds.includes(kind))
|
|
411
|
+
probe.emptyKinds.push(kind);
|
|
412
|
+
}
|
|
413
|
+
return probe;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* THE search-tuning lint (`lintSearchTuning`, shared with `shrk search tuning
|
|
417
|
+
* doctor`), each issue prefixed `search-tuning-`.
|
|
418
|
+
*
|
|
419
|
+
* A boost key is a search-document id, `<kind>:<id>`. This used to look the
|
|
420
|
+
* WHOLE prefixed key up in bare-id registries, so every correctly prefixed key
|
|
421
|
+
* (which fires) was reported missing and a bare key (which never fires)
|
|
422
|
+
* passed — the exact inverse of the truth. The key is now split on its first
|
|
423
|
+
* `:` and resolved within the named kind; a bare key is `key-unprefixed`.
|
|
424
|
+
*/
|
|
425
|
+
async function checkSearchTuning(inspection, findings) {
|
|
426
|
+
const lint = await lintSearchTuning(inspection);
|
|
427
|
+
for (const issue of lint.issues) {
|
|
428
|
+
const fix = issue.code === 'target-missing'
|
|
429
|
+
? issue.suggestion
|
|
430
|
+
? `Use "${issue.suggestion}", or register ${issue.referenceKind} "${issue.targetId}", or remove the boost.`
|
|
431
|
+
: `Register ${issue.referenceKind} "${issue.targetId}" or remove the boost.`
|
|
432
|
+
: issue.code === 'key-unprefixed' || issue.code === 'key-unknown-kind'
|
|
433
|
+
? issue.suggestion
|
|
434
|
+
? `Rewrite the key as "${issue.suggestion}".`
|
|
435
|
+
: 'Prefix the key with its document kind (`knowledge:`, `rule:`, `template:`, …) or remove it.'
|
|
436
|
+
: issue.suggestion
|
|
437
|
+
? `Did you mean "${issue.suggestion}"?`
|
|
438
|
+
: undefined;
|
|
439
|
+
const target = searchTuningTarget(issue);
|
|
440
|
+
pushFinding(findings, {
|
|
441
|
+
severity: severityV2(issue.severity),
|
|
442
|
+
code: `search-tuning-${issue.code}`,
|
|
443
|
+
sourceKind: 'search-tuning',
|
|
444
|
+
sourceId: issue.tuningId,
|
|
445
|
+
targetKind: target.targetKind,
|
|
446
|
+
targetId: target.targetId,
|
|
447
|
+
relation: target.relation,
|
|
448
|
+
file: issue.source,
|
|
449
|
+
message: issue.message,
|
|
450
|
+
suggestedFix: fix,
|
|
451
|
+
nextCommand: 'shrk search tuning doctor',
|
|
452
|
+
occurrences: issue.occurrences,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
probes: lint.probes,
|
|
457
|
+
coverage: lint.coverage,
|
|
458
|
+
deadUnits: lint.deadUnits,
|
|
459
|
+
liveness: lint.liveness,
|
|
460
|
+
refProbe: tuningReferenceProbe(inspection, lint.keyProbes),
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* THE label of a search-tuning finding's target, in order:
|
|
465
|
+
*
|
|
466
|
+
* 1. a kind THE key resolver (or the index) placed it in → `<kind>:<id>`
|
|
467
|
+
* (a missing / excluded / resolvable bare key, a capped document);
|
|
468
|
+
* 2. a document with no id registry, or the cap summary → `search-document`;
|
|
469
|
+
* 3. a key no registry can be asked about (`doc:`, `preset:`, …) →
|
|
470
|
+
* `search-document`;
|
|
471
|
+
* 4. a key that resolves NOWHERE (a bare id no registry lists, an unknown
|
|
472
|
+
* `<kind>:` prefix) → `unknown:<the whole key>` — the genuine case;
|
|
473
|
+
* 5. a declaration-shape finding (triggers, vocabulary) → `schema:<field>`.
|
|
474
|
+
*
|
|
475
|
+
* Everything without a `referenceKind` used to fall to `unknown`, so a capped
|
|
476
|
+
* document (`unknown:knowledge:gamma.entry`), a resolvable bare key, a tag, a
|
|
477
|
+
* doc id and the finding CODE itself rendered `unknown:` — while the one really
|
|
478
|
+
* missing id did not (round 12, 12.4).
|
|
479
|
+
*/
|
|
480
|
+
function searchTuningTarget(t) {
|
|
481
|
+
if (t.referenceKind !== undefined) {
|
|
482
|
+
return {
|
|
483
|
+
targetKind: selfKindOf(t.referenceKind),
|
|
484
|
+
targetId: t.targetId ?? t.key ?? t.docId ?? t.code,
|
|
485
|
+
relation: 'tunes',
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
if (t.docId !== undefined)
|
|
489
|
+
return { targetKind: 'search-document', targetId: t.docId, relation: 'tunes' };
|
|
490
|
+
if (t.moreDocuments !== undefined) {
|
|
491
|
+
return { targetKind: 'search-document', targetId: `${t.moreDocuments} more document(s)`, relation: 'tunes' };
|
|
492
|
+
}
|
|
493
|
+
if (t.key !== undefined) {
|
|
494
|
+
if (t.status === SearchTuningKeyStatus.Unverified) {
|
|
495
|
+
return { targetKind: 'search-document', targetId: t.key, relation: 'tunes' };
|
|
196
496
|
}
|
|
497
|
+
// The WHOLE key: an unknown prefix's right-hand id may well exist.
|
|
498
|
+
return { targetKind: selfKindOf(undefined), targetId: t.key, relation: 'tunes' };
|
|
499
|
+
}
|
|
500
|
+
return { targetKind: 'schema', targetId: t.field ?? t.code, relation: 'validates' };
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* A loader issue's target. A file-level issue (load-failed, missing-file, an
|
|
504
|
+
* entry with no id) is about the file. A clamped `boostIds` key is a
|
|
505
|
+
* search-document id, labelled through THE key resolver exactly like the lint's
|
|
506
|
+
* keys (the registries are warm: `buildLookupsV2` and the lint both warmed
|
|
507
|
+
* them); a clamped tag / source / kind name is no id at all — it is the
|
|
508
|
+
* declaration `schema:<map>.<name>`. Every clamped key used to read
|
|
509
|
+
* `unknown:<key>`: a resolvable id, or a tag labelled an unknown id.
|
|
510
|
+
*/
|
|
511
|
+
function searchTuningLoadTarget(inspection, i) {
|
|
512
|
+
if (i.key === undefined)
|
|
513
|
+
return { targetKind: 'file', targetId: i.source ?? i.code };
|
|
514
|
+
if (i.field === 'boostIds' || i.field?.endsWith('.boostIds') === true) {
|
|
515
|
+
const r = resolveSearchTuningKey(inspection, i.key);
|
|
516
|
+
const t = searchTuningTarget({
|
|
517
|
+
code: i.code,
|
|
518
|
+
key: i.key,
|
|
519
|
+
status: r.status,
|
|
520
|
+
referenceKind: r.referenceKind,
|
|
521
|
+
targetId: r.id,
|
|
522
|
+
});
|
|
523
|
+
return { targetKind: t.targetKind, targetId: t.targetId };
|
|
524
|
+
}
|
|
525
|
+
return { targetKind: 'schema', targetId: i.field !== undefined ? `${i.field}.${i.key}` : i.key };
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* The tuning loader's own issues (a file that failed to import, a clamped
|
|
529
|
+
* boost). A `missing-id` entry is a REJECTED entry: THE rejection family
|
|
530
|
+
* reports it (error, `search-tuning-invalid`) — never a second time here.
|
|
531
|
+
*/
|
|
532
|
+
function checkSearchTuningLoadIssues(inspection, findings) {
|
|
533
|
+
for (const i of listSearchTuningIssues(inspection)) {
|
|
534
|
+
// A refused entry (no id; round 13: a malformed boost value or marker) is
|
|
535
|
+
// THE rejection family's to report — once, as an error.
|
|
536
|
+
if (i.code === 'missing-id' || i.code === 'invalid-entry')
|
|
537
|
+
continue;
|
|
538
|
+
const target = searchTuningLoadTarget(inspection, i);
|
|
539
|
+
pushFinding(findings, {
|
|
540
|
+
severity: severityV2(i.severity),
|
|
541
|
+
code: `search-tuning-${i.code}`,
|
|
542
|
+
sourceKind: 'search-tuning',
|
|
543
|
+
sourceId: i.tuningId ?? i.source ?? 'search-tuning',
|
|
544
|
+
targetKind: target.targetKind,
|
|
545
|
+
targetId: target.targetId,
|
|
546
|
+
relation: 'validates',
|
|
547
|
+
file: i.source,
|
|
548
|
+
message: i.message,
|
|
549
|
+
nextCommand: 'shrk search tuning doctor',
|
|
550
|
+
});
|
|
197
551
|
}
|
|
198
552
|
}
|
|
199
553
|
// ─── 3. Agent-tests → helpers / commands / playbooks / policies ───────────
|
|
@@ -231,108 +585,160 @@ async function checkAgentTests(inspection, lookups, findings) {
|
|
|
231
585
|
probe(t, t.expectedHelpers, 'helper', lookups.helpers, 'expects');
|
|
232
586
|
probe(t, t.expectedPlaybooks, 'playbook', lookups.playbooks, 'expects');
|
|
233
587
|
probe(t, t.expectedPolicies, 'policy', lookups.policies, 'expects');
|
|
234
|
-
|
|
588
|
+
// `expectedCommands` go through `checkCommandStrings` (the injected resolver).
|
|
235
589
|
}
|
|
236
590
|
}
|
|
237
591
|
// ─── 4. Templates → conventions / helpers / profiles / registration hints ─
|
|
238
|
-
|
|
592
|
+
/**
|
|
593
|
+
* Template `metadata` id fields, each probed through THE id resolver per THE
|
|
594
|
+
* binding table (`PROBED_ID_FIELDS`): `requiredProfileIds` → workspace-profile,
|
|
595
|
+
* `requiredConventionIds` → convention, `requiredHelperIds` → helper,
|
|
596
|
+
* `registrationHintIds` → registration-hint.
|
|
597
|
+
*
|
|
598
|
+
* This used bare `lookups.X.has(id)` loops: `requiredProfileIds` resolved
|
|
599
|
+
* against the MIGRATION registry, and every field warned "not registered"
|
|
600
|
+
* over an EMPTY registry — while the same id in a registration hint was a loud
|
|
601
|
+
* skip. One question, two answers (round 12, 12.3b). An id checked against an
|
|
602
|
+
* empty registry is now an unexamined unit (NOT VERIFIED), never a warning.
|
|
603
|
+
*/
|
|
604
|
+
function checkTemplateMetadata(inspection, findings) {
|
|
239
605
|
const templates = (inspection.templates ?? []);
|
|
606
|
+
const rows = PROBED_ID_FIELDS.filter((r) => r.source === ProbedIdSource.Template);
|
|
607
|
+
const probe = newIdProbe();
|
|
240
608
|
for (const t of templates) {
|
|
241
|
-
|
|
242
|
-
if (!m)
|
|
609
|
+
if (!t.metadata)
|
|
243
610
|
continue;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
sourceId: t.id,
|
|
252
|
-
targetKind: 'convention',
|
|
253
|
-
targetId: id,
|
|
254
|
-
relation: 'requires',
|
|
255
|
-
message: `Template "${t.id}" requires convention "${id}" but it is not registered.`,
|
|
256
|
-
nextCommand: 'shrk conventions list --source pack',
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
for (const id of m.requiredHelperIds ?? []) {
|
|
260
|
-
if (lookups.helpers.has(id))
|
|
261
|
-
continue;
|
|
262
|
-
pushFinding(findings, {
|
|
611
|
+
const src = inspection.templateSources?.get(t.id);
|
|
612
|
+
for (const row of rows) {
|
|
613
|
+
probeIds(inspection, row.kind, readIdList(t, row.field), probe,
|
|
614
|
+
// The unexamined label names the KIND resolved against (the report's
|
|
615
|
+
// `kind`), never the code label — `profile` was ambiguous once
|
|
616
|
+
// `workspace-profile` existed (round 12 review, T4).
|
|
617
|
+
(id) => `${t.id} → ${row.kind} ${id}`, (id, nearest) => pushFinding(findings, {
|
|
263
618
|
severity: SelfConfigSeverityV2.Warning,
|
|
264
|
-
code:
|
|
619
|
+
code: `template-${row.label}-missing`,
|
|
265
620
|
sourceKind: 'template',
|
|
266
621
|
sourceId: t.id,
|
|
267
|
-
targetKind:
|
|
622
|
+
targetKind: selfKindOf(row.kind),
|
|
268
623
|
targetId: id,
|
|
269
624
|
relation: 'requires',
|
|
270
|
-
message: `Template "${t.id}"
|
|
271
|
-
|
|
272
|
-
|
|
625
|
+
message: `Template "${t.id}" ${row.field} names ${row.label} "${id}"${notResolvedTail(row.kind)}.`,
|
|
626
|
+
...(nearest ? { suggestedFix: `Did you mean "${nearest}"?` } : {}),
|
|
627
|
+
nextCommand: referenceKindListVerb(row.kind),
|
|
628
|
+
}), { sourceKind: 'template', sourceId: t.id, field: row.field, file: src?.file, packageName: src?.packageName });
|
|
273
629
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
630
|
+
}
|
|
631
|
+
return { coverage: idProbeCoverage('templates', 'required ids', probe), deadUnits: [], refProbe: probe };
|
|
632
|
+
}
|
|
633
|
+
// ─── 5. Routing hints → commands / templates / helpers / playbooks / profiles ───
|
|
634
|
+
/**
|
|
635
|
+
* Routing hints: the loader's issues (which had NO consumer), whether each
|
|
636
|
+
* hint can match at all, and every `recommends` id through THE channel table
|
|
637
|
+
* and THE id resolver. The old probe list skipped the declared `knowledge` and
|
|
638
|
+
* `policies` channels entirely. An invalid hint or a duplicate id is a
|
|
639
|
+
* REJECTED entry: THE rejection family reports it (`routing-hint-invalid` /
|
|
640
|
+
* `routing-hint-duplicate-id`, one reporter) — this pass skips those codes.
|
|
641
|
+
*/
|
|
642
|
+
async function checkRoutingHints(inspection, findings) {
|
|
643
|
+
const loaded = await loadTaskRoutingHints(inspection);
|
|
644
|
+
const entries = loaded.entries;
|
|
645
|
+
for (const i of await listTaskRoutingHintIssues(inspection)) {
|
|
646
|
+
if (i.code === 'invalid-hint' || i.code === 'duplicate-id')
|
|
647
|
+
continue;
|
|
648
|
+
pushFinding(findings, {
|
|
649
|
+
severity: severityV2(i.severity),
|
|
650
|
+
code: `routing-hint-${i.code}`,
|
|
651
|
+
sourceKind: 'routing-hint',
|
|
652
|
+
sourceId: i.hintId ?? i.source ?? 'routing-hints',
|
|
653
|
+
targetKind: i.code === 'load-failed' || i.code === 'missing-file' ? 'file' : 'schema',
|
|
654
|
+
targetId: i.code,
|
|
655
|
+
relation: 'validates',
|
|
656
|
+
file: i.source,
|
|
657
|
+
message: i.message,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
const probe = newIdProbe();
|
|
661
|
+
// A hint that can never match is dead by SHAPE (out of expectEmpty scope — no
|
|
662
|
+
// target can ever make it match); it still settles through THE liveness
|
|
663
|
+
// authority, unmarked, so this file's dead list has one derivation.
|
|
664
|
+
const hintObservations = [];
|
|
665
|
+
for (const e of entries) {
|
|
666
|
+
const canMatch = routingHintCanMatch(e.hint);
|
|
667
|
+
hintObservations.push({
|
|
668
|
+
list: 'routing hints',
|
|
669
|
+
unit: e.hint.id,
|
|
670
|
+
exists: canMatch,
|
|
671
|
+
live: canMatch,
|
|
672
|
+
...(canMatch
|
|
673
|
+
? {}
|
|
674
|
+
: { cause: UnitDeadCause.Defect, deadReason: 'declares no criterion the matcher scores, so it can never match' }),
|
|
675
|
+
});
|
|
676
|
+
const rec = (e.hint.recommends ?? {});
|
|
677
|
+
for (const channel of ROUTING_RECOMMENDS_CHANNEL_KEYS) {
|
|
678
|
+
const spec = ROUTING_RECOMMENDS_CHANNELS[channel];
|
|
679
|
+
// `recommends.commands` go through `checkCommandStrings` (the injected resolver).
|
|
680
|
+
if (spec.kind === 'command')
|
|
291
681
|
continue;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
682
|
+
const kind = spec.kind;
|
|
683
|
+
probeIds(inspection, kind, rec[channel], probe, (id) => `${e.hint.id} → ${kind} ${id}`, (id, nearest) => pushFinding(findings, {
|
|
684
|
+
severity: SelfConfigSeverityV2.Info,
|
|
685
|
+
code: `routing-hint-${spec.label}-missing`,
|
|
686
|
+
sourceKind: 'routing-hint',
|
|
687
|
+
sourceId: e.hint.id,
|
|
688
|
+
// THE kind the id was resolved against — no relabel. (`profile` was a
|
|
689
|
+
// display alias no registry or list verb has; round 12, 12.3.)
|
|
690
|
+
targetKind: selfKindOf(kind),
|
|
298
691
|
targetId: id,
|
|
299
|
-
relation: '
|
|
300
|
-
|
|
301
|
-
|
|
692
|
+
relation: 'routes-to',
|
|
693
|
+
file: e.sourceFile,
|
|
694
|
+
message: `Routing hint "${e.hint.id}" recommends ${kind === 'migration-profile' ? 'migration profile' : spec.label} "${id}" (recommends.${channel}) but it is not registered.`,
|
|
695
|
+
...(nearest ? { suggestedFix: `Did you mean "${nearest}"?` } : {}),
|
|
696
|
+
nextCommand: referenceKindListVerb(kind),
|
|
697
|
+
}), {
|
|
698
|
+
sourceKind: 'routing-hint',
|
|
699
|
+
sourceId: e.hint.id,
|
|
700
|
+
field: `recommends.${channel}`,
|
|
701
|
+
file: e.sourceFile,
|
|
702
|
+
packageName: e.packageName,
|
|
302
703
|
});
|
|
303
704
|
}
|
|
304
705
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
probe(rec.profiles, 'profile', lookups.migrationProfiles);
|
|
333
|
-
probe(rec.commands, 'command', lookups.commands);
|
|
334
|
-
probe(rec.playbooks, 'playbook', lookups.playbooks);
|
|
706
|
+
const hints = settleUnitLiveness({
|
|
707
|
+
subject: 'routing hints',
|
|
708
|
+
unitLabel: 'hints',
|
|
709
|
+
weight: UnitDeadWeight.Coverage,
|
|
710
|
+
observations: hintObservations,
|
|
711
|
+
marks: [],
|
|
712
|
+
deadSummary: 'declare no criterion the matcher scores (keyword, phrase, compiling regex), so they can never match',
|
|
713
|
+
});
|
|
714
|
+
const coverage = [...hints.coverage];
|
|
715
|
+
// A hint file that failed to import (or a pack-declared one that is absent)
|
|
716
|
+
// hides every hint in it: the file is unexamined, never a healthy registry.
|
|
717
|
+
const brokenHintFiles = [
|
|
718
|
+
...new Set(loaded.issues
|
|
719
|
+
.filter((i) => i.code === 'load-failed' || i.code === 'missing-file')
|
|
720
|
+
.map((i) => nodePath.relative(inspection.projectRoot, i.source ?? '') || (i.source ?? '?'))),
|
|
721
|
+
];
|
|
722
|
+
if (loaded.files > 0) {
|
|
723
|
+
coverage.push({
|
|
724
|
+
subject: 'routing hints',
|
|
725
|
+
unit: 'hint files',
|
|
726
|
+
expected: loaded.files,
|
|
727
|
+
examined: Math.max(0, loaded.files - brokenHintFiles.length),
|
|
728
|
+
...(brokenHintFiles.length > 0
|
|
729
|
+
? { unexamined: brokenHintFiles.slice(0, 20), unexaminedTotal: brokenHintFiles.length }
|
|
730
|
+
: {}),
|
|
731
|
+
reason: 'failed to load or are missing, so none of their hints are known',
|
|
732
|
+
});
|
|
335
733
|
}
|
|
734
|
+
coverage.push(...idProbeCoverage('routing hints', 'recommended ids', probe));
|
|
735
|
+
return {
|
|
736
|
+
probes: { probed: probe.probed, resolved: probe.resolved, missing: probe.missing, unverified: probe.unverified },
|
|
737
|
+
coverage,
|
|
738
|
+
deadUnits: hints.dead.map((u) => `routing hint ${u.unit} (can never match)`),
|
|
739
|
+
refProbe: probe,
|
|
740
|
+
liveness: [hints],
|
|
741
|
+
};
|
|
336
742
|
}
|
|
337
743
|
// ─── 6. Pipelines → templates / commands (via cliCommands) ────────────────
|
|
338
744
|
function checkPipelines(inspection, lookups, findings) {
|
|
@@ -340,7 +746,8 @@ function checkPipelines(inspection, lookups, findings) {
|
|
|
340
746
|
(inspection.pipelines ?? []);
|
|
341
747
|
for (const p of pipelines) {
|
|
342
748
|
for (const step of p.steps ?? []) {
|
|
343
|
-
//
|
|
749
|
+
// A step reference names a knowledge entry / template / path convention
|
|
750
|
+
// (`IPipelineStep.references`), or a convention / helper.
|
|
344
751
|
for (const ref of step.references ?? []) {
|
|
345
752
|
if (lookups.templates.has(ref) ||
|
|
346
753
|
lookups.knowledge.has(ref) ||
|
|
@@ -348,16 +755,24 @@ function checkPipelines(inspection, lookups, findings) {
|
|
|
348
755
|
lookups.paths.has(ref) ||
|
|
349
756
|
lookups.helpers.has(ref))
|
|
350
757
|
continue;
|
|
758
|
+
// Not an accepted kind — but THE resolver may still place it (a
|
|
759
|
+
// construct id): that is the wrong kind, never "unknown". It used to
|
|
760
|
+
// read `references unknown:<id>` for an id `self-config resolve` finds.
|
|
761
|
+
const kinds = referenceKindsOf(inspection, ref);
|
|
762
|
+
const resolvedAs = kinds[0];
|
|
351
763
|
pushFinding(findings, {
|
|
352
764
|
severity: SelfConfigSeverityV2.Info,
|
|
353
|
-
code: 'pipeline-reference-missing',
|
|
765
|
+
code: resolvedAs === undefined ? 'pipeline-reference-missing' : 'pipeline-reference-wrong-kind',
|
|
354
766
|
sourceKind: 'pipeline',
|
|
355
767
|
sourceId: p.id,
|
|
356
|
-
targetKind:
|
|
768
|
+
targetKind: selfKindOf(resolvedAs),
|
|
357
769
|
targetId: ref,
|
|
358
770
|
relation: 'references',
|
|
359
771
|
file: p.source?.origin ?? undefined,
|
|
360
|
-
message:
|
|
772
|
+
message: resolvedAs === undefined
|
|
773
|
+
? `Pipeline "${p.id}" step "${step.id}" references unknown id "${ref}".`
|
|
774
|
+
: `Pipeline "${p.id}" step "${step.id}" references "${ref}", which is a ${kinds.join(' | ')} — a step reference names a knowledge entry, template, path convention, convention or helper.`,
|
|
775
|
+
nextCommand: `shrk self-config resolve ${ref}`,
|
|
361
776
|
confidence: 'medium',
|
|
362
777
|
});
|
|
363
778
|
}
|
|
@@ -365,13 +780,31 @@ function checkPipelines(inspection, lookups, findings) {
|
|
|
365
780
|
}
|
|
366
781
|
}
|
|
367
782
|
// ─── 7. Playbooks → templates / helpers / commands / profiles ─────────────
|
|
783
|
+
/**
|
|
784
|
+
* Playbooks: the loader's issues (an import failure used to be swallowed, so a
|
|
785
|
+
* broken `playbooks.ts` reported a healthy, merely smaller registry), file
|
|
786
|
+
* coverage, and each playbook's template / pipeline ids. Reads the LOADER —
|
|
787
|
+
* the cache-only `listPlaybooks` answered "no playbooks" whenever nothing had
|
|
788
|
+
* warmed it.
|
|
789
|
+
*/
|
|
368
790
|
async function checkPlaybooks(inspection, lookups, findings) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
791
|
+
const { playbooks, issues, files } = await loadPlaybooksWithIssues(inspection);
|
|
792
|
+
const brokenFiles = [];
|
|
793
|
+
for (const i of issues) {
|
|
794
|
+
const rel = nodePath.relative(inspection.projectRoot, i.source) || i.source;
|
|
795
|
+
if (!brokenFiles.includes(rel))
|
|
796
|
+
brokenFiles.push(rel);
|
|
797
|
+
pushFinding(findings, {
|
|
798
|
+
severity: severityV2(i.severity),
|
|
799
|
+
code: `playbook-${i.code}`,
|
|
800
|
+
sourceKind: 'playbook',
|
|
801
|
+
sourceId: i.packageName ? `${i.packageName}:${rel}` : rel,
|
|
802
|
+
targetKind: 'file',
|
|
803
|
+
targetId: i.code,
|
|
804
|
+
relation: 'validates',
|
|
805
|
+
file: i.source,
|
|
806
|
+
message: i.message,
|
|
807
|
+
});
|
|
375
808
|
}
|
|
376
809
|
for (const p of playbooks) {
|
|
377
810
|
for (const id of p.recommendedTemplateIds ?? []) {
|
|
@@ -405,36 +838,178 @@ async function checkPlaybooks(inspection, lookups, findings) {
|
|
|
405
838
|
});
|
|
406
839
|
}
|
|
407
840
|
}
|
|
841
|
+
const coverage = files > 0
|
|
842
|
+
? [
|
|
843
|
+
{
|
|
844
|
+
subject: 'playbooks',
|
|
845
|
+
unit: 'playbook files',
|
|
846
|
+
expected: files,
|
|
847
|
+
examined: Math.max(0, files - brokenFiles.length),
|
|
848
|
+
...(brokenFiles.length > 0
|
|
849
|
+
? { unexamined: brokenFiles.slice(0, 20), unexaminedTotal: brokenFiles.length }
|
|
850
|
+
: {}),
|
|
851
|
+
reason: 'failed to load or are missing, so none of their playbooks are known',
|
|
852
|
+
},
|
|
853
|
+
]
|
|
854
|
+
: [];
|
|
855
|
+
return { coverage, deadUnits: [] };
|
|
408
856
|
}
|
|
409
857
|
// ─── 8. Registration hints → templates / conventions / profiles ───────────
|
|
410
|
-
|
|
411
|
-
|
|
858
|
+
/**
|
|
859
|
+
* Registration hints, through THE discovery authority
|
|
860
|
+
* (`buildRegistrationHintDoctorReport` — the one `registrations doctor` and
|
|
861
|
+
* `preview` read): load issues, dead / ambiguous / capped discovery, missing
|
|
862
|
+
* anchors. Plus the hint's real cross-references, `discovery.conventionIds` /
|
|
863
|
+
* `discovery.profileIds`. This used to read `relatedTemplateIds` & co. through
|
|
864
|
+
* a cast to fields `IRegistrationHint` does not declare — a probe that could
|
|
865
|
+
* never fire.
|
|
866
|
+
*/
|
|
867
|
+
async function checkRegistrationHints(inspection, findings) {
|
|
868
|
+
const report = await buildRegistrationHintDoctorReport(inspection);
|
|
869
|
+
for (const i of report.issues) {
|
|
870
|
+
// An invalid hint / a duplicate id is a REJECTED entry — THE rejection
|
|
871
|
+
// family reports it (`registration-hint-invalid` / `-duplicate-id`).
|
|
872
|
+
if (i.code === 'invalid-hint' || i.code === 'duplicate-id')
|
|
873
|
+
continue;
|
|
874
|
+
pushFinding(findings, {
|
|
875
|
+
severity: severityV2(i.severity),
|
|
876
|
+
code: `registration-hint-${i.code}`,
|
|
877
|
+
sourceKind: 'registration-hint',
|
|
878
|
+
sourceId: i.hintId ?? i.source ?? 'registration-hints',
|
|
879
|
+
targetKind: 'file',
|
|
880
|
+
targetId: i.target ?? i.code,
|
|
881
|
+
relation: 'validates',
|
|
882
|
+
file: i.source,
|
|
883
|
+
message: i.message,
|
|
884
|
+
nextCommand: 'shrk registrations doctor',
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
const ids = await probeRegistrationHintIds(inspection, findings);
|
|
888
|
+
return {
|
|
889
|
+
coverage: [...report.coverage, ...ids.coverage],
|
|
890
|
+
deadUnits: report.deadUnits,
|
|
891
|
+
...(ids.refProbe ? { refProbe: ids.refProbe } : {}),
|
|
892
|
+
liveness: [report.liveness],
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* A registration hint's cross-references — THE reference-probe half of the
|
|
897
|
+
* family, with no discovery walk, so `collectUnresolvableReferences` runs
|
|
898
|
+
* exactly what the doctor runs.
|
|
899
|
+
*/
|
|
900
|
+
async function probeRegistrationHintIds(inspection, findings) {
|
|
901
|
+
// `discovery.conventionIds` / `discovery.profileIds`, per THE binding table:
|
|
902
|
+
// profileIds are WorkspaceProfile ids (`workspace-profile`). They were bound
|
|
903
|
+
// to `migration-profile` and relabelled `profile` — NOT VERIFIED forever with
|
|
904
|
+
// no migration profiles, a false "not registered" with any (round 12, 12.3).
|
|
905
|
+
const probe = newIdProbe();
|
|
906
|
+
const rows = PROBED_ID_FIELDS.filter((r) => r.source === ProbedIdSource.RegistrationHint);
|
|
907
|
+
for (const e of await listRegistrationHints(inspection)) {
|
|
908
|
+
for (const row of rows) {
|
|
909
|
+
probeIds(inspection, row.kind, readIdList(e.hint, row.field), probe, (id) => `${e.hint.id} → ${row.kind} ${id}`, (id, nearest) => pushFinding(findings, {
|
|
910
|
+
severity: SelfConfigSeverityV2.Info,
|
|
911
|
+
code: `registration-hint-${row.label}-missing`,
|
|
912
|
+
sourceKind: 'registration-hint',
|
|
913
|
+
sourceId: e.hint.id,
|
|
914
|
+
targetKind: selfKindOf(row.kind),
|
|
915
|
+
targetId: id,
|
|
916
|
+
relation: 'related',
|
|
917
|
+
file: e.sourceFile,
|
|
918
|
+
message: `Registration hint "${e.hint.id}" ${row.field} references ${row.label} "${id}"${notResolvedTail(row.kind)}.`,
|
|
919
|
+
...(nearest ? { suggestedFix: `Did you mean "${nearest}"?` } : {}),
|
|
920
|
+
nextCommand: referenceKindListVerb(row.kind),
|
|
921
|
+
}), {
|
|
922
|
+
sourceKind: 'registration-hint',
|
|
923
|
+
sourceId: e.hint.id,
|
|
924
|
+
field: row.field,
|
|
925
|
+
file: e.sourceFile,
|
|
926
|
+
packageName: e.packageName,
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return { coverage: idProbeCoverage('registration hints', 'related ids', probe), deadUnits: [], refProbe: probe };
|
|
931
|
+
}
|
|
932
|
+
// ─── 8b. Conventions → applicability profile ids ──────────────────────────
|
|
933
|
+
/**
|
|
934
|
+
* A convention's `appliesTo` id fields, per THE binding table — today
|
|
935
|
+
* `appliesTo.profileIds` → `workspace-profile`. It was never probed at all, so
|
|
936
|
+
* a typo'd profile id could never surface. Resolution only: `conventions
|
|
937
|
+
* check` does not yet FILTER on profileIds (making a firing gate fire less is
|
|
938
|
+
* not strictly more honest), which the field's JSDoc says.
|
|
939
|
+
*/
|
|
940
|
+
async function checkConventionApplicability(inspection, findings) {
|
|
941
|
+
let entries;
|
|
942
|
+
try {
|
|
943
|
+
entries = await listConventions(inspection);
|
|
944
|
+
}
|
|
945
|
+
catch {
|
|
946
|
+
// A convention file that will not load is `conventions doctor`'s to report.
|
|
947
|
+
return { coverage: [], deadUnits: [] };
|
|
948
|
+
}
|
|
949
|
+
const rows = PROBED_ID_FIELDS.filter((r) => r.source === ProbedIdSource.Convention);
|
|
950
|
+
const probe = newIdProbe();
|
|
412
951
|
for (const e of entries) {
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
probe(h.relatedTemplateIds, 'template', lookups.templates);
|
|
435
|
-
probe(h.relatedConventionIds, 'convention', lookups.conventions);
|
|
436
|
-
probe(h.relatedProfileIds, 'profile', lookups.migrationProfiles);
|
|
952
|
+
for (const row of rows) {
|
|
953
|
+
probeIds(inspection, row.kind, readIdList(e.convention, row.field), probe, (id) => `${e.convention.id} → ${row.kind} ${id}`, (id, nearest) => pushFinding(findings, {
|
|
954
|
+
severity: SelfConfigSeverityV2.Info,
|
|
955
|
+
code: `convention-${row.label}-missing`,
|
|
956
|
+
sourceKind: 'convention',
|
|
957
|
+
sourceId: e.convention.id,
|
|
958
|
+
targetKind: selfKindOf(row.kind),
|
|
959
|
+
targetId: id,
|
|
960
|
+
relation: 'related',
|
|
961
|
+
file: e.sourceFile,
|
|
962
|
+
message: `Convention "${e.convention.id}" ${row.field} names ${row.label} "${id}"${notResolvedTail(row.kind)}.`,
|
|
963
|
+
...(nearest ? { suggestedFix: `Did you mean "${nearest}"?` } : {}),
|
|
964
|
+
nextCommand: referenceKindListVerb(row.kind),
|
|
965
|
+
}), {
|
|
966
|
+
sourceKind: 'convention',
|
|
967
|
+
sourceId: e.convention.id,
|
|
968
|
+
field: row.field,
|
|
969
|
+
file: e.sourceFile,
|
|
970
|
+
packageName: e.packageName,
|
|
971
|
+
});
|
|
972
|
+
}
|
|
437
973
|
}
|
|
974
|
+
return {
|
|
975
|
+
coverage: idProbeCoverage('conventions', 'applicability profile ids', probe),
|
|
976
|
+
deadUnits: [],
|
|
977
|
+
refProbe: probe,
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Scaffold patterns' dead selectors: a `matchPaths` glob matching no file, a
|
|
982
|
+
* pattern matching none after `excludePaths` — counted by THE enumeration
|
|
983
|
+
* `infer templates` attributes candidates with. (The definition checks stay
|
|
984
|
+
* `shrk scaffolds doctor`'s.)
|
|
985
|
+
*/
|
|
986
|
+
async function checkScaffoldPatterns(inspection, findings) {
|
|
987
|
+
let patterns;
|
|
988
|
+
try {
|
|
989
|
+
patterns = (await loadScaffoldPatternsFromInspection(inspection)).patterns;
|
|
990
|
+
}
|
|
991
|
+
catch {
|
|
992
|
+
return { coverage: [], deadUnits: [] };
|
|
993
|
+
}
|
|
994
|
+
if (patterns.length === 0)
|
|
995
|
+
return { coverage: [], deadUnits: [] };
|
|
996
|
+
const enumeration = enumerateScaffoldPatternCandidates(inspection.projectRoot, patterns);
|
|
997
|
+
for (const i of doctorScaffoldPatterns(patterns, inspection, enumeration)) {
|
|
998
|
+
if (!i.code)
|
|
999
|
+
continue;
|
|
1000
|
+
pushFinding(findings, {
|
|
1001
|
+
severity: severityV2(i.severity),
|
|
1002
|
+
code: `scaffold-pattern-${i.code}`,
|
|
1003
|
+
sourceKind: 'scaffold-pattern',
|
|
1004
|
+
sourceId: i.patternId,
|
|
1005
|
+
targetKind: 'file',
|
|
1006
|
+
targetId: i.target ?? '(any file)',
|
|
1007
|
+
relation: 'references',
|
|
1008
|
+
message: `Scaffold pattern "${i.patternId}" ${i.message}.`,
|
|
1009
|
+
nextCommand: 'shrk scaffolds doctor',
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
return scaffoldPatternCoverage(patterns, enumeration);
|
|
438
1013
|
}
|
|
439
1014
|
/**
|
|
440
1015
|
* Decision records are parsed from Markdown via the heading text; the
|
|
@@ -513,51 +1088,451 @@ function checkDecisions(inspection, lookups, findings) {
|
|
|
513
1088
|
}
|
|
514
1089
|
}
|
|
515
1090
|
}
|
|
516
|
-
// ───
|
|
517
|
-
|
|
1091
|
+
// ─── 9b. Declared cross-references (knowledge / construct / boundary / template) ─
|
|
1092
|
+
/**
|
|
1093
|
+
* Every id in a declared `related`-style field (knowledge `related` / `seeAlso`
|
|
1094
|
+
* / `supersededBy` / `actionHints.related*`, construct `related*` + facets that
|
|
1095
|
+
* declare `resolvesAs`, boundary `related*`, template `related`), read from THE
|
|
1096
|
+
* collector — this pass never walks a field itself. `buildLookupsV2` already
|
|
1097
|
+
* warmed the registries, so the collector's sync half is enough.
|
|
1098
|
+
*
|
|
1099
|
+
* A dangling id is a warning (it shrinks a result, it does not misroute —
|
|
1100
|
+
* `--strict` fails on it); a dangling `supersededBy`, a supersession cycle and
|
|
1101
|
+
* a facet naming an unknown kind are errors. An id that could not be looked up
|
|
1102
|
+
* is `xref-unverified` (info) AND an unexamined unit in the coverage record,
|
|
1103
|
+
* so the doctor cannot settle to a pass over it.
|
|
1104
|
+
*/
|
|
1105
|
+
function checkDeclaredCrossReferences(inspection, findings) {
|
|
1106
|
+
const report = collectDeclaredXrefs(inspection);
|
|
1107
|
+
for (const row of report.rows) {
|
|
1108
|
+
if (row.status === DeclaredXrefStatus.Ok)
|
|
1109
|
+
continue;
|
|
1110
|
+
const firstAccepted = row.accepts === 'any' ? undefined : row.accepts[0];
|
|
1111
|
+
const wrongKindAs = row.status === DeclaredXrefStatus.WrongKind ? row.resolvedAs[0] : undefined;
|
|
1112
|
+
pushFinding(findings, {
|
|
1113
|
+
severity: severityV2(row.severity),
|
|
1114
|
+
code: row.status === DeclaredXrefStatus.Dangling
|
|
1115
|
+
? 'xref-dangling'
|
|
1116
|
+
: row.status === DeclaredXrefStatus.WrongKind
|
|
1117
|
+
? 'xref-wrong-kind'
|
|
1118
|
+
: 'xref-unverified',
|
|
1119
|
+
sourceKind: selfKindOf(row.sourceKind),
|
|
1120
|
+
sourceId: row.sourceId,
|
|
1121
|
+
targetKind: selfKindOf(wrongKindAs ?? firstAccepted),
|
|
1122
|
+
targetId: row.targetId,
|
|
1123
|
+
relation: row.relation,
|
|
1124
|
+
file: row.file,
|
|
1125
|
+
message: row.message,
|
|
1126
|
+
suggestedFix: row.status === DeclaredXrefStatus.Unverified
|
|
1127
|
+
? 'Run `shrk self-config doctor` from the CLI (it warms every registry), or fix the empty registry the id resolves against.'
|
|
1128
|
+
: row.didYouMean.length > 0
|
|
1129
|
+
? `Did you mean "${row.didYouMean[0]}"? Correct the id, register the target, or remove it from \`${row.field}\`.`
|
|
1130
|
+
: `Register the target, or remove the id from \`${row.field}\`.`,
|
|
1131
|
+
nextCommand: `shrk self-config resolve ${row.targetId}`,
|
|
1132
|
+
confidence: 'high',
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
for (const issue of report.issues) {
|
|
1136
|
+
const superseded = issue.code === 'xref-superseded-cycle' || issue.code === 'xref-superseded-chain';
|
|
1137
|
+
// A supersession issue names the successor (a knowledge id). A declaration
|
|
1138
|
+
// issue (`xref-unknown-kind`, `xref-malformed`) is about the FIELD: the
|
|
1139
|
+
// facet value there may well resolve — its declared KIND is what is wrong.
|
|
1140
|
+
// It used to read `related unknown:<value>` (round 12, 12.4).
|
|
1141
|
+
const target = superseded
|
|
1142
|
+
? { targetKind: 'knowledge', targetId: issue.targetId ?? issue.field, relation: 'supersedes' }
|
|
1143
|
+
: {
|
|
1144
|
+
targetKind: 'schema',
|
|
1145
|
+
targetId: issue.facetId !== undefined ? `${issue.field}[${issue.facetId}]` : issue.field,
|
|
1146
|
+
relation: 'validates',
|
|
1147
|
+
};
|
|
1148
|
+
pushFinding(findings, {
|
|
1149
|
+
severity: severityV2(issue.severity),
|
|
1150
|
+
code: issue.code,
|
|
1151
|
+
sourceKind: selfKindOf(issue.sourceKind),
|
|
1152
|
+
sourceId: issue.sourceId,
|
|
1153
|
+
targetKind: target.targetKind,
|
|
1154
|
+
targetId: target.targetId,
|
|
1155
|
+
relation: target.relation,
|
|
1156
|
+
file: issue.file,
|
|
1157
|
+
message: issue.message,
|
|
1158
|
+
confidence: 'high',
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
// Like every probe family: a workspace that declares no cross-reference ids
|
|
1162
|
+
// contributes no record (it is not "nothing examined" for the whole doctor).
|
|
1163
|
+
const coverage = report.counts.ids > 0 || report.examined.unreadSources.length > 0
|
|
1164
|
+
? [{ ...declaredXrefCoverage(report), subject: 'declared cross-references' }]
|
|
1165
|
+
: [];
|
|
1166
|
+
return {
|
|
1167
|
+
summary: { examined: report.examined, counts: report.counts, summary: declaredXrefSummaryLine(report) },
|
|
1168
|
+
coverage,
|
|
1169
|
+
report,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
/** THE declared-xref rows that could not be looked up, as unresolvable references (one per row). */
|
|
1173
|
+
function xrefUnresolvableReferences(inspection, report) {
|
|
1174
|
+
return report.rows
|
|
1175
|
+
.filter((r) => r.status === DeclaredXrefStatus.Unverified)
|
|
1176
|
+
.map((r) => {
|
|
1177
|
+
const kind = r.accepts === 'any' ? 'any' : r.accepts[0];
|
|
1178
|
+
const declarable = kind === undefined || kind === 'any' || isReferenceKindDeclarable(kind);
|
|
1179
|
+
return {
|
|
1180
|
+
// The collector records the file project-relative when possible.
|
|
1181
|
+
...(r.file ? { file: nodePath.resolve(inspection.projectRoot, r.file) } : {}),
|
|
1182
|
+
...(r.packageName ? { packageName: r.packageName } : {}),
|
|
1183
|
+
sourceKind: r.sourceKind,
|
|
1184
|
+
sourceId: r.sourceId,
|
|
1185
|
+
field: r.field,
|
|
1186
|
+
kind: kind ?? 'any',
|
|
1187
|
+
id: r.targetId,
|
|
1188
|
+
reason: declarable ? UnresolvableReason.RegistryEmpty : UnresolvableReason.UndeclarableKind,
|
|
1189
|
+
};
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* THE unresolvable-reference scan over the reference families' probes and the
|
|
1194
|
+
* declared-xref report: what was probed (`expected` — the reference families'
|
|
1195
|
+
* units plus the xref ids), what could be checked, and each that could not.
|
|
1196
|
+
* The doctor carries it; `collectUnresolvableReferences` returns it.
|
|
1197
|
+
*/
|
|
1198
|
+
function referenceScanOf(inspection, probes, xrefs) {
|
|
1199
|
+
const present = probes.filter((p) => p !== undefined);
|
|
1200
|
+
const references = [...present.flatMap((p) => p.unverifiedRefs), ...xrefUnresolvableReferences(inspection, xrefs)];
|
|
1201
|
+
const expected = present.reduce((n, p) => n + p.probed, 0) + xrefs.counts.ids;
|
|
1202
|
+
return { expected, examined: expected - references.length, references };
|
|
1203
|
+
}
|
|
1204
|
+
// ─── 10. Pack contribution conflicts (every kind) ─────────────────────────
|
|
1205
|
+
/**
|
|
1206
|
+
* EVERY pack contribution conflict (duplicate id, shadowed, invalid,
|
|
1207
|
+
* missing-loader, stale signature). This used to surface only stale
|
|
1208
|
+
* signatures, so a duplicate-id or shadowed conflict was visible over MCP (v1)
|
|
1209
|
+
* and invisible to `shrk self-config doctor` (v2). Stale signatures keep their
|
|
1210
|
+
* v2 code `pack-signature-stale`; the v1 projection maps it back to
|
|
1211
|
+
* `pack-conflict:stale-signature`.
|
|
1212
|
+
*/
|
|
1213
|
+
async function checkPackConflicts(inspection, findings) {
|
|
518
1214
|
try {
|
|
519
|
-
|
|
1215
|
+
// The ASYNC inventory (loaders consulted); the stale-signature conflict it
|
|
1216
|
+
// carries is read from the one pack-asset freshness authority.
|
|
1217
|
+
const inv = await buildPackContributionsInventoryAsync(inspection);
|
|
1218
|
+
checkContributionRejections(findings, inv.rejections);
|
|
520
1219
|
for (const c of inv.conflicts) {
|
|
521
|
-
|
|
522
|
-
continue;
|
|
1220
|
+
const stale = c.kind === 'stale-signature';
|
|
523
1221
|
pushFinding(findings, {
|
|
524
|
-
severity: SelfConfigSeverityV2.Warning,
|
|
525
|
-
code: 'pack-signature-stale'
|
|
1222
|
+
severity: stale ? SelfConfigSeverityV2.Warning : severityV2(c.severity),
|
|
1223
|
+
code: stale ? 'pack-signature-stale' : `pack-conflict:${c.kind}`,
|
|
526
1224
|
sourceKind: 'pack',
|
|
527
1225
|
sourceId: c.id,
|
|
528
|
-
targetKind: 'schema',
|
|
529
|
-
targetId: 'sharkcraft.pack-manifest/v1',
|
|
1226
|
+
targetKind: stale ? 'schema' : 'pack',
|
|
1227
|
+
targetId: stale ? 'sharkcraft.pack-manifest/v1' : String(c.contributionKind),
|
|
530
1228
|
relation: 'validates',
|
|
531
1229
|
message: c.message,
|
|
532
1230
|
...(c.nextCommand ? { nextCommand: c.nextCommand } : {}),
|
|
533
1231
|
confidence: 'high',
|
|
534
1232
|
});
|
|
535
1233
|
}
|
|
1234
|
+
return inv;
|
|
1235
|
+
}
|
|
1236
|
+
catch {
|
|
1237
|
+
// An inventory that cannot be built is `shrk packs doctor`'s to report.
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* THE rejection family (round 12, 12.1): every declared contribution entry a
|
|
1243
|
+
* loader refused — from THE rejection channel the inventory carries — is an
|
|
1244
|
+
* ERROR, one finding per failing field (`<kind>-invalid`, or
|
|
1245
|
+
* `<kind>-duplicate-id`), local and pack alike: an entry that never takes
|
|
1246
|
+
* effect is a defect, and it used to reach no surface but its kind's doctor
|
|
1247
|
+
* (conventions, helpers) or none at all (knowledge, templates, playbooks, …).
|
|
1248
|
+
* The routing / registration hint families no longer report their own
|
|
1249
|
+
* `invalid-hint` / `duplicate-id` issues, so each rejection has one reporter.
|
|
1250
|
+
*/
|
|
1251
|
+
function checkContributionRejections(findings, rejections) {
|
|
1252
|
+
for (const r of rejections) {
|
|
1253
|
+
const f = rejectionFindingOf(r.kind, r.cause);
|
|
1254
|
+
const where = r.index >= 0 ? `${r.exportName ?? ''}[${r.index}]` : (r.exportName ?? 'default');
|
|
1255
|
+
const who = r.entryId !== undefined ? `"${r.entryId}"` : `at ${where}`;
|
|
1256
|
+
for (const reason of r.reasons) {
|
|
1257
|
+
pushFinding(findings, {
|
|
1258
|
+
severity: SelfConfigSeverityV2.Error,
|
|
1259
|
+
code: f.code,
|
|
1260
|
+
sourceKind: f.kind,
|
|
1261
|
+
sourceId: r.entryId ?? `${r.file}:${where}`,
|
|
1262
|
+
targetKind: 'schema',
|
|
1263
|
+
targetId: validatorField(reason),
|
|
1264
|
+
relation: 'validates',
|
|
1265
|
+
file: r.file,
|
|
1266
|
+
message: `${f.kind} ${who} in ${r.file} (${where}) was rejected by its loader — ${reason}. It does not take effect.`,
|
|
1267
|
+
nextCommand: r.packageName ? `shrk packs contributions --pack ${r.packageName}` : f.doctor,
|
|
1268
|
+
confidence: 'high',
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* A reference that resolves nowhere may name an entry that WAS declared — and
|
|
1275
|
+
* refused by its loader. Its `*-missing` finding then says so instead of
|
|
1276
|
+
* offering a did-you-mean (round 12, 12.1): the id is not a typo.
|
|
1277
|
+
*/
|
|
1278
|
+
function annotateRejectedTargets(findings, rejections) {
|
|
1279
|
+
const byId = new Map();
|
|
1280
|
+
for (const r of rejections)
|
|
1281
|
+
if (r.entryId !== undefined && !byId.has(r.entryId))
|
|
1282
|
+
byId.set(r.entryId, r);
|
|
1283
|
+
if (byId.size === 0)
|
|
1284
|
+
return;
|
|
1285
|
+
findings.forEach((f, i) => {
|
|
1286
|
+
if (!isUnresolvedReferenceFinding(f))
|
|
1287
|
+
return;
|
|
1288
|
+
const r = byId.get(f.targetId);
|
|
1289
|
+
if (!r)
|
|
1290
|
+
return;
|
|
1291
|
+
const rejectedAs = rejectionFindingOf(r.kind, r.cause).kind;
|
|
1292
|
+
if (f.targetKind !== rejectedAs && f.targetKind !== selfKindOf(undefined))
|
|
1293
|
+
return;
|
|
1294
|
+
findings[i] = {
|
|
1295
|
+
...f,
|
|
1296
|
+
suggestedFix: `'${f.targetId}' is declared in ${r.file} but was rejected: ${r.reasons.join('; ')}`,
|
|
1297
|
+
};
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
function actionHintCommand(c) {
|
|
1301
|
+
if (typeof c === 'string')
|
|
1302
|
+
return c;
|
|
1303
|
+
if (c && typeof c === 'object' && typeof c.command === 'string') {
|
|
1304
|
+
return c.command;
|
|
1305
|
+
}
|
|
1306
|
+
return undefined;
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Every command string an asset prescribes, with where it came from. A broken
|
|
1310
|
+
* source degrades to "no commands from it" — that asset's own loader reports
|
|
1311
|
+
* the load failure.
|
|
1312
|
+
*/
|
|
1313
|
+
async function collectCommandSites(inspection) {
|
|
1314
|
+
const sites = [];
|
|
1315
|
+
const add = (sourceKind, sourceId, field, commands, severity, relation, file, assumeShrk = false) => {
|
|
1316
|
+
for (const raw of commands ?? []) {
|
|
1317
|
+
const command = actionHintCommand(raw)?.trim();
|
|
1318
|
+
if (command) {
|
|
1319
|
+
sites.push({
|
|
1320
|
+
sourceKind,
|
|
1321
|
+
sourceId,
|
|
1322
|
+
field,
|
|
1323
|
+
command,
|
|
1324
|
+
severity,
|
|
1325
|
+
relation,
|
|
1326
|
+
file,
|
|
1327
|
+
...(assumeShrk ? { assumeShrk: true } : {}),
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
const W = SelfConfigSeverityV2.Warning;
|
|
1333
|
+
try {
|
|
1334
|
+
const { loadAgentContractTests } = await import("./test-runner.js");
|
|
1335
|
+
// An agent test's expectation is load-bearing: a dead expected command fails
|
|
1336
|
+
// the test forever, so it is an error here too.
|
|
1337
|
+
for (const t of await loadAgentContractTests(inspection)) {
|
|
1338
|
+
add('agent-test', t.id, 'expectedCommands', t.expectedCommands, SelfConfigSeverityV2.Error, 'expects', undefined, true);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
catch {
|
|
1342
|
+
// agent-test loader failure is reported by `shrk test agent`
|
|
1343
|
+
}
|
|
1344
|
+
try {
|
|
1345
|
+
for (const e of await listTaskRoutingHints(inspection)) {
|
|
1346
|
+
add('routing-hint', e.hint.id, 'recommends.commands', e.hint.recommends?.commands, W, 'routes-to', e.sourceFile);
|
|
1347
|
+
}
|
|
536
1348
|
}
|
|
537
1349
|
catch {
|
|
538
|
-
//
|
|
1350
|
+
// reported by the routing-hint loader
|
|
539
1351
|
}
|
|
1352
|
+
try {
|
|
1353
|
+
for (const e of await listRegistrationHints(inspection)) {
|
|
1354
|
+
add('registration-hint', e.hint.id, 'validationCommands', e.hint.validationCommands, W, 'validates', e.sourceFile);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
catch {
|
|
1358
|
+
// reported by the registration-hint loader
|
|
1359
|
+
}
|
|
1360
|
+
for (const p of inspection.pipelineRegistry?.list?.() ?? []) {
|
|
1361
|
+
for (const step of p.steps ?? []) {
|
|
1362
|
+
add('pipeline', p.id, `steps.${step.id}.cliCommands`, step.cliCommands, W, 'references');
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
for (const p of listPlaybooks(inspection)) {
|
|
1366
|
+
// The loader refuses a playbook without an array `steps` (12.1d: an
|
|
1367
|
+
// accepted one crashed the whole doctor here); guard anyway.
|
|
1368
|
+
(Array.isArray(p.steps) ? p.steps : []).forEach((step, i) => {
|
|
1369
|
+
add('playbook', p.id, `steps[${i}].commands`, step?.commands, W, 'references');
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
for (const c of listConstructs(inspection)) {
|
|
1373
|
+
add('construct', c.id, 'commands', c.commands, W, 'references');
|
|
1374
|
+
}
|
|
1375
|
+
for (const p of inspection.presetRegistry?.list?.() ?? []) {
|
|
1376
|
+
add('preset', p.id, 'recommendedNextCommands', p.recommendedNextCommands, W, 'references');
|
|
1377
|
+
}
|
|
1378
|
+
for (const k of inspection.knowledgeEntries) {
|
|
1379
|
+
const origin = k.source?.origin ?? undefined;
|
|
1380
|
+
add('knowledge', k.id, 'actionHints.commands', k.actionHints?.commands, W, 'references', origin);
|
|
1381
|
+
const refCommands = (k.references ?? [])
|
|
1382
|
+
.filter((r) => r.kind === 'command')
|
|
1383
|
+
.map((r) => r.id ?? r.command)
|
|
1384
|
+
.filter((c) => typeof c === 'string' && c.length > 0);
|
|
1385
|
+
add('knowledge', k.id, 'references', refCommands, W, 'references', origin, true);
|
|
1386
|
+
}
|
|
1387
|
+
try {
|
|
1388
|
+
for (const d of await loadTsDecisions(inspection)) {
|
|
1389
|
+
add('decision', d.id, 'relatedCommands', d.relatedCommands, W, 'related', d.sourceFile);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
catch {
|
|
1393
|
+
// reported by the decisions loader
|
|
1394
|
+
}
|
|
1395
|
+
return sites;
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* The command-strings pass: every command an asset prescribes, resolved
|
|
1399
|
+
* through THE injected command resolver, deduped per (asset, command).
|
|
1400
|
+
*
|
|
1401
|
+
* Without a resolver (outside the CLI) nothing is checked, and the pass says
|
|
1402
|
+
* so ONCE (`command-probe-unverified`, info) — never a finding per command,
|
|
1403
|
+
* and never an error. The old probes checked against a set initialised empty,
|
|
1404
|
+
* so every command in every hint was "missing", correct ones included.
|
|
1405
|
+
*/
|
|
1406
|
+
async function checkCommandStrings(inspection, findings) {
|
|
1407
|
+
const seen = new Set();
|
|
1408
|
+
const sites = (await collectCommandSites(inspection)).filter((s) => {
|
|
1409
|
+
// The reading is part of the key: one asset may cite `doctor` both as free
|
|
1410
|
+
// shell text and as a command reference, and the two resolve differently.
|
|
1411
|
+
const key = `${s.sourceKind}:${s.sourceId}|${s.assumeShrk === true ? 'ref' : 'sh'}|${s.command}`;
|
|
1412
|
+
if (seen.has(key))
|
|
1413
|
+
return false;
|
|
1414
|
+
seen.add(key);
|
|
1415
|
+
return true;
|
|
1416
|
+
});
|
|
1417
|
+
const counts = { probed: sites.length, exists: 0, prefixOnly: 0, notShrk: 0, unknown: 0, unverified: 0 };
|
|
1418
|
+
if (sites.length === 0)
|
|
1419
|
+
return counts;
|
|
1420
|
+
if (!hasCommandResolver(inspection)) {
|
|
1421
|
+
counts.unverified = sites.length;
|
|
1422
|
+
pushFinding(findings, {
|
|
1423
|
+
severity: SelfConfigSeverityV2.Info,
|
|
1424
|
+
code: 'command-probe-unverified',
|
|
1425
|
+
// The doctor's own probe summary — it has a kind (it borrowed `unknown`).
|
|
1426
|
+
sourceKind: 'self-config',
|
|
1427
|
+
sourceId: 'command-probes',
|
|
1428
|
+
targetKind: 'command',
|
|
1429
|
+
targetId: `${sites.length} command string(s)`,
|
|
1430
|
+
relation: 'references',
|
|
1431
|
+
message: `${sites.length} command string(s) prescribed by assets were NOT checked: ${COMMAND_INDEX_NOT_INJECTED}.`,
|
|
1432
|
+
suggestedFix: 'Run `shrk self-config doctor` from the CLI, which injects the live command index.',
|
|
1433
|
+
confidence: 'high',
|
|
1434
|
+
});
|
|
1435
|
+
return counts;
|
|
1436
|
+
}
|
|
1437
|
+
for (const site of sites) {
|
|
1438
|
+
const resolution = site.assumeShrk
|
|
1439
|
+
? resolveShrkCommandReference(inspection, site.command)
|
|
1440
|
+
: resolveCommandReference(inspection, site.command);
|
|
1441
|
+
switch (resolution.status) {
|
|
1442
|
+
case CommandResolutionStatus.Ok:
|
|
1443
|
+
counts.exists += 1;
|
|
1444
|
+
break;
|
|
1445
|
+
case CommandResolutionStatus.PrefixOnly:
|
|
1446
|
+
counts.prefixOnly += 1;
|
|
1447
|
+
break;
|
|
1448
|
+
case CommandResolutionStatus.NotShrk:
|
|
1449
|
+
counts.notShrk += 1;
|
|
1450
|
+
break;
|
|
1451
|
+
case CommandResolutionStatus.Unverified:
|
|
1452
|
+
counts.unverified += 1;
|
|
1453
|
+
break;
|
|
1454
|
+
default: {
|
|
1455
|
+
counts.unknown += 1;
|
|
1456
|
+
const closest = resolution.closest ?? [];
|
|
1457
|
+
pushFinding(findings, {
|
|
1458
|
+
severity: site.severity,
|
|
1459
|
+
code: 'unknown-command',
|
|
1460
|
+
sourceKind: site.sourceKind,
|
|
1461
|
+
sourceId: site.sourceId,
|
|
1462
|
+
targetKind: 'command',
|
|
1463
|
+
targetId: site.command,
|
|
1464
|
+
relation: site.relation,
|
|
1465
|
+
file: site.file,
|
|
1466
|
+
message: `${site.sourceKind} "${site.sourceId}" ${site.field} prescribes \`${site.command}\`, which does not resolve (${resolution.status}${resolution.reason ? ` — ${resolution.reason}` : ''}).`,
|
|
1467
|
+
suggestedFix: closest.length > 0
|
|
1468
|
+
? `Did you mean \`${closest[0]}\`? Update or remove the command.`
|
|
1469
|
+
: 'Update or remove the command.',
|
|
1470
|
+
nextCommand: 'shrk surface list',
|
|
1471
|
+
confidence: 'high',
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
return counts;
|
|
540
1477
|
}
|
|
541
1478
|
// ─── Public entry ─────────────────────────────────────────────────────────
|
|
542
1479
|
export async function buildSelfConfigDoctorReportV2(inspection) {
|
|
543
1480
|
const findings = [];
|
|
544
1481
|
const lookups = await buildLookupsV2(inspection);
|
|
545
1482
|
checkKnowledgeFileRefs(inspection, findings);
|
|
546
|
-
await checkSearchTuning(inspection,
|
|
1483
|
+
const tuning = await checkSearchTuning(inspection, findings);
|
|
1484
|
+
checkSearchTuningLoadIssues(inspection, findings);
|
|
547
1485
|
await checkAgentTests(inspection, lookups, findings);
|
|
548
|
-
checkTemplateMetadata(inspection,
|
|
549
|
-
await checkRoutingHints(inspection,
|
|
1486
|
+
const templateFamily = checkTemplateMetadata(inspection, findings);
|
|
1487
|
+
const routing = await checkRoutingHints(inspection, findings);
|
|
550
1488
|
checkPipelines(inspection, lookups, findings);
|
|
551
|
-
await checkPlaybooks(inspection, lookups, findings);
|
|
552
|
-
await checkRegistrationHints(inspection,
|
|
1489
|
+
const playbookFamily = await checkPlaybooks(inspection, lookups, findings);
|
|
1490
|
+
const registration = await checkRegistrationHints(inspection, findings);
|
|
1491
|
+
const conventionFamily = await checkConventionApplicability(inspection, findings);
|
|
1492
|
+
const scaffolds = await checkScaffoldPatterns(inspection, findings);
|
|
553
1493
|
checkDecisions(inspection, lookups, findings);
|
|
554
|
-
|
|
1494
|
+
const xrefs = checkDeclaredCrossReferences(inspection, findings);
|
|
1495
|
+
const inventory = await checkPackConflicts(inspection, findings);
|
|
1496
|
+
const commandProbes = await checkCommandStrings(inspection, findings);
|
|
1497
|
+
if (inventory)
|
|
1498
|
+
annotateRejectedTargets(findings, inventory.rejections);
|
|
1499
|
+
const referenceScan = referenceScanOf(inspection, [templateFamily.refProbe, routing.refProbe, registration.refProbe, conventionFamily.refProbe, tuning.refProbe], xrefs.report);
|
|
1500
|
+
const selectorUnits = [
|
|
1501
|
+
...(tuning.liveness ?? []),
|
|
1502
|
+
...(routing.liveness ?? []),
|
|
1503
|
+
...(registration.liveness ?? []),
|
|
1504
|
+
...(scaffolds.liveness ?? []),
|
|
1505
|
+
]
|
|
1506
|
+
.flatMap((s) => s.units)
|
|
1507
|
+
.filter((u) => u.state !== UnitLivenessState.Live);
|
|
1508
|
+
const coverage = [
|
|
1509
|
+
...commandProbeCoverage(commandProbes),
|
|
1510
|
+
...tuning.coverage,
|
|
1511
|
+
...routing.coverage,
|
|
1512
|
+
...playbookFamily.coverage,
|
|
1513
|
+
...registration.coverage,
|
|
1514
|
+
...conventionFamily.coverage,
|
|
1515
|
+
...templateFamily.coverage,
|
|
1516
|
+
...scaffolds.coverage,
|
|
1517
|
+
...xrefs.coverage,
|
|
1518
|
+
];
|
|
1519
|
+
const deadUnits = [
|
|
1520
|
+
...tuning.deadUnits,
|
|
1521
|
+
...routing.deadUnits,
|
|
1522
|
+
...registration.deadUnits,
|
|
1523
|
+
...scaffolds.deadUnits,
|
|
1524
|
+
];
|
|
555
1525
|
const totals = computeTotalsV2(findings);
|
|
556
|
-
|
|
1526
|
+
// One derivation: core's `coverageShortfall`, the rule the CLI settles with.
|
|
1527
|
+
const unverified = coverage.some((c) => coverageShortfall(c) !== undefined);
|
|
1528
|
+
const verdict = totals.error > 0 ? 'errors' : unverified ? 'unverified' : totals.warning > 0 ? 'warnings' : 'ok';
|
|
557
1529
|
const nextCommands = [];
|
|
558
1530
|
if (totals.error > 0) {
|
|
559
1531
|
nextCommands.push('Fix the errored ids first — agent-test expectations and template requirements are load-bearing.', 'shrk packs conflicts');
|
|
560
1532
|
}
|
|
1533
|
+
else if (unverified) {
|
|
1534
|
+
nextCommands.push(`Some units were NOT verified (see coverage): a dead selector / boost is a ${DEAD_SELECTOR_CAUSES} — fix it, or mark a planned target expectEmpty; and run from the CLI, which injects the command index.`);
|
|
1535
|
+
}
|
|
561
1536
|
else if (totals.warning > 0) {
|
|
562
1537
|
nextCommands.push('Review warnings — most resolve by registering the missing id or removing the stale link.');
|
|
563
1538
|
}
|
|
@@ -572,8 +1547,91 @@ export async function buildSelfConfigDoctorReportV2(inspection) {
|
|
|
572
1547
|
totals,
|
|
573
1548
|
verdict,
|
|
574
1549
|
nextCommands,
|
|
1550
|
+
coverage,
|
|
1551
|
+
deadUnits,
|
|
1552
|
+
selectorUnits,
|
|
1553
|
+
unresolvableReferences: referenceScan.references,
|
|
1554
|
+
probes: {
|
|
1555
|
+
command: commandProbes,
|
|
1556
|
+
'search-tuning-target': tuning.probes,
|
|
1557
|
+
'routing-hint-target': routing.probes,
|
|
1558
|
+
},
|
|
1559
|
+
crossReferences: xrefs.summary,
|
|
575
1560
|
};
|
|
576
1561
|
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Every declared reference that could not be resolved because its kind's
|
|
1564
|
+
* registry is empty here, or can never be filled (round 12, ONE-CHANGE) —
|
|
1565
|
+
* running EXACTLY the doctor's reference probes (template metadata, routing
|
|
1566
|
+
* `recommends`, registration-hint and convention id fields, declared
|
|
1567
|
+
* cross-references) over a warmed inspection, without the rest of the doctor.
|
|
1568
|
+
* `buildSelfConfigDoctorReportV2` carries the same list, so the doctor and
|
|
1569
|
+
* `shrk packs contributions` cannot disagree.
|
|
1570
|
+
*/
|
|
1571
|
+
export async function collectUnresolvableReferences(inspection) {
|
|
1572
|
+
await warmReferenceRegistries(inspection);
|
|
1573
|
+
const scratch = [];
|
|
1574
|
+
const templateFamily = checkTemplateMetadata(inspection, scratch);
|
|
1575
|
+
const routing = await checkRoutingHints(inspection, scratch);
|
|
1576
|
+
const registration = await probeRegistrationHintIds(inspection, scratch);
|
|
1577
|
+
const conventionFamily = await checkConventionApplicability(inspection, scratch);
|
|
1578
|
+
const xrefs = checkDeclaredCrossReferences(inspection, scratch);
|
|
1579
|
+
// THE key probes the tuning lint judges (round 13) — the same function, so
|
|
1580
|
+
// `packs contributions` and `search tuning doctor` answer from one resolution.
|
|
1581
|
+
const { entries: tunings } = await loadSearchTuning(inspection);
|
|
1582
|
+
const tuning = tuningReferenceProbe(inspection, searchTuningKeyProbes(inspection, tunings));
|
|
1583
|
+
return referenceScanOf(inspection, [templateFamily.refProbe, routing.refProbe, registration.refProbe, conventionFamily.refProbe, tuning], xrefs.report);
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* The command-string probes' coverage: every prescribed command string must
|
|
1587
|
+
* have been resolved. Only present when assets prescribe any — a repo with no
|
|
1588
|
+
* command strings is not "nothing examined" for the whole doctor.
|
|
1589
|
+
*
|
|
1590
|
+
* `not-shrk` strings (git, tsc, a script with no package.json to read) are a
|
|
1591
|
+
* DELIBERATE narrowing — not this engine's to judge — so they leave `expected`
|
|
1592
|
+
* rather than counting as examined. A set that is ALL not-shrk narrows to
|
|
1593
|
+
* nothing and carries no record, exactly like a set with no strings at all.
|
|
1594
|
+
* `prefix-only` strings stay in scope (the verb was proven); the clean line
|
|
1595
|
+
* names how many tails went unproven.
|
|
1596
|
+
*/
|
|
1597
|
+
function commandProbeCoverage(c) {
|
|
1598
|
+
const expected = c.probed - c.notShrk;
|
|
1599
|
+
if (expected <= 0)
|
|
1600
|
+
return [];
|
|
1601
|
+
return [
|
|
1602
|
+
{
|
|
1603
|
+
unit: 'command strings',
|
|
1604
|
+
expected,
|
|
1605
|
+
examined: expected - c.unverified,
|
|
1606
|
+
// `reason` names why a GAP exists. Only the no-resolver path returns
|
|
1607
|
+
// Unverified, so `unverified > 0` means exactly "index not injected";
|
|
1608
|
+
// a fully examined run carries no reason (a verified row labelled
|
|
1609
|
+
// NOT VERIFIED misled JSON readers).
|
|
1610
|
+
...(c.unverified > 0 ? { reason: COMMAND_INDEX_NOT_INJECTED } : {}),
|
|
1611
|
+
},
|
|
1612
|
+
];
|
|
1613
|
+
}
|
|
1614
|
+
/** One-line summary of the command-string probes, shared by the renderers. */
|
|
1615
|
+
function commandProbeLine(report) {
|
|
1616
|
+
const c = report.probes.command;
|
|
1617
|
+
return `probed ${c.probed} · unknown ${c.unknown} · prefix-only ${c.prefixOnly} · not-shrk ${c.notShrk} · unverified ${c.unverified}`;
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* The tuning-key probes on ONE line — so a 100% failure rate reads as a probe
|
|
1621
|
+
* fault, not as N independent content errors.
|
|
1622
|
+
*/
|
|
1623
|
+
function tuningProbeLine(report) {
|
|
1624
|
+
const t = report.probes['search-tuning-target'];
|
|
1625
|
+
return `probed ${t.probed} · resolved ${t.resolved} · missing ${t.missing} · unprefixed ${t.unprefixed} · unknown-kind ${t.unknownKind} · unverified ${t.unverified}`;
|
|
1626
|
+
}
|
|
1627
|
+
function routingProbeLine(report) {
|
|
1628
|
+
const r = report.probes['routing-hint-target'];
|
|
1629
|
+
return `probed ${r.probed} · resolved ${r.resolved} · missing ${r.missing} · unverified ${r.unverified}`;
|
|
1630
|
+
}
|
|
1631
|
+
/** `<subject>: <formatCoverage>` per record — what each family examined. */
|
|
1632
|
+
function coverageLines(report) {
|
|
1633
|
+
return report.coverage.map((c) => `${c.subject ? `${c.subject}: ` : ''}${formatCoverage(c)}`);
|
|
1634
|
+
}
|
|
577
1635
|
function computeTotalsV2(findings) {
|
|
578
1636
|
const byRelation = {};
|
|
579
1637
|
const bySourceKind = {};
|
|
@@ -603,13 +1661,22 @@ export function renderSelfConfigDoctorV2Text(report) {
|
|
|
603
1661
|
lines.push(` errors ${report.totals.error}`);
|
|
604
1662
|
lines.push(` warnings ${report.totals.warning}`);
|
|
605
1663
|
lines.push(` info ${report.totals.info}`);
|
|
1664
|
+
lines.push(` commands ${commandProbeLine(report)}`);
|
|
1665
|
+
lines.push(` tuning keys ${tuningProbeLine(report)}`);
|
|
1666
|
+
lines.push(` routing ids ${routingProbeLine(report)}`);
|
|
1667
|
+
if (report.crossReferences)
|
|
1668
|
+
lines.push(` xrefs ${report.crossReferences.summary}`);
|
|
1669
|
+
for (const c of coverageLines(report))
|
|
1670
|
+
lines.push(` coverage ${c}`);
|
|
606
1671
|
lines.push('');
|
|
607
1672
|
if (report.findings.length === 0) {
|
|
608
|
-
|
|
1673
|
+
// No ✓ here: whether this is a pass is the settled verdict's call (a dead
|
|
1674
|
+
// or unverified unit is not a finding, and still is not a pass).
|
|
1675
|
+
lines.push(' No findings.');
|
|
609
1676
|
return lines.join('\n') + '\n';
|
|
610
1677
|
}
|
|
611
1678
|
for (const f of report.findings.slice(0, 200)) {
|
|
612
|
-
lines.push(` ${f.severity.padEnd(7)} [${f.code}] ${f.sourceKind}:${f.sourceId} ${f.relation} ${f.targetKind}:${f.targetId}`);
|
|
1679
|
+
lines.push(` ${f.severity.padEnd(7)} [${f.code}] ${f.sourceKind}:${f.sourceId} ${f.relation} ${f.targetKind}:${f.targetId}${f.occurrences && f.occurrences > 1 ? ` (×${f.occurrences})` : ''}`);
|
|
613
1680
|
lines.push(` ${f.message}`);
|
|
614
1681
|
if (f.suggestedFix)
|
|
615
1682
|
lines.push(` fix: ${f.suggestedFix}`);
|
|
@@ -635,9 +1702,16 @@ export function renderSelfConfigDoctorV2Markdown(report) {
|
|
|
635
1702
|
lines.push(`- errors: ${report.totals.error}`);
|
|
636
1703
|
lines.push(`- warnings: ${report.totals.warning}`);
|
|
637
1704
|
lines.push(`- info: ${report.totals.info}`);
|
|
1705
|
+
lines.push(`- commands: ${commandProbeLine(report)}`);
|
|
1706
|
+
lines.push(`- tuning keys: ${tuningProbeLine(report)}`);
|
|
1707
|
+
lines.push(`- routing ids: ${routingProbeLine(report)}`);
|
|
1708
|
+
if (report.crossReferences)
|
|
1709
|
+
lines.push(`- xrefs: ${report.crossReferences.summary}`);
|
|
1710
|
+
for (const c of coverageLines(report))
|
|
1711
|
+
lines.push(`- coverage: ${c}`);
|
|
638
1712
|
lines.push('');
|
|
639
1713
|
if (report.findings.length === 0) {
|
|
640
|
-
lines.push('No
|
|
1714
|
+
lines.push('No findings.');
|
|
641
1715
|
return lines.join('\n') + '\n';
|
|
642
1716
|
}
|
|
643
1717
|
lines.push('| Severity | Source | Relation | Target | Message | Next |');
|