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