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