@shrkcrft/inspector 0.1.0-alpha.29 → 0.1.0-alpha.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (694) 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 +51 -4
  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 +40 -0
  69. package/dist/code-intelligence-doctor.d.ts.map +1 -1
  70. package/dist/code-intelligence-doctor.js +86 -12
  71. package/dist/command-recommender.d.ts +45 -2
  72. package/dist/command-recommender.d.ts.map +1 -1
  73. package/dist/command-recommender.js +41 -263
  74. package/dist/command-resolution-status.d.ts +44 -0
  75. package/dist/command-resolution-status.d.ts.map +1 -0
  76. package/dist/command-resolution-status.js +44 -0
  77. package/dist/command-safety-level.d.ts +15 -0
  78. package/dist/command-safety-level.d.ts.map +1 -0
  79. package/dist/command-safety-level.js +43 -0
  80. package/dist/command-taxonomy.js +1 -1
  81. package/dist/compliance-evidence.js +1 -1
  82. package/dist/compliance-profiles.js +1 -1
  83. package/dist/construct-registry.d.ts +40 -0
  84. package/dist/construct-registry.d.ts.map +1 -1
  85. package/dist/construct-registry.js +186 -82
  86. package/dist/context-tuning.d.ts +4 -0
  87. package/dist/context-tuning.d.ts.map +1 -1
  88. package/dist/context-tuning.js +10 -7
  89. package/dist/contract-template-registry.d.ts +10 -0
  90. package/dist/contract-template-registry.d.ts.map +1 -1
  91. package/dist/contract-template-registry.js +59 -33
  92. package/dist/contribution-kind.d.ts +52 -0
  93. package/dist/contribution-kind.d.ts.map +1 -0
  94. package/dist/contribution-kind.js +52 -0
  95. package/dist/contribution-load-failures.d.ts +106 -0
  96. package/dist/contribution-load-failures.d.ts.map +1 -0
  97. package/dist/contribution-load-failures.js +575 -0
  98. package/dist/contributions-report.d.ts +28 -0
  99. package/dist/contributions-report.d.ts.map +1 -0
  100. package/dist/contributions-report.js +242 -0
  101. package/dist/convention-registry.d.ts +22 -0
  102. package/dist/convention-registry.d.ts.map +1 -1
  103. package/dist/convention-registry.js +79 -39
  104. package/dist/coverage-report.d.ts.map +1 -1
  105. package/dist/coverage-report.js +6 -6
  106. package/dist/custom-checks.d.ts +60 -1
  107. package/dist/custom-checks.d.ts.map +1 -1
  108. package/dist/custom-checks.js +159 -6
  109. package/dist/dashboard/dashboard-data.d.ts +6 -0
  110. package/dist/dashboard/dashboard-data.d.ts.map +1 -1
  111. package/dist/dashboard/dashboard-data.js +99 -8
  112. package/dist/dashboard/dashboard-knowledge.d.ts.map +1 -1
  113. package/dist/dashboard/dashboard-knowledge.js +14 -2
  114. package/dist/decision-records.d.ts +20 -0
  115. package/dist/decision-records.d.ts.map +1 -1
  116. package/dist/decision-records.js +68 -20
  117. package/dist/declared-cross-references.d.ts +61 -0
  118. package/dist/declared-cross-references.d.ts.map +1 -0
  119. package/dist/declared-cross-references.js +550 -0
  120. package/dist/declared-gate-plane-rules.d.ts +24 -0
  121. package/dist/declared-gate-plane-rules.d.ts.map +1 -0
  122. package/dist/declared-gate-plane-rules.js +39 -0
  123. package/dist/declared-reference-coverage.d.ts +27 -0
  124. package/dist/declared-reference-coverage.d.ts.map +1 -0
  125. package/dist/declared-reference-coverage.js +50 -0
  126. package/dist/declared-xref-status.d.ts +19 -0
  127. package/dist/declared-xref-status.d.ts.map +1 -0
  128. package/dist/declared-xref-status.js +19 -0
  129. package/dist/delegate-pack-recipes.d.ts +13 -2
  130. package/dist/delegate-pack-recipes.d.ts.map +1 -1
  131. package/dist/delegate-pack-recipes.js +45 -14
  132. package/dist/demo-package.js +2 -2
  133. package/dist/demo-script.js +2 -2
  134. package/dist/demo-workflow.js +3 -3
  135. package/dist/dev-cycle.js +1 -1
  136. package/dist/dev-session.js +1 -1
  137. package/dist/doc-references.d.ts +80 -0
  138. package/dist/doc-references.d.ts.map +1 -0
  139. package/dist/doc-references.js +269 -0
  140. package/dist/doctor-result.d.ts +8 -0
  141. package/dist/doctor-result.d.ts.map +1 -1
  142. package/dist/doctor-verdict-kind.d.ts +19 -0
  143. package/dist/doctor-verdict-kind.d.ts.map +1 -0
  144. package/dist/doctor-verdict-kind.js +19 -0
  145. package/dist/doctor-verdict.d.ts +18 -0
  146. package/dist/doctor-verdict.d.ts.map +1 -0
  147. package/dist/doctor-verdict.js +28 -0
  148. package/dist/drift.d.ts +17 -0
  149. package/dist/drift.d.ts.map +1 -1
  150. package/dist/drift.js +40 -10
  151. package/dist/entrypoint-matrix.d.ts.map +1 -1
  152. package/dist/entrypoint-matrix.js +4 -1
  153. package/dist/feedback-ingestion.d.ts +28 -2
  154. package/dist/feedback-ingestion.d.ts.map +1 -1
  155. package/dist/feedback-ingestion.js +68 -19
  156. package/dist/fix-preview.d.ts.map +1 -1
  157. package/dist/fix-preview.js +9 -5
  158. package/dist/framework-extractor-outcomes.d.ts +27 -0
  159. package/dist/framework-extractor-outcomes.d.ts.map +1 -0
  160. package/dist/framework-extractor-outcomes.js +83 -0
  161. package/dist/fuzzy-impact.d.ts.map +1 -1
  162. package/dist/fuzzy-impact.js +12 -21
  163. package/dist/healing-plan.d.ts.map +1 -1
  164. package/dist/healing-plan.js +3 -4
  165. package/dist/helper-catalog.d.ts +25 -0
  166. package/dist/helper-catalog.d.ts.map +1 -0
  167. package/dist/helper-catalog.js +91 -0
  168. package/dist/helper-registry.d.ts.map +1 -1
  169. package/dist/helper-registry.js +17 -1
  170. package/dist/helper-view.d.ts +33 -0
  171. package/dist/helper-view.d.ts.map +1 -0
  172. package/dist/helper-view.js +1 -0
  173. package/dist/i-asset-doctor-flags.d.ts +16 -0
  174. package/dist/i-asset-doctor-flags.d.ts.map +1 -0
  175. package/dist/i-asset-doctor-flags.js +1 -0
  176. package/dist/i-command-resolution.d.ts +22 -0
  177. package/dist/i-command-resolution.d.ts.map +1 -0
  178. package/dist/i-command-resolution.js +1 -0
  179. package/dist/i-command-resolve-options.d.ts +24 -0
  180. package/dist/i-command-resolve-options.d.ts.map +1 -0
  181. package/dist/i-command-resolve-options.js +1 -0
  182. package/dist/i-command-safety.d.ts +13 -0
  183. package/dist/i-command-safety.d.ts.map +1 -0
  184. package/dist/i-command-safety.js +1 -0
  185. package/dist/i-contribution-accepted-entry.d.ts +18 -0
  186. package/dist/i-contribution-accepted-entry.d.ts.map +1 -0
  187. package/dist/i-contribution-accepted-entry.js +1 -0
  188. package/dist/i-contribution-entry-rejection.d.ts +21 -0
  189. package/dist/i-contribution-entry-rejection.d.ts.map +1 -0
  190. package/dist/i-contribution-entry-rejection.js +1 -0
  191. package/dist/i-contribution-file-issue.d.ts +18 -0
  192. package/dist/i-contribution-file-issue.d.ts.map +1 -0
  193. package/dist/i-contribution-file-issue.js +1 -0
  194. package/dist/i-contribution-file-report.d.ts +40 -0
  195. package/dist/i-contribution-file-report.d.ts.map +1 -0
  196. package/dist/i-contribution-file-report.js +1 -0
  197. package/dist/i-contribution-file-validation.d.ts +24 -0
  198. package/dist/i-contribution-file-validation.d.ts.map +1 -0
  199. package/dist/i-contribution-file-validation.js +1 -0
  200. package/dist/i-contributions-report.d.ts +31 -0
  201. package/dist/i-contributions-report.d.ts.map +1 -0
  202. package/dist/i-contributions-report.js +1 -0
  203. package/dist/i-declared-xref-field.d.ts +25 -0
  204. package/dist/i-declared-xref-field.d.ts.map +1 -0
  205. package/dist/i-declared-xref-field.js +1 -0
  206. package/dist/i-declared-xref-issue.d.ts +25 -0
  207. package/dist/i-declared-xref-issue.d.ts.map +1 -0
  208. package/dist/i-declared-xref-issue.js +1 -0
  209. package/dist/i-declared-xref-report.d.ts +48 -0
  210. package/dist/i-declared-xref-report.d.ts.map +1 -0
  211. package/dist/i-declared-xref-report.js +1 -0
  212. package/dist/i-declared-xref-row.d.ts +29 -0
  213. package/dist/i-declared-xref-row.d.ts.map +1 -0
  214. package/dist/i-declared-xref-row.js +1 -0
  215. package/dist/i-doctor-verdict.d.ts +10 -0
  216. package/dist/i-doctor-verdict.d.ts.map +1 -0
  217. package/dist/i-doctor-verdict.js +1 -0
  218. package/dist/i-kind-outcomes.d.ts +13 -0
  219. package/dist/i-kind-outcomes.d.ts.map +1 -0
  220. package/dist/i-kind-outcomes.js +1 -0
  221. package/dist/i-pack-doctor-verdict.d.ts +15 -0
  222. package/dist/i-pack-doctor-verdict.d.ts.map +1 -0
  223. package/dist/i-pack-doctor-verdict.js +1 -0
  224. package/dist/i-probed-id-field.d.ts +17 -0
  225. package/dist/i-probed-id-field.d.ts.map +1 -0
  226. package/dist/i-probed-id-field.js +1 -0
  227. package/dist/i-reference-kind-declaration.d.ts +38 -0
  228. package/dist/i-reference-kind-declaration.d.ts.map +1 -0
  229. package/dist/i-reference-kind-declaration.js +1 -0
  230. package/dist/i-reference-warm-options.d.ts +21 -0
  231. package/dist/i-reference-warm-options.d.ts.map +1 -0
  232. package/dist/i-reference-warm-options.js +1 -0
  233. package/dist/i-registry-outcomes.d.ts +16 -0
  234. package/dist/i-registry-outcomes.d.ts.map +1 -0
  235. package/dist/i-registry-outcomes.js +1 -0
  236. package/dist/i-scaffold-pattern-doctor-report.d.ts +35 -0
  237. package/dist/i-scaffold-pattern-doctor-report.d.ts.map +1 -0
  238. package/dist/i-scaffold-pattern-doctor-report.js +1 -0
  239. package/dist/i-search-tuning-key-probe.d.ts +30 -0
  240. package/dist/i-search-tuning-key-probe.d.ts.map +1 -0
  241. package/dist/i-search-tuning-key-probe.js +1 -0
  242. package/dist/i-search-tuning-lint-issue.d.ts +61 -0
  243. package/dist/i-search-tuning-lint-issue.d.ts.map +1 -0
  244. package/dist/i-search-tuning-lint-issue.js +1 -0
  245. package/dist/i-search-tuning-lint-report.d.ts +38 -0
  246. package/dist/i-search-tuning-lint-report.d.ts.map +1 -0
  247. package/dist/i-search-tuning-lint-report.js +1 -0
  248. package/dist/i-unresolvable-reference-scan.d.ts +14 -0
  249. package/dist/i-unresolvable-reference-scan.d.ts.map +1 -0
  250. package/dist/i-unresolvable-reference-scan.js +1 -0
  251. package/dist/i-unresolvable-reference.d.ts +29 -0
  252. package/dist/i-unresolvable-reference.d.ts.map +1 -0
  253. package/dist/i-unresolvable-reference.js +1 -0
  254. package/dist/i-workspace-profile-payload.d.ts +12 -0
  255. package/dist/i-workspace-profile-payload.d.ts.map +1 -0
  256. package/dist/i-workspace-profile-payload.js +1 -0
  257. package/dist/impact-analysis.d.ts +15 -0
  258. package/dist/impact-analysis.d.ts.map +1 -1
  259. package/dist/impact-analysis.js +58 -13
  260. package/dist/impact-render.d.ts.map +1 -1
  261. package/dist/impact-render.js +22 -0
  262. package/dist/import-hygiene.d.ts +35 -1
  263. package/dist/import-hygiene.d.ts.map +1 -1
  264. package/dist/import-hygiene.js +107 -13
  265. package/dist/index.d.ts +145 -0
  266. package/dist/index.d.ts.map +1 -1
  267. package/dist/index.js +176 -0
  268. package/dist/inspection-discovery.d.ts +45 -0
  269. package/dist/inspection-discovery.d.ts.map +1 -0
  270. package/dist/inspection-discovery.js +70 -0
  271. package/dist/knowledge-advisory-code.d.ts +17 -0
  272. package/dist/knowledge-advisory-code.d.ts.map +1 -0
  273. package/dist/knowledge-advisory-code.js +17 -0
  274. package/dist/knowledge-aged-entry.d.ts +10 -0
  275. package/dist/knowledge-aged-entry.d.ts.map +1 -0
  276. package/dist/knowledge-aged-entry.js +1 -0
  277. package/dist/knowledge-authoring.d.ts +24 -1
  278. package/dist/knowledge-authoring.d.ts.map +1 -1
  279. package/dist/knowledge-authoring.js +46 -7
  280. package/dist/knowledge-entry-verdict-record.d.ts +22 -0
  281. package/dist/knowledge-entry-verdict-record.d.ts.map +1 -0
  282. package/dist/knowledge-entry-verdict-record.js +1 -0
  283. package/dist/knowledge-entry-verdict.d.ts +17 -0
  284. package/dist/knowledge-entry-verdict.d.ts.map +1 -0
  285. package/dist/knowledge-entry-verdict.js +17 -0
  286. package/dist/knowledge-graph.d.ts +6 -0
  287. package/dist/knowledge-graph.d.ts.map +1 -1
  288. package/dist/knowledge-graph.js +18 -7
  289. package/dist/knowledge-kind-bucket.d.ts +19 -0
  290. package/dist/knowledge-kind-bucket.d.ts.map +1 -0
  291. package/dist/knowledge-kind-bucket.js +1 -0
  292. package/dist/knowledge-load-failure.d.ts +23 -0
  293. package/dist/knowledge-load-failure.d.ts.map +1 -0
  294. package/dist/knowledge-load-failure.js +1 -0
  295. package/dist/knowledge-reference-kind-bucket.d.ts +11 -0
  296. package/dist/knowledge-reference-kind-bucket.d.ts.map +1 -0
  297. package/dist/knowledge-reference-kind-bucket.js +1 -0
  298. package/dist/knowledge-stale-advisory.d.ts +14 -0
  299. package/dist/knowledge-stale-advisory.d.ts.map +1 -0
  300. package/dist/knowledge-stale-advisory.js +1 -0
  301. package/dist/knowledge-stale-coverage.d.ts +19 -0
  302. package/dist/knowledge-stale-coverage.d.ts.map +1 -0
  303. package/dist/knowledge-stale-coverage.js +1 -0
  304. package/dist/knowledge-stale-gate-flags.d.ts +33 -0
  305. package/dist/knowledge-stale-gate-flags.d.ts.map +1 -0
  306. package/dist/knowledge-stale-gate-flags.js +1 -0
  307. package/dist/knowledge-stale-gate-input.d.ts +46 -0
  308. package/dist/knowledge-stale-gate-input.d.ts.map +1 -0
  309. package/dist/knowledge-stale-gate-input.js +1 -0
  310. package/dist/knowledge-stale-gate-result.d.ts +33 -0
  311. package/dist/knowledge-stale-gate-result.d.ts.map +1 -0
  312. package/dist/knowledge-stale-gate-result.js +1 -0
  313. package/dist/knowledge-stale-gate-rule.d.ts +33 -0
  314. package/dist/knowledge-stale-gate-rule.d.ts.map +1 -0
  315. package/dist/knowledge-stale-gate-rule.js +1 -0
  316. package/dist/knowledge-stale-gate-violation.d.ts +13 -0
  317. package/dist/knowledge-stale-gate-violation.d.ts.map +1 -0
  318. package/dist/knowledge-stale-gate-violation.js +1 -0
  319. package/dist/knowledge-stale-gate.d.ts +83 -0
  320. package/dist/knowledge-stale-gate.d.ts.map +1 -0
  321. package/dist/knowledge-stale-gate.js +429 -0
  322. package/dist/knowledge-stale-quality-gate.d.ts +26 -0
  323. package/dist/knowledge-stale-quality-gate.d.ts.map +1 -0
  324. package/dist/knowledge-stale-quality-gate.js +110 -0
  325. package/dist/knowledge-stale.d.ts +122 -9
  326. package/dist/knowledge-stale.d.ts.map +1 -1
  327. package/dist/knowledge-stale.js +1002 -316
  328. package/dist/knowledge-unverifiable-reason.d.ts +12 -0
  329. package/dist/knowledge-unverifiable-reason.d.ts.map +1 -0
  330. package/dist/knowledge-unverifiable-reason.js +12 -0
  331. package/dist/loader-diagnostics.d.ts +14 -2
  332. package/dist/loader-diagnostics.d.ts.map +1 -1
  333. package/dist/match-terms.d.ts +48 -0
  334. package/dist/match-terms.d.ts.map +1 -0
  335. package/dist/match-terms.js +155 -0
  336. package/dist/migration-profile-registry.d.ts +10 -0
  337. package/dist/migration-profile-registry.d.ts.map +1 -1
  338. package/dist/migration-profile-registry.js +57 -34
  339. package/dist/migration-readiness.d.ts.map +1 -1
  340. package/dist/migration-readiness.js +1 -3
  341. package/dist/nearest-id.d.ts +14 -0
  342. package/dist/nearest-id.d.ts.map +1 -0
  343. package/dist/nearest-id.js +12 -0
  344. package/dist/pack-asset-freshness.d.ts +65 -0
  345. package/dist/pack-asset-freshness.d.ts.map +1 -0
  346. package/dist/pack-asset-freshness.js +340 -0
  347. package/dist/pack-author-ux.d.ts +24 -3
  348. package/dist/pack-author-ux.d.ts.map +1 -1
  349. package/dist/pack-author-ux.js +87 -93
  350. package/dist/pack-contributions-inventory.d.ts +79 -39
  351. package/dist/pack-contributions-inventory.d.ts.map +1 -1
  352. package/dist/pack-contributions-inventory.js +443 -286
  353. package/dist/pack-doctor-verdict.d.ts +29 -0
  354. package/dist/pack-doctor-verdict.d.ts.map +1 -0
  355. package/dist/pack-doctor-verdict.js +45 -0
  356. package/dist/pack-doctor.d.ts +74 -1
  357. package/dist/pack-doctor.d.ts.map +1 -1
  358. package/dist/pack-doctor.js +281 -1
  359. package/dist/pack-helper-plan.d.ts +24 -0
  360. package/dist/pack-helper-plan.d.ts.map +1 -0
  361. package/dist/pack-helper-plan.js +77 -0
  362. package/dist/pack-helper-registry.d.ts +14 -0
  363. package/dist/pack-helper-registry.d.ts.map +1 -1
  364. package/dist/pack-helper-registry.js +76 -37
  365. package/dist/pack-manifest-reader.d.ts +8 -0
  366. package/dist/pack-manifest-reader.d.ts.map +1 -0
  367. package/dist/pack-manifest-reader.js +79 -0
  368. package/dist/pack-release-check.d.ts +14 -1
  369. package/dist/pack-release-check.d.ts.map +1 -1
  370. package/dist/pack-release-check.js +23 -1
  371. package/dist/pack-signature-status.d.ts +18 -6
  372. package/dist/pack-signature-status.d.ts.map +1 -1
  373. package/dist/pack-signature-status.js +59 -105
  374. package/dist/pack-test-runner.d.ts +27 -5
  375. package/dist/pack-test-runner.d.ts.map +1 -1
  376. package/dist/pack-test-runner.js +91 -35
  377. package/dist/pack-typecheck.d.ts +8 -0
  378. package/dist/pack-typecheck.d.ts.map +1 -0
  379. package/dist/pack-typecheck.js +76 -0
  380. package/dist/plan-review.d.ts +2 -0
  381. package/dist/plan-review.d.ts.map +1 -1
  382. package/dist/plan-review.js +17 -5
  383. package/dist/plan-simulation.d.ts.map +1 -1
  384. package/dist/plan-simulation.js +16 -5
  385. package/dist/playbook-load-issue.d.ts +16 -0
  386. package/dist/playbook-load-issue.d.ts.map +1 -0
  387. package/dist/playbook-load-issue.js +1 -0
  388. package/dist/playbook-registry.d.ts +32 -1
  389. package/dist/playbook-registry.d.ts.map +1 -1
  390. package/dist/playbook-registry.js +144 -55
  391. package/dist/policy-declaration.d.ts +18 -0
  392. package/dist/policy-declaration.d.ts.map +1 -0
  393. package/dist/policy-declaration.js +1 -0
  394. package/dist/policy-engine.d.ts +8 -0
  395. package/dist/policy-engine.d.ts.map +1 -1
  396. package/dist/policy-engine.js +26 -9
  397. package/dist/policy-overrides.d.ts.map +1 -1
  398. package/dist/policy-overrides.js +4 -4
  399. package/dist/policy-registry.d.ts +42 -0
  400. package/dist/policy-registry.d.ts.map +1 -0
  401. package/dist/policy-registry.js +131 -0
  402. package/dist/probed-id-fields.d.ts +21 -0
  403. package/dist/probed-id-fields.d.ts.map +1 -0
  404. package/dist/probed-id-fields.js +25 -0
  405. package/dist/probed-id-source.d.ts +11 -0
  406. package/dist/probed-id-source.d.ts.map +1 -0
  407. package/dist/probed-id-source.js +11 -0
  408. package/dist/profile-registry.d.ts +25 -2
  409. package/dist/profile-registry.d.ts.map +1 -1
  410. package/dist/profile-registry.js +59 -6
  411. package/dist/quality-gate-examination.d.ts +15 -0
  412. package/dist/quality-gate-examination.d.ts.map +1 -0
  413. package/dist/quality-gate-examination.js +15 -0
  414. package/dist/quality-gate-row-status.d.ts +19 -0
  415. package/dist/quality-gate-row-status.d.ts.map +1 -0
  416. package/dist/quality-gate-row-status.js +28 -0
  417. package/dist/quality-gate-status.d.ts +16 -0
  418. package/dist/quality-gate-status.d.ts.map +1 -0
  419. package/dist/quality-gate-status.js +16 -0
  420. package/dist/quality-html.d.ts.map +1 -1
  421. package/dist/quality-html.js +14 -3
  422. package/dist/quality-report-coverage.d.ts +26 -0
  423. package/dist/quality-report-coverage.d.ts.map +1 -0
  424. package/dist/quality-report-coverage.js +48 -0
  425. package/dist/quality-report.d.ts +28 -1
  426. package/dist/quality-report.d.ts.map +1 -1
  427. package/dist/quality-report.js +263 -35
  428. package/dist/query-intent-kind.d.ts +18 -0
  429. package/dist/query-intent-kind.d.ts.map +1 -0
  430. package/dist/query-intent-kind.js +18 -0
  431. package/dist/query-intent-result.d.ts +23 -0
  432. package/dist/query-intent-result.d.ts.map +1 -0
  433. package/dist/query-intent-result.js +1 -0
  434. package/dist/query-intent.d.ts +18 -0
  435. package/dist/query-intent.d.ts.map +1 -0
  436. package/dist/query-intent.js +336 -0
  437. package/dist/query-resolver.d.ts +18 -0
  438. package/dist/query-resolver.d.ts.map +1 -1
  439. package/dist/query-resolver.js +15 -12
  440. package/dist/ranked-recommendations.d.ts +36 -0
  441. package/dist/ranked-recommendations.d.ts.map +1 -0
  442. package/dist/ranked-recommendations.js +1 -0
  443. package/dist/ranker-explainability.d.ts.map +1 -1
  444. package/dist/ranker-explainability.js +38 -10
  445. package/dist/recommend-recipe.d.ts +17 -0
  446. package/dist/recommend-recipe.d.ts.map +1 -0
  447. package/dist/recommend-recipe.js +1 -0
  448. package/dist/recommend-recipes.d.ts +9 -0
  449. package/dist/recommend-recipes.d.ts.map +1 -0
  450. package/dist/recommend-recipes.js +106 -0
  451. package/dist/recommendation-candidate.d.ts +30 -0
  452. package/dist/recommendation-candidate.d.ts.map +1 -0
  453. package/dist/recommendation-candidate.js +1 -0
  454. package/dist/recommendation-confidence.d.ts +27 -0
  455. package/dist/recommendation-confidence.d.ts.map +1 -0
  456. package/dist/recommendation-confidence.js +1 -0
  457. package/dist/recommendation-ranking-options.d.ts +24 -0
  458. package/dist/recommendation-ranking-options.d.ts.map +1 -0
  459. package/dist/recommendation-ranking-options.js +1 -0
  460. package/dist/recommendation-ranking.d.ts +50 -0
  461. package/dist/recommendation-ranking.d.ts.map +1 -0
  462. package/dist/recommendation-ranking.js +527 -0
  463. package/dist/recommendation-source.d.ts +23 -0
  464. package/dist/recommendation-source.d.ts.map +1 -0
  465. package/dist/recommendation-source.js +23 -0
  466. package/dist/recommendation-suppression.d.ts +14 -0
  467. package/dist/recommendation-suppression.d.ts.map +1 -0
  468. package/dist/recommendation-suppression.js +14 -0
  469. package/dist/reference-asset-kind.d.ts +14 -0
  470. package/dist/reference-asset-kind.d.ts.map +1 -0
  471. package/dist/reference-asset-kind.js +14 -0
  472. package/dist/reference-content-check.d.ts +32 -0
  473. package/dist/reference-content-check.d.ts.map +1 -0
  474. package/dist/reference-content-check.js +143 -0
  475. package/dist/reference-failure.d.ts +31 -0
  476. package/dist/reference-failure.d.ts.map +1 -0
  477. package/dist/reference-failure.js +31 -0
  478. package/dist/reference-id-status.d.ts +15 -0
  479. package/dist/reference-id-status.d.ts.map +1 -0
  480. package/dist/reference-id-status.js +15 -0
  481. package/dist/reference-kind-declarations.d.ts +42 -0
  482. package/dist/reference-kind-declarations.d.ts.map +1 -0
  483. package/dist/reference-kind-declarations.js +142 -0
  484. package/dist/reference-lookup.d.ts +6 -0
  485. package/dist/reference-lookup.d.ts.map +1 -1
  486. package/dist/reference-lookup.js +13 -5
  487. package/dist/reference-registry.d.ts +210 -0
  488. package/dist/reference-registry.d.ts.map +1 -0
  489. package/dist/reference-registry.js +395 -0
  490. package/dist/reference-subject.d.ts +20 -0
  491. package/dist/reference-subject.d.ts.map +1 -0
  492. package/dist/reference-subject.js +1 -0
  493. package/dist/registration-hint-discovery-status.d.ts +25 -0
  494. package/dist/registration-hint-discovery-status.d.ts.map +1 -0
  495. package/dist/registration-hint-discovery-status.js +25 -0
  496. package/dist/registration-hint-registry.d.ts +80 -0
  497. package/dist/registration-hint-registry.d.ts.map +1 -1
  498. package/dist/registration-hint-registry.js +356 -138
  499. package/dist/registry-lifecycle.d.ts +200 -19
  500. package/dist/registry-lifecycle.d.ts.map +1 -1
  501. package/dist/registry-lifecycle.js +526 -136
  502. package/dist/release-readiness.d.ts.map +1 -1
  503. package/dist/release-readiness.js +10 -7
  504. package/dist/release-smoke.d.ts.map +1 -1
  505. package/dist/release-smoke.js +11 -7
  506. package/dist/report-site.d.ts.map +1 -1
  507. package/dist/report-site.js +25 -7
  508. package/dist/repository-intelligence.d.ts.map +1 -1
  509. package/dist/repository-intelligence.js +3 -2
  510. package/dist/repository-knowledge-model.d.ts.map +1 -1
  511. package/dist/repository-knowledge-model.js +18 -5
  512. package/dist/resolve-project-config.d.ts +64 -0
  513. package/dist/resolve-project-config.d.ts.map +1 -1
  514. package/dist/resolve-project-config.js +258 -32
  515. package/dist/reuse/compute-reuse-coverage.d.ts +29 -0
  516. package/dist/reuse/compute-reuse-coverage.d.ts.map +1 -0
  517. package/dist/reuse/compute-reuse-coverage.js +198 -0
  518. package/dist/reuse/rank-reuse-candidates.d.ts +38 -0
  519. package/dist/reuse/rank-reuse-candidates.d.ts.map +1 -0
  520. package/dist/reuse/rank-reuse-candidates.js +212 -0
  521. package/dist/reuse/rank-reuse-options.d.ts +20 -0
  522. package/dist/reuse/rank-reuse-options.d.ts.map +1 -0
  523. package/dist/reuse/rank-reuse-options.js +1 -0
  524. package/dist/reuse/rank-reuse-suggestions.d.ts +15 -0
  525. package/dist/reuse/rank-reuse-suggestions.d.ts.map +1 -0
  526. package/dist/reuse/rank-reuse-suggestions.js +31 -0
  527. package/dist/reuse/resolve-curated-reuse.d.ts +43 -0
  528. package/dist/reuse/resolve-curated-reuse.d.ts.map +1 -0
  529. package/dist/reuse/resolve-curated-reuse.js +115 -0
  530. package/dist/reuse/reuse-candidate.d.ts +29 -0
  531. package/dist/reuse/reuse-candidate.d.ts.map +1 -0
  532. package/dist/reuse/reuse-candidate.js +1 -0
  533. package/dist/reuse/reuse-coverage-report.d.ts +74 -0
  534. package/dist/reuse/reuse-coverage-report.d.ts.map +1 -0
  535. package/dist/reuse/reuse-coverage-report.js +1 -0
  536. package/dist/reuse/reuse-curated-coverage.d.ts +36 -0
  537. package/dist/reuse/reuse-curated-coverage.d.ts.map +1 -0
  538. package/dist/reuse/reuse-curated-coverage.js +1 -0
  539. package/dist/reuse/reuse-curated-resolution.d.ts +36 -0
  540. package/dist/reuse/reuse-curated-resolution.d.ts.map +1 -0
  541. package/dist/reuse/reuse-curated-resolution.js +1 -0
  542. package/dist/reuse/reuse-curated-status.d.ts +21 -0
  543. package/dist/reuse/reuse-curated-status.d.ts.map +1 -0
  544. package/dist/reuse/reuse-curated-status.js +21 -0
  545. package/dist/reuse/reuse-match-detail.d.ts +15 -0
  546. package/dist/reuse/reuse-match-detail.d.ts.map +1 -0
  547. package/dist/reuse/reuse-match-detail.js +1 -0
  548. package/dist/reuse/reuse-ranking.d.ts +37 -0
  549. package/dist/reuse/reuse-ranking.d.ts.map +1 -0
  550. package/dist/reuse/reuse-ranking.js +1 -0
  551. package/dist/reuse/reuse-suggestion.d.ts +19 -0
  552. package/dist/reuse/reuse-suggestion.d.ts.map +1 -0
  553. package/dist/reuse/reuse-suggestion.js +1 -0
  554. package/dist/reuse/reuse-symbol-lookup.d.ts +28 -0
  555. package/dist/reuse/reuse-symbol-lookup.d.ts.map +1 -0
  556. package/dist/reuse/reuse-symbol-lookup.js +1 -0
  557. package/dist/reuse/reuse-tokenize.d.ts +11 -0
  558. package/dist/reuse/reuse-tokenize.d.ts.map +1 -0
  559. package/dist/reuse/reuse-tokenize.js +26 -0
  560. package/dist/reuse/score-reuse-name.d.ts +16 -0
  561. package/dist/reuse/score-reuse-name.d.ts.map +1 -0
  562. package/dist/reuse/score-reuse-name.js +35 -0
  563. package/dist/reuse/score-reuse-primitive.d.ts +33 -0
  564. package/dist/reuse/score-reuse-primitive.d.ts.map +1 -0
  565. package/dist/reuse/score-reuse-primitive.js +92 -0
  566. package/dist/review-packet.d.ts +2 -0
  567. package/dist/review-packet.d.ts.map +1 -1
  568. package/dist/review-packet.js +13 -6
  569. package/dist/role-views.d.ts.map +1 -1
  570. package/dist/role-views.js +16 -16
  571. package/dist/routing-recommends-channels.d.ts +24 -0
  572. package/dist/routing-recommends-channels.d.ts.map +1 -0
  573. package/dist/routing-recommends-channels.js +19 -0
  574. package/dist/run-boundary-check.d.ts +118 -0
  575. package/dist/run-boundary-check.d.ts.map +1 -0
  576. package/dist/run-boundary-check.js +592 -0
  577. package/dist/safety-audit.js +1 -1
  578. package/dist/scaffold-coverage.js +1 -1
  579. package/dist/scaffold-pattern-doctor-report.d.ts +19 -0
  580. package/dist/scaffold-pattern-doctor-report.d.ts.map +1 -0
  581. package/dist/scaffold-pattern-doctor-report.js +54 -0
  582. package/dist/scaffold-patterns.d.ts +75 -1
  583. package/dist/scaffold-patterns.d.ts.map +1 -1
  584. package/dist/scaffold-patterns.js +375 -84
  585. package/dist/schema-inventory.d.ts.map +1 -1
  586. package/dist/schema-inventory.js +4 -3
  587. package/dist/search-document-id.d.ts +52 -0
  588. package/dist/search-document-id.d.ts.map +1 -0
  589. package/dist/search-document-id.js +72 -0
  590. package/dist/search-document-prefix.d.ts +7 -0
  591. package/dist/search-document-prefix.d.ts.map +1 -0
  592. package/dist/search-document-prefix.js +1 -0
  593. package/dist/search-index.d.ts.map +1 -1
  594. package/dist/search-index.js +20 -23
  595. package/dist/search-tuning-explain.d.ts +6 -0
  596. package/dist/search-tuning-explain.d.ts.map +1 -1
  597. package/dist/search-tuning-explain.js +19 -17
  598. package/dist/search-tuning-key-probes.d.ts +16 -0
  599. package/dist/search-tuning-key-probes.d.ts.map +1 -0
  600. package/dist/search-tuning-key-probes.js +65 -0
  601. package/dist/search-tuning-key-resolution.d.ts +25 -0
  602. package/dist/search-tuning-key-resolution.d.ts.map +1 -0
  603. package/dist/search-tuning-key-resolution.js +1 -0
  604. package/dist/search-tuning-key-resolver.d.ts +4 -0
  605. package/dist/search-tuning-key-resolver.d.ts.map +1 -0
  606. package/dist/search-tuning-key-resolver.js +107 -0
  607. package/dist/search-tuning-key-status.d.ts +24 -0
  608. package/dist/search-tuning-key-status.d.ts.map +1 -0
  609. package/dist/search-tuning-key-status.js +24 -0
  610. package/dist/search-tuning-lint.d.ts +9 -0
  611. package/dist/search-tuning-lint.d.ts.map +1 -0
  612. package/dist/search-tuning-lint.js +468 -0
  613. package/dist/search-tuning-registry.d.ts +43 -0
  614. package/dist/search-tuning-registry.d.ts.map +1 -1
  615. package/dist/search-tuning-registry.js +214 -49
  616. package/dist/self-audit.d.ts +12 -0
  617. package/dist/self-audit.d.ts.map +1 -1
  618. package/dist/self-audit.js +36 -10
  619. package/dist/self-config-doctor-v2.d.ts +145 -3
  620. package/dist/self-config-doctor-v2.d.ts.map +1 -1
  621. package/dist/self-config-doctor-v2.js +1329 -255
  622. package/dist/self-config-doctor.d.ts +20 -1
  623. package/dist/self-config-doctor.d.ts.map +1 -1
  624. package/dist/self-config-doctor.js +65 -309
  625. package/dist/settled-unit-states.d.ts +9 -0
  626. package/dist/settled-unit-states.d.ts.map +1 -0
  627. package/dist/settled-unit-states.js +14 -0
  628. package/dist/sharkcraft-inspector.d.ts +47 -1
  629. package/dist/sharkcraft-inspector.d.ts.map +1 -1
  630. package/dist/sharkcraft-inspector.js +278 -29
  631. package/dist/spec/spec-evidence.d.ts.map +1 -1
  632. package/dist/spec/spec-evidence.js +14 -20
  633. package/dist/split-identifier.d.ts +21 -0
  634. package/dist/split-identifier.d.ts.map +1 -0
  635. package/dist/split-identifier.js +27 -0
  636. package/dist/start-here.d.ts.map +1 -1
  637. package/dist/start-here.js +1 -2
  638. package/dist/substitute-placeholders.d.ts +8 -0
  639. package/dist/substitute-placeholders.d.ts.map +1 -0
  640. package/dist/substitute-placeholders.js +9 -0
  641. package/dist/symbol-index.d.ts +33 -0
  642. package/dist/symbol-index.d.ts.map +1 -1
  643. package/dist/symbol-index.js +241 -3
  644. package/dist/symbol-member-entry.d.ts +26 -0
  645. package/dist/symbol-member-entry.d.ts.map +1 -0
  646. package/dist/symbol-member-entry.js +1 -0
  647. package/dist/symbol-member-kind.d.ts +14 -0
  648. package/dist/symbol-member-kind.d.ts.map +1 -0
  649. package/dist/symbol-member-kind.js +14 -0
  650. package/dist/task-packet.d.ts.map +1 -1
  651. package/dist/task-packet.js +52 -9
  652. package/dist/task-ranker.d.ts +8 -0
  653. package/dist/task-ranker.d.ts.map +1 -1
  654. package/dist/task-ranker.js +25 -13
  655. package/dist/task-risk.js +1 -1
  656. package/dist/task-routing-hint-registry.d.ts +20 -1
  657. package/dist/task-routing-hint-registry.d.ts.map +1 -1
  658. package/dist/task-routing-hint-registry.js +186 -42
  659. package/dist/template-drift.d.ts.map +1 -1
  660. package/dist/template-drift.js +30 -9
  661. package/dist/template-lint.d.ts.map +1 -1
  662. package/dist/template-lint.js +160 -2
  663. package/dist/term-query.d.ts +10 -0
  664. package/dist/term-query.d.ts.map +1 -0
  665. package/dist/term-query.js +1 -0
  666. package/dist/test-definitions.d.ts +6 -3
  667. package/dist/test-definitions.d.ts.map +1 -1
  668. package/dist/test-runner.d.ts +95 -10
  669. package/dist/test-runner.d.ts.map +1 -1
  670. package/dist/test-runner.js +309 -318
  671. package/dist/tuning-query-tokens.d.ts +29 -0
  672. package/dist/tuning-query-tokens.d.ts.map +1 -0
  673. package/dist/tuning-query-tokens.js +51 -0
  674. package/dist/typecheck-files.d.ts +36 -0
  675. package/dist/typecheck-files.d.ts.map +1 -0
  676. package/dist/typecheck-files.js +158 -0
  677. package/dist/uncertainty.js +1 -1
  678. package/dist/universal-search.d.ts.map +1 -1
  679. package/dist/universal-search.js +5 -3
  680. package/dist/unregistered-exports.d.ts +19 -0
  681. package/dist/unregistered-exports.d.ts.map +1 -0
  682. package/dist/unregistered-exports.js +141 -0
  683. package/dist/unresolvable-reason.d.ts +11 -0
  684. package/dist/unresolvable-reason.d.ts.map +1 -0
  685. package/dist/unresolvable-reason.js +11 -0
  686. package/dist/upgrade-advisor.d.ts.map +1 -1
  687. package/dist/upgrade-advisor.js +16 -9
  688. package/dist/validate-contribution-file.d.ts +4 -0
  689. package/dist/validate-contribution-file.d.ts.map +1 -0
  690. package/dist/validate-contribution-file.js +228 -0
  691. package/dist/why-file.d.ts +3 -0
  692. package/dist/why-file.d.ts.map +1 -1
  693. package/dist/why-file.js +7 -3
  694. package/package.json +17 -17
@@ -1,18 +1,42 @@
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";
26
+ import { globListSelects, globToRegex } from '@shrkcrft/boundaries';
27
+ import { KNOWLEDGE_REFERENCE_KINDS, todayUtcIso, verifiedOnAgeDays, } from '@shrkcrft/knowledge';
28
+ import { isCacheBackedKind, isReferenceCacheWarm, referenceIdExists, referenceIdsFor, } from "./reference-registry.js";
29
+ import { listPolicyDeclarations } from "./policy-registry.js";
30
+ import { checkReferenceContent, checkReferenceCount, hasContentAssertion, } from "./reference-content-check.js";
31
+ import { ReferenceFailure } from "./reference-failure.js";
32
+ import { KnowledgeEntryVerdict } from "./knowledge-entry-verdict.js";
33
+ import { KnowledgeUnverifiableReason } from "./knowledge-unverifiable-reason.js";
34
+ import { KnowledgeAdvisoryCode } from "./knowledge-advisory-code.js";
35
+ import { ReferenceAssetKind } from "./reference-asset-kind.js";
36
+ import { buildSymbolIndex } from "./symbol-index.js";
37
+ // The `command` reference case (and its anchor twin) — one injected resolver.
38
+ import { COMMAND_INDEX_NOT_INJECTED, resolveShrkCommandReference } from "./reference-registry.js";
39
+ import { CommandResolutionStatus } from "./command-resolution-status.js";
16
40
  import { resolveSymbolInFile, SymbolResolution } from "./symbol-index.js";
17
41
  export const KNOWLEDGE_STALE_SCHEMA = 'sharkcraft.knowledge-stale/v1';
18
42
  export var ReferenceCheckOutcome;
@@ -20,7 +44,16 @@ export var ReferenceCheckOutcome;
20
44
  ReferenceCheckOutcome["Ok"] = "ok";
21
45
  ReferenceCheckOutcome["Stale"] = "stale";
22
46
  ReferenceCheckOutcome["Missing"] = "missing";
47
+ /** Well-formed, but the check cannot evaluate it (a `url`, an unwarmed registry). */
23
48
  ReferenceCheckOutcome["Unknown"] = "unknown";
49
+ /**
50
+ * MALFORMED — a kind outside the vocabulary, or the field its kind cannot be
51
+ * checked without is missing (a symbol reference with no `symbol`). It
52
+ * verifies nothing; it used to share `unknown` with an unfetched url and
53
+ * count toward a green run. Now it is a coverage shortfall on the verdict
54
+ * (not verified) and fails under `--fail-on invalid`.
55
+ */
56
+ ReferenceCheckOutcome["Invalid"] = "invalid";
24
57
  })(ReferenceCheckOutcome || (ReferenceCheckOutcome = {}));
25
58
  export var SymbolConfidence;
26
59
  (function (SymbolConfidence) {
@@ -64,222 +97,453 @@ function dirExists(projectRoot, rel) {
64
97
  return false;
65
98
  }
66
99
  }
67
- function commandExistsInInspection(inspection, id) {
68
- // Both `shrk` commands and pack-contributed commands live on the
69
- // inspection. We do a permissive lookup against the recommendation
70
- // catalog if available.
71
- const commands = inspection.commandCatalog;
72
- if (Array.isArray(commands)) {
73
- if (commands.some((c) => c.id === id))
74
- return true;
100
+ /*
101
+ * Existence checks delegate to the SHARED reference registry.
102
+ *
103
+ * They used to be private copies here, each reading a differently-shaped
104
+ * structural cast off the inspection (`playbookRegistry`, `constructRegistry`,
105
+ * `policyChecks`) — properties nothing ever attached, so those kinds answered
106
+ * "does not exist" for every id including correct ones. An unchecked cast to a
107
+ * hoped-for shape compiles perfectly and fails silently forever; the registry
108
+ * imports the real accessors so the compiler can see a rename.
109
+ */
110
+ /**
111
+ * A `command` reference (or anchor) through THE injected command resolver.
112
+ *
113
+ * Without a resolver (outside the CLI) the command was NOT checked: that is
114
+ * `Unknown` with a loud NOT VERIFIED message — never `Ok`. It used to be `Ok`
115
+ * for every `shrk …` string, which certified three dead commands in shrk's own
116
+ * knowledge as "Command available".
117
+ *
118
+ * Read as a command REFERENCE (`resolveShrkCommandReference`): a bare
119
+ * `frobnicate` is `shrk frobnicate` — stale — exactly as the self-config
120
+ * doctor and the agent-test runner read it, never `not-shrk` → `ok`.
121
+ */
122
+ function checkCommandString(inspection, id, label) {
123
+ const resolution = resolveShrkCommandReference(inspection, id);
124
+ switch (resolution.status) {
125
+ case CommandResolutionStatus.Unverified:
126
+ return {
127
+ outcome: ReferenceCheckOutcome.Unknown,
128
+ message: `${label} ${id}: ${COMMAND_INDEX_NOT_INJECTED}`,
129
+ suggestion: 'Run the check through the CLI (`shrk knowledge stale-check`), which injects the live command index.',
130
+ };
131
+ case CommandResolutionStatus.Ok:
132
+ case CommandResolutionStatus.PrefixOnly:
133
+ case CommandResolutionStatus.NotShrk:
134
+ return {
135
+ outcome: ReferenceCheckOutcome.Ok,
136
+ message: resolution.status === CommandResolutionStatus.Ok
137
+ ? `Command available: ${id}`
138
+ : `Command ${id}: ${resolution.status}${resolution.reason ? ` (${resolution.reason})` : ''}`,
139
+ };
140
+ default: {
141
+ const closest = resolution.closest ?? [];
142
+ return {
143
+ outcome: ReferenceCheckOutcome.Stale,
144
+ message: `Command not registered: ${id} (${resolution.status}${resolution.reason ? ` — ${resolution.reason}` : ''})`,
145
+ suggestion: closest.length > 0
146
+ ? `Did you mean \`${closest[0]}\`? Update the reference (or run \`shrk surface list\` for every real command).`
147
+ : 'Update the reference — run `shrk surface list` for every real command.',
148
+ };
149
+ }
75
150
  }
76
- // Fallback — accept anything that looks like a valid `shrk` command.
77
- return id.startsWith('shrk ') || id.startsWith('bun ');
78
- }
79
- function templateExists(inspection, id) {
80
- return inspection.templates.some((t) => t.id === id);
81
151
  }
82
- function playbookExists(inspection, id) {
83
- const inspectionAny = inspection;
84
- const reg = inspectionAny.playbookRegistry;
85
- if (reg && typeof reg.list === 'function') {
86
- return (reg.list() ?? []).some((p) => p.id === id);
152
+ /**
153
+ * Why an id could not be CHECKED against a cache-backed registry, or undefined
154
+ * when the registry is loaded and non-empty.
155
+ *
156
+ * A negative answer from an unloaded (or empty) registry proves nothing: every
157
+ * id checked against it would be reported stale, including correct ones — the
158
+ * fastest way to get a check switched off. So it reads as NOT VERIFIED
159
+ * (`unknown`), the same safety net the doc-reference linter uses. A POSITIVE
160
+ * answer is always trusted.
161
+ */
162
+ function unloadedRegistryReason(inspection, kind) {
163
+ if (!isCacheBackedKind(kind))
164
+ return undefined;
165
+ if (!isReferenceCacheWarm(inspection)) {
166
+ return `the ${kind} registry was not loaded — call warmReferenceRegistries() before building the report (NOT VERIFIED)`;
87
167
  }
88
- return false;
89
- }
90
- function constructExists(inspection, id) {
91
- const inspectionAny = inspection;
92
- const reg = inspectionAny.constructRegistry;
93
- if (reg && typeof reg.list === 'function') {
94
- return (reg.list() ?? []).some((c) => c.id === id);
168
+ if (referenceIdsFor(inspection, kind).length === 0) {
169
+ return `the ${kind} registry is empty — it failed to load, or this repo declares no ${kind}s (NOT VERIFIED)`;
95
170
  }
96
- return false;
171
+ return undefined;
97
172
  }
98
- function helperExists(id) {
99
- return HELPERS.some((h) => h.id === id);
173
+ /**
174
+ * An id-keyed reference, through THE shared resolver. The helper kind reads
175
+ * built-ins AND pack helpers there; a private built-in-only list used to answer
176
+ * here, calling every pack helper stale.
177
+ */
178
+ function checkRegisteredId(inspection, kind, id, label) {
179
+ if (referenceIdExists(inspection, kind, id)) {
180
+ return { outcome: ReferenceCheckOutcome.Ok, message: `${label} exists: ${id}` };
181
+ }
182
+ const unloaded = unloadedRegistryReason(inspection, kind);
183
+ if (unloaded) {
184
+ return {
185
+ outcome: ReferenceCheckOutcome.Unknown,
186
+ failure: ReferenceFailure.Unverifiable,
187
+ message: `${label} ${id}: ${unloaded}`,
188
+ suggestion: 'Run through the CLI (`shrk knowledge stale-check`), which loads every registry first.',
189
+ };
190
+ }
191
+ return staleId(kind, id);
100
192
  }
101
- function policyExists(inspection, id) {
102
- const inspectionAny = inspection;
103
- const checks = inspectionAny.policyChecks;
104
- if (Array.isArray(checks))
105
- return checks.some((c) => c.id === id);
106
- return false;
193
+ /** Workspace package name → its directory (relative), per inspection. */
194
+ const WORKSPACE_PACKAGES = new WeakMap();
195
+ function readPackageJson(abs) {
196
+ try {
197
+ return JSON.parse(readFileSync(abs, 'utf8'));
198
+ }
199
+ catch {
200
+ return null;
201
+ }
107
202
  }
108
- function boundaryRuleExists(inspection, id) {
109
- const reg = inspection.boundaryRegistry;
110
- if (reg && typeof reg.list === 'function')
111
- return reg.list().some((b) => b.id === id);
112
- return false;
203
+ /** Directories (relative) matching one `workspaces` glob. */
204
+ function expandWorkspaceGlob(projectRoot, pattern) {
205
+ const norm = pattern.replace(/^\.\//, '').replace(/\/+$/, '');
206
+ const segs = norm.split('/');
207
+ const firstGlob = segs.findIndex((s) => /[*?[{]/.test(s));
208
+ if (firstGlob === -1)
209
+ return [norm];
210
+ const maxDepth = segs.includes('**') ? 4 : segs.length - firstGlob;
211
+ const out = [];
212
+ const walk = (rel, depth) => {
213
+ if (depth > maxDepth)
214
+ return;
215
+ let names;
216
+ try {
217
+ names = readdirSync(nodePath.join(projectRoot, rel));
218
+ }
219
+ catch {
220
+ return;
221
+ }
222
+ for (const n of names) {
223
+ if (n === 'node_modules' || n.startsWith('.'))
224
+ continue;
225
+ const child = rel ? `${rel}/${n}` : n;
226
+ if (!dirExists(projectRoot, child))
227
+ continue;
228
+ // One `workspaces` pattern against one directory — not a gate-plane list.
229
+ if (globToRegex(norm).test(child))
230
+ out.push(child);
231
+ walk(child, depth + 1);
232
+ }
233
+ };
234
+ walk(segs.slice(0, firstGlob).join('/'), 1);
235
+ return out;
113
236
  }
114
- function pathConventionExists(inspection, id) {
115
- const svc = inspection.pathService;
116
- if (svc && typeof svc.list === 'function') {
117
- return svc.list().some((p) => p.id === id);
237
+ /**
238
+ * Every workspace package the root `package.json` declares — the packages its
239
+ * `workspaces` globs reach, by their `name`. This replaces a read of
240
+ * `inspection.packages`, a property the inspection never has (the phantom-cast
241
+ * bug class): it answered "not a package" for every id and fell through to a
242
+ * hard-coded `packages/<name>` layout.
243
+ */
244
+ function workspacePackages(inspection) {
245
+ const cached = WORKSPACE_PACKAGES.get(inspection);
246
+ if (cached)
247
+ return cached;
248
+ const out = new Map();
249
+ const root = readPackageJson(nodePath.join(inspection.projectRoot, 'package.json'));
250
+ if (root) {
251
+ if (typeof root.name === 'string')
252
+ out.set(root.name, '.');
253
+ const ws = root.workspaces;
254
+ const patterns = Array.isArray(ws)
255
+ ? ws
256
+ : Array.isArray(ws?.packages)
257
+ ? (ws.packages)
258
+ : [];
259
+ for (const pattern of patterns) {
260
+ if (typeof pattern !== 'string' || pattern.startsWith('!'))
261
+ continue;
262
+ for (const dir of expandWorkspaceGlob(inspection.projectRoot, pattern)) {
263
+ const pkg = readPackageJson(nodePath.join(inspection.projectRoot, dir, 'package.json'));
264
+ if (pkg && typeof pkg.name === 'string' && !out.has(pkg.name))
265
+ out.set(pkg.name, dir);
266
+ }
267
+ }
118
268
  }
119
- return false;
269
+ WORKSPACE_PACKAGES.set(inspection, out);
270
+ return out;
120
271
  }
121
- function packageExists(inspection, id) {
122
- // Project packages live under `packages/<name>` for SharkCraft + many
123
- // monorepos. Trust the inspection's package map if available, else
124
- // fall back to a filesystem check.
125
- const pkgs = inspection.packages;
126
- if (Array.isArray(pkgs)) {
127
- if (pkgs.some((p) => p.name === id))
128
- return true;
272
+ function checkPackageReference(inspection, id) {
273
+ const dir = workspacePackages(inspection).get(id);
274
+ if (dir) {
275
+ return { outcome: ReferenceCheckOutcome.Ok, message: `Package exists: ${id} (${dir})` };
276
+ }
277
+ // Last-resort backstop for repos without a `workspaces` field — reported as
278
+ // probable, because a directory name is not a package name.
279
+ const rel = id.startsWith('@') ? (id.split('/')[1] ?? '') : id;
280
+ if (rel && dirExists(inspection.projectRoot, `packages/${rel}`)) {
281
+ return {
282
+ outcome: ReferenceCheckOutcome.Ok,
283
+ message: `Package exists (probable): packages/${rel}/ is present, but no workspace package.json declares the name ${id}.`,
284
+ };
129
285
  }
130
- // file lookup as a backstop
131
- const rel = id.startsWith('@') ? id.split('/')[1] ?? '' : id;
132
- return dirExists(inspection.projectRoot, `packages/${rel}`);
286
+ return staleId('package', id);
133
287
  }
134
288
  function normalizeRel(p) {
135
289
  return p.split(/[\\/]/).join('/').replace(/^\.\//, '');
136
290
  }
291
+ /** `Foo` for `Foo.bar` / `Foo.prototype.bar`; null for a bare name. */
292
+ function qualifiedOwner(sym) {
293
+ const cleaned = sym.replace(/\.prototype\./g, '.');
294
+ const dot = cleaned.indexOf('.');
295
+ return dot > 0 && dot < cleaned.length - 1 ? cleaned.slice(0, dot) : null;
296
+ }
137
297
  function checkSymbolReference(projectRoot, ref, graph) {
138
298
  const sym = ref.symbol ?? '';
139
299
  if (!sym) {
140
300
  return {
141
- outcome: ReferenceCheckOutcome.Unknown,
301
+ outcome: ReferenceCheckOutcome.Invalid,
142
302
  confidence: SymbolConfidence.Unknown,
143
- message: 'Symbol reference has no `symbol` field.',
303
+ failure: ReferenceFailure.Malformed,
304
+ message: 'malformed reference: symbol reference missing required field `symbol`.',
144
305
  };
145
306
  }
307
+ // `Owner.member`: the graph indexes top-level symbols only, so it answers for
308
+ // the OWNER (moved-detection); the member itself is verified by the AST on
309
+ // the owner's declaring file.
310
+ const owner = qualifiedOwner(sym);
311
+ const lookup = owner ?? sym;
146
312
  // Graph-resolved, cross-file path (preferred when a graph is supplied).
147
313
  // A single-file AST scan cannot tell a *moved* symbol from a deleted one;
148
314
  // the graph's global symbol index can, so consult it first.
149
315
  if (graph) {
150
316
  const decl = graph
151
- .findSymbol(sym)
317
+ .findSymbol(lookup)
152
318
  .map((n) => n.path)
153
319
  .filter((p) => typeof p === 'string' && p.length > 0)
154
320
  .map(normalizeRel);
155
321
  if (decl.length > 0) {
156
322
  if (ref.path) {
157
323
  const target = normalizeRel(ref.path);
158
- if (decl.includes(target)) {
324
+ if (!decl.includes(target)) {
325
+ // Symbol exists but no longer at the pinned file — it moved.
326
+ return {
327
+ outcome: ReferenceCheckOutcome.Stale,
328
+ confidence: SymbolConfidence.Missing,
329
+ failure: ReferenceFailure.AnchorMissing,
330
+ message: `Symbol \`${lookup}\` is no longer in ${ref.path}; the graph resolves it to ${decl
331
+ .slice(0, 3)
332
+ .join(', ')}.`,
333
+ };
334
+ }
335
+ if (!owner && !hasContentAssertion(ref)) {
159
336
  return {
160
337
  outcome: ReferenceCheckOutcome.Ok,
161
338
  confidence: SymbolConfidence.Exact,
162
339
  message: `\`${sym}\` resolves to ${ref.path} (graph).`,
163
340
  };
164
341
  }
165
- // Symbol exists but no longer at the pinned file — it moved.
342
+ // The owner is at the pinned file; the member (and any content span)
343
+ // is verified by the AST below.
344
+ }
345
+ else if (!owner) {
346
+ // No file pin, but the graph resolved it cross-file.
166
347
  return {
167
- outcome: ReferenceCheckOutcome.Stale,
168
- confidence: SymbolConfidence.Missing,
169
- message: `Symbol \`${sym}\` is no longer in ${ref.path}; the graph resolves it to ${decl
170
- .slice(0, 3)
171
- .join(', ')}.`,
348
+ outcome: ReferenceCheckOutcome.Ok,
349
+ confidence: SymbolConfidence.Probable,
350
+ message: `\`${sym}\` resolves via the code graph to ${decl.slice(0, 3).join(', ')}.`,
351
+ };
352
+ }
353
+ else if (decl.length === 1) {
354
+ return resolveSymbolAtPath(projectRoot, decl[0], sym, false);
355
+ }
356
+ else {
357
+ return {
358
+ outcome: ReferenceCheckOutcome.Unknown,
359
+ confidence: SymbolConfidence.Unknown,
360
+ failure: ReferenceFailure.Unverifiable,
361
+ message: `\`${owner}\` is declared in ${decl.length} files (${decl.slice(0, 3).join(', ')}) — pin the path to check \`${sym}\`.`,
172
362
  };
173
363
  }
174
- // No file pin, but the graph resolved it cross-file.
175
- return {
176
- outcome: ReferenceCheckOutcome.Ok,
177
- confidence: SymbolConfidence.Probable,
178
- message: `\`${sym}\` resolves via the code graph to ${decl.slice(0, 3).join(', ')}.`,
179
- };
180
364
  }
181
- // Absent from the graph. The graph may be partial (locals, unindexed
182
- // languages), so fall through to the AST/text backstop when a file is
183
- // pinned; otherwise we cannot verify.
184
- if (!ref.path) {
365
+ else if (!ref.path) {
366
+ // Absent from the graph. The graph may be partial (locals, unindexed
367
+ // languages), so fall through to the AST/text backstop when a file is
368
+ // pinned; otherwise we cannot verify.
185
369
  return {
186
370
  outcome: ReferenceCheckOutcome.Unknown,
187
371
  confidence: SymbolConfidence.Unknown,
372
+ failure: ReferenceFailure.Unverifiable,
188
373
  message: `Symbol reference \`${sym}\` has no file pin and is absent from the code graph; stale-check cannot verify.`,
189
374
  };
190
375
  }
191
376
  }
192
- const file = ref.path ? nodePath.join(projectRoot, ref.path) : null;
193
- if (file) {
194
- if (!existsSync(file)) {
195
- return {
196
- outcome: ReferenceCheckOutcome.Missing,
197
- confidence: SymbolConfidence.Missing,
198
- message: `Referenced file does not exist: ${ref.path}`,
199
- };
200
- }
201
- // AST-backed resolution (falls back to text-scan if parse fails).
202
- try {
203
- const res = resolveSymbolInFile(file, sym);
204
- switch (res.resolution) {
205
- case SymbolResolution.ExactExport:
206
- return {
207
- outcome: ReferenceCheckOutcome.Ok,
208
- confidence: SymbolConfidence.Exact,
209
- message: res.message,
210
- };
211
- case SymbolResolution.ExactLocal:
212
- case SymbolResolution.ExactReExport:
213
- return {
214
- outcome: ReferenceCheckOutcome.Ok,
215
- confidence: SymbolConfidence.Exact,
216
- message: res.message,
217
- };
218
- case SymbolResolution.ProbableText:
219
- return {
220
- outcome: ReferenceCheckOutcome.Ok,
221
- confidence: SymbolConfidence.Probable,
222
- message: res.message,
223
- };
224
- case SymbolResolution.Missing:
225
- return {
226
- outcome: ReferenceCheckOutcome.Stale,
227
- confidence: SymbolConfidence.Missing,
228
- message: res.message,
229
- };
230
- default:
231
- return {
232
- outcome: ReferenceCheckOutcome.Unknown,
233
- confidence: SymbolConfidence.Unknown,
234
- message: res.message,
235
- };
236
- }
237
- }
238
- catch {
239
- // Fallback to text scan.
240
- try {
241
- const text = readFileSync(file, 'utf8');
242
- const declRe = new RegExp(`(export\\s+(?:async\\s+)?(?:function|class|interface|enum|type|const|let|var)\\s+|class\\s+|function\\s+)${escapeRe(sym)}\\b`);
243
- if (declRe.test(text)) {
244
- return {
245
- outcome: ReferenceCheckOutcome.Ok,
246
- confidence: SymbolConfidence.Exact,
247
- message: `Found declaration of \`${sym}\` in ${ref.path}.`,
248
- };
249
- }
250
- if (text.includes(sym)) {
251
- return {
252
- outcome: ReferenceCheckOutcome.Ok,
253
- confidence: SymbolConfidence.Probable,
254
- message: `\`${sym}\` appears in ${ref.path}, but not as an exported declaration.`,
255
- };
256
- }
377
+ if (ref.path)
378
+ return resolveSymbolAtPath(projectRoot, ref.path, sym, true);
379
+ // No file pinned — best-effort confidence is `unknown`.
380
+ return {
381
+ outcome: ReferenceCheckOutcome.Unknown,
382
+ confidence: SymbolConfidence.Unknown,
383
+ failure: ReferenceFailure.Unverifiable,
384
+ message: `Symbol reference \`${sym}\` has no file pin; stale-check cannot verify (pin it as \`symbol:${sym}@<path>\`).`,
385
+ };
386
+ }
387
+ /** Resolve `sym` in one file through the AST index (text-scan backstop). */
388
+ function resolveSymbolAtPath(projectRoot, relPath, sym, pinned) {
389
+ const file = nodePath.join(projectRoot, relPath);
390
+ if (!existsSync(file)) {
391
+ return {
392
+ outcome: ReferenceCheckOutcome.Missing,
393
+ confidence: SymbolConfidence.Missing,
394
+ failure: ReferenceFailure.PathMissing,
395
+ message: `Referenced file does not exist: ${relPath}`,
396
+ };
397
+ }
398
+ const via = pinned ? '' : ` (declaring file ${relPath}, from the code graph)`;
399
+ // AST-backed resolution (falls back to text-scan if parse fails).
400
+ try {
401
+ const res = resolveSymbolInFile(file, sym);
402
+ switch (res.resolution) {
403
+ case SymbolResolution.ExactExport:
404
+ case SymbolResolution.ExactLocal:
405
+ case SymbolResolution.ExactReExport:
406
+ case SymbolResolution.ExactMember:
407
+ case SymbolResolution.ExactLocalMember:
408
+ return {
409
+ outcome: ReferenceCheckOutcome.Ok,
410
+ confidence: pinned ? SymbolConfidence.Exact : SymbolConfidence.Probable,
411
+ message: res.message + via,
412
+ ...(res.span ? { span: res.span } : {}),
413
+ };
414
+ case SymbolResolution.ProbableText:
415
+ return {
416
+ outcome: ReferenceCheckOutcome.Ok,
417
+ confidence: SymbolConfidence.Probable,
418
+ message: res.message + via,
419
+ };
420
+ case SymbolResolution.Missing:
257
421
  return {
258
422
  outcome: ReferenceCheckOutcome.Stale,
259
423
  confidence: SymbolConfidence.Missing,
260
- message: `Symbol \`${sym}\` not found in ${ref.path}.`,
424
+ failure: ReferenceFailure.AnchorMissing,
425
+ message: res.message + via,
426
+ ...(res.suggestedSymbol ? { suggestedSymbol: res.suggestedSymbol } : {}),
261
427
  };
262
- }
263
- catch {
428
+ default:
264
429
  return {
265
430
  outcome: ReferenceCheckOutcome.Unknown,
266
431
  confidence: SymbolConfidence.Unknown,
267
- message: `Failed to read ${ref.path}.`,
432
+ failure: ReferenceFailure.Unverifiable,
433
+ message: res.message + via,
434
+ };
435
+ }
436
+ }
437
+ catch {
438
+ // Fallback to text scan.
439
+ const token = qualifiedOwner(sym) ? sym.slice(sym.lastIndexOf('.') + 1) : sym;
440
+ try {
441
+ const text = readFileSync(file, 'utf8');
442
+ const declRe = new RegExp(`(export\\s+(?:async\\s+)?(?:function|class|interface|enum|type|const|let|var)\\s+|class\\s+|function\\s+)${escapeRe(token)}\\b`);
443
+ if (declRe.test(text)) {
444
+ return {
445
+ outcome: ReferenceCheckOutcome.Ok,
446
+ confidence: SymbolConfidence.Exact,
447
+ message: `Found declaration of \`${sym}\` in ${relPath}.`,
268
448
  };
269
449
  }
450
+ if (text.includes(token)) {
451
+ return {
452
+ outcome: ReferenceCheckOutcome.Ok,
453
+ confidence: SymbolConfidence.Probable,
454
+ message: `\`${sym}\` appears in ${relPath}, but not as an exported declaration.`,
455
+ };
456
+ }
457
+ return {
458
+ outcome: ReferenceCheckOutcome.Stale,
459
+ confidence: SymbolConfidence.Missing,
460
+ failure: ReferenceFailure.AnchorMissing,
461
+ message: `Symbol \`${sym}\` not found in ${relPath}.`,
462
+ };
463
+ }
464
+ catch {
465
+ return {
466
+ outcome: ReferenceCheckOutcome.Unknown,
467
+ confidence: SymbolConfidence.Unknown,
468
+ failure: ReferenceFailure.Unverifiable,
469
+ message: `Failed to read ${relPath}.`,
470
+ };
270
471
  }
271
472
  }
272
- // No file pinned — best-effort confidence is `unknown`.
273
- return {
274
- outcome: ReferenceCheckOutcome.Unknown,
275
- confidence: SymbolConfidence.Unknown,
276
- message: `Symbol reference \`${sym}\` has no file pin; stale-check cannot verify.`,
277
- };
278
473
  }
279
474
  function escapeRe(s) {
280
475
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
281
476
  }
282
- function checkReference(inspection, ref, graph) {
477
+ /**
478
+ * Check one reference: does its target exist — and, when it declares
479
+ * `contains` / `matches` / `count`, does the target still say what the asset
480
+ * claims. Every non-ok result carries a {@link ReferenceFailure}.
481
+ */
482
+ function checkReference(inspection, ref, graph, excludeDirs = []) {
483
+ const base = checkReferenceTarget(inspection, ref, graph);
484
+ const result = base.outcome === ReferenceCheckOutcome.Ok
485
+ ? applyAssertions(inspection.projectRoot, ref, base, excludeDirs)
486
+ : base;
487
+ if (result.outcome === ReferenceCheckOutcome.Ok || result.failure !== undefined)
488
+ return result;
489
+ return { ...result, failure: defaultFailure(ref.kind, result.outcome) };
490
+ }
491
+ /** The failure mode of a non-ok check that did not name one. */
492
+ function defaultFailure(kind, outcome) {
493
+ if (outcome === ReferenceCheckOutcome.Unknown)
494
+ return ReferenceFailure.Unverifiable;
495
+ if (outcome === ReferenceCheckOutcome.Invalid)
496
+ return ReferenceFailure.Malformed;
497
+ if (kind === 'file' || kind === 'directory')
498
+ return ReferenceFailure.PathMissing;
499
+ if (kind === 'symbol')
500
+ return ReferenceFailure.AnchorMissing;
501
+ return ReferenceFailure.IdUnregistered;
502
+ }
503
+ /**
504
+ * Content and count assertions, over a target that EXISTS — a mismatch on a
505
+ * missing file would report one break twice.
506
+ */
507
+ function applyAssertions(projectRoot, ref, base, excludeDirs) {
508
+ const content = checkReferenceContent(projectRoot, ref, ref.kind === 'symbol' ? base.span : undefined);
509
+ if (content && !content.ok)
510
+ return assertionFailure(base, content, 'content');
511
+ const count = checkReferenceCount(projectRoot, ref, countExcludeDirsFor(ref, excludeDirs));
512
+ if (count && !count.ok)
513
+ return assertionFailure(base, count, 'count');
514
+ const held = [content?.message, count?.message].filter((m) => m !== undefined);
515
+ if (held.length === 0)
516
+ return base;
517
+ return {
518
+ ...base,
519
+ message: `${base.message} ${held.join(' ')}`,
520
+ ...(count ? { expected: count.expected, actual: count.actual } : {}),
521
+ };
522
+ }
523
+ function assertionFailure(base, a, what) {
524
+ const confidence = base.confidence ? { confidence: base.confidence } : {};
525
+ if (a.unverifiable) {
526
+ return {
527
+ outcome: ReferenceCheckOutcome.Unknown,
528
+ ...confidence,
529
+ failure: ReferenceFailure.Unverifiable,
530
+ message: `${base.message} Its ${what} assertion could not be evaluated: ${a.message}`,
531
+ };
532
+ }
533
+ return {
534
+ outcome: ReferenceCheckOutcome.Stale,
535
+ ...confidence,
536
+ failure: a.failure ?? (what === 'count' ? ReferenceFailure.CountMismatch : ReferenceFailure.ContentMismatch),
537
+ ...(a.expected !== undefined ? { expected: a.expected } : {}),
538
+ ...(a.actual !== undefined ? { actual: a.actual } : {}),
539
+ message: a.message,
540
+ suggestion: what === 'count'
541
+ ? `Update the claim in the entry, then set count.expected: ${String(a.actual)}.`
542
+ : 'Update the entry (and the assertion) to what the code says now.',
543
+ };
544
+ }
545
+ /** Does the reference's TARGET exist? (Assertions are layered on by {@link checkReference}.) */
546
+ function checkReferenceTarget(inspection, ref, graph) {
283
547
  const projectRoot = inspection.projectRoot;
284
548
  switch (ref.kind) {
285
549
  case 'file': {
@@ -298,6 +562,7 @@ function checkReference(inspection, ref, graph) {
298
562
  }
299
563
  return {
300
564
  outcome: ReferenceCheckOutcome.Stale,
565
+ failure: ReferenceFailure.PathMissing,
301
566
  message: `Directory missing: ${ref.path}`,
302
567
  suggestion: 'Move the directory or update the knowledge reference.',
303
568
  };
@@ -310,98 +575,78 @@ function checkReference(inspection, ref, graph) {
310
575
  const id = ref.id ?? ref.command ?? '';
311
576
  if (!id)
312
577
  return missingField('command', 'id or command');
313
- if (commandExistsInInspection(inspection, id)) {
314
- return { outcome: ReferenceCheckOutcome.Ok, message: `Command available: ${id}` };
315
- }
316
- return {
317
- outcome: ReferenceCheckOutcome.Stale,
318
- message: `Command not registered: ${id}`,
319
- suggestion: 'Register the command in the command catalog or update the reference.',
320
- };
578
+ return checkCommandString(inspection, id, 'Command');
321
579
  }
322
580
  case 'template': {
323
581
  if (!ref.id)
324
582
  return missingField('template', 'id');
325
- if (templateExists(inspection, ref.id)) {
326
- return { outcome: ReferenceCheckOutcome.Ok, message: `Template exists: ${ref.id}` };
327
- }
328
- return staleId('template', ref.id);
583
+ return checkRegisteredId(inspection, 'template', ref.id, 'Template');
329
584
  }
330
585
  case 'playbook': {
331
586
  if (!ref.id)
332
587
  return missingField('playbook', 'id');
333
- if (playbookExists(inspection, ref.id)) {
334
- return { outcome: ReferenceCheckOutcome.Ok, message: `Playbook exists: ${ref.id}` };
335
- }
336
- return staleId('playbook', ref.id);
588
+ return checkRegisteredId(inspection, 'playbook', ref.id, 'Playbook');
337
589
  }
338
590
  case 'construct': {
339
591
  if (!ref.id)
340
592
  return missingField('construct', 'id');
341
- if (constructExists(inspection, ref.id)) {
342
- return { outcome: ReferenceCheckOutcome.Ok, message: `Construct exists: ${ref.id}` };
343
- }
344
- return staleId('construct', ref.id);
593
+ return checkRegisteredId(inspection, 'construct', ref.id, 'Construct');
345
594
  }
346
595
  case 'helper': {
347
596
  if (!ref.id)
348
597
  return missingField('helper', 'id');
349
- if (helperExists(ref.id)) {
350
- return { outcome: ReferenceCheckOutcome.Ok, message: `Helper exists: ${ref.id}` };
351
- }
352
- return staleId('helper', ref.id);
598
+ return checkRegisteredId(inspection, 'helper', ref.id, 'Helper');
353
599
  }
354
600
  case 'policy': {
355
601
  if (!ref.id)
356
602
  return missingField('policy', 'id');
357
- if (policyExists(inspection, ref.id)) {
358
- return { outcome: ReferenceCheckOutcome.Ok, message: `Policy exists: ${ref.id}` };
359
- }
360
- return staleId('policy', ref.id);
603
+ return checkRegisteredId(inspection, 'policy', ref.id, 'Policy');
361
604
  }
362
605
  case 'boundary-rule': {
363
606
  if (!ref.id)
364
607
  return missingField('boundary-rule', 'id');
365
- if (boundaryRuleExists(inspection, ref.id)) {
366
- return { outcome: ReferenceCheckOutcome.Ok, message: `Boundary rule exists: ${ref.id}` };
367
- }
368
- return staleId('boundary-rule', ref.id);
608
+ return checkRegisteredId(inspection, 'boundary-rule', ref.id, 'Boundary rule');
369
609
  }
370
610
  case 'path-convention': {
371
611
  if (!ref.id)
372
612
  return missingField('path-convention', 'id');
373
- if (pathConventionExists(inspection, ref.id)) {
374
- return { outcome: ReferenceCheckOutcome.Ok, message: `Path convention exists: ${ref.id}` };
375
- }
376
- return staleId('path-convention', ref.id);
613
+ return checkRegisteredId(inspection, 'path-convention', ref.id, 'Path convention');
377
614
  }
378
615
  case 'package': {
379
616
  if (!ref.id)
380
617
  return missingField('package', 'id');
381
- if (packageExists(inspection, ref.id)) {
382
- return { outcome: ReferenceCheckOutcome.Ok, message: `Package exists: ${ref.id}` };
383
- }
384
- return staleId('package', ref.id);
618
+ return checkPackageReference(inspection, ref.id);
385
619
  }
386
620
  case 'url': {
387
621
  // We never fetch URLs. Mark them unknown unless we can resolve to a
388
622
  // local docs file.
389
623
  return {
390
624
  outcome: ReferenceCheckOutcome.Unknown,
625
+ failure: ReferenceFailure.Unverifiable,
391
626
  message: 'URL references are not verified (no network).',
392
627
  };
393
628
  }
629
+ default:
630
+ // A kind outside the vocabulary still loads (the validator reports it as
631
+ // an error); here it is MALFORMED — never a crash, never a silent row.
632
+ return {
633
+ outcome: ReferenceCheckOutcome.Invalid,
634
+ failure: ReferenceFailure.Malformed,
635
+ message: `malformed reference: unsupported kind "${String(ref.kind)}" — expected one of: ${KNOWLEDGE_REFERENCE_KINDS.join(', ')}.`,
636
+ };
394
637
  }
395
638
  }
396
639
  function missingField(kind, field) {
397
640
  return {
398
- outcome: ReferenceCheckOutcome.Unknown,
399
- message: `${kind} reference missing required field: ${field}`,
641
+ outcome: ReferenceCheckOutcome.Invalid,
642
+ failure: ReferenceFailure.Malformed,
643
+ message: `malformed reference: ${kind} reference missing required field \`${field}\`.`,
400
644
  };
401
645
  }
402
646
  function staleFile(rel) {
403
647
  return {
404
648
  outcome: ReferenceCheckOutcome.Stale,
649
+ failure: ReferenceFailure.PathMissing,
405
650
  message: `File missing: ${rel}`,
406
651
  suggestion: 'Restore the file or run `shrk knowledge rename-file <old> <new> --dry-run`.',
407
652
  };
@@ -409,29 +654,237 @@ function staleFile(rel) {
409
654
  function staleId(kind, id) {
410
655
  return {
411
656
  outcome: ReferenceCheckOutcome.Stale,
657
+ failure: ReferenceFailure.IdUnregistered,
412
658
  message: `${kind} not found: ${id}`,
413
659
  suggestion: 'Register the target or remove the reference.',
414
660
  };
415
661
  }
416
- function entryTouchesChangedFiles(entry, changed) {
662
+ function normalizeSlashes(p) {
663
+ return p.split(/[\\/]/).join('/');
664
+ }
665
+ /** `./src/a.ts` / `src\a.ts` / `src/svc/` → `src/a.ts` / `src/svc`. */
666
+ function normalizeScopePath(p) {
667
+ return normalizeSlashes(p).replace(/^(?:\.\/)+/, '').replace(/\/+$/, '');
668
+ }
669
+ /**
670
+ * A changed path IS the target, or lies UNDER it — a `directory` reference
671
+ * whose files changed, or a directory deleted wholesale (git lists its files,
672
+ * never the directory itself).
673
+ */
674
+ function pathTouched(target, changed) {
675
+ if (!target)
676
+ return false;
677
+ const p = normalizeScopePath(target);
678
+ if (p === '' || p === '.')
679
+ return false;
680
+ const under = `${p}/`;
681
+ return changed.some((c) => c === p || c.startsWith(under));
682
+ }
683
+ /**
684
+ * Is a subject — a knowledge entry, a boundary rule, a policy check — in a
685
+ * changeset's scope? THE one scoping rule for `--changed-only` / `--files`:
686
+ *
687
+ * - the file that DECLARES it changed (its references were added or edited —
688
+ * a typo in a new reference must be checked by the run that introduced it);
689
+ * - a changed path equals a reference / anchor path, or lies under one;
690
+ * - a changed path matches a `count` source glob (the number re-derives from
691
+ * those globs, so a change there can move it with no pinned path changing).
692
+ *
693
+ * `source` is project-relative (`relativeSource`); `(unknown source)` matches
694
+ * nothing.
695
+ */
696
+ function subjectTouchesChangedFiles(subject, changed) {
417
697
  if (changed.length === 0)
418
698
  return true;
419
- const changedSet = new Set(changed.map((c) => c.split(/[\\/]/).join('/')));
420
- for (const ref of entry.references ?? []) {
421
- if (ref.path && changedSet.has(ref.path.split(/[\\/]/).join('/')))
699
+ const changedList = changed.map(normalizeScopePath);
700
+ if (changedList.includes(normalizeScopePath(subject.source)))
701
+ return true;
702
+ for (const ref of subject.references ?? []) {
703
+ if (pathTouched(ref.path, changedList))
704
+ return true;
705
+ // The scope the count is MEASURED over (`inspectSource` selects through
706
+ // `globListSelects`), so a change to a file its `!` excludes is no touch.
707
+ const globs = ref.count?.source?.files;
708
+ if (globs && globs.length > 0 && changedList.some((c) => globListSelects(c, globs)))
422
709
  return true;
423
710
  }
424
- for (const anchor of entry.anchors ?? []) {
425
- if (anchor.path && changedSet.has(anchor.path.split(/[\\/]/).join('/')))
711
+ for (const anchor of subject.anchors ?? []) {
712
+ if (pathTouched(anchor.path, changedList))
426
713
  return true;
427
714
  }
428
715
  return false;
429
716
  }
717
+ /**
718
+ * The directories a `count` source never walks by default: the sharkcraft dir.
719
+ * Its `.ts` files hold the claims themselves — prose saying `registerService("<name>")`
720
+ * inside a count glob would count itself, and the mismatch hint would tell the
721
+ * author to bake that self-match into `expected` (policy-lint and finish prune
722
+ * the same dir for the same reason).
723
+ */
724
+ function defaultCountExcludeDirs(inspection) {
725
+ const dir = inspection.sharkcraftDir;
726
+ if (!dir)
727
+ return [];
728
+ const rel = normalizeScopePath(nodePath.relative(inspection.projectRoot, dir));
729
+ return rel && rel !== '.' && !rel.startsWith('..') && !nodePath.isAbsolute(rel) ? [rel] : [];
730
+ }
731
+ /**
732
+ * The exclusions ONE count source walks under: an exclusion is dropped when the
733
+ * source's own globs target inside it (`sharkcraft/**` counts there on purpose).
734
+ */
735
+ function countExcludeDirsFor(ref, excludeDirs) {
736
+ const globs = ref.count?.source?.files ?? [];
737
+ if (excludeDirs.length === 0 || globs.length === 0)
738
+ return excludeDirs;
739
+ const prefixes = globs.filter((g) => !g.startsWith('!')).map((g) => staticGlobPrefix(g));
740
+ return excludeDirs.filter((d) => !prefixes.some((p) => p === d || p.startsWith(`${d}/`)));
741
+ }
742
+ /** Every {@link ReferenceFailure} at 0 — the report always carries the full map. */
743
+ function emptyFailureCounts() {
744
+ const out = {};
745
+ for (const f of Object.values(ReferenceFailure))
746
+ out[f] = 0;
747
+ return out;
748
+ }
749
+ function emptyKindBucket() {
750
+ return { scanned: 0, zeroReferences: 0, referencesChecked: 0, verified: 0, stale: 0, unverifiable: 0 };
751
+ }
752
+ /**
753
+ * ok / stale / missing are real checks; unknown and invalid (malformed) proved
754
+ * nothing either way. THE predicate — the stale-check's entry buckets, its
755
+ * verdict fold (`declaredReferenceCoverage`) and the `shrk gate`
756
+ * knowledge-symbol gate all read it.
757
+ */
758
+ export function isCheckableOutcome(o) {
759
+ return o !== ReferenceCheckOutcome.Unknown && o !== ReferenceCheckOutcome.Invalid;
760
+ }
761
+ function isFailingOutcome(o) {
762
+ return o === ReferenceCheckOutcome.Stale || o === ReferenceCheckOutcome.Missing;
763
+ }
764
+ /**
765
+ * One subject's bucket from its checks: any failing check makes it stale; any
766
+ * passing one (and no failing) makes it verified; NOTHING checkable makes it
767
+ * unverifiable — never healthy.
768
+ */
769
+ function classifyChecks(checkable, failing) {
770
+ if (failing > 0)
771
+ return KnowledgeEntryVerdict.Stale;
772
+ if (checkable > 0)
773
+ return KnowledgeEntryVerdict.Verified;
774
+ return KnowledgeEntryVerdict.Unverifiable;
775
+ }
776
+ function bumpBucket(b, declared, verdict) {
777
+ b.scanned += 1;
778
+ if (declared === 0)
779
+ b.zeroReferences += 1;
780
+ b.referencesChecked += declared;
781
+ if (verdict === KnowledgeEntryVerdict.Verified)
782
+ b.verified += 1;
783
+ else if (verdict === KnowledgeEntryVerdict.Stale)
784
+ b.stale += 1;
785
+ else
786
+ b.unverifiable += 1;
787
+ }
788
+ function tallyOutcome(b, outcome) {
789
+ b.checked += 1;
790
+ if (outcome === ReferenceCheckOutcome.Ok)
791
+ b.ok += 1;
792
+ else if (outcome === ReferenceCheckOutcome.Stale)
793
+ b.stale += 1;
794
+ else if (outcome === ReferenceCheckOutcome.Missing)
795
+ b.missing += 1;
796
+ else if (outcome === ReferenceCheckOutcome.Invalid)
797
+ b.invalid += 1;
798
+ else
799
+ b.unknown += 1;
800
+ }
801
+ function relativeSource(projectRoot, origin) {
802
+ if (!origin)
803
+ return '(unknown source)';
804
+ return normalizeSlashes(nodePath.isAbsolute(origin) ? nodePath.relative(projectRoot, origin) : origin);
805
+ }
806
+ function pct1(part, whole) {
807
+ return whole > 0 ? Math.round((part / whole) * 1000) / 10 : 0;
808
+ }
809
+ /** The static directory part of a glob (`apps/legacy/**` → `apps/legacy`); `''` when it starts with a wildcard. */
810
+ function staticGlobPrefix(glob) {
811
+ const segs = normalizeSlashes(glob).replace(/^\.\//, '').split('/');
812
+ const firstGlob = segs.findIndex((s) => /[*?[{]/.test(s));
813
+ return (firstGlob === -1 ? segs : segs.slice(0, firstGlob)).join('/');
814
+ }
815
+ const BACKTICKED_IDENTIFIER = /`([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)?)(?:\(\))?`/g;
816
+ const TS_SOURCE = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
817
+ const PATH_ONLY_ADVISORY_CAP = 5;
818
+ /**
819
+ * The path-only advisory: an entry whose references are all paths, whose prose
820
+ * names (in backticks) a symbol one of its referenced files DECLARES. Renaming
821
+ * that symbol would leave the entry wrong while every reference still resolves
822
+ * — the strong form (`symbol` pinned to the path) catches it. Never gating.
823
+ */
824
+ function pathOnlyAdvisories(projectRoot, entry, refs, cache) {
825
+ if (refs.length === 0 || !refs.every((r) => r.kind === 'file' || r.kind === 'directory'))
826
+ return [];
827
+ const files = refs
828
+ .filter((r) => r.kind === 'file' && r.path !== undefined && TS_SOURCE.test(r.path))
829
+ .map((r) => r.path);
830
+ if (files.length === 0)
831
+ return [];
832
+ const names = new Set();
833
+ for (const text of [entry.summary ?? '', typeof entry.content === 'string' ? entry.content : '']) {
834
+ for (const m of text.matchAll(BACKTICKED_IDENTIFIER))
835
+ names.add(m[1]);
836
+ }
837
+ const out = [];
838
+ for (const name of names) {
839
+ if (out.length >= PATH_ONLY_ADVISORY_CAP)
840
+ break;
841
+ for (const rel of files) {
842
+ let idx = cache.get(rel);
843
+ if (idx === undefined) {
844
+ const abs = nodePath.join(projectRoot, rel);
845
+ idx = existsSync(abs) ? buildSymbolIndex(abs) : null;
846
+ cache.set(rel, idx);
847
+ }
848
+ if (!idx?.parsed)
849
+ continue;
850
+ const declared = name.includes('.')
851
+ ? (idx.members ?? []).some((m) => `${m.owner}.${m.name}` === name)
852
+ : idx.exports.some((e) => e.name === name);
853
+ if (!declared)
854
+ continue;
855
+ out.push({
856
+ code: KnowledgeAdvisoryCode.PathOnlyReference,
857
+ subjectId: entry.id,
858
+ assetKind: ReferenceAssetKind.Knowledge,
859
+ 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}' }.`,
860
+ suggestion: { kind: 'symbol', symbol: name, path: rel },
861
+ });
862
+ break;
863
+ }
864
+ }
865
+ return out;
866
+ }
430
867
  export function buildKnowledgeStaleReport(inspection, options = {}) {
431
868
  const strategy = options.renameStrategy ?? RenameStrategy.Strict;
869
+ const projectRoot = inspection.projectRoot;
870
+ // Default: never walk the sharkcraft dir — the claims live there and would
871
+ // count themselves (every caller gets this; none has to remember to pass it).
872
+ const excludeDirs = options.excludeDirs ?? defaultCountExcludeDirs(inspection);
432
873
  const referenceChecks = [];
433
874
  const anchorChecks = [];
434
- const counts = { ok: 0, stale: 0, missing: 0, unknown: 0 };
875
+ const assetReferenceChecks = [];
876
+ const counts = { ok: 0, stale: 0, missing: 0, unknown: 0, invalid: 0 };
877
+ const failureCounts = emptyFailureCounts();
878
+ const byAssetKind = {
879
+ [ReferenceAssetKind.Knowledge]: emptyKindBucket(),
880
+ [ReferenceAssetKind.BoundaryRule]: emptyKindBucket(),
881
+ [ReferenceAssetKind.Policy]: emptyKindBucket(),
882
+ };
883
+ const byEntryType = {};
884
+ const byReferenceKind = {};
885
+ const entryVerdicts = [];
886
+ const advisories = [];
887
+ const symbolIndexCache = new Map();
435
888
  let totalReferences = 0;
436
889
  let totalAnchors = 0;
437
890
  // Lazy symbol → file index, built on first stale-symbol need.
@@ -458,108 +911,180 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
458
911
  dirBasenameIndex = buildBasenameDirIndex(inspection.projectRoot);
459
912
  return dirBasenameIndex;
460
913
  };
914
+ /**
915
+ * Check ONE declared reference and attach its rename signal — the same path
916
+ * for a knowledge entry, a boundary rule and a policy check, so every asset
917
+ * kind is verified by one checker.
918
+ */
919
+ const checkOne = (subjectId, ref, assetKind) => {
920
+ const result = checkReference(inspection, ref, options.graph, excludeDirs);
921
+ const outcome = result.outcome;
922
+ const check = {
923
+ entryId: subjectId,
924
+ reference: ref,
925
+ outcome,
926
+ message: result.message,
927
+ ...(result.confidence ? { symbolConfidence: result.confidence } : {}),
928
+ ...(result.suggestion ? { suggestion: result.suggestion } : {}),
929
+ ...(result.failure ? { failure: result.failure } : {}),
930
+ ...(result.expected !== undefined ? { expected: result.expected } : {}),
931
+ ...(result.actual !== undefined ? { actual: result.actual } : {}),
932
+ ...(assetKind ? { assetKind } : {}),
933
+ };
934
+ tallyOutcome((byReferenceKind[ref.kind] ??= { checked: 0, ok: 0, stale: 0, missing: 0, unknown: 0, invalid: 0 }), outcome);
935
+ if (result.failure)
936
+ failureCounts[result.failure] += 1;
937
+ const isStaleOrMissing = outcome === ReferenceCheckOutcome.Stale || outcome === ReferenceCheckOutcome.Missing;
938
+ // A bare MEMBER name: the qualified spelling resolves in the same file, so
939
+ // it is the replacement (`shrk fix --knowledge-stale` applies it).
940
+ if (isStaleOrMissing && result.suggestedSymbol) {
941
+ check.replaceWith = {
942
+ symbol: result.suggestedSymbol,
943
+ rationale: `\`${ref.symbol}\` is a member, not a top-level declaration — \`${result.suggestedSymbol}\` resolves in ${ref.path ?? 'the pinned file'}.`,
944
+ strategy: RenameStrategy.Strict,
945
+ };
946
+ }
947
+ // Symbol rename detection. Strict mode: emit `replaceWith.path`
948
+ // only for the single unambiguous candidate. Wide mode also
949
+ // emits scored candidate lists for the multi-candidate cases that
950
+ // strict silently drops.
951
+ if (isStaleOrMissing && ref.kind === 'symbol' && ref.symbol && !check.replaceWith) {
952
+ const all = (getSymbolIndex().get(ref.symbol) ?? []).filter((p) => p !== ref.path);
953
+ if (all.length === 1) {
954
+ check.replaceWith = {
955
+ path: all[0],
956
+ rationale: `\`${ref.symbol}\` is exported from \`${all[0]}\` — sole candidate.`,
957
+ strategy: RenameStrategy.Strict,
958
+ };
959
+ }
960
+ else if (strategy === RenameStrategy.Wide && all.length > 1) {
961
+ check.replaceWith = buildWideReplacement({
962
+ stalePath: ref.path ?? '',
963
+ paths: all,
964
+ kindLabel: `symbol \`${ref.symbol}\``,
965
+ });
966
+ }
967
+ }
968
+ // File rename detection (directory move, basename match).
969
+ if (isStaleOrMissing && ref.kind === 'file' && ref.path && !check.replaceWith) {
970
+ const indexed = getFileBasenameIndex();
971
+ const uniq = pickUniqueRenameCandidate(ref.path, indexed);
972
+ if (uniq) {
973
+ check.replaceWith = {
974
+ path: uniq,
975
+ rationale: `File basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\` (likely directory rename).`,
976
+ strategy: RenameStrategy.Strict,
977
+ };
978
+ }
979
+ else if (strategy === RenameStrategy.Wide) {
980
+ const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
981
+ if (all.length > 0) {
982
+ check.replaceWith = buildWideReplacement({
983
+ stalePath: ref.path,
984
+ paths: all,
985
+ kindLabel: `file \`${nodePath.basename(ref.path)}\``,
986
+ });
987
+ }
988
+ }
989
+ }
990
+ // Directory rename detection.
991
+ if (isStaleOrMissing && ref.kind === 'directory' && ref.path && !check.replaceWith) {
992
+ const indexed = getDirBasenameIndex();
993
+ const uniq = pickUniqueRenameCandidate(ref.path, indexed);
994
+ if (uniq) {
995
+ check.replaceWith = {
996
+ path: uniq,
997
+ rationale: `Directory basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\`.`,
998
+ strategy: RenameStrategy.Strict,
999
+ };
1000
+ }
1001
+ else if (strategy === RenameStrategy.Wide) {
1002
+ const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
1003
+ if (all.length > 0) {
1004
+ check.replaceWith = buildWideReplacement({
1005
+ stalePath: ref.path,
1006
+ paths: all,
1007
+ kindLabel: `directory \`${nodePath.basename(ref.path)}\``,
1008
+ });
1009
+ }
1010
+ }
1011
+ }
1012
+ return check;
1013
+ };
461
1014
  for (const entry of inspection.knowledgeEntries) {
462
- if (options.changedFiles && !entryTouchesChangedFiles(entry, options.changedFiles)) {
1015
+ if (options.changedFiles &&
1016
+ !subjectTouchesChangedFiles({
1017
+ source: relativeSource(projectRoot, entry.source?.origin),
1018
+ references: entry.references ?? [],
1019
+ anchors: entry.anchors ?? [],
1020
+ }, options.changedFiles)) {
463
1021
  continue;
464
1022
  }
465
- for (const ref of entry.references ?? []) {
1023
+ const refs = entry.references ?? [];
1024
+ const anchors = entry.anchors ?? [];
1025
+ let checkable = 0;
1026
+ let failing = 0;
1027
+ for (const ref of refs) {
466
1028
  totalReferences += 1;
467
- const result = checkReference(inspection, ref, options.graph);
468
- const outcome = result.outcome;
469
- if (outcome === ReferenceCheckOutcome.Ok)
1029
+ const check = checkOne(entry.id, ref);
1030
+ if (check.outcome === ReferenceCheckOutcome.Ok)
470
1031
  counts.ok += 1;
471
- else if (outcome === ReferenceCheckOutcome.Stale)
1032
+ else if (check.outcome === ReferenceCheckOutcome.Stale)
472
1033
  counts.stale += 1;
473
- else if (outcome === ReferenceCheckOutcome.Missing)
1034
+ else if (check.outcome === ReferenceCheckOutcome.Missing)
474
1035
  counts.missing += 1;
1036
+ else if (check.outcome === ReferenceCheckOutcome.Invalid)
1037
+ counts.invalid += 1;
475
1038
  else
476
1039
  counts.unknown += 1;
477
- const check = {
478
- entryId: entry.id,
479
- reference: ref,
480
- outcome,
481
- message: result.message,
482
- ...(result.confidence ? { symbolConfidence: result.confidence } : {}),
483
- ...(result.suggestion ? { suggestion: result.suggestion } : {}),
484
- };
485
- const isStaleOrMissing = outcome === ReferenceCheckOutcome.Stale || outcome === ReferenceCheckOutcome.Missing;
486
- // Symbol rename detection. Strict mode: emit `replaceWith.path`
487
- // only for the single unambiguous candidate. Wide mode also
488
- // emits scored candidate lists for the multi-candidate cases that
489
- // strict silently drops.
490
- if (isStaleOrMissing && ref.kind === 'symbol' && ref.symbol) {
491
- const all = (getSymbolIndex().get(ref.symbol) ?? []).filter((p) => p !== ref.path);
492
- if (all.length === 1) {
493
- check.replaceWith = {
494
- path: all[0],
495
- rationale: `\`${ref.symbol}\` is exported from \`${all[0]}\` — sole candidate.`,
496
- strategy: RenameStrategy.Strict,
497
- };
498
- }
499
- else if (strategy === RenameStrategy.Wide && all.length > 1) {
500
- check.replaceWith = buildWideReplacement({
501
- stalePath: ref.path ?? '',
502
- paths: all,
503
- kindLabel: `symbol \`${ref.symbol}\``,
504
- });
505
- }
506
- }
507
- // File rename detection (directory move, basename match).
508
- if (isStaleOrMissing && ref.kind === 'file' && ref.path && !check.replaceWith) {
509
- const indexed = getFileBasenameIndex();
510
- const uniq = pickUniqueRenameCandidate(ref.path, indexed);
511
- if (uniq) {
512
- check.replaceWith = {
513
- path: uniq,
514
- rationale: `File basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\` (likely directory rename).`,
515
- strategy: RenameStrategy.Strict,
516
- };
517
- }
518
- else if (strategy === RenameStrategy.Wide) {
519
- const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
520
- if (all.length > 0) {
521
- check.replaceWith = buildWideReplacement({
522
- stalePath: ref.path,
523
- paths: all,
524
- kindLabel: `file \`${nodePath.basename(ref.path)}\``,
525
- });
526
- }
527
- }
528
- }
529
- // Directory rename detection.
530
- if (isStaleOrMissing && ref.kind === 'directory' && ref.path && !check.replaceWith) {
531
- const indexed = getDirBasenameIndex();
532
- const uniq = pickUniqueRenameCandidate(ref.path, indexed);
533
- if (uniq) {
534
- check.replaceWith = {
535
- path: uniq,
536
- rationale: `Directory basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\`.`,
537
- strategy: RenameStrategy.Strict,
538
- };
539
- }
540
- else if (strategy === RenameStrategy.Wide) {
541
- const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
542
- if (all.length > 0) {
543
- check.replaceWith = buildWideReplacement({
544
- stalePath: ref.path,
545
- paths: all,
546
- kindLabel: `directory \`${nodePath.basename(ref.path)}\``,
547
- });
548
- }
549
- }
1040
+ if (isCheckableOutcome(check.outcome)) {
1041
+ checkable += 1;
1042
+ if (isFailingOutcome(check.outcome))
1043
+ failing += 1;
550
1044
  }
551
1045
  referenceChecks.push(check);
552
1046
  }
553
- for (const anchor of entry.anchors ?? []) {
1047
+ for (const anchor of anchors) {
554
1048
  totalAnchors += 1;
555
1049
  const inspected = checkAnchor(inspection, anchor, options.graph);
1050
+ const failure = inspected.outcome === ReferenceCheckOutcome.Ok
1051
+ ? undefined
1052
+ : (inspected.failure ?? defaultFailure(anchor.kind, inspected.outcome));
1053
+ if (failure)
1054
+ failureCounts[failure] += 1;
1055
+ if (isCheckableOutcome(inspected.outcome)) {
1056
+ checkable += 1;
1057
+ if (isFailingOutcome(inspected.outcome))
1058
+ failing += 1;
1059
+ }
556
1060
  anchorChecks.push({
557
1061
  entryId: entry.id,
558
1062
  anchor,
559
1063
  outcome: inspected.outcome,
560
1064
  message: inspected.message,
1065
+ ...(failure ? { failure } : {}),
561
1066
  });
562
1067
  }
1068
+ const verdict = classifyChecks(checkable, failing);
1069
+ const declared = refs.length + anchors.length;
1070
+ const reason = verdict !== KnowledgeEntryVerdict.Unverifiable
1071
+ ? undefined
1072
+ : declared === 0
1073
+ ? KnowledgeUnverifiableReason.NoReferences
1074
+ : KnowledgeUnverifiableReason.OnlyUnverifiableReferences;
1075
+ const type = String(entry.type);
1076
+ entryVerdicts.push({
1077
+ entryId: entry.id,
1078
+ verdict,
1079
+ ...(reason ? { reason } : {}),
1080
+ source: relativeSource(projectRoot, entry.source?.origin),
1081
+ type,
1082
+ checkable,
1083
+ failing,
1084
+ });
1085
+ bumpBucket(byAssetKind[ReferenceAssetKind.Knowledge], declared, verdict);
1086
+ bumpBucket((byEntryType[type] ??= emptyKindBucket()), declared, verdict);
1087
+ advisories.push(...pathOnlyAdvisories(projectRoot, entry, refs, symbolIndexCache));
563
1088
  }
564
1089
  // Content-similarity boost: when multiple wide replacements in the
565
1090
  // SAME entry name the same candidate path, raise that candidate's score
@@ -567,6 +1092,129 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
567
1092
  // followed" case where a single per-reference signal is weak but the
568
1093
  // aggregate is strong.
569
1094
  applyEntryCorroborationBoost(referenceChecks);
1095
+ // Boundary rules and policy checks declare references too — swept by the
1096
+ // same checker, counted per kind, and NOT folded into the knowledge buckets
1097
+ // (their coverage is reported, not gated by default).
1098
+ const includeAssets = options.includeAssets !== false;
1099
+ const boundaryRules = includeAssets ? (inspection.boundaryRegistry?.list() ?? []) : [];
1100
+ const policyDecls = includeAssets ? listPolicyDeclarations(inspection) : [];
1101
+ const subjects = [
1102
+ ...boundaryRules.map((rule) => ({
1103
+ assetKind: ReferenceAssetKind.BoundaryRule,
1104
+ id: rule.id,
1105
+ source: relativeSource(projectRoot, inspection.boundarySources?.get(rule.id)?.file),
1106
+ references: rule.references ?? [],
1107
+ })),
1108
+ ...policyDecls.map((p) => ({
1109
+ assetKind: ReferenceAssetKind.Policy,
1110
+ id: p.qualifiedId,
1111
+ source: relativeSource(projectRoot, p.sourceFile),
1112
+ references: p.references ?? [],
1113
+ })),
1114
+ ];
1115
+ for (const subject of subjects) {
1116
+ if (options.changedFiles &&
1117
+ !subjectTouchesChangedFiles({ source: subject.source ?? '(unknown source)', references: subject.references }, options.changedFiles)) {
1118
+ continue;
1119
+ }
1120
+ let checkable = 0;
1121
+ let failing = 0;
1122
+ for (const ref of subject.references) {
1123
+ const check = checkOne(subject.id, ref, subject.assetKind);
1124
+ if (isCheckableOutcome(check.outcome)) {
1125
+ checkable += 1;
1126
+ if (isFailingOutcome(check.outcome))
1127
+ failing += 1;
1128
+ }
1129
+ assetReferenceChecks.push(check);
1130
+ }
1131
+ bumpBucket(byAssetKind[subject.assetKind], subject.references.length, classifyChecks(checkable, failing));
1132
+ }
1133
+ // IMPLICIT references (boundary rules only — their scope is data): a `from`
1134
+ // glob whose static prefix does not exist governs nothing there. Advisory
1135
+ // unless `--fail-on implicit`; never counted in `failureCounts`. Policy
1136
+ // checks keep their scope inside `evaluate()`, which cannot be read without
1137
+ // running it — that gap is reported, not guessed at.
1138
+ if (!options.changedFiles) {
1139
+ for (const rule of boundaryRules) {
1140
+ for (const glob of rule.from ?? []) {
1141
+ if (glob.startsWith('!'))
1142
+ continue;
1143
+ const prefix = staticGlobPrefix(glob);
1144
+ if (!prefix || fileExists(projectRoot, prefix))
1145
+ continue;
1146
+ const message = `boundary rule ${rule.id} scopes '${glob}' but ${prefix} does not exist — the rule governs nothing there.`;
1147
+ assetReferenceChecks.push({
1148
+ entryId: rule.id,
1149
+ reference: { kind: 'directory', path: prefix, note: `implicit: from '${glob}'` },
1150
+ outcome: ReferenceCheckOutcome.Stale,
1151
+ failure: ReferenceFailure.PathMissing,
1152
+ implicit: true,
1153
+ assetKind: ReferenceAssetKind.BoundaryRule,
1154
+ message,
1155
+ suggestion: 'Fix or remove the glob (gate it with --fail-on implicit).',
1156
+ });
1157
+ advisories.push({
1158
+ code: KnowledgeAdvisoryCode.ImplicitPathMissing,
1159
+ subjectId: rule.id,
1160
+ assetKind: ReferenceAssetKind.BoundaryRule,
1161
+ message,
1162
+ });
1163
+ }
1164
+ }
1165
+ }
1166
+ const policySweep = {
1167
+ loaded: isReferenceCacheWarm(inspection),
1168
+ declared: policyDecls.length,
1169
+ withReferences: policyDecls.filter((p) => (p.references?.length ?? 0) > 0).length,
1170
+ };
1171
+ if (policySweep.declared > policySweep.withReferences) {
1172
+ advisories.push({
1173
+ code: KnowledgeAdvisoryCode.PolicyScopeUnverifiable,
1174
+ subjectId: 'policy',
1175
+ assetKind: ReferenceAssetKind.Policy,
1176
+ message: `policy: ${policySweep.declared} declared check(s), ${policySweep.withReferences} with references — the scope of the rest is unverifiable (it lives inside evaluate()).`,
1177
+ });
1178
+ }
1179
+ const verified = entryVerdicts.filter((v) => v.verdict === KnowledgeEntryVerdict.Verified).length;
1180
+ const stale = entryVerdicts.filter((v) => v.verdict === KnowledgeEntryVerdict.Stale).length;
1181
+ const unverifiableIds = entryVerdicts
1182
+ .filter((v) => v.verdict === KnowledgeEntryVerdict.Unverifiable)
1183
+ .map((v) => v.entryId);
1184
+ const entriesInScope = entryVerdicts.length;
1185
+ const coverage = {
1186
+ entriesInScope,
1187
+ verified,
1188
+ stale,
1189
+ unverifiable: unverifiableIds.length,
1190
+ unverifiablePct: pct1(unverifiableIds.length, entriesInScope),
1191
+ referencedRatio: entriesInScope > 0 ? (verified + stale) / entriesInScope : 0,
1192
+ };
1193
+ let age;
1194
+ if (options.staleAfterDays !== undefined) {
1195
+ const asOf = options.asOf ?? todayUtcIso();
1196
+ const inScope = new Set(entryVerdicts.map((v) => v.entryId));
1197
+ const aged = [];
1198
+ const neverVerified = [];
1199
+ for (const entry of inspection.knowledgeEntries) {
1200
+ if (!inScope.has(entry.id))
1201
+ continue;
1202
+ const days = entry.verifiedOn ? verifiedOnAgeDays(entry.verifiedOn, asOf) : undefined;
1203
+ if (days === undefined) {
1204
+ neverVerified.push(entry.id);
1205
+ }
1206
+ else if (days > options.staleAfterDays) {
1207
+ aged.push({
1208
+ entryId: entry.id,
1209
+ verifiedOn: entry.verifiedOn,
1210
+ ageDays: days,
1211
+ source: relativeSource(projectRoot, entry.source?.origin),
1212
+ });
1213
+ }
1214
+ }
1215
+ aged.sort((a, b) => b.ageDays - a.ageDays || a.entryId.localeCompare(b.entryId));
1216
+ age = { asOf, staleAfterDays: options.staleAfterDays, aged, neverVerified };
1217
+ }
570
1218
  return {
571
1219
  schema: KNOWLEDGE_STALE_SCHEMA,
572
1220
  entries: inspection.knowledgeEntries.length,
@@ -575,6 +1223,18 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
575
1223
  counts,
576
1224
  referenceChecks,
577
1225
  anchorChecks,
1226
+ entriesInScope,
1227
+ coverage,
1228
+ entryVerdicts,
1229
+ unverifiableIds,
1230
+ failureCounts,
1231
+ byAssetKind,
1232
+ byEntryType,
1233
+ byReferenceKind,
1234
+ assetReferenceChecks,
1235
+ advisories,
1236
+ policySweep,
1237
+ ...(age ? { age } : {}),
578
1238
  };
579
1239
  }
580
1240
  /**
@@ -891,51 +1551,77 @@ function checkAnchor(inspection, anchor, graph) {
891
1551
  switch (anchor.kind) {
892
1552
  case 'file':
893
1553
  if (!anchor.path) {
894
- return { outcome: ReferenceCheckOutcome.Unknown, message: 'anchor has no path' };
1554
+ return {
1555
+ outcome: ReferenceCheckOutcome.Invalid,
1556
+ failure: ReferenceFailure.Malformed,
1557
+ message: 'malformed anchor: file anchor missing required field `path`.',
1558
+ };
895
1559
  }
896
1560
  if (fileExists(inspection.projectRoot, anchor.path)) {
897
1561
  return { outcome: ReferenceCheckOutcome.Ok, message: `anchor file exists: ${anchor.path}` };
898
1562
  }
899
- return { outcome: ReferenceCheckOutcome.Stale, message: `anchor file missing: ${anchor.path}` };
1563
+ return {
1564
+ outcome: ReferenceCheckOutcome.Stale,
1565
+ failure: ReferenceFailure.PathMissing,
1566
+ message: `anchor file missing: ${anchor.path}`,
1567
+ };
900
1568
  case 'symbol':
901
1569
  return checkSymbolAnchor(inspection, anchor, graph);
902
- case 'command':
903
- if (anchor.targetId && commandExistsInInspection(inspection, anchor.targetId)) {
904
- return { outcome: ReferenceCheckOutcome.Ok, message: `command exists: ${anchor.targetId}` };
1570
+ case 'command': {
1571
+ if (!anchor.targetId) {
1572
+ return { outcome: ReferenceCheckOutcome.Stale, message: 'command anchor unresolved: ?' };
905
1573
  }
906
- return { outcome: ReferenceCheckOutcome.Stale, message: `command anchor unresolved: ${anchor.targetId ?? '?'}` };
1574
+ const r = checkCommandString(inspection, anchor.targetId, 'command anchor');
1575
+ return { outcome: r.outcome, message: r.message };
1576
+ }
907
1577
  case 'construct':
908
- if (anchor.targetId && constructExists(inspection, anchor.targetId)) {
909
- return { outcome: ReferenceCheckOutcome.Ok, message: `construct exists: ${anchor.targetId}` };
910
- }
911
- return { outcome: ReferenceCheckOutcome.Stale, message: `construct anchor unresolved: ${anchor.targetId ?? '?'}` };
1578
+ return checkAnchorId(inspection, 'construct', anchor.targetId);
912
1579
  case 'template':
913
- if (anchor.targetId && templateExists(inspection, anchor.targetId)) {
914
- return { outcome: ReferenceCheckOutcome.Ok, message: `template exists: ${anchor.targetId}` };
915
- }
916
- return { outcome: ReferenceCheckOutcome.Stale, message: `template anchor unresolved: ${anchor.targetId ?? '?'}` };
1580
+ return checkAnchorId(inspection, 'template', anchor.targetId);
917
1581
  case 'helper':
918
- if (anchor.targetId && helperExists(anchor.targetId)) {
919
- return { outcome: ReferenceCheckOutcome.Ok, message: `helper exists: ${anchor.targetId}` };
920
- }
921
- return { outcome: ReferenceCheckOutcome.Stale, message: `helper anchor unresolved: ${anchor.targetId ?? '?'}` };
1582
+ return checkAnchorId(inspection, 'helper', anchor.targetId);
922
1583
  case 'playbook':
923
- if (anchor.targetId && playbookExists(inspection, anchor.targetId)) {
924
- return { outcome: ReferenceCheckOutcome.Ok, message: `playbook exists: ${anchor.targetId}` };
925
- }
926
- return { outcome: ReferenceCheckOutcome.Stale, message: `playbook anchor unresolved: ${anchor.targetId ?? '?'}` };
1584
+ return checkAnchorId(inspection, 'playbook', anchor.targetId);
927
1585
  case 'policy':
928
- if (anchor.targetId && policyExists(inspection, anchor.targetId)) {
929
- return { outcome: ReferenceCheckOutcome.Ok, message: `policy exists: ${anchor.targetId}` };
930
- }
931
- return { outcome: ReferenceCheckOutcome.Stale, message: `policy anchor unresolved: ${anchor.targetId ?? '?'}` };
1586
+ return checkAnchorId(inspection, 'policy', anchor.targetId);
1587
+ default:
1588
+ return {
1589
+ outcome: ReferenceCheckOutcome.Invalid,
1590
+ failure: ReferenceFailure.Malformed,
1591
+ message: `malformed anchor: unsupported anchor kind "${String(anchor.kind)}".`,
1592
+ };
932
1593
  }
933
1594
  }
1595
+ /**
1596
+ * An id-keyed anchor, through the same guarded resolver as a reference — an
1597
+ * unloaded registry is NOT VERIFIED, never "unresolved".
1598
+ */
1599
+ function checkAnchorId(inspection, kind, targetId) {
1600
+ if (!targetId) {
1601
+ return {
1602
+ outcome: ReferenceCheckOutcome.Stale,
1603
+ failure: ReferenceFailure.IdUnregistered,
1604
+ message: `${kind} anchor unresolved: ?`,
1605
+ };
1606
+ }
1607
+ const r = checkRegisteredId(inspection, kind, targetId, kind);
1608
+ if (r.outcome === ReferenceCheckOutcome.Ok) {
1609
+ return { outcome: ReferenceCheckOutcome.Ok, message: `${kind} exists: ${targetId}` };
1610
+ }
1611
+ if (r.outcome === ReferenceCheckOutcome.Unknown) {
1612
+ return { outcome: r.outcome, message: r.message, failure: ReferenceFailure.Unverifiable };
1613
+ }
1614
+ return {
1615
+ outcome: ReferenceCheckOutcome.Stale,
1616
+ failure: ReferenceFailure.IdUnregistered,
1617
+ message: `${kind} anchor unresolved: ${targetId}`,
1618
+ };
1619
+ }
934
1620
  function checkSymbolAnchor(inspection, anchor, graph) {
935
1621
  const r = checkSymbolReference(inspection.projectRoot, {
936
1622
  kind: 'symbol',
937
1623
  symbol: anchor.symbol,
938
1624
  ...(anchor.path ? { path: anchor.path } : {}),
939
1625
  }, graph);
940
- return { outcome: r.outcome, message: r.message };
1626
+ return { outcome: r.outcome, message: r.message, ...(r.failure ? { failure: r.failure } : {}) };
941
1627
  }