@shrkcrft/inspector 0.1.0-alpha.30 → 0.1.0-alpha.32

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