@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
@@ -1,27 +1,75 @@
1
1
  /**
2
- * Knowledge stale-check.
2
+ * Knowledge stale-check — THE staleness engine (every consumer reads it).
3
3
  *
4
- * Walks every knowledge entry's `references[]` + `anchors[]` and checks
5
- * whether each target still resolves against the current workspace.
4
+ * Walks every knowledge entry's `references[]` + `anchors[]` — and the
5
+ * references boundary rules and policy checks declare — and checks whether each
6
+ * target still resolves against the current workspace, and (with `contains` /
7
+ * `matches` / `count`) still says what the asset claims.
6
8
  *
7
- * Pure file-system + registry lookups — no network, no AST compilation.
8
- * Symbol checks are deterministic best-effort text scans so the doctor
9
- * stays cheap.
9
+ * It classifies every entry in scope into ONE of three buckets: `verified`,
10
+ * `stale`, `unverifiable`. An entry with nothing checkable used to contribute
11
+ * nothing and read as healthy — the less verifiable a corpus, the healthier it
12
+ * looked. The buckets make that shortfall a number the verdict can refuse on.
10
13
  *
11
- * Schema: sharkcraft.knowledge-stale/v1
14
+ * Callers MUST `await warmReferenceRegistries(inspection)` first: the
15
+ * playbook / policy / construct / helper registries are async-filled, and an
16
+ * unwarmed registry is reported `unknown` (NOT VERIFIED) here — never `stale`
17
+ * against a correct id.
18
+ *
19
+ * Pure file-system + registry lookups — no network, no whole-program
20
+ * compilation. Symbol checks use the single-file AST index.
21
+ *
22
+ * Schema: sharkcraft.knowledge-stale/v1 (additive fields since round 11)
12
23
  */
13
24
  import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
14
25
  import * as nodePath from 'node:path';
15
- import { HELPERS } from "./helper-registry.js";
16
- import { referenceIdExists } from "./reference-registry.js";
26
+ import { globListSelects, globToRegex } from '@shrkcrft/boundaries';
27
+ import { KnowledgeReferenceRoot } from '@shrkcrft/core';
28
+ import { KNOWLEDGE_REFERENCE_KINDS, referenceRootProblem, anchorShapeProblem, anchorsListProblem, declaredAnchorItems, declaredReferenceItems, knowledgeAnchors, knowledgeSourceFormat, referenceShapeProblem, referencesListProblem, todayUtcIso, verifiedOnAgeDays, } from '@shrkcrft/knowledge';
29
+ import { isCacheBackedKind, isReferenceCacheWarm, referenceIdExists, referenceIdsFor, } from "./reference-registry.js";
30
+ import { listPolicyDeclarations } from "./policy-registry.js";
31
+ import { checkReferenceContent, checkReferenceCount, hasContentAssertion, } from "./reference-content-check.js";
32
+ import { ReferenceFailure } from "./reference-failure.js";
33
+ import { KnowledgeEntryVerdict } from "./knowledge-entry-verdict.js";
34
+ import { KnowledgeUnverifiableReason } from "./knowledge-unverifiable-reason.js";
35
+ import { KnowledgeAdvisoryCode } from "./knowledge-advisory-code.js";
36
+ import { knowledgeRejectedEntries } from "./knowledge-entry-rejections.js";
37
+ import { ReferenceAssetKind } from "./reference-asset-kind.js";
38
+ import { stalePathHint } from "./stale-path-hint.js";
39
+ import { buildSymbolIndex } from "./symbol-index.js";
40
+ // The `command` reference case (and its anchor twin) — one injected resolver.
41
+ import { COMMAND_INDEX_NOT_INJECTED, resolveShrkCommandReference } from "./reference-registry.js";
42
+ import { CommandResolutionStatus } from "./command-resolution-status.js";
17
43
  import { resolveSymbolInFile, SymbolResolution } from "./symbol-index.js";
44
+ import { packOriginByName, packOriginOfFile } from "./reference-pack-origin.js";
18
45
  export const KNOWLEDGE_STALE_SCHEMA = 'sharkcraft.knowledge-stale/v1';
46
+ /** The `byReferenceKind` bucket of an item with no kind to bucket under (a string, a non-list value). */
47
+ const MALFORMED_KIND_BUCKET = '(malformed)';
48
+ /**
49
+ * A declared value as a check row carries it: `null` / `undefined` become their
50
+ * text, so no renderer dereferences them; anything else as declared
51
+ * (`formatKnowledgeReference` renders a non-object as written). The CHECK reads
52
+ * the original, so a `null` item says "is not an object (got null)" — the
53
+ * validator's words — not "is the string \"null\"".
54
+ */
55
+ function displayedReference(value) {
56
+ return (value === null || value === undefined ? String(value) : value);
57
+ }
19
58
  export var ReferenceCheckOutcome;
20
59
  (function (ReferenceCheckOutcome) {
21
60
  ReferenceCheckOutcome["Ok"] = "ok";
22
61
  ReferenceCheckOutcome["Stale"] = "stale";
23
62
  ReferenceCheckOutcome["Missing"] = "missing";
63
+ /** Well-formed, but the check cannot evaluate it (a `url`, an unwarmed registry). */
24
64
  ReferenceCheckOutcome["Unknown"] = "unknown";
65
+ /**
66
+ * MALFORMED — a kind outside the vocabulary, or the field its kind cannot be
67
+ * checked without is missing (a symbol reference with no `symbol`). It
68
+ * verifies nothing; it used to share `unknown` with an unfetched url and
69
+ * count toward a green run. Now it is a coverage shortfall on the verdict
70
+ * (not verified) and fails under `--fail-on invalid`.
71
+ */
72
+ ReferenceCheckOutcome["Invalid"] = "invalid";
25
73
  })(ReferenceCheckOutcome || (ReferenceCheckOutcome = {}));
26
74
  export var SymbolConfidence;
27
75
  (function (SymbolConfidence) {
@@ -75,193 +123,543 @@ function dirExists(projectRoot, rel) {
75
123
  * hoped-for shape compiles perfectly and fails silently forever; the registry
76
124
  * imports the real accessors so the compiler can see a rename.
77
125
  */
78
- function commandExistsInInspection(inspection, id) {
79
- return referenceIdExists(inspection, 'command', id);
80
- }
81
- function templateExists(inspection, id) {
82
- return referenceIdExists(inspection, 'template', id);
83
- }
84
- function playbookExists(inspection, id) {
85
- return referenceIdExists(inspection, 'playbook', id);
126
+ /**
127
+ * A `command` reference (or anchor) through THE injected command resolver.
128
+ *
129
+ * Without a resolver (outside the CLI) the command was NOT checked: that is
130
+ * `Unknown` with a loud NOT VERIFIED message — never `Ok`. It used to be `Ok`
131
+ * for every `shrk …` string, which certified three dead commands in shrk's own
132
+ * knowledge as "Command available".
133
+ *
134
+ * Read as a command REFERENCE (`resolveShrkCommandReference`): a bare
135
+ * `frobnicate` is `shrk frobnicate` — stale — exactly as the self-config
136
+ * doctor and the agent-test runner read it, never `not-shrk` → `ok`.
137
+ */
138
+ function checkCommandString(inspection, id, label) {
139
+ const resolution = resolveShrkCommandReference(inspection, id);
140
+ switch (resolution.status) {
141
+ case CommandResolutionStatus.Unverified:
142
+ return {
143
+ outcome: ReferenceCheckOutcome.Unknown,
144
+ message: `${label} ${id}: ${COMMAND_INDEX_NOT_INJECTED}`,
145
+ suggestion: 'Run the check through the CLI (`shrk knowledge stale-check`), which injects the live command index.',
146
+ };
147
+ case CommandResolutionStatus.Ok:
148
+ case CommandResolutionStatus.PrefixOnly:
149
+ case CommandResolutionStatus.NotShrk:
150
+ return {
151
+ outcome: ReferenceCheckOutcome.Ok,
152
+ message: resolution.status === CommandResolutionStatus.Ok
153
+ ? `Command available: ${id}`
154
+ : `Command ${id}: ${resolution.status}${resolution.reason ? ` (${resolution.reason})` : ''}`,
155
+ };
156
+ default: {
157
+ const closest = resolution.closest ?? [];
158
+ return {
159
+ outcome: ReferenceCheckOutcome.Stale,
160
+ message: `Command not registered: ${id} (${resolution.status}${resolution.reason ? ` — ${resolution.reason}` : ''})`,
161
+ suggestion: closest.length > 0
162
+ ? `Did you mean \`${closest[0]}\`? Update the reference (or run \`shrk surface list\` for every real command).`
163
+ : 'Update the reference — run `shrk surface list` for every real command.',
164
+ };
165
+ }
166
+ }
86
167
  }
87
- function constructExists(inspection, id) {
88
- return referenceIdExists(inspection, 'construct', id);
168
+ /**
169
+ * Why an id could not be CHECKED against a cache-backed registry, or undefined
170
+ * when the registry is loaded and non-empty.
171
+ *
172
+ * A negative answer from an unloaded (or empty) registry proves nothing: every
173
+ * id checked against it would be reported stale, including correct ones — the
174
+ * fastest way to get a check switched off. So it reads as NOT VERIFIED
175
+ * (`unknown`), the same safety net the doc-reference linter uses. A POSITIVE
176
+ * answer is always trusted.
177
+ */
178
+ function unloadedRegistryReason(inspection, kind) {
179
+ if (!isCacheBackedKind(kind))
180
+ return undefined;
181
+ if (!isReferenceCacheWarm(inspection)) {
182
+ return `the ${kind} registry was not loaded — call warmReferenceRegistries() before building the report (NOT VERIFIED)`;
183
+ }
184
+ if (referenceIdsFor(inspection, kind).length === 0) {
185
+ return `the ${kind} registry is empty — it failed to load, or this repo declares no ${kind}s (NOT VERIFIED)`;
186
+ }
187
+ return undefined;
89
188
  }
90
- function helperExists(id) {
91
- return HELPERS.some((h) => h.id === id);
189
+ /**
190
+ * An id-keyed reference, through THE shared resolver. The helper kind reads
191
+ * built-ins AND pack helpers there; a private built-in-only list used to answer
192
+ * here, calling every pack helper stale.
193
+ */
194
+ function checkRegisteredId(inspection, kind, id, label) {
195
+ if (referenceIdExists(inspection, kind, id)) {
196
+ return { outcome: ReferenceCheckOutcome.Ok, message: `${label} exists: ${id}` };
197
+ }
198
+ const unloaded = unloadedRegistryReason(inspection, kind);
199
+ if (unloaded) {
200
+ return {
201
+ outcome: ReferenceCheckOutcome.Unknown,
202
+ failure: ReferenceFailure.Unverifiable,
203
+ message: `${label} ${id}: ${unloaded}`,
204
+ suggestion: 'Run through the CLI (`shrk knowledge stale-check`), which loads every registry first.',
205
+ };
206
+ }
207
+ return staleId(kind, id);
92
208
  }
93
- function policyExists(inspection, id) {
94
- return referenceIdExists(inspection, 'policy', id);
209
+ /** Workspace package name → its directory (relative), per inspection. */
210
+ const WORKSPACE_PACKAGES = new WeakMap();
211
+ function readPackageJson(abs) {
212
+ try {
213
+ return JSON.parse(readFileSync(abs, 'utf8'));
214
+ }
215
+ catch {
216
+ return null;
217
+ }
95
218
  }
96
- function boundaryRuleExists(inspection, id) {
97
- return referenceIdExists(inspection, 'boundary-rule', id);
219
+ /** Directories (relative) matching one `workspaces` glob. */
220
+ function expandWorkspaceGlob(projectRoot, pattern) {
221
+ const norm = pattern.replace(/^\.\//, '').replace(/\/+$/, '');
222
+ const segs = norm.split('/');
223
+ const firstGlob = segs.findIndex((s) => /[*?[{]/.test(s));
224
+ if (firstGlob === -1)
225
+ return [norm];
226
+ const maxDepth = segs.includes('**') ? 4 : segs.length - firstGlob;
227
+ const out = [];
228
+ const walk = (rel, depth) => {
229
+ if (depth > maxDepth)
230
+ return;
231
+ let names;
232
+ try {
233
+ names = readdirSync(nodePath.join(projectRoot, rel));
234
+ }
235
+ catch {
236
+ return;
237
+ }
238
+ for (const n of names) {
239
+ if (n === 'node_modules' || n.startsWith('.'))
240
+ continue;
241
+ const child = rel ? `${rel}/${n}` : n;
242
+ if (!dirExists(projectRoot, child))
243
+ continue;
244
+ // One `workspaces` pattern against one directory — not a gate-plane list.
245
+ if (globToRegex(norm).test(child))
246
+ out.push(child);
247
+ walk(child, depth + 1);
248
+ }
249
+ };
250
+ walk(segs.slice(0, firstGlob).join('/'), 1);
251
+ return out;
98
252
  }
99
- function pathConventionExists(inspection, id) {
100
- return referenceIdExists(inspection, 'path-convention', id);
253
+ /**
254
+ * Every workspace package the root `package.json` declares — the packages its
255
+ * `workspaces` globs reach, by their `name`. This replaces a read of
256
+ * `inspection.packages`, a property the inspection never has (the phantom-cast
257
+ * bug class): it answered "not a package" for every id and fell through to a
258
+ * hard-coded `packages/<name>` layout.
259
+ */
260
+ function workspacePackages(inspection) {
261
+ const cached = WORKSPACE_PACKAGES.get(inspection);
262
+ if (cached)
263
+ return cached;
264
+ const out = new Map();
265
+ const root = readPackageJson(nodePath.join(inspection.projectRoot, 'package.json'));
266
+ if (root) {
267
+ if (typeof root.name === 'string')
268
+ out.set(root.name, '.');
269
+ const ws = root.workspaces;
270
+ const patterns = Array.isArray(ws)
271
+ ? ws
272
+ : Array.isArray(ws?.packages)
273
+ ? (ws.packages)
274
+ : [];
275
+ for (const pattern of patterns) {
276
+ if (typeof pattern !== 'string' || pattern.startsWith('!'))
277
+ continue;
278
+ for (const dir of expandWorkspaceGlob(inspection.projectRoot, pattern)) {
279
+ const pkg = readPackageJson(nodePath.join(inspection.projectRoot, dir, 'package.json'));
280
+ if (pkg && typeof pkg.name === 'string' && !out.has(pkg.name))
281
+ out.set(pkg.name, dir);
282
+ }
283
+ }
284
+ }
285
+ WORKSPACE_PACKAGES.set(inspection, out);
286
+ return out;
101
287
  }
102
- function packageExists(inspection, id) {
103
- // Project packages live under `packages/<name>` for SharkCraft + many
104
- // monorepos. Trust the inspection's package map if available, else
105
- // fall back to a filesystem check.
106
- const pkgs = inspection.packages;
107
- if (Array.isArray(pkgs)) {
108
- if (pkgs.some((p) => p.name === id))
109
- return true;
288
+ function checkPackageReference(inspection, id, origin) {
289
+ const dir = workspacePackages(inspection).get(id);
290
+ if (dir) {
291
+ return { outcome: ReferenceCheckOutcome.Ok, message: `Package exists: ${id} (${dir})` };
292
+ }
293
+ // A pack's entry naming the pack that ships it (round 15 follow-up): the
294
+ // consumer's root package.json names neither its installed packs nor their
295
+ // names, but the contributing pack is installed by definition.
296
+ if (origin && origin.packageName === id) {
297
+ return {
298
+ outcome: ReferenceCheckOutcome.Ok,
299
+ message: `Package exists: ${id} (the contributing pack, installed at ${origin.displayRoot})`,
300
+ };
110
301
  }
111
- // file lookup as a backstop
112
- const rel = id.startsWith('@') ? id.split('/')[1] ?? '' : id;
113
- return dirExists(inspection.projectRoot, `packages/${rel}`);
302
+ // Last-resort backstop for repos without a `workspaces` field — reported as
303
+ // probable, because a directory name is not a package name.
304
+ const rel = id.startsWith('@') ? (id.split('/')[1] ?? '') : id;
305
+ if (rel && dirExists(inspection.projectRoot, `packages/${rel}`)) {
306
+ return {
307
+ outcome: ReferenceCheckOutcome.Ok,
308
+ message: `Package exists (probable): packages/${rel}/ is present, but no workspace package.json declares the name ${id}.`,
309
+ };
310
+ }
311
+ return staleId('package', id);
114
312
  }
115
313
  function normalizeRel(p) {
116
314
  return p.split(/[\\/]/).join('/').replace(/^\.\//, '');
117
315
  }
316
+ /** `Foo` for `Foo.bar` / `Foo.prototype.bar`; null for a bare name. */
317
+ function qualifiedOwner(sym) {
318
+ const cleaned = sym.replace(/\.prototype\./g, '.');
319
+ const dot = cleaned.indexOf('.');
320
+ return dot > 0 && dot < cleaned.length - 1 ? cleaned.slice(0, dot) : null;
321
+ }
118
322
  function checkSymbolReference(projectRoot, ref, graph) {
119
323
  const sym = ref.symbol ?? '';
120
324
  if (!sym) {
121
325
  return {
122
- outcome: ReferenceCheckOutcome.Unknown,
326
+ outcome: ReferenceCheckOutcome.Invalid,
123
327
  confidence: SymbolConfidence.Unknown,
124
- message: 'Symbol reference has no `symbol` field.',
328
+ failure: ReferenceFailure.Malformed,
329
+ message: 'malformed reference: symbol reference missing required field `symbol`.',
125
330
  };
126
331
  }
332
+ // `Owner.member`: the graph indexes top-level symbols only, so it answers for
333
+ // the OWNER (moved-detection); the member itself is verified by the AST on
334
+ // the owner's declaring file.
335
+ const owner = qualifiedOwner(sym);
336
+ const lookup = owner ?? sym;
127
337
  // Graph-resolved, cross-file path (preferred when a graph is supplied).
128
338
  // A single-file AST scan cannot tell a *moved* symbol from a deleted one;
129
339
  // the graph's global symbol index can, so consult it first.
130
340
  if (graph) {
131
341
  const decl = graph
132
- .findSymbol(sym)
342
+ .findSymbol(lookup)
133
343
  .map((n) => n.path)
134
344
  .filter((p) => typeof p === 'string' && p.length > 0)
135
345
  .map(normalizeRel);
136
346
  if (decl.length > 0) {
137
347
  if (ref.path) {
138
348
  const target = normalizeRel(ref.path);
139
- if (decl.includes(target)) {
349
+ if (!decl.includes(target)) {
350
+ // Symbol exists but no longer at the pinned file — it moved.
351
+ return {
352
+ outcome: ReferenceCheckOutcome.Stale,
353
+ confidence: SymbolConfidence.Missing,
354
+ failure: ReferenceFailure.AnchorMissing,
355
+ message: `Symbol \`${lookup}\` is no longer in ${ref.path}; the graph resolves it to ${decl
356
+ .slice(0, 3)
357
+ .join(', ')}.`,
358
+ };
359
+ }
360
+ if (!owner && !hasContentAssertion(ref)) {
140
361
  return {
141
362
  outcome: ReferenceCheckOutcome.Ok,
142
363
  confidence: SymbolConfidence.Exact,
143
364
  message: `\`${sym}\` resolves to ${ref.path} (graph).`,
144
365
  };
145
366
  }
146
- // Symbol exists but no longer at the pinned file — it moved.
367
+ // The owner is at the pinned file; the member (and any content span)
368
+ // is verified by the AST below.
369
+ }
370
+ else if (!owner) {
371
+ // No file pin, but the graph resolved it cross-file.
147
372
  return {
148
- outcome: ReferenceCheckOutcome.Stale,
149
- confidence: SymbolConfidence.Missing,
150
- message: `Symbol \`${sym}\` is no longer in ${ref.path}; the graph resolves it to ${decl
151
- .slice(0, 3)
152
- .join(', ')}.`,
373
+ outcome: ReferenceCheckOutcome.Ok,
374
+ confidence: SymbolConfidence.Probable,
375
+ message: `\`${sym}\` resolves via the code graph to ${decl.slice(0, 3).join(', ')}.`,
376
+ };
377
+ }
378
+ else if (decl.length === 1) {
379
+ return resolveSymbolAtPath(projectRoot, decl[0], sym, false);
380
+ }
381
+ else {
382
+ return {
383
+ outcome: ReferenceCheckOutcome.Unknown,
384
+ confidence: SymbolConfidence.Unknown,
385
+ failure: ReferenceFailure.Unverifiable,
386
+ message: `\`${owner}\` is declared in ${decl.length} files (${decl.slice(0, 3).join(', ')}) — pin the path to check \`${sym}\`.`,
153
387
  };
154
388
  }
155
- // No file pin, but the graph resolved it cross-file.
156
- return {
157
- outcome: ReferenceCheckOutcome.Ok,
158
- confidence: SymbolConfidence.Probable,
159
- message: `\`${sym}\` resolves via the code graph to ${decl.slice(0, 3).join(', ')}.`,
160
- };
161
389
  }
162
- // Absent from the graph. The graph may be partial (locals, unindexed
163
- // languages), so fall through to the AST/text backstop when a file is
164
- // pinned; otherwise we cannot verify.
165
- if (!ref.path) {
390
+ else if (!ref.path) {
391
+ // Absent from the graph. The graph may be partial (locals, unindexed
392
+ // languages), so fall through to the AST/text backstop when a file is
393
+ // pinned; otherwise we cannot verify.
166
394
  return {
167
395
  outcome: ReferenceCheckOutcome.Unknown,
168
396
  confidence: SymbolConfidence.Unknown,
397
+ failure: ReferenceFailure.Unverifiable,
169
398
  message: `Symbol reference \`${sym}\` has no file pin and is absent from the code graph; stale-check cannot verify.`,
170
399
  };
171
400
  }
172
401
  }
173
- const file = ref.path ? nodePath.join(projectRoot, ref.path) : null;
174
- if (file) {
175
- if (!existsSync(file)) {
176
- return {
177
- outcome: ReferenceCheckOutcome.Missing,
178
- confidence: SymbolConfidence.Missing,
179
- message: `Referenced file does not exist: ${ref.path}`,
180
- };
181
- }
182
- // AST-backed resolution (falls back to text-scan if parse fails).
183
- try {
184
- const res = resolveSymbolInFile(file, sym);
185
- switch (res.resolution) {
186
- case SymbolResolution.ExactExport:
187
- return {
188
- outcome: ReferenceCheckOutcome.Ok,
189
- confidence: SymbolConfidence.Exact,
190
- message: res.message,
191
- };
192
- case SymbolResolution.ExactLocal:
193
- case SymbolResolution.ExactReExport:
194
- return {
195
- outcome: ReferenceCheckOutcome.Ok,
196
- confidence: SymbolConfidence.Exact,
197
- message: res.message,
198
- };
199
- case SymbolResolution.ProbableText:
200
- return {
201
- outcome: ReferenceCheckOutcome.Ok,
202
- confidence: SymbolConfidence.Probable,
203
- message: res.message,
204
- };
205
- case SymbolResolution.Missing:
206
- return {
207
- outcome: ReferenceCheckOutcome.Stale,
208
- confidence: SymbolConfidence.Missing,
209
- message: res.message,
210
- };
211
- default:
212
- return {
213
- outcome: ReferenceCheckOutcome.Unknown,
214
- confidence: SymbolConfidence.Unknown,
215
- message: res.message,
216
- };
217
- }
218
- }
219
- catch {
220
- // Fallback to text scan.
221
- try {
222
- const text = readFileSync(file, 'utf8');
223
- const declRe = new RegExp(`(export\\s+(?:async\\s+)?(?:function|class|interface|enum|type|const|let|var)\\s+|class\\s+|function\\s+)${escapeRe(sym)}\\b`);
224
- if (declRe.test(text)) {
225
- return {
226
- outcome: ReferenceCheckOutcome.Ok,
227
- confidence: SymbolConfidence.Exact,
228
- message: `Found declaration of \`${sym}\` in ${ref.path}.`,
229
- };
230
- }
231
- if (text.includes(sym)) {
232
- return {
233
- outcome: ReferenceCheckOutcome.Ok,
234
- confidence: SymbolConfidence.Probable,
235
- message: `\`${sym}\` appears in ${ref.path}, but not as an exported declaration.`,
236
- };
237
- }
402
+ if (ref.path)
403
+ return resolveSymbolAtPath(projectRoot, ref.path, sym, true);
404
+ // No file pinned — best-effort confidence is `unknown`.
405
+ return {
406
+ outcome: ReferenceCheckOutcome.Unknown,
407
+ confidence: SymbolConfidence.Unknown,
408
+ failure: ReferenceFailure.Unverifiable,
409
+ message: `Symbol reference \`${sym}\` has no file pin; stale-check cannot verify (pin it as \`symbol:${sym}@<path>\`).`,
410
+ };
411
+ }
412
+ /** Resolve `sym` in one file through the AST index (text-scan backstop). */
413
+ function resolveSymbolAtPath(projectRoot, relPath, sym, pinned) {
414
+ const file = nodePath.join(projectRoot, relPath);
415
+ if (!existsSync(file)) {
416
+ return {
417
+ outcome: ReferenceCheckOutcome.Missing,
418
+ confidence: SymbolConfidence.Missing,
419
+ failure: ReferenceFailure.PathMissing,
420
+ message: `Referenced file does not exist: ${relPath}`,
421
+ };
422
+ }
423
+ const via = pinned ? '' : ` (declaring file ${relPath}, from the code graph)`;
424
+ // AST-backed resolution (falls back to text-scan if parse fails).
425
+ try {
426
+ const res = resolveSymbolInFile(file, sym);
427
+ switch (res.resolution) {
428
+ case SymbolResolution.ExactExport:
429
+ case SymbolResolution.ExactLocal:
430
+ case SymbolResolution.ExactReExport:
431
+ case SymbolResolution.ExactMember:
432
+ case SymbolResolution.ExactLocalMember:
433
+ return {
434
+ outcome: ReferenceCheckOutcome.Ok,
435
+ confidence: pinned ? SymbolConfidence.Exact : SymbolConfidence.Probable,
436
+ message: res.message + via,
437
+ ...(res.span ? { span: res.span } : {}),
438
+ };
439
+ case SymbolResolution.ProbableText:
440
+ return {
441
+ outcome: ReferenceCheckOutcome.Ok,
442
+ confidence: SymbolConfidence.Probable,
443
+ message: res.message + via,
444
+ };
445
+ case SymbolResolution.Missing:
238
446
  return {
239
447
  outcome: ReferenceCheckOutcome.Stale,
240
448
  confidence: SymbolConfidence.Missing,
241
- message: `Symbol \`${sym}\` not found in ${ref.path}.`,
449
+ failure: ReferenceFailure.AnchorMissing,
450
+ message: res.message + via,
451
+ ...(res.suggestedSymbol ? { suggestedSymbol: res.suggestedSymbol } : {}),
242
452
  };
243
- }
244
- catch {
453
+ default:
245
454
  return {
246
455
  outcome: ReferenceCheckOutcome.Unknown,
247
456
  confidence: SymbolConfidence.Unknown,
248
- message: `Failed to read ${ref.path}.`,
457
+ failure: ReferenceFailure.Unverifiable,
458
+ message: res.message + via,
459
+ };
460
+ }
461
+ }
462
+ catch {
463
+ // Fallback to text scan.
464
+ const token = qualifiedOwner(sym) ? sym.slice(sym.lastIndexOf('.') + 1) : sym;
465
+ try {
466
+ const text = readFileSync(file, 'utf8');
467
+ const declRe = new RegExp(`(export\\s+(?:async\\s+)?(?:function|class|interface|enum|type|const|let|var)\\s+|class\\s+|function\\s+)${escapeRe(token)}\\b`);
468
+ if (declRe.test(text)) {
469
+ return {
470
+ outcome: ReferenceCheckOutcome.Ok,
471
+ confidence: SymbolConfidence.Exact,
472
+ message: `Found declaration of \`${sym}\` in ${relPath}.`,
249
473
  };
250
474
  }
475
+ if (text.includes(token)) {
476
+ return {
477
+ outcome: ReferenceCheckOutcome.Ok,
478
+ confidence: SymbolConfidence.Probable,
479
+ message: `\`${sym}\` appears in ${relPath}, but not as an exported declaration.`,
480
+ };
481
+ }
482
+ return {
483
+ outcome: ReferenceCheckOutcome.Stale,
484
+ confidence: SymbolConfidence.Missing,
485
+ failure: ReferenceFailure.AnchorMissing,
486
+ message: `Symbol \`${sym}\` not found in ${relPath}.`,
487
+ };
488
+ }
489
+ catch {
490
+ return {
491
+ outcome: ReferenceCheckOutcome.Unknown,
492
+ confidence: SymbolConfidence.Unknown,
493
+ failure: ReferenceFailure.Unverifiable,
494
+ message: `Failed to read ${relPath}.`,
495
+ };
251
496
  }
252
497
  }
253
- // No file pinned — best-effort confidence is `unknown`.
254
- return {
255
- outcome: ReferenceCheckOutcome.Unknown,
256
- confidence: SymbolConfidence.Unknown,
257
- message: `Symbol reference \`${sym}\` has no file pin; stale-check cannot verify.`,
258
- };
259
498
  }
260
499
  function escapeRe(s) {
261
500
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
262
501
  }
263
- function checkReference(inspection, ref, graph) {
264
- const projectRoot = inspection.projectRoot;
502
+ /**
503
+ * Check one reference: does its target exist — and, when it declares
504
+ * `contains` / `matches` / `count`, does the target still say what the asset
505
+ * claims. Every non-ok result carries a {@link ReferenceFailure}.
506
+ */
507
+ function checkReference(inspection, ref, graph, excludeDirs = [], origin, subject) {
508
+ // THE item-shape predicate the validator applies (a string the grammar
509
+ // refused, a number, a non-string `path`): MALFORMED here — never a crash in a
510
+ // path join, never a silent row.
511
+ const shape = referenceShapeProblem(ref);
512
+ if (shape) {
513
+ return {
514
+ outcome: ReferenceCheckOutcome.Invalid,
515
+ failure: ReferenceFailure.Malformed,
516
+ message: `malformed reference: ${shape}.`,
517
+ };
518
+ }
519
+ // THE `root` predicate the validator applies (round 15 follow-up): `root:
520
+ // pack` on an asset no pack contributes is MALFORMED — never a silent
521
+ // fallback to the project root, which would read a different file.
522
+ const rootProblem = referenceRootProblem(ref, origin !== undefined);
523
+ if (rootProblem?.severity === 'error') {
524
+ return {
525
+ outcome: ReferenceCheckOutcome.Invalid,
526
+ failure: ReferenceFailure.Malformed,
527
+ message: `malformed reference: ${rootProblem.message}.`,
528
+ };
529
+ }
530
+ // A `root: pack` that has an effect (the predicate's warning means none)
531
+ // moves every path read — the target, `contains` / `matches`, a `count`
532
+ // source — into the contributing pack's directory. The consumer's code graph
533
+ // never indexes a pack's files, and its count exclusions are consumer paths.
534
+ const pack = origin !== undefined && rootProblem === undefined && isPackRooted(ref) ? origin : undefined;
535
+ const rootDir = pack ? pack.packageRoot : inspection.projectRoot;
536
+ const base = checkReferenceTarget(inspection, ref, pack ? undefined : graph, rootDir, origin);
537
+ const asserted = base.outcome === ReferenceCheckOutcome.Ok ? applyAssertions(rootDir, ref, base, pack ? [] : excludeDirs) : base;
538
+ const result = asserted.outcome === ReferenceCheckOutcome.Ok || asserted.failure !== undefined
539
+ ? asserted
540
+ : { ...asserted, failure: defaultFailure(ref.kind, asserted.outcome) };
541
+ const labelled = pack ? packRootedResult(result, pack) : result;
542
+ return withStalePathHint(labelled, ref, origin, pack !== undefined, subject);
543
+ }
544
+ /** A reference whose path resolves against its contributing pack's directory (`root: pack`). */
545
+ function isPackRooted(ref) {
546
+ return ref !== null && typeof ref === 'object' && ref.root === KnowledgeReferenceRoot.Pack;
547
+ }
548
+ /**
549
+ * Every row of a pack-rooted reference names the root it resolved against —
550
+ * the renderers (text, gate violations, markdown, MCP) all print the message.
551
+ * A missing path is fixed in the pack, never by a consumer-side rename (the
552
+ * hint is {@link stalePathHint}'s).
553
+ */
554
+ function packRootedResult(result, pack) {
555
+ return { ...result, message: `${result.message} (root: pack — ${pack.packageName} at ${pack.displayRoot})` };
556
+ }
557
+ /**
558
+ * A missing path's hint, from THE authority ({@link stalePathHint}, round 15
559
+ * closing A3): who declared the reference decides who fixes it — a pack (the
560
+ * round-15 repro: a pack doc referencing its own file read STALE, with a hint
561
+ * to rename a file the consumer does not own), a Markdown entry's frontmatter,
562
+ * or a rename. No other code path sets a missing path's `suggestion`.
563
+ */
564
+ function withStalePathHint(result, ref, origin, packRooted, subject) {
565
+ if (result.failure !== ReferenceFailure.PathMissing || typeof ref.path !== 'string' || ref.path === '')
566
+ return result;
567
+ const suggestion = stalePathHint({
568
+ kind: String(ref.kind),
569
+ path: normalizeRel(ref.path),
570
+ packRooted,
571
+ ...(origin !== undefined ? { pack: origin } : {}),
572
+ ...(subject?.sourceFormat !== undefined ? { sourceFormat: subject.sourceFormat } : {}),
573
+ ...(subject?.source !== undefined ? { source: subject.source } : {}),
574
+ });
575
+ return suggestion === undefined ? result : { ...result, suggestion };
576
+ }
577
+ /**
578
+ * A reference as the changeset scope sees it: a pack-rooted path — and a
579
+ * pack-rooted `count` source's globs, the tree the count is MEASURED over —
580
+ * made project-relative. Unprefixed, a count over the pack's `src/*.ts` was
581
+ * scoped in by a change to the CONSUMER's `src/` and never by the pack's.
582
+ */
583
+ function scopedReference(ref, origin) {
584
+ if (!origin || !isPackRooted(ref) || origin.displayRoot === '.')
585
+ return ref;
586
+ const at = (rel) => `${origin.displayRoot}/${normalizeRel(rel)}`;
587
+ const globs = ref.count?.source?.files;
588
+ return {
589
+ ...ref,
590
+ ...(typeof ref.path === 'string' ? { path: at(ref.path) } : {}),
591
+ ...(ref.count && Array.isArray(globs)
592
+ ? {
593
+ count: {
594
+ ...ref.count,
595
+ source: {
596
+ ...ref.count.source,
597
+ files: globs.map((g) => (typeof g === 'string' && g.startsWith('!') ? `!${at(g.slice(1))}` : at(String(g)))),
598
+ },
599
+ },
600
+ }
601
+ : {}),
602
+ };
603
+ }
604
+ /** The failure mode of a non-ok check that did not name one. */
605
+ function defaultFailure(kind, outcome) {
606
+ if (outcome === ReferenceCheckOutcome.Unknown)
607
+ return ReferenceFailure.Unverifiable;
608
+ if (outcome === ReferenceCheckOutcome.Invalid)
609
+ return ReferenceFailure.Malformed;
610
+ if (kind === 'file' || kind === 'directory')
611
+ return ReferenceFailure.PathMissing;
612
+ if (kind === 'symbol')
613
+ return ReferenceFailure.AnchorMissing;
614
+ return ReferenceFailure.IdUnregistered;
615
+ }
616
+ /**
617
+ * Content and count assertions, over a target that EXISTS — a mismatch on a
618
+ * missing file would report one break twice.
619
+ */
620
+ function applyAssertions(projectRoot, ref, base, excludeDirs) {
621
+ const content = checkReferenceContent(projectRoot, ref, ref.kind === 'symbol' ? base.span : undefined);
622
+ if (content && !content.ok)
623
+ return assertionFailure(base, content, 'content');
624
+ const count = checkReferenceCount(projectRoot, ref, countExcludeDirsFor(ref, excludeDirs));
625
+ if (count && !count.ok)
626
+ return assertionFailure(base, count, 'count');
627
+ const held = [content?.message, count?.message].filter((m) => m !== undefined);
628
+ if (held.length === 0)
629
+ return base;
630
+ return {
631
+ ...base,
632
+ message: `${base.message} ${held.join(' ')}`,
633
+ ...(count ? { expected: count.expected, actual: count.actual } : {}),
634
+ };
635
+ }
636
+ function assertionFailure(base, a, what) {
637
+ const confidence = base.confidence ? { confidence: base.confidence } : {};
638
+ if (a.unverifiable) {
639
+ return {
640
+ outcome: ReferenceCheckOutcome.Unknown,
641
+ ...confidence,
642
+ failure: ReferenceFailure.Unverifiable,
643
+ message: `${base.message} Its ${what} assertion could not be evaluated: ${a.message}`,
644
+ };
645
+ }
646
+ return {
647
+ outcome: ReferenceCheckOutcome.Stale,
648
+ ...confidence,
649
+ failure: a.failure ?? (what === 'count' ? ReferenceFailure.CountMismatch : ReferenceFailure.ContentMismatch),
650
+ ...(a.expected !== undefined ? { expected: a.expected } : {}),
651
+ ...(a.actual !== undefined ? { actual: a.actual } : {}),
652
+ message: a.message,
653
+ suggestion: what === 'count'
654
+ ? `Update the claim in the entry, then set count.expected: ${String(a.actual)}.`
655
+ : 'Update the entry (and the assertion) to what the code says now.',
656
+ };
657
+ }
658
+ /** Does the reference's TARGET exist? (Assertions are layered on by {@link checkReference}.) */
659
+ function checkReferenceTarget(inspection, ref, graph, rootDir = inspection.projectRoot, origin) {
660
+ // The directory a path resolves against: the project root, or the
661
+ // contributing pack's directory for a `root: pack` reference.
662
+ const projectRoot = rootDir;
265
663
  switch (ref.kind) {
266
664
  case 'file': {
267
665
  if (!ref.path)
@@ -279,8 +677,9 @@ function checkReference(inspection, ref, graph) {
279
677
  }
280
678
  return {
281
679
  outcome: ReferenceCheckOutcome.Stale,
680
+ failure: ReferenceFailure.PathMissing,
282
681
  message: `Directory missing: ${ref.path}`,
283
- suggestion: 'Move the directory or update the knowledge reference.',
682
+ // The hint is {@link stalePathHint}'s — who declared it decides (A3).
284
683
  };
285
684
  }
286
685
  case 'symbol': {
@@ -291,128 +690,323 @@ function checkReference(inspection, ref, graph) {
291
690
  const id = ref.id ?? ref.command ?? '';
292
691
  if (!id)
293
692
  return missingField('command', 'id or command');
294
- if (commandExistsInInspection(inspection, id)) {
295
- return { outcome: ReferenceCheckOutcome.Ok, message: `Command available: ${id}` };
296
- }
297
- return {
298
- outcome: ReferenceCheckOutcome.Stale,
299
- message: `Command not registered: ${id}`,
300
- suggestion: 'Register the command in the command catalog or update the reference.',
301
- };
693
+ return checkCommandString(inspection, id, 'Command');
302
694
  }
303
695
  case 'template': {
304
696
  if (!ref.id)
305
697
  return missingField('template', 'id');
306
- if (templateExists(inspection, ref.id)) {
307
- return { outcome: ReferenceCheckOutcome.Ok, message: `Template exists: ${ref.id}` };
308
- }
309
- return staleId('template', ref.id);
698
+ return checkRegisteredId(inspection, 'template', ref.id, 'Template');
310
699
  }
311
700
  case 'playbook': {
312
701
  if (!ref.id)
313
702
  return missingField('playbook', 'id');
314
- if (playbookExists(inspection, ref.id)) {
315
- return { outcome: ReferenceCheckOutcome.Ok, message: `Playbook exists: ${ref.id}` };
316
- }
317
- return staleId('playbook', ref.id);
703
+ return checkRegisteredId(inspection, 'playbook', ref.id, 'Playbook');
318
704
  }
319
705
  case 'construct': {
320
706
  if (!ref.id)
321
707
  return missingField('construct', 'id');
322
- if (constructExists(inspection, ref.id)) {
323
- return { outcome: ReferenceCheckOutcome.Ok, message: `Construct exists: ${ref.id}` };
324
- }
325
- return staleId('construct', ref.id);
708
+ return checkRegisteredId(inspection, 'construct', ref.id, 'Construct');
326
709
  }
327
710
  case 'helper': {
328
711
  if (!ref.id)
329
712
  return missingField('helper', 'id');
330
- if (helperExists(ref.id)) {
331
- return { outcome: ReferenceCheckOutcome.Ok, message: `Helper exists: ${ref.id}` };
332
- }
333
- return staleId('helper', ref.id);
713
+ return checkRegisteredId(inspection, 'helper', ref.id, 'Helper');
334
714
  }
335
715
  case 'policy': {
336
716
  if (!ref.id)
337
717
  return missingField('policy', 'id');
338
- if (policyExists(inspection, ref.id)) {
339
- return { outcome: ReferenceCheckOutcome.Ok, message: `Policy exists: ${ref.id}` };
340
- }
341
- return staleId('policy', ref.id);
718
+ return checkRegisteredId(inspection, 'policy', ref.id, 'Policy');
342
719
  }
343
720
  case 'boundary-rule': {
344
721
  if (!ref.id)
345
722
  return missingField('boundary-rule', 'id');
346
- if (boundaryRuleExists(inspection, ref.id)) {
347
- return { outcome: ReferenceCheckOutcome.Ok, message: `Boundary rule exists: ${ref.id}` };
348
- }
349
- return staleId('boundary-rule', ref.id);
723
+ return checkRegisteredId(inspection, 'boundary-rule', ref.id, 'Boundary rule');
350
724
  }
351
725
  case 'path-convention': {
352
726
  if (!ref.id)
353
727
  return missingField('path-convention', 'id');
354
- if (pathConventionExists(inspection, ref.id)) {
355
- return { outcome: ReferenceCheckOutcome.Ok, message: `Path convention exists: ${ref.id}` };
356
- }
357
- return staleId('path-convention', ref.id);
728
+ return checkRegisteredId(inspection, 'path-convention', ref.id, 'Path convention');
358
729
  }
359
730
  case 'package': {
360
731
  if (!ref.id)
361
732
  return missingField('package', 'id');
362
- if (packageExists(inspection, ref.id)) {
363
- return { outcome: ReferenceCheckOutcome.Ok, message: `Package exists: ${ref.id}` };
364
- }
365
- return staleId('package', ref.id);
733
+ return checkPackageReference(inspection, ref.id, origin);
366
734
  }
367
735
  case 'url': {
368
736
  // We never fetch URLs. Mark them unknown unless we can resolve to a
369
737
  // local docs file.
370
738
  return {
371
739
  outcome: ReferenceCheckOutcome.Unknown,
740
+ failure: ReferenceFailure.Unverifiable,
372
741
  message: 'URL references are not verified (no network).',
373
742
  };
374
743
  }
744
+ default:
745
+ // A kind outside the vocabulary still loads (the validator reports it as
746
+ // an error); here it is MALFORMED — never a crash, never a silent row.
747
+ return {
748
+ outcome: ReferenceCheckOutcome.Invalid,
749
+ failure: ReferenceFailure.Malformed,
750
+ message: `malformed reference: unsupported kind "${String(ref.kind)}" — expected one of: ${KNOWLEDGE_REFERENCE_KINDS.join(', ')}.`,
751
+ };
375
752
  }
376
753
  }
377
754
  function missingField(kind, field) {
378
755
  return {
379
- outcome: ReferenceCheckOutcome.Unknown,
380
- message: `${kind} reference missing required field: ${field}`,
756
+ outcome: ReferenceCheckOutcome.Invalid,
757
+ failure: ReferenceFailure.Malformed,
758
+ message: `malformed reference: ${kind} reference missing required field \`${field}\`.`,
381
759
  };
382
760
  }
761
+ /** A missing file — its hint is {@link stalePathHint}'s, who declared the reference decides (A3). */
383
762
  function staleFile(rel) {
384
763
  return {
385
764
  outcome: ReferenceCheckOutcome.Stale,
765
+ failure: ReferenceFailure.PathMissing,
386
766
  message: `File missing: ${rel}`,
387
- suggestion: 'Restore the file or run `shrk knowledge rename-file <old> <new> --dry-run`.',
388
767
  };
389
768
  }
390
769
  function staleId(kind, id) {
391
770
  return {
392
771
  outcome: ReferenceCheckOutcome.Stale,
772
+ failure: ReferenceFailure.IdUnregistered,
393
773
  message: `${kind} not found: ${id}`,
394
774
  suggestion: 'Register the target or remove the reference.',
395
775
  };
396
776
  }
397
- function entryTouchesChangedFiles(entry, changed) {
777
+ function normalizeSlashes(p) {
778
+ return p.split(/[\\/]/).join('/');
779
+ }
780
+ /** `./src/a.ts` / `src\a.ts` / `src/svc/` → `src/a.ts` / `src/svc`. */
781
+ function normalizeScopePath(p) {
782
+ return normalizeSlashes(p).replace(/^(?:\.\/)+/, '').replace(/\/+$/, '');
783
+ }
784
+ /**
785
+ * A changed path IS the target, or lies UNDER it — a `directory` reference
786
+ * whose files changed, or a directory deleted wholesale (git lists its files,
787
+ * never the directory itself).
788
+ */
789
+ function pathTouched(target, changed) {
790
+ if (!target)
791
+ return false;
792
+ const p = normalizeScopePath(target);
793
+ if (p === '' || p === '.')
794
+ return false;
795
+ const under = `${p}/`;
796
+ return changed.some((c) => c === p || c.startsWith(under));
797
+ }
798
+ /**
799
+ * Is a subject — a knowledge entry, a boundary rule, a policy check — in a
800
+ * changeset's scope? THE one scoping rule for `--changed-only` / `--files`:
801
+ *
802
+ * - the file that DECLARES it changed (its references were added or edited —
803
+ * a typo in a new reference must be checked by the run that introduced it);
804
+ * - a changed path equals a reference / anchor path, or lies under one;
805
+ * - a changed path matches a `count` source glob (the number re-derives from
806
+ * those globs, so a change there can move it with no pinned path changing).
807
+ *
808
+ * `source` is project-relative (`relativeSource`); `(unknown source)` matches
809
+ * nothing.
810
+ */
811
+ function subjectTouchesChangedFiles(subject, changed) {
398
812
  if (changed.length === 0)
399
813
  return true;
400
- const changedSet = new Set(changed.map((c) => c.split(/[\\/]/).join('/')));
401
- for (const ref of entry.references ?? []) {
402
- if (ref.path && changedSet.has(ref.path.split(/[\\/]/).join('/')))
814
+ const changedList = changed.map(normalizeScopePath);
815
+ if (changedList.includes(normalizeScopePath(subject.source)))
816
+ return true;
817
+ for (const ref of subject.references ?? []) {
818
+ // A malformed item (a string, a number) points at no path.
819
+ if (ref === null || typeof ref !== 'object')
820
+ continue;
821
+ if (pathTouched(typeof ref.path === 'string' ? ref.path : undefined, changedList))
822
+ return true;
823
+ // The scope the count is MEASURED over (`inspectSource` selects through
824
+ // `globListSelects`), so a change to a file its `!` excludes is no touch.
825
+ const globs = ref.count?.source?.files;
826
+ if (globs && globs.length > 0 && changedList.some((c) => globListSelects(c, globs)))
403
827
  return true;
404
828
  }
405
- for (const anchor of entry.anchors ?? []) {
406
- if (anchor.path && changedSet.has(anchor.path.split(/[\\/]/).join('/')))
829
+ for (const anchor of subject.anchors ?? []) {
830
+ if (pathTouched(anchor.path, changedList))
407
831
  return true;
408
832
  }
409
833
  return false;
410
834
  }
835
+ /**
836
+ * The directories a `count` source never walks by default: the sharkcraft dir.
837
+ * Its `.ts` files hold the claims themselves — prose saying `registerService("<name>")`
838
+ * inside a count glob would count itself, and the mismatch hint would tell the
839
+ * author to bake that self-match into `expected` (policy-lint and finish prune
840
+ * the same dir for the same reason).
841
+ */
842
+ function defaultCountExcludeDirs(inspection) {
843
+ const dir = inspection.sharkcraftDir;
844
+ if (!dir)
845
+ return [];
846
+ const rel = normalizeScopePath(nodePath.relative(inspection.projectRoot, dir));
847
+ return rel && rel !== '.' && !rel.startsWith('..') && !nodePath.isAbsolute(rel) ? [rel] : [];
848
+ }
849
+ /**
850
+ * The exclusions ONE count source walks under: an exclusion is dropped when the
851
+ * source's own globs target inside it (`sharkcraft/**` counts there on purpose).
852
+ */
853
+ function countExcludeDirsFor(ref, excludeDirs) {
854
+ const globs = ref.count?.source?.files ?? [];
855
+ if (excludeDirs.length === 0 || globs.length === 0)
856
+ return excludeDirs;
857
+ const prefixes = globs.filter((g) => !g.startsWith('!')).map((g) => staticGlobPrefix(g));
858
+ return excludeDirs.filter((d) => !prefixes.some((p) => p === d || p.startsWith(`${d}/`)));
859
+ }
860
+ /** Every {@link ReferenceFailure} at 0 — the report always carries the full map. */
861
+ function emptyFailureCounts() {
862
+ const out = {};
863
+ for (const f of Object.values(ReferenceFailure))
864
+ out[f] = 0;
865
+ return out;
866
+ }
867
+ function emptyKindBucket() {
868
+ return { scanned: 0, zeroReferences: 0, referencesChecked: 0, verified: 0, stale: 0, unverifiable: 0 };
869
+ }
870
+ /**
871
+ * ok / stale / missing are real checks; unknown and invalid (malformed) proved
872
+ * nothing either way. THE predicate — the stale-check's entry buckets, its
873
+ * verdict fold (`declaredReferenceCoverage`) and the `shrk gate`
874
+ * knowledge-symbol gate all read it.
875
+ */
876
+ export function isCheckableOutcome(o) {
877
+ return o !== ReferenceCheckOutcome.Unknown && o !== ReferenceCheckOutcome.Invalid;
878
+ }
879
+ function isFailingOutcome(o) {
880
+ return o === ReferenceCheckOutcome.Stale || o === ReferenceCheckOutcome.Missing;
881
+ }
882
+ /**
883
+ * One subject's bucket from its checks: any failing check makes it stale; any
884
+ * passing one (and no failing) makes it verified; NOTHING checkable makes it
885
+ * unverifiable — never healthy.
886
+ */
887
+ function classifyChecks(checkable, failing) {
888
+ if (failing > 0)
889
+ return KnowledgeEntryVerdict.Stale;
890
+ if (checkable > 0)
891
+ return KnowledgeEntryVerdict.Verified;
892
+ return KnowledgeEntryVerdict.Unverifiable;
893
+ }
894
+ function bumpBucket(b, declared, verdict) {
895
+ b.scanned += 1;
896
+ if (declared === 0)
897
+ b.zeroReferences += 1;
898
+ b.referencesChecked += declared;
899
+ if (verdict === KnowledgeEntryVerdict.Verified)
900
+ b.verified += 1;
901
+ else if (verdict === KnowledgeEntryVerdict.Stale)
902
+ b.stale += 1;
903
+ else
904
+ b.unverifiable += 1;
905
+ }
906
+ function tallyOutcome(b, outcome) {
907
+ b.checked += 1;
908
+ if (outcome === ReferenceCheckOutcome.Ok)
909
+ b.ok += 1;
910
+ else if (outcome === ReferenceCheckOutcome.Stale)
911
+ b.stale += 1;
912
+ else if (outcome === ReferenceCheckOutcome.Missing)
913
+ b.missing += 1;
914
+ else if (outcome === ReferenceCheckOutcome.Invalid)
915
+ b.invalid += 1;
916
+ else
917
+ b.unknown += 1;
918
+ }
919
+ function relativeSource(projectRoot, origin) {
920
+ if (!origin)
921
+ return '(unknown source)';
922
+ return normalizeSlashes(nodePath.isAbsolute(origin) ? nodePath.relative(projectRoot, origin) : origin);
923
+ }
924
+ function pct1(part, whole) {
925
+ return whole > 0 ? Math.round((part / whole) * 1000) / 10 : 0;
926
+ }
927
+ /** The static directory part of a glob (`apps/legacy/**` → `apps/legacy`); `''` when it starts with a wildcard. */
928
+ function staticGlobPrefix(glob) {
929
+ const segs = normalizeSlashes(glob).replace(/^\.\//, '').split('/');
930
+ const firstGlob = segs.findIndex((s) => /[*?[{]/.test(s));
931
+ return (firstGlob === -1 ? segs : segs.slice(0, firstGlob)).join('/');
932
+ }
933
+ const BACKTICKED_IDENTIFIER = /`([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)?)(?:\(\))?`/g;
934
+ const TS_SOURCE = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
935
+ const PATH_ONLY_ADVISORY_CAP = 5;
936
+ /**
937
+ * The path-only advisory: an entry whose references are all paths, whose prose
938
+ * names (in backticks) a symbol one of its referenced files DECLARES. Renaming
939
+ * that symbol would leave the entry wrong while every reference still resolves
940
+ * — the strong form (`symbol` pinned to the path) catches it. Never gating.
941
+ */
942
+ function pathOnlyAdvisories(projectRoot, entry, refs, cache) {
943
+ // A malformed item (a string, a null, a non-string path) earns no advisory — it is an INVALID row.
944
+ if (refs.length === 0 ||
945
+ !refs.every((r) => referenceShapeProblem(r) === undefined && (r.kind === 'file' || r.kind === 'directory'))) {
946
+ return [];
947
+ }
948
+ const files = refs
949
+ // A pack-rooted path names a file in the pack, not in this tree (round 15 follow-up).
950
+ .filter((r) => r.kind === 'file' && r.path !== undefined && TS_SOURCE.test(r.path) && !isPackRooted(r))
951
+ .map((r) => r.path);
952
+ if (files.length === 0)
953
+ return [];
954
+ const names = new Set();
955
+ for (const text of [entry.summary ?? '', typeof entry.content === 'string' ? entry.content : '']) {
956
+ for (const m of text.matchAll(BACKTICKED_IDENTIFIER))
957
+ names.add(m[1]);
958
+ }
959
+ const out = [];
960
+ for (const name of names) {
961
+ if (out.length >= PATH_ONLY_ADVISORY_CAP)
962
+ break;
963
+ for (const rel of files) {
964
+ let idx = cache.get(rel);
965
+ if (idx === undefined) {
966
+ const abs = nodePath.join(projectRoot, rel);
967
+ idx = existsSync(abs) ? buildSymbolIndex(abs) : null;
968
+ cache.set(rel, idx);
969
+ }
970
+ if (!idx?.parsed)
971
+ continue;
972
+ const declared = name.includes('.')
973
+ ? (idx.members ?? []).some((m) => `${m.owner}.${m.name}` === name)
974
+ : idx.exports.some((e) => e.name === name);
975
+ if (!declared)
976
+ continue;
977
+ out.push({
978
+ code: KnowledgeAdvisoryCode.PathOnlyReference,
979
+ subjectId: entry.id,
980
+ assetKind: ReferenceAssetKind.Knowledge,
981
+ message: `entry names \`${name}\`, which ${rel} declares, but pins only the path — a rename inside the file would pass unnoticed. Add { kind: 'symbol', symbol: '${name}', path: '${rel}' }.`,
982
+ suggestion: { kind: 'symbol', symbol: name, path: rel },
983
+ });
984
+ break;
985
+ }
986
+ }
987
+ return out;
988
+ }
411
989
  export function buildKnowledgeStaleReport(inspection, options = {}) {
412
990
  const strategy = options.renameStrategy ?? RenameStrategy.Strict;
991
+ const projectRoot = inspection.projectRoot;
992
+ // Default: never walk the sharkcraft dir — the claims live there and would
993
+ // count themselves (every caller gets this; none has to remember to pass it).
994
+ const excludeDirs = options.excludeDirs ?? defaultCountExcludeDirs(inspection);
413
995
  const referenceChecks = [];
414
996
  const anchorChecks = [];
415
- const counts = { ok: 0, stale: 0, missing: 0, unknown: 0 };
997
+ const assetReferenceChecks = [];
998
+ const counts = { ok: 0, stale: 0, missing: 0, unknown: 0, invalid: 0 };
999
+ const failureCounts = emptyFailureCounts();
1000
+ const byAssetKind = {
1001
+ [ReferenceAssetKind.Knowledge]: emptyKindBucket(),
1002
+ [ReferenceAssetKind.BoundaryRule]: emptyKindBucket(),
1003
+ [ReferenceAssetKind.Policy]: emptyKindBucket(),
1004
+ };
1005
+ const byEntryType = {};
1006
+ const byReferenceKind = {};
1007
+ const entryVerdicts = [];
1008
+ const advisories = [];
1009
+ const symbolIndexCache = new Map();
416
1010
  let totalReferences = 0;
417
1011
  let totalAnchors = 0;
418
1012
  // Lazy symbol → file index, built on first stale-symbol need.
@@ -439,108 +1033,237 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
439
1033
  dirBasenameIndex = buildBasenameDirIndex(inspection.projectRoot);
440
1034
  return dirBasenameIndex;
441
1035
  };
1036
+ /**
1037
+ * Check ONE declared reference and attach its rename signal — the same path
1038
+ * for a knowledge entry, a boundary rule and a policy check, so every asset
1039
+ * kind is verified by one checker.
1040
+ */
1041
+ const checkOne = (subjectId, ref, assetKind, origin, subject) => {
1042
+ const result = checkReference(inspection, ref, options.graph, excludeDirs, origin, subject);
1043
+ const outcome = result.outcome;
1044
+ const check = {
1045
+ entryId: subjectId,
1046
+ reference: displayedReference(ref),
1047
+ outcome,
1048
+ message: result.message,
1049
+ ...(result.confidence ? { symbolConfidence: result.confidence } : {}),
1050
+ ...(result.suggestion ? { suggestion: result.suggestion } : {}),
1051
+ ...(result.failure ? { failure: result.failure } : {}),
1052
+ ...(result.expected !== undefined ? { expected: result.expected } : {}),
1053
+ ...(result.actual !== undefined ? { actual: result.actual } : {}),
1054
+ ...(assetKind ? { assetKind } : {}),
1055
+ };
1056
+ // A malformed item (a string, a number) has no kind to bucket under.
1057
+ const kindKey = ref !== null && typeof ref === 'object' && typeof ref.kind === 'string' ? ref.kind : MALFORMED_KIND_BUCKET;
1058
+ tallyOutcome((byReferenceKind[kindKey] ??= { checked: 0, ok: 0, stale: 0, missing: 0, unknown: 0, invalid: 0 }), outcome);
1059
+ if (result.failure)
1060
+ failureCounts[result.failure] += 1;
1061
+ const isStaleOrMissing = outcome === ReferenceCheckOutcome.Stale || outcome === ReferenceCheckOutcome.Missing;
1062
+ // A pack's reference is fixed in the pack (round 15 follow-up): a candidate
1063
+ // from THIS tree is never its rename — `fix --knowledge-stale` would point
1064
+ // a pack's entry at a consumer file. Its hint names `root: pack` instead.
1065
+ const findRename = isStaleOrMissing && origin === undefined;
1066
+ // A bare MEMBER name: the qualified spelling resolves in the same file, so
1067
+ // it is the replacement (`shrk fix --knowledge-stale` applies it).
1068
+ if (isStaleOrMissing && result.suggestedSymbol) {
1069
+ check.replaceWith = {
1070
+ symbol: result.suggestedSymbol,
1071
+ rationale: `\`${ref.symbol}\` is a member, not a top-level declaration — \`${result.suggestedSymbol}\` resolves in ${ref.path ?? 'the pinned file'}.`,
1072
+ strategy: RenameStrategy.Strict,
1073
+ };
1074
+ }
1075
+ // Symbol rename detection. Strict mode: emit `replaceWith.path`
1076
+ // only for the single unambiguous candidate. Wide mode also
1077
+ // emits scored candidate lists for the multi-candidate cases that
1078
+ // strict silently drops.
1079
+ if (findRename && ref.kind === 'symbol' && ref.symbol && !check.replaceWith) {
1080
+ const all = (getSymbolIndex().get(ref.symbol) ?? []).filter((p) => p !== ref.path);
1081
+ if (all.length === 1) {
1082
+ check.replaceWith = {
1083
+ path: all[0],
1084
+ rationale: `\`${ref.symbol}\` is exported from \`${all[0]}\` — sole candidate.`,
1085
+ strategy: RenameStrategy.Strict,
1086
+ };
1087
+ }
1088
+ else if (strategy === RenameStrategy.Wide && all.length > 1) {
1089
+ check.replaceWith = buildWideReplacement({
1090
+ stalePath: ref.path ?? '',
1091
+ paths: all,
1092
+ kindLabel: `symbol \`${ref.symbol}\``,
1093
+ });
1094
+ }
1095
+ }
1096
+ // File rename detection (directory move, basename match).
1097
+ if (findRename && ref.kind === 'file' && ref.path && !check.replaceWith) {
1098
+ const indexed = getFileBasenameIndex();
1099
+ const uniq = pickUniqueRenameCandidate(ref.path, indexed);
1100
+ if (uniq) {
1101
+ check.replaceWith = {
1102
+ path: uniq,
1103
+ rationale: `File basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\` (likely directory rename).`,
1104
+ strategy: RenameStrategy.Strict,
1105
+ };
1106
+ }
1107
+ else if (strategy === RenameStrategy.Wide) {
1108
+ const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
1109
+ if (all.length > 0) {
1110
+ check.replaceWith = buildWideReplacement({
1111
+ stalePath: ref.path,
1112
+ paths: all,
1113
+ kindLabel: `file \`${nodePath.basename(ref.path)}\``,
1114
+ });
1115
+ }
1116
+ }
1117
+ }
1118
+ // Directory rename detection.
1119
+ if (findRename && ref.kind === 'directory' && ref.path && !check.replaceWith) {
1120
+ const indexed = getDirBasenameIndex();
1121
+ const uniq = pickUniqueRenameCandidate(ref.path, indexed);
1122
+ if (uniq) {
1123
+ check.replaceWith = {
1124
+ path: uniq,
1125
+ rationale: `Directory basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\`.`,
1126
+ strategy: RenameStrategy.Strict,
1127
+ };
1128
+ }
1129
+ else if (strategy === RenameStrategy.Wide) {
1130
+ const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
1131
+ if (all.length > 0) {
1132
+ check.replaceWith = buildWideReplacement({
1133
+ stalePath: ref.path,
1134
+ paths: all,
1135
+ kindLabel: `directory \`${nodePath.basename(ref.path)}\``,
1136
+ });
1137
+ }
1138
+ }
1139
+ }
1140
+ return check;
1141
+ };
442
1142
  for (const entry of inspection.knowledgeEntries) {
443
- if (options.changedFiles && !entryTouchesChangedFiles(entry, options.changedFiles)) {
1143
+ // EVERY declared item is judged (`declaredReferenceItems` /
1144
+ // `declaredAnchorItems`): a non-list `references` or `anchors` is never
1145
+ // iterated (it crashed every inspection-backed verb) — each is reported
1146
+ // below as one INVALID row — and a malformed item is checked into an
1147
+ // INVALID row carrying the validator's words (`displayedReference`).
1148
+ const refs = declaredReferenceItems(entry);
1149
+ const anchorItems = declaredAnchorItems(entry);
1150
+ // The contributing pack (round 15 follow-up) — what a `root: pack` reference resolves against.
1151
+ const entrySource = inspection.entrySources?.get(entry.id);
1152
+ const packOrigin = entrySource?.type === 'pack' ? packOriginByName(inspection, entrySource.packageName) : undefined;
1153
+ if (options.changedFiles &&
1154
+ !subjectTouchesChangedFiles({
1155
+ source: relativeSource(projectRoot, entry.source?.origin),
1156
+ references: refs.map((r) => scopedReference(r, packOrigin)),
1157
+ anchors: knowledgeAnchors(entry),
1158
+ }, options.changedFiles)) {
444
1159
  continue;
445
1160
  }
446
- for (const ref of entry.references ?? []) {
1161
+ const sourceFormat = knowledgeSourceFormat(entry);
1162
+ let checkable = 0;
1163
+ let failing = 0;
1164
+ // A non-list `references` / `anchors` value (TypeScript or Markdown), or a
1165
+ // malformed anchor item: declared, never checkable — MALFORMED, the same
1166
+ // problem the validator reports. One INVALID row each.
1167
+ const malformedRow = (declaredValue, message) => {
1168
+ counts.invalid += 1;
1169
+ failureCounts[ReferenceFailure.Malformed] += 1;
1170
+ tallyOutcome((byReferenceKind[MALFORMED_KIND_BUCKET] ??= { checked: 0, ok: 0, stale: 0, missing: 0, unknown: 0, invalid: 0 }), ReferenceCheckOutcome.Invalid);
1171
+ referenceChecks.push({
1172
+ entryId: entry.id,
1173
+ // As declared: a string renders as written, a map as `kind:value`.
1174
+ reference: displayedReference(declaredValue),
1175
+ outcome: ReferenceCheckOutcome.Invalid,
1176
+ failure: ReferenceFailure.Malformed,
1177
+ message,
1178
+ });
1179
+ };
1180
+ const listProblem = referencesListProblem(entry.references, sourceFormat);
1181
+ if (listProblem) {
447
1182
  totalReferences += 1;
448
- const result = checkReference(inspection, ref, options.graph);
449
- const outcome = result.outcome;
450
- if (outcome === ReferenceCheckOutcome.Ok)
1183
+ malformedRow(entry.references, `malformed reference: ${listProblem}.`);
1184
+ }
1185
+ // An anchor row names WHICH anchor (`anchors`, `anchor #N` — the doctor's
1186
+ // spelling): rendered through the reference grammar, an anchor object read
1187
+ // `undefined:<id>`.
1188
+ const anchorsProblem = anchorsListProblem(entry.anchors);
1189
+ if (anchorsProblem) {
1190
+ totalAnchors += 1;
1191
+ malformedRow('anchors', `malformed anchor: ${anchorsProblem}.`);
1192
+ }
1193
+ // Who fixes a missing path (A3): a Markdown entry names its `references:` frontmatter.
1194
+ const hintSubject = { sourceFormat, source: relativeSource(projectRoot, entry.source?.origin) };
1195
+ for (const ref of refs) {
1196
+ totalReferences += 1;
1197
+ const check = checkOne(entry.id, ref, undefined, packOrigin, hintSubject);
1198
+ if (check.outcome === ReferenceCheckOutcome.Ok)
451
1199
  counts.ok += 1;
452
- else if (outcome === ReferenceCheckOutcome.Stale)
1200
+ else if (check.outcome === ReferenceCheckOutcome.Stale)
453
1201
  counts.stale += 1;
454
- else if (outcome === ReferenceCheckOutcome.Missing)
1202
+ else if (check.outcome === ReferenceCheckOutcome.Missing)
455
1203
  counts.missing += 1;
1204
+ else if (check.outcome === ReferenceCheckOutcome.Invalid)
1205
+ counts.invalid += 1;
456
1206
  else
457
1207
  counts.unknown += 1;
458
- const check = {
459
- entryId: entry.id,
460
- reference: ref,
461
- outcome,
462
- message: result.message,
463
- ...(result.confidence ? { symbolConfidence: result.confidence } : {}),
464
- ...(result.suggestion ? { suggestion: result.suggestion } : {}),
465
- };
466
- const isStaleOrMissing = outcome === ReferenceCheckOutcome.Stale || outcome === ReferenceCheckOutcome.Missing;
467
- // Symbol rename detection. Strict mode: emit `replaceWith.path`
468
- // only for the single unambiguous candidate. Wide mode also
469
- // emits scored candidate lists for the multi-candidate cases that
470
- // strict silently drops.
471
- if (isStaleOrMissing && ref.kind === 'symbol' && ref.symbol) {
472
- const all = (getSymbolIndex().get(ref.symbol) ?? []).filter((p) => p !== ref.path);
473
- if (all.length === 1) {
474
- check.replaceWith = {
475
- path: all[0],
476
- rationale: `\`${ref.symbol}\` is exported from \`${all[0]}\` — sole candidate.`,
477
- strategy: RenameStrategy.Strict,
478
- };
479
- }
480
- else if (strategy === RenameStrategy.Wide && all.length > 1) {
481
- check.replaceWith = buildWideReplacement({
482
- stalePath: ref.path ?? '',
483
- paths: all,
484
- kindLabel: `symbol \`${ref.symbol}\``,
485
- });
486
- }
487
- }
488
- // File rename detection (directory move, basename match).
489
- if (isStaleOrMissing && ref.kind === 'file' && ref.path && !check.replaceWith) {
490
- const indexed = getFileBasenameIndex();
491
- const uniq = pickUniqueRenameCandidate(ref.path, indexed);
492
- if (uniq) {
493
- check.replaceWith = {
494
- path: uniq,
495
- rationale: `File basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\` (likely directory rename).`,
496
- strategy: RenameStrategy.Strict,
497
- };
498
- }
499
- else if (strategy === RenameStrategy.Wide) {
500
- const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
501
- if (all.length > 0) {
502
- check.replaceWith = buildWideReplacement({
503
- stalePath: ref.path,
504
- paths: all,
505
- kindLabel: `file \`${nodePath.basename(ref.path)}\``,
506
- });
507
- }
508
- }
509
- }
510
- // Directory rename detection.
511
- if (isStaleOrMissing && ref.kind === 'directory' && ref.path && !check.replaceWith) {
512
- const indexed = getDirBasenameIndex();
513
- const uniq = pickUniqueRenameCandidate(ref.path, indexed);
514
- if (uniq) {
515
- check.replaceWith = {
516
- path: uniq,
517
- rationale: `Directory basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\`.`,
518
- strategy: RenameStrategy.Strict,
519
- };
520
- }
521
- else if (strategy === RenameStrategy.Wide) {
522
- const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
523
- if (all.length > 0) {
524
- check.replaceWith = buildWideReplacement({
525
- stalePath: ref.path,
526
- paths: all,
527
- kindLabel: `directory \`${nodePath.basename(ref.path)}\``,
528
- });
529
- }
530
- }
1208
+ if (isCheckableOutcome(check.outcome)) {
1209
+ checkable += 1;
1210
+ if (isFailingOutcome(check.outcome))
1211
+ failing += 1;
531
1212
  }
532
1213
  referenceChecks.push(check);
533
1214
  }
534
- for (const anchor of entry.anchors ?? []) {
1215
+ for (const [i, item] of anchorItems.entries()) {
535
1216
  totalAnchors += 1;
1217
+ // THE anchor item-shape predicate the validator applies: a `null` item
1218
+ // crashed `checkAnchor` (`anchor.kind`), a non-string path its path join.
1219
+ const shape = anchorShapeProblem(item);
1220
+ if (shape) {
1221
+ malformedRow(`anchor #${i + 1}`, `malformed anchor: ${shape}.`);
1222
+ continue;
1223
+ }
1224
+ const anchor = item;
536
1225
  const inspected = checkAnchor(inspection, anchor, options.graph);
1226
+ const failure = inspected.outcome === ReferenceCheckOutcome.Ok
1227
+ ? undefined
1228
+ : (inspected.failure ?? defaultFailure(anchor.kind, inspected.outcome));
1229
+ if (failure)
1230
+ failureCounts[failure] += 1;
1231
+ if (isCheckableOutcome(inspected.outcome)) {
1232
+ checkable += 1;
1233
+ if (isFailingOutcome(inspected.outcome))
1234
+ failing += 1;
1235
+ }
537
1236
  anchorChecks.push({
538
1237
  entryId: entry.id,
539
1238
  anchor,
540
1239
  outcome: inspected.outcome,
541
1240
  message: inspected.message,
1241
+ ...(failure ? { failure } : {}),
542
1242
  });
543
1243
  }
1244
+ const verdict = classifyChecks(checkable, failing);
1245
+ const declared = refs.length + anchorItems.length + (listProblem ? 1 : 0) + (anchorsProblem ? 1 : 0);
1246
+ const reason = verdict !== KnowledgeEntryVerdict.Unverifiable
1247
+ ? undefined
1248
+ : declared === 0
1249
+ ? KnowledgeUnverifiableReason.NoReferences
1250
+ : KnowledgeUnverifiableReason.OnlyUnverifiableReferences;
1251
+ const type = String(entry.type);
1252
+ const origin = inspection.entrySources?.get(entry.id);
1253
+ entryVerdicts.push({
1254
+ entryId: entry.id,
1255
+ verdict,
1256
+ ...(reason ? { reason } : {}),
1257
+ source: relativeSource(projectRoot, entry.source?.origin),
1258
+ sourceFormat,
1259
+ ...(origin?.type === 'pack' && origin.packageName ? { pack: origin.packageName } : {}),
1260
+ type,
1261
+ checkable,
1262
+ failing,
1263
+ });
1264
+ bumpBucket(byAssetKind[ReferenceAssetKind.Knowledge], declared, verdict);
1265
+ bumpBucket((byEntryType[type] ??= emptyKindBucket()), declared, verdict);
1266
+ advisories.push(...pathOnlyAdvisories(projectRoot, entry, refs, symbolIndexCache));
544
1267
  }
545
1268
  // Content-similarity boost: when multiple wide replacements in the
546
1269
  // SAME entry name the same candidate path, raise that candidate's score
@@ -548,6 +1271,143 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
548
1271
  // followed" case where a single per-reference signal is weak but the
549
1272
  // aggregate is strong.
550
1273
  applyEntryCorroborationBoost(referenceChecks);
1274
+ // Boundary rules and policy checks declare references too — swept by the
1275
+ // same checker, counted per kind, and NOT folded into the knowledge buckets
1276
+ // (their coverage is reported, not gated by default).
1277
+ const includeAssets = options.includeAssets !== false;
1278
+ const boundaryRules = includeAssets ? (inspection.boundaryRegistry?.list() ?? []) : [];
1279
+ const policyDecls = includeAssets ? listPolicyDeclarations(inspection) : [];
1280
+ const subjects = [
1281
+ ...boundaryRules.map((rule) => ({
1282
+ assetKind: ReferenceAssetKind.BoundaryRule,
1283
+ id: rule.id,
1284
+ source: relativeSource(projectRoot, inspection.boundarySources?.get(rule.id)?.file),
1285
+ references: rule.references ?? [],
1286
+ })),
1287
+ ...policyDecls.map((p) => ({
1288
+ assetKind: ReferenceAssetKind.Policy,
1289
+ id: p.qualifiedId,
1290
+ source: relativeSource(projectRoot, p.sourceFile),
1291
+ references: p.references ?? [],
1292
+ })),
1293
+ ];
1294
+ // The contributing pack of a boundary rule / policy check a pack declares
1295
+ // (round 15 follow-up) — the same `root: pack` resolution a knowledge entry gets.
1296
+ const subjectPackOrigin = (subject) => {
1297
+ if (subject.assetKind === ReferenceAssetKind.BoundaryRule) {
1298
+ const src = inspection.boundarySources?.get(subject.id);
1299
+ return src?.type === 'pack' ? packOriginByName(inspection, src.packageName) : undefined;
1300
+ }
1301
+ const decl = policyDecls.find((p) => p.qualifiedId === subject.id);
1302
+ return decl?.source === 'pack' ? packOriginOfFile(inspection, decl.sourceFile) : undefined;
1303
+ };
1304
+ for (const subject of subjects) {
1305
+ const packOrigin = subjectPackOrigin(subject);
1306
+ if (options.changedFiles &&
1307
+ !subjectTouchesChangedFiles({
1308
+ source: subject.source ?? '(unknown source)',
1309
+ references: subject.references.map((r) => scopedReference(r, packOrigin)),
1310
+ }, options.changedFiles)) {
1311
+ continue;
1312
+ }
1313
+ let checkable = 0;
1314
+ let failing = 0;
1315
+ for (const ref of subject.references) {
1316
+ const check = checkOne(subject.id, ref, subject.assetKind, packOrigin);
1317
+ if (isCheckableOutcome(check.outcome)) {
1318
+ checkable += 1;
1319
+ if (isFailingOutcome(check.outcome))
1320
+ failing += 1;
1321
+ }
1322
+ assetReferenceChecks.push(check);
1323
+ }
1324
+ bumpBucket(byAssetKind[subject.assetKind], subject.references.length, classifyChecks(checkable, failing));
1325
+ }
1326
+ // IMPLICIT references (boundary rules only — their scope is data): a `from`
1327
+ // glob whose static prefix does not exist governs nothing there. Advisory
1328
+ // unless `--fail-on implicit`; never counted in `failureCounts`. Policy
1329
+ // checks keep their scope inside `evaluate()`, which cannot be read without
1330
+ // running it — that gap is reported, not guessed at.
1331
+ if (!options.changedFiles) {
1332
+ for (const rule of boundaryRules) {
1333
+ for (const glob of rule.from ?? []) {
1334
+ if (glob.startsWith('!'))
1335
+ continue;
1336
+ const prefix = staticGlobPrefix(glob);
1337
+ if (!prefix || fileExists(projectRoot, prefix))
1338
+ continue;
1339
+ const message = `boundary rule ${rule.id} scopes '${glob}' but ${prefix} does not exist — the rule governs nothing there.`;
1340
+ assetReferenceChecks.push({
1341
+ entryId: rule.id,
1342
+ reference: { kind: 'directory', path: prefix, note: `implicit: from '${glob}'` },
1343
+ outcome: ReferenceCheckOutcome.Stale,
1344
+ failure: ReferenceFailure.PathMissing,
1345
+ implicit: true,
1346
+ assetKind: ReferenceAssetKind.BoundaryRule,
1347
+ message,
1348
+ suggestion: 'Fix or remove the glob (gate it with --fail-on implicit).',
1349
+ });
1350
+ advisories.push({
1351
+ code: KnowledgeAdvisoryCode.ImplicitPathMissing,
1352
+ subjectId: rule.id,
1353
+ assetKind: ReferenceAssetKind.BoundaryRule,
1354
+ message,
1355
+ });
1356
+ }
1357
+ }
1358
+ }
1359
+ const policySweep = {
1360
+ loaded: isReferenceCacheWarm(inspection),
1361
+ declared: policyDecls.length,
1362
+ withReferences: policyDecls.filter((p) => (p.references?.length ?? 0) > 0).length,
1363
+ };
1364
+ if (policySweep.declared > policySweep.withReferences) {
1365
+ advisories.push({
1366
+ code: KnowledgeAdvisoryCode.PolicyScopeUnverifiable,
1367
+ subjectId: 'policy',
1368
+ assetKind: ReferenceAssetKind.Policy,
1369
+ message: `policy: ${policySweep.declared} declared check(s), ${policySweep.withReferences} with references — the scope of the rest is unverifiable (it lives inside evaluate()).`,
1370
+ });
1371
+ }
1372
+ const verified = entryVerdicts.filter((v) => v.verdict === KnowledgeEntryVerdict.Verified).length;
1373
+ const stale = entryVerdicts.filter((v) => v.verdict === KnowledgeEntryVerdict.Stale).length;
1374
+ const unverifiableIds = entryVerdicts
1375
+ .filter((v) => v.verdict === KnowledgeEntryVerdict.Unverifiable)
1376
+ .map((v) => v.entryId);
1377
+ const entriesInScope = entryVerdicts.length;
1378
+ const coverage = {
1379
+ entriesInScope,
1380
+ verified,
1381
+ stale,
1382
+ unverifiable: unverifiableIds.length,
1383
+ unverifiablePct: pct1(unverifiableIds.length, entriesInScope),
1384
+ referencedRatio: entriesInScope > 0 ? (verified + stale) / entriesInScope : 0,
1385
+ };
1386
+ let age;
1387
+ if (options.staleAfterDays !== undefined) {
1388
+ const asOf = options.asOf ?? todayUtcIso();
1389
+ const inScope = new Set(entryVerdicts.map((v) => v.entryId));
1390
+ const aged = [];
1391
+ const neverVerified = [];
1392
+ for (const entry of inspection.knowledgeEntries) {
1393
+ if (!inScope.has(entry.id))
1394
+ continue;
1395
+ const days = entry.verifiedOn ? verifiedOnAgeDays(entry.verifiedOn, asOf) : undefined;
1396
+ if (days === undefined) {
1397
+ neverVerified.push(entry.id);
1398
+ }
1399
+ else if (days > options.staleAfterDays) {
1400
+ aged.push({
1401
+ entryId: entry.id,
1402
+ verifiedOn: entry.verifiedOn,
1403
+ ageDays: days,
1404
+ source: relativeSource(projectRoot, entry.source?.origin),
1405
+ });
1406
+ }
1407
+ }
1408
+ aged.sort((a, b) => b.ageDays - a.ageDays || a.entryId.localeCompare(b.entryId));
1409
+ age = { asOf, staleAfterDays: options.staleAfterDays, aged, neverVerified };
1410
+ }
551
1411
  return {
552
1412
  schema: KNOWLEDGE_STALE_SCHEMA,
553
1413
  entries: inspection.knowledgeEntries.length,
@@ -556,6 +1416,19 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
556
1416
  counts,
557
1417
  referenceChecks,
558
1418
  anchorChecks,
1419
+ entriesInScope,
1420
+ coverage,
1421
+ entryVerdicts,
1422
+ unverifiableIds,
1423
+ rejectedEntries: knowledgeRejectedEntries(inspection),
1424
+ failureCounts,
1425
+ byAssetKind,
1426
+ byEntryType,
1427
+ byReferenceKind,
1428
+ assetReferenceChecks,
1429
+ advisories,
1430
+ policySweep,
1431
+ ...(age ? { age } : {}),
559
1432
  };
560
1433
  }
561
1434
  /**
@@ -872,51 +1745,77 @@ function checkAnchor(inspection, anchor, graph) {
872
1745
  switch (anchor.kind) {
873
1746
  case 'file':
874
1747
  if (!anchor.path) {
875
- return { outcome: ReferenceCheckOutcome.Unknown, message: 'anchor has no path' };
1748
+ return {
1749
+ outcome: ReferenceCheckOutcome.Invalid,
1750
+ failure: ReferenceFailure.Malformed,
1751
+ message: 'malformed anchor: file anchor missing required field `path`.',
1752
+ };
876
1753
  }
877
1754
  if (fileExists(inspection.projectRoot, anchor.path)) {
878
1755
  return { outcome: ReferenceCheckOutcome.Ok, message: `anchor file exists: ${anchor.path}` };
879
1756
  }
880
- return { outcome: ReferenceCheckOutcome.Stale, message: `anchor file missing: ${anchor.path}` };
1757
+ return {
1758
+ outcome: ReferenceCheckOutcome.Stale,
1759
+ failure: ReferenceFailure.PathMissing,
1760
+ message: `anchor file missing: ${anchor.path}`,
1761
+ };
881
1762
  case 'symbol':
882
1763
  return checkSymbolAnchor(inspection, anchor, graph);
883
- case 'command':
884
- if (anchor.targetId && commandExistsInInspection(inspection, anchor.targetId)) {
885
- return { outcome: ReferenceCheckOutcome.Ok, message: `command exists: ${anchor.targetId}` };
1764
+ case 'command': {
1765
+ if (!anchor.targetId) {
1766
+ return { outcome: ReferenceCheckOutcome.Stale, message: 'command anchor unresolved: ?' };
886
1767
  }
887
- return { outcome: ReferenceCheckOutcome.Stale, message: `command anchor unresolved: ${anchor.targetId ?? '?'}` };
1768
+ const r = checkCommandString(inspection, anchor.targetId, 'command anchor');
1769
+ return { outcome: r.outcome, message: r.message };
1770
+ }
888
1771
  case 'construct':
889
- if (anchor.targetId && constructExists(inspection, anchor.targetId)) {
890
- return { outcome: ReferenceCheckOutcome.Ok, message: `construct exists: ${anchor.targetId}` };
891
- }
892
- return { outcome: ReferenceCheckOutcome.Stale, message: `construct anchor unresolved: ${anchor.targetId ?? '?'}` };
1772
+ return checkAnchorId(inspection, 'construct', anchor.targetId);
893
1773
  case 'template':
894
- if (anchor.targetId && templateExists(inspection, anchor.targetId)) {
895
- return { outcome: ReferenceCheckOutcome.Ok, message: `template exists: ${anchor.targetId}` };
896
- }
897
- return { outcome: ReferenceCheckOutcome.Stale, message: `template anchor unresolved: ${anchor.targetId ?? '?'}` };
1774
+ return checkAnchorId(inspection, 'template', anchor.targetId);
898
1775
  case 'helper':
899
- if (anchor.targetId && helperExists(anchor.targetId)) {
900
- return { outcome: ReferenceCheckOutcome.Ok, message: `helper exists: ${anchor.targetId}` };
901
- }
902
- return { outcome: ReferenceCheckOutcome.Stale, message: `helper anchor unresolved: ${anchor.targetId ?? '?'}` };
1776
+ return checkAnchorId(inspection, 'helper', anchor.targetId);
903
1777
  case 'playbook':
904
- if (anchor.targetId && playbookExists(inspection, anchor.targetId)) {
905
- return { outcome: ReferenceCheckOutcome.Ok, message: `playbook exists: ${anchor.targetId}` };
906
- }
907
- return { outcome: ReferenceCheckOutcome.Stale, message: `playbook anchor unresolved: ${anchor.targetId ?? '?'}` };
1778
+ return checkAnchorId(inspection, 'playbook', anchor.targetId);
908
1779
  case 'policy':
909
- if (anchor.targetId && policyExists(inspection, anchor.targetId)) {
910
- return { outcome: ReferenceCheckOutcome.Ok, message: `policy exists: ${anchor.targetId}` };
911
- }
912
- return { outcome: ReferenceCheckOutcome.Stale, message: `policy anchor unresolved: ${anchor.targetId ?? '?'}` };
1780
+ return checkAnchorId(inspection, 'policy', anchor.targetId);
1781
+ default:
1782
+ return {
1783
+ outcome: ReferenceCheckOutcome.Invalid,
1784
+ failure: ReferenceFailure.Malformed,
1785
+ message: `malformed anchor: unsupported anchor kind "${String(anchor.kind)}".`,
1786
+ };
913
1787
  }
914
1788
  }
1789
+ /**
1790
+ * An id-keyed anchor, through the same guarded resolver as a reference — an
1791
+ * unloaded registry is NOT VERIFIED, never "unresolved".
1792
+ */
1793
+ function checkAnchorId(inspection, kind, targetId) {
1794
+ if (!targetId) {
1795
+ return {
1796
+ outcome: ReferenceCheckOutcome.Stale,
1797
+ failure: ReferenceFailure.IdUnregistered,
1798
+ message: `${kind} anchor unresolved: ?`,
1799
+ };
1800
+ }
1801
+ const r = checkRegisteredId(inspection, kind, targetId, kind);
1802
+ if (r.outcome === ReferenceCheckOutcome.Ok) {
1803
+ return { outcome: ReferenceCheckOutcome.Ok, message: `${kind} exists: ${targetId}` };
1804
+ }
1805
+ if (r.outcome === ReferenceCheckOutcome.Unknown) {
1806
+ return { outcome: r.outcome, message: r.message, failure: ReferenceFailure.Unverifiable };
1807
+ }
1808
+ return {
1809
+ outcome: ReferenceCheckOutcome.Stale,
1810
+ failure: ReferenceFailure.IdUnregistered,
1811
+ message: `${kind} anchor unresolved: ${targetId}`,
1812
+ };
1813
+ }
915
1814
  function checkSymbolAnchor(inspection, anchor, graph) {
916
1815
  const r = checkSymbolReference(inspection.projectRoot, {
917
1816
  kind: 'symbol',
918
1817
  symbol: anchor.symbol,
919
1818
  ...(anchor.path ? { path: anchor.path } : {}),
920
1819
  }, graph);
921
- return { outcome: r.outcome, message: r.message };
1820
+ return { outcome: r.outcome, message: r.message, ...(r.failure ? { failure: r.failure } : {}) };
922
1821
  }