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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (693) 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 +6 -0
  69. package/dist/code-intelligence-doctor.d.ts.map +1 -1
  70. package/dist/code-intelligence-doctor.js +22 -7
  71. package/dist/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 +23 -2
  138. package/dist/doc-references.d.ts.map +1 -1
  139. package/dist/doc-references.js +96 -17
  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.js +1 -1
  162. package/dist/healing-plan.d.ts.map +1 -1
  163. package/dist/healing-plan.js +3 -4
  164. package/dist/helper-catalog.d.ts +25 -0
  165. package/dist/helper-catalog.d.ts.map +1 -0
  166. package/dist/helper-catalog.js +91 -0
  167. package/dist/helper-registry.d.ts.map +1 -1
  168. package/dist/helper-registry.js +17 -1
  169. package/dist/helper-view.d.ts +33 -0
  170. package/dist/helper-view.d.ts.map +1 -0
  171. package/dist/helper-view.js +1 -0
  172. package/dist/i-asset-doctor-flags.d.ts +16 -0
  173. package/dist/i-asset-doctor-flags.d.ts.map +1 -0
  174. package/dist/i-asset-doctor-flags.js +1 -0
  175. package/dist/i-command-resolution.d.ts +22 -0
  176. package/dist/i-command-resolution.d.ts.map +1 -0
  177. package/dist/i-command-resolution.js +1 -0
  178. package/dist/i-command-resolve-options.d.ts +24 -0
  179. package/dist/i-command-resolve-options.d.ts.map +1 -0
  180. package/dist/i-command-resolve-options.js +1 -0
  181. package/dist/i-command-safety.d.ts +13 -0
  182. package/dist/i-command-safety.d.ts.map +1 -0
  183. package/dist/i-command-safety.js +1 -0
  184. package/dist/i-contribution-accepted-entry.d.ts +18 -0
  185. package/dist/i-contribution-accepted-entry.d.ts.map +1 -0
  186. package/dist/i-contribution-accepted-entry.js +1 -0
  187. package/dist/i-contribution-entry-rejection.d.ts +21 -0
  188. package/dist/i-contribution-entry-rejection.d.ts.map +1 -0
  189. package/dist/i-contribution-entry-rejection.js +1 -0
  190. package/dist/i-contribution-file-issue.d.ts +18 -0
  191. package/dist/i-contribution-file-issue.d.ts.map +1 -0
  192. package/dist/i-contribution-file-issue.js +1 -0
  193. package/dist/i-contribution-file-report.d.ts +40 -0
  194. package/dist/i-contribution-file-report.d.ts.map +1 -0
  195. package/dist/i-contribution-file-report.js +1 -0
  196. package/dist/i-contribution-file-validation.d.ts +24 -0
  197. package/dist/i-contribution-file-validation.d.ts.map +1 -0
  198. package/dist/i-contribution-file-validation.js +1 -0
  199. package/dist/i-contributions-report.d.ts +31 -0
  200. package/dist/i-contributions-report.d.ts.map +1 -0
  201. package/dist/i-contributions-report.js +1 -0
  202. package/dist/i-declared-xref-field.d.ts +25 -0
  203. package/dist/i-declared-xref-field.d.ts.map +1 -0
  204. package/dist/i-declared-xref-field.js +1 -0
  205. package/dist/i-declared-xref-issue.d.ts +25 -0
  206. package/dist/i-declared-xref-issue.d.ts.map +1 -0
  207. package/dist/i-declared-xref-issue.js +1 -0
  208. package/dist/i-declared-xref-report.d.ts +48 -0
  209. package/dist/i-declared-xref-report.d.ts.map +1 -0
  210. package/dist/i-declared-xref-report.js +1 -0
  211. package/dist/i-declared-xref-row.d.ts +29 -0
  212. package/dist/i-declared-xref-row.d.ts.map +1 -0
  213. package/dist/i-declared-xref-row.js +1 -0
  214. package/dist/i-doctor-verdict.d.ts +10 -0
  215. package/dist/i-doctor-verdict.d.ts.map +1 -0
  216. package/dist/i-doctor-verdict.js +1 -0
  217. package/dist/i-kind-outcomes.d.ts +13 -0
  218. package/dist/i-kind-outcomes.d.ts.map +1 -0
  219. package/dist/i-kind-outcomes.js +1 -0
  220. package/dist/i-pack-doctor-verdict.d.ts +15 -0
  221. package/dist/i-pack-doctor-verdict.d.ts.map +1 -0
  222. package/dist/i-pack-doctor-verdict.js +1 -0
  223. package/dist/i-probed-id-field.d.ts +17 -0
  224. package/dist/i-probed-id-field.d.ts.map +1 -0
  225. package/dist/i-probed-id-field.js +1 -0
  226. package/dist/i-reference-kind-declaration.d.ts +38 -0
  227. package/dist/i-reference-kind-declaration.d.ts.map +1 -0
  228. package/dist/i-reference-kind-declaration.js +1 -0
  229. package/dist/i-reference-warm-options.d.ts +21 -0
  230. package/dist/i-reference-warm-options.d.ts.map +1 -0
  231. package/dist/i-reference-warm-options.js +1 -0
  232. package/dist/i-registry-outcomes.d.ts +16 -0
  233. package/dist/i-registry-outcomes.d.ts.map +1 -0
  234. package/dist/i-registry-outcomes.js +1 -0
  235. package/dist/i-scaffold-pattern-doctor-report.d.ts +35 -0
  236. package/dist/i-scaffold-pattern-doctor-report.d.ts.map +1 -0
  237. package/dist/i-scaffold-pattern-doctor-report.js +1 -0
  238. package/dist/i-search-tuning-key-probe.d.ts +30 -0
  239. package/dist/i-search-tuning-key-probe.d.ts.map +1 -0
  240. package/dist/i-search-tuning-key-probe.js +1 -0
  241. package/dist/i-search-tuning-lint-issue.d.ts +61 -0
  242. package/dist/i-search-tuning-lint-issue.d.ts.map +1 -0
  243. package/dist/i-search-tuning-lint-issue.js +1 -0
  244. package/dist/i-search-tuning-lint-report.d.ts +38 -0
  245. package/dist/i-search-tuning-lint-report.d.ts.map +1 -0
  246. package/dist/i-search-tuning-lint-report.js +1 -0
  247. package/dist/i-unresolvable-reference-scan.d.ts +14 -0
  248. package/dist/i-unresolvable-reference-scan.d.ts.map +1 -0
  249. package/dist/i-unresolvable-reference-scan.js +1 -0
  250. package/dist/i-unresolvable-reference.d.ts +29 -0
  251. package/dist/i-unresolvable-reference.d.ts.map +1 -0
  252. package/dist/i-unresolvable-reference.js +1 -0
  253. package/dist/i-workspace-profile-payload.d.ts +12 -0
  254. package/dist/i-workspace-profile-payload.d.ts.map +1 -0
  255. package/dist/i-workspace-profile-payload.js +1 -0
  256. package/dist/impact-analysis.d.ts +15 -0
  257. package/dist/impact-analysis.d.ts.map +1 -1
  258. package/dist/impact-analysis.js +58 -13
  259. package/dist/impact-render.d.ts.map +1 -1
  260. package/dist/impact-render.js +22 -0
  261. package/dist/import-hygiene.d.ts +35 -1
  262. package/dist/import-hygiene.d.ts.map +1 -1
  263. package/dist/import-hygiene.js +107 -13
  264. package/dist/index.d.ts +141 -0
  265. package/dist/index.d.ts.map +1 -1
  266. package/dist/index.js +172 -0
  267. package/dist/inspection-discovery.d.ts +45 -0
  268. package/dist/inspection-discovery.d.ts.map +1 -0
  269. package/dist/inspection-discovery.js +70 -0
  270. package/dist/knowledge-advisory-code.d.ts +17 -0
  271. package/dist/knowledge-advisory-code.d.ts.map +1 -0
  272. package/dist/knowledge-advisory-code.js +17 -0
  273. package/dist/knowledge-aged-entry.d.ts +10 -0
  274. package/dist/knowledge-aged-entry.d.ts.map +1 -0
  275. package/dist/knowledge-aged-entry.js +1 -0
  276. package/dist/knowledge-authoring.d.ts +24 -1
  277. package/dist/knowledge-authoring.d.ts.map +1 -1
  278. package/dist/knowledge-authoring.js +46 -7
  279. package/dist/knowledge-entry-verdict-record.d.ts +22 -0
  280. package/dist/knowledge-entry-verdict-record.d.ts.map +1 -0
  281. package/dist/knowledge-entry-verdict-record.js +1 -0
  282. package/dist/knowledge-entry-verdict.d.ts +17 -0
  283. package/dist/knowledge-entry-verdict.d.ts.map +1 -0
  284. package/dist/knowledge-entry-verdict.js +17 -0
  285. package/dist/knowledge-graph.d.ts +6 -0
  286. package/dist/knowledge-graph.d.ts.map +1 -1
  287. package/dist/knowledge-graph.js +18 -7
  288. package/dist/knowledge-kind-bucket.d.ts +19 -0
  289. package/dist/knowledge-kind-bucket.d.ts.map +1 -0
  290. package/dist/knowledge-kind-bucket.js +1 -0
  291. package/dist/knowledge-load-failure.d.ts +23 -0
  292. package/dist/knowledge-load-failure.d.ts.map +1 -0
  293. package/dist/knowledge-load-failure.js +1 -0
  294. package/dist/knowledge-reference-kind-bucket.d.ts +11 -0
  295. package/dist/knowledge-reference-kind-bucket.d.ts.map +1 -0
  296. package/dist/knowledge-reference-kind-bucket.js +1 -0
  297. package/dist/knowledge-stale-advisory.d.ts +14 -0
  298. package/dist/knowledge-stale-advisory.d.ts.map +1 -0
  299. package/dist/knowledge-stale-advisory.js +1 -0
  300. package/dist/knowledge-stale-coverage.d.ts +19 -0
  301. package/dist/knowledge-stale-coverage.d.ts.map +1 -0
  302. package/dist/knowledge-stale-coverage.js +1 -0
  303. package/dist/knowledge-stale-gate-flags.d.ts +33 -0
  304. package/dist/knowledge-stale-gate-flags.d.ts.map +1 -0
  305. package/dist/knowledge-stale-gate-flags.js +1 -0
  306. package/dist/knowledge-stale-gate-input.d.ts +46 -0
  307. package/dist/knowledge-stale-gate-input.d.ts.map +1 -0
  308. package/dist/knowledge-stale-gate-input.js +1 -0
  309. package/dist/knowledge-stale-gate-result.d.ts +33 -0
  310. package/dist/knowledge-stale-gate-result.d.ts.map +1 -0
  311. package/dist/knowledge-stale-gate-result.js +1 -0
  312. package/dist/knowledge-stale-gate-rule.d.ts +33 -0
  313. package/dist/knowledge-stale-gate-rule.d.ts.map +1 -0
  314. package/dist/knowledge-stale-gate-rule.js +1 -0
  315. package/dist/knowledge-stale-gate-violation.d.ts +13 -0
  316. package/dist/knowledge-stale-gate-violation.d.ts.map +1 -0
  317. package/dist/knowledge-stale-gate-violation.js +1 -0
  318. package/dist/knowledge-stale-gate.d.ts +83 -0
  319. package/dist/knowledge-stale-gate.d.ts.map +1 -0
  320. package/dist/knowledge-stale-gate.js +429 -0
  321. package/dist/knowledge-stale-quality-gate.d.ts +26 -0
  322. package/dist/knowledge-stale-quality-gate.d.ts.map +1 -0
  323. package/dist/knowledge-stale-quality-gate.js +110 -0
  324. package/dist/knowledge-stale.d.ts +122 -9
  325. package/dist/knowledge-stale.d.ts.map +1 -1
  326. package/dist/knowledge-stale.js +996 -291
  327. package/dist/knowledge-unverifiable-reason.d.ts +12 -0
  328. package/dist/knowledge-unverifiable-reason.d.ts.map +1 -0
  329. package/dist/knowledge-unverifiable-reason.js +12 -0
  330. package/dist/loader-diagnostics.d.ts +14 -2
  331. package/dist/loader-diagnostics.d.ts.map +1 -1
  332. package/dist/match-terms.d.ts +48 -0
  333. package/dist/match-terms.d.ts.map +1 -0
  334. package/dist/match-terms.js +155 -0
  335. package/dist/migration-profile-registry.d.ts +10 -0
  336. package/dist/migration-profile-registry.d.ts.map +1 -1
  337. package/dist/migration-profile-registry.js +57 -34
  338. package/dist/migration-readiness.d.ts.map +1 -1
  339. package/dist/migration-readiness.js +1 -3
  340. package/dist/nearest-id.d.ts +10 -26
  341. package/dist/nearest-id.d.ts.map +1 -1
  342. package/dist/nearest-id.js +9 -59
  343. package/dist/pack-asset-freshness.d.ts +65 -0
  344. package/dist/pack-asset-freshness.d.ts.map +1 -0
  345. package/dist/pack-asset-freshness.js +340 -0
  346. package/dist/pack-author-ux.d.ts +24 -3
  347. package/dist/pack-author-ux.d.ts.map +1 -1
  348. package/dist/pack-author-ux.js +87 -93
  349. package/dist/pack-contributions-inventory.d.ts +79 -39
  350. package/dist/pack-contributions-inventory.d.ts.map +1 -1
  351. package/dist/pack-contributions-inventory.js +443 -286
  352. package/dist/pack-doctor-verdict.d.ts +29 -0
  353. package/dist/pack-doctor-verdict.d.ts.map +1 -0
  354. package/dist/pack-doctor-verdict.js +45 -0
  355. package/dist/pack-doctor.d.ts +74 -1
  356. package/dist/pack-doctor.d.ts.map +1 -1
  357. package/dist/pack-doctor.js +281 -1
  358. package/dist/pack-helper-plan.d.ts +24 -0
  359. package/dist/pack-helper-plan.d.ts.map +1 -0
  360. package/dist/pack-helper-plan.js +77 -0
  361. package/dist/pack-helper-registry.d.ts +14 -0
  362. package/dist/pack-helper-registry.d.ts.map +1 -1
  363. package/dist/pack-helper-registry.js +76 -37
  364. package/dist/pack-manifest-reader.d.ts +8 -0
  365. package/dist/pack-manifest-reader.d.ts.map +1 -0
  366. package/dist/pack-manifest-reader.js +79 -0
  367. package/dist/pack-release-check.d.ts +14 -1
  368. package/dist/pack-release-check.d.ts.map +1 -1
  369. package/dist/pack-release-check.js +23 -1
  370. package/dist/pack-signature-status.d.ts +18 -6
  371. package/dist/pack-signature-status.d.ts.map +1 -1
  372. package/dist/pack-signature-status.js +59 -105
  373. package/dist/pack-test-runner.d.ts +27 -5
  374. package/dist/pack-test-runner.d.ts.map +1 -1
  375. package/dist/pack-test-runner.js +91 -35
  376. package/dist/pack-typecheck.d.ts +8 -0
  377. package/dist/pack-typecheck.d.ts.map +1 -0
  378. package/dist/pack-typecheck.js +76 -0
  379. package/dist/plan-review.d.ts +2 -0
  380. package/dist/plan-review.d.ts.map +1 -1
  381. package/dist/plan-review.js +17 -5
  382. package/dist/plan-simulation.d.ts.map +1 -1
  383. package/dist/plan-simulation.js +16 -5
  384. package/dist/playbook-load-issue.d.ts +16 -0
  385. package/dist/playbook-load-issue.d.ts.map +1 -0
  386. package/dist/playbook-load-issue.js +1 -0
  387. package/dist/playbook-registry.d.ts +32 -1
  388. package/dist/playbook-registry.d.ts.map +1 -1
  389. package/dist/playbook-registry.js +144 -55
  390. package/dist/policy-declaration.d.ts +18 -0
  391. package/dist/policy-declaration.d.ts.map +1 -0
  392. package/dist/policy-declaration.js +1 -0
  393. package/dist/policy-engine.d.ts +8 -0
  394. package/dist/policy-engine.d.ts.map +1 -1
  395. package/dist/policy-engine.js +26 -9
  396. package/dist/policy-overrides.d.ts.map +1 -1
  397. package/dist/policy-overrides.js +4 -4
  398. package/dist/policy-registry.d.ts +27 -0
  399. package/dist/policy-registry.d.ts.map +1 -1
  400. package/dist/policy-registry.js +87 -20
  401. package/dist/probed-id-fields.d.ts +21 -0
  402. package/dist/probed-id-fields.d.ts.map +1 -0
  403. package/dist/probed-id-fields.js +25 -0
  404. package/dist/probed-id-source.d.ts +11 -0
  405. package/dist/probed-id-source.d.ts.map +1 -0
  406. package/dist/probed-id-source.js +11 -0
  407. package/dist/profile-registry.d.ts +25 -2
  408. package/dist/profile-registry.d.ts.map +1 -1
  409. package/dist/profile-registry.js +59 -6
  410. package/dist/quality-gate-examination.d.ts +15 -0
  411. package/dist/quality-gate-examination.d.ts.map +1 -0
  412. package/dist/quality-gate-examination.js +15 -0
  413. package/dist/quality-gate-row-status.d.ts +19 -0
  414. package/dist/quality-gate-row-status.d.ts.map +1 -0
  415. package/dist/quality-gate-row-status.js +28 -0
  416. package/dist/quality-gate-status.d.ts +16 -0
  417. package/dist/quality-gate-status.d.ts.map +1 -0
  418. package/dist/quality-gate-status.js +16 -0
  419. package/dist/quality-html.d.ts.map +1 -1
  420. package/dist/quality-html.js +14 -3
  421. package/dist/quality-report-coverage.d.ts +26 -0
  422. package/dist/quality-report-coverage.d.ts.map +1 -0
  423. package/dist/quality-report-coverage.js +48 -0
  424. package/dist/quality-report.d.ts +28 -1
  425. package/dist/quality-report.d.ts.map +1 -1
  426. package/dist/quality-report.js +263 -35
  427. package/dist/query-intent-kind.d.ts +18 -0
  428. package/dist/query-intent-kind.d.ts.map +1 -0
  429. package/dist/query-intent-kind.js +18 -0
  430. package/dist/query-intent-result.d.ts +23 -0
  431. package/dist/query-intent-result.d.ts.map +1 -0
  432. package/dist/query-intent-result.js +1 -0
  433. package/dist/query-intent.d.ts +18 -0
  434. package/dist/query-intent.d.ts.map +1 -0
  435. package/dist/query-intent.js +336 -0
  436. package/dist/query-resolver.d.ts +18 -0
  437. package/dist/query-resolver.d.ts.map +1 -1
  438. package/dist/query-resolver.js +7 -4
  439. package/dist/ranked-recommendations.d.ts +36 -0
  440. package/dist/ranked-recommendations.d.ts.map +1 -0
  441. package/dist/ranked-recommendations.js +1 -0
  442. package/dist/ranker-explainability.d.ts.map +1 -1
  443. package/dist/ranker-explainability.js +38 -10
  444. package/dist/recommend-recipe.d.ts +17 -0
  445. package/dist/recommend-recipe.d.ts.map +1 -0
  446. package/dist/recommend-recipe.js +1 -0
  447. package/dist/recommend-recipes.d.ts +9 -0
  448. package/dist/recommend-recipes.d.ts.map +1 -0
  449. package/dist/recommend-recipes.js +106 -0
  450. package/dist/recommendation-candidate.d.ts +30 -0
  451. package/dist/recommendation-candidate.d.ts.map +1 -0
  452. package/dist/recommendation-candidate.js +1 -0
  453. package/dist/recommendation-confidence.d.ts +27 -0
  454. package/dist/recommendation-confidence.d.ts.map +1 -0
  455. package/dist/recommendation-confidence.js +1 -0
  456. package/dist/recommendation-ranking-options.d.ts +24 -0
  457. package/dist/recommendation-ranking-options.d.ts.map +1 -0
  458. package/dist/recommendation-ranking-options.js +1 -0
  459. package/dist/recommendation-ranking.d.ts +50 -0
  460. package/dist/recommendation-ranking.d.ts.map +1 -0
  461. package/dist/recommendation-ranking.js +527 -0
  462. package/dist/recommendation-source.d.ts +23 -0
  463. package/dist/recommendation-source.d.ts.map +1 -0
  464. package/dist/recommendation-source.js +23 -0
  465. package/dist/recommendation-suppression.d.ts +14 -0
  466. package/dist/recommendation-suppression.d.ts.map +1 -0
  467. package/dist/recommendation-suppression.js +14 -0
  468. package/dist/reference-asset-kind.d.ts +14 -0
  469. package/dist/reference-asset-kind.d.ts.map +1 -0
  470. package/dist/reference-asset-kind.js +14 -0
  471. package/dist/reference-content-check.d.ts +32 -0
  472. package/dist/reference-content-check.d.ts.map +1 -0
  473. package/dist/reference-content-check.js +143 -0
  474. package/dist/reference-failure.d.ts +31 -0
  475. package/dist/reference-failure.d.ts.map +1 -0
  476. package/dist/reference-failure.js +31 -0
  477. package/dist/reference-id-status.d.ts +15 -0
  478. package/dist/reference-id-status.d.ts.map +1 -0
  479. package/dist/reference-id-status.js +15 -0
  480. package/dist/reference-kind-declarations.d.ts +42 -0
  481. package/dist/reference-kind-declarations.d.ts.map +1 -0
  482. package/dist/reference-kind-declarations.js +142 -0
  483. package/dist/reference-lookup.d.ts +6 -0
  484. package/dist/reference-lookup.d.ts.map +1 -1
  485. package/dist/reference-lookup.js +13 -5
  486. package/dist/reference-registry.d.ts +109 -11
  487. package/dist/reference-registry.d.ts.map +1 -1
  488. package/dist/reference-registry.js +186 -30
  489. package/dist/reference-subject.d.ts +20 -0
  490. package/dist/reference-subject.d.ts.map +1 -0
  491. package/dist/reference-subject.js +1 -0
  492. package/dist/registration-hint-discovery-status.d.ts +25 -0
  493. package/dist/registration-hint-discovery-status.d.ts.map +1 -0
  494. package/dist/registration-hint-discovery-status.js +25 -0
  495. package/dist/registration-hint-registry.d.ts +80 -0
  496. package/dist/registration-hint-registry.d.ts.map +1 -1
  497. package/dist/registration-hint-registry.js +356 -138
  498. package/dist/registry-lifecycle.d.ts +200 -19
  499. package/dist/registry-lifecycle.d.ts.map +1 -1
  500. package/dist/registry-lifecycle.js +526 -136
  501. package/dist/release-readiness.d.ts.map +1 -1
  502. package/dist/release-readiness.js +10 -7
  503. package/dist/release-smoke.d.ts.map +1 -1
  504. package/dist/release-smoke.js +11 -7
  505. package/dist/report-site.d.ts.map +1 -1
  506. package/dist/report-site.js +25 -7
  507. package/dist/repository-intelligence.d.ts.map +1 -1
  508. package/dist/repository-intelligence.js +3 -2
  509. package/dist/repository-knowledge-model.d.ts.map +1 -1
  510. package/dist/repository-knowledge-model.js +18 -5
  511. package/dist/resolve-project-config.d.ts +64 -0
  512. package/dist/resolve-project-config.d.ts.map +1 -1
  513. package/dist/resolve-project-config.js +240 -32
  514. package/dist/reuse/compute-reuse-coverage.d.ts +29 -0
  515. package/dist/reuse/compute-reuse-coverage.d.ts.map +1 -0
  516. package/dist/reuse/compute-reuse-coverage.js +198 -0
  517. package/dist/reuse/rank-reuse-candidates.d.ts +38 -0
  518. package/dist/reuse/rank-reuse-candidates.d.ts.map +1 -0
  519. package/dist/reuse/rank-reuse-candidates.js +212 -0
  520. package/dist/reuse/rank-reuse-options.d.ts +20 -0
  521. package/dist/reuse/rank-reuse-options.d.ts.map +1 -0
  522. package/dist/reuse/rank-reuse-options.js +1 -0
  523. package/dist/reuse/rank-reuse-suggestions.d.ts +15 -0
  524. package/dist/reuse/rank-reuse-suggestions.d.ts.map +1 -0
  525. package/dist/reuse/rank-reuse-suggestions.js +31 -0
  526. package/dist/reuse/resolve-curated-reuse.d.ts +43 -0
  527. package/dist/reuse/resolve-curated-reuse.d.ts.map +1 -0
  528. package/dist/reuse/resolve-curated-reuse.js +115 -0
  529. package/dist/reuse/reuse-candidate.d.ts +29 -0
  530. package/dist/reuse/reuse-candidate.d.ts.map +1 -0
  531. package/dist/reuse/reuse-candidate.js +1 -0
  532. package/dist/reuse/reuse-coverage-report.d.ts +74 -0
  533. package/dist/reuse/reuse-coverage-report.d.ts.map +1 -0
  534. package/dist/reuse/reuse-coverage-report.js +1 -0
  535. package/dist/reuse/reuse-curated-coverage.d.ts +36 -0
  536. package/dist/reuse/reuse-curated-coverage.d.ts.map +1 -0
  537. package/dist/reuse/reuse-curated-coverage.js +1 -0
  538. package/dist/reuse/reuse-curated-resolution.d.ts +36 -0
  539. package/dist/reuse/reuse-curated-resolution.d.ts.map +1 -0
  540. package/dist/reuse/reuse-curated-resolution.js +1 -0
  541. package/dist/reuse/reuse-curated-status.d.ts +21 -0
  542. package/dist/reuse/reuse-curated-status.d.ts.map +1 -0
  543. package/dist/reuse/reuse-curated-status.js +21 -0
  544. package/dist/reuse/reuse-match-detail.d.ts +15 -0
  545. package/dist/reuse/reuse-match-detail.d.ts.map +1 -0
  546. package/dist/reuse/reuse-match-detail.js +1 -0
  547. package/dist/reuse/reuse-ranking.d.ts +37 -0
  548. package/dist/reuse/reuse-ranking.d.ts.map +1 -0
  549. package/dist/reuse/reuse-ranking.js +1 -0
  550. package/dist/reuse/reuse-suggestion.d.ts +19 -0
  551. package/dist/reuse/reuse-suggestion.d.ts.map +1 -0
  552. package/dist/reuse/reuse-suggestion.js +1 -0
  553. package/dist/reuse/reuse-symbol-lookup.d.ts +28 -0
  554. package/dist/reuse/reuse-symbol-lookup.d.ts.map +1 -0
  555. package/dist/reuse/reuse-symbol-lookup.js +1 -0
  556. package/dist/reuse/reuse-tokenize.d.ts +11 -0
  557. package/dist/reuse/reuse-tokenize.d.ts.map +1 -0
  558. package/dist/reuse/reuse-tokenize.js +26 -0
  559. package/dist/reuse/score-reuse-name.d.ts +16 -0
  560. package/dist/reuse/score-reuse-name.d.ts.map +1 -0
  561. package/dist/reuse/score-reuse-name.js +35 -0
  562. package/dist/reuse/score-reuse-primitive.d.ts +33 -0
  563. package/dist/reuse/score-reuse-primitive.d.ts.map +1 -0
  564. package/dist/reuse/score-reuse-primitive.js +92 -0
  565. package/dist/review-packet.d.ts +2 -0
  566. package/dist/review-packet.d.ts.map +1 -1
  567. package/dist/review-packet.js +13 -6
  568. package/dist/role-views.d.ts.map +1 -1
  569. package/dist/role-views.js +16 -16
  570. package/dist/routing-recommends-channels.d.ts +24 -0
  571. package/dist/routing-recommends-channels.d.ts.map +1 -0
  572. package/dist/routing-recommends-channels.js +19 -0
  573. package/dist/run-boundary-check.d.ts +118 -0
  574. package/dist/run-boundary-check.d.ts.map +1 -0
  575. package/dist/run-boundary-check.js +592 -0
  576. package/dist/safety-audit.js +1 -1
  577. package/dist/scaffold-coverage.js +1 -1
  578. package/dist/scaffold-pattern-doctor-report.d.ts +19 -0
  579. package/dist/scaffold-pattern-doctor-report.d.ts.map +1 -0
  580. package/dist/scaffold-pattern-doctor-report.js +54 -0
  581. package/dist/scaffold-patterns.d.ts +75 -1
  582. package/dist/scaffold-patterns.d.ts.map +1 -1
  583. package/dist/scaffold-patterns.js +375 -84
  584. package/dist/schema-inventory.d.ts.map +1 -1
  585. package/dist/schema-inventory.js +4 -3
  586. package/dist/search-document-id.d.ts +52 -0
  587. package/dist/search-document-id.d.ts.map +1 -0
  588. package/dist/search-document-id.js +72 -0
  589. package/dist/search-document-prefix.d.ts +7 -0
  590. package/dist/search-document-prefix.d.ts.map +1 -0
  591. package/dist/search-document-prefix.js +1 -0
  592. package/dist/search-index.d.ts.map +1 -1
  593. package/dist/search-index.js +20 -23
  594. package/dist/search-tuning-explain.d.ts +6 -0
  595. package/dist/search-tuning-explain.d.ts.map +1 -1
  596. package/dist/search-tuning-explain.js +19 -17
  597. package/dist/search-tuning-key-probes.d.ts +16 -0
  598. package/dist/search-tuning-key-probes.d.ts.map +1 -0
  599. package/dist/search-tuning-key-probes.js +65 -0
  600. package/dist/search-tuning-key-resolution.d.ts +25 -0
  601. package/dist/search-tuning-key-resolution.d.ts.map +1 -0
  602. package/dist/search-tuning-key-resolution.js +1 -0
  603. package/dist/search-tuning-key-resolver.d.ts +4 -0
  604. package/dist/search-tuning-key-resolver.d.ts.map +1 -0
  605. package/dist/search-tuning-key-resolver.js +107 -0
  606. package/dist/search-tuning-key-status.d.ts +24 -0
  607. package/dist/search-tuning-key-status.d.ts.map +1 -0
  608. package/dist/search-tuning-key-status.js +24 -0
  609. package/dist/search-tuning-lint.d.ts +9 -0
  610. package/dist/search-tuning-lint.d.ts.map +1 -0
  611. package/dist/search-tuning-lint.js +468 -0
  612. package/dist/search-tuning-registry.d.ts +43 -0
  613. package/dist/search-tuning-registry.d.ts.map +1 -1
  614. package/dist/search-tuning-registry.js +214 -49
  615. package/dist/self-audit.d.ts +12 -0
  616. package/dist/self-audit.d.ts.map +1 -1
  617. package/dist/self-audit.js +36 -10
  618. package/dist/self-config-doctor-v2.d.ts +145 -3
  619. package/dist/self-config-doctor-v2.d.ts.map +1 -1
  620. package/dist/self-config-doctor-v2.js +1304 -181
  621. package/dist/self-config-doctor.d.ts +20 -1
  622. package/dist/self-config-doctor.d.ts.map +1 -1
  623. package/dist/self-config-doctor.js +53 -280
  624. package/dist/settled-unit-states.d.ts +9 -0
  625. package/dist/settled-unit-states.d.ts.map +1 -0
  626. package/dist/settled-unit-states.js +14 -0
  627. package/dist/sharkcraft-inspector.d.ts +34 -0
  628. package/dist/sharkcraft-inspector.d.ts.map +1 -1
  629. package/dist/sharkcraft-inspector.js +273 -26
  630. package/dist/spec/spec-evidence.d.ts.map +1 -1
  631. package/dist/spec/spec-evidence.js +14 -20
  632. package/dist/split-identifier.d.ts +21 -0
  633. package/dist/split-identifier.d.ts.map +1 -0
  634. package/dist/split-identifier.js +27 -0
  635. package/dist/start-here.d.ts.map +1 -1
  636. package/dist/start-here.js +1 -2
  637. package/dist/substitute-placeholders.d.ts +8 -0
  638. package/dist/substitute-placeholders.d.ts.map +1 -0
  639. package/dist/substitute-placeholders.js +9 -0
  640. package/dist/symbol-index.d.ts +33 -0
  641. package/dist/symbol-index.d.ts.map +1 -1
  642. package/dist/symbol-index.js +241 -3
  643. package/dist/symbol-member-entry.d.ts +26 -0
  644. package/dist/symbol-member-entry.d.ts.map +1 -0
  645. package/dist/symbol-member-entry.js +1 -0
  646. package/dist/symbol-member-kind.d.ts +14 -0
  647. package/dist/symbol-member-kind.d.ts.map +1 -0
  648. package/dist/symbol-member-kind.js +14 -0
  649. package/dist/task-packet.d.ts.map +1 -1
  650. package/dist/task-packet.js +52 -9
  651. package/dist/task-ranker.d.ts +8 -0
  652. package/dist/task-ranker.d.ts.map +1 -1
  653. package/dist/task-ranker.js +25 -13
  654. package/dist/task-risk.js +1 -1
  655. package/dist/task-routing-hint-registry.d.ts +20 -1
  656. package/dist/task-routing-hint-registry.d.ts.map +1 -1
  657. package/dist/task-routing-hint-registry.js +186 -42
  658. package/dist/template-drift.d.ts.map +1 -1
  659. package/dist/template-drift.js +30 -9
  660. package/dist/template-lint.d.ts.map +1 -1
  661. package/dist/template-lint.js +160 -2
  662. package/dist/term-query.d.ts +10 -0
  663. package/dist/term-query.d.ts.map +1 -0
  664. package/dist/term-query.js +1 -0
  665. package/dist/test-definitions.d.ts +6 -3
  666. package/dist/test-definitions.d.ts.map +1 -1
  667. package/dist/test-runner.d.ts +95 -10
  668. package/dist/test-runner.d.ts.map +1 -1
  669. package/dist/test-runner.js +309 -308
  670. package/dist/tuning-query-tokens.d.ts +29 -0
  671. package/dist/tuning-query-tokens.d.ts.map +1 -0
  672. package/dist/tuning-query-tokens.js +51 -0
  673. package/dist/typecheck-files.d.ts +36 -0
  674. package/dist/typecheck-files.d.ts.map +1 -0
  675. package/dist/typecheck-files.js +158 -0
  676. package/dist/uncertainty.js +1 -1
  677. package/dist/universal-search.d.ts.map +1 -1
  678. package/dist/universal-search.js +5 -3
  679. package/dist/unregistered-exports.d.ts +19 -0
  680. package/dist/unregistered-exports.d.ts.map +1 -0
  681. package/dist/unregistered-exports.js +141 -0
  682. package/dist/unresolvable-reason.d.ts +11 -0
  683. package/dist/unresolvable-reason.d.ts.map +1 -0
  684. package/dist/unresolvable-reason.js +11 -0
  685. package/dist/upgrade-advisor.d.ts.map +1 -1
  686. package/dist/upgrade-advisor.js +16 -9
  687. package/dist/validate-contribution-file.d.ts +4 -0
  688. package/dist/validate-contribution-file.d.ts.map +1 -0
  689. package/dist/validate-contribution-file.js +228 -0
  690. package/dist/why-file.d.ts +3 -0
  691. package/dist/why-file.d.ts.map +1 -1
  692. package/dist/why-file.js +7 -3
  693. package/package.json +17 -17
@@ -1,19 +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";
16
- import { referenceIdExists } from "./reference-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";
17
40
  import { resolveSymbolInFile, SymbolResolution } from "./symbol-index.js";
18
41
  export const KNOWLEDGE_STALE_SCHEMA = 'sharkcraft.knowledge-stale/v1';
19
42
  export var ReferenceCheckOutcome;
@@ -21,7 +44,16 @@ export var ReferenceCheckOutcome;
21
44
  ReferenceCheckOutcome["Ok"] = "ok";
22
45
  ReferenceCheckOutcome["Stale"] = "stale";
23
46
  ReferenceCheckOutcome["Missing"] = "missing";
47
+ /** Well-formed, but the check cannot evaluate it (a `url`, an unwarmed registry). */
24
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";
25
57
  })(ReferenceCheckOutcome || (ReferenceCheckOutcome = {}));
26
58
  export var SymbolConfidence;
27
59
  (function (SymbolConfidence) {
@@ -75,192 +107,443 @@ function dirExists(projectRoot, rel) {
75
107
  * hoped-for shape compiles perfectly and fails silently forever; the registry
76
108
  * imports the real accessors so the compiler can see a rename.
77
109
  */
78
- function commandExistsInInspection(inspection, id) {
79
- return referenceIdExists(inspection, 'command', id);
80
- }
81
- function templateExists(inspection, id) {
82
- return referenceIdExists(inspection, 'template', id);
83
- }
84
- function playbookExists(inspection, id) {
85
- return referenceIdExists(inspection, 'playbook', id);
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
+ }
150
+ }
86
151
  }
87
- function constructExists(inspection, id) {
88
- return referenceIdExists(inspection, 'construct', 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)`;
167
+ }
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)`;
170
+ }
171
+ return undefined;
89
172
  }
90
- function helperExists(id) {
91
- 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);
92
192
  }
93
- function policyExists(inspection, id) {
94
- return referenceIdExists(inspection, 'policy', id);
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
+ }
95
202
  }
96
- function boundaryRuleExists(inspection, id) {
97
- return referenceIdExists(inspection, 'boundary-rule', id);
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;
98
236
  }
99
- function pathConventionExists(inspection, id) {
100
- return referenceIdExists(inspection, 'path-convention', 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
+ }
268
+ }
269
+ WORKSPACE_PACKAGES.set(inspection, out);
270
+ return out;
101
271
  }
102
- function packageExists(inspection, id) {
103
- // Project packages live under `packages/<name>` for SharkCraft + many
104
- // monorepos. Trust the inspection's package map if available, else
105
- // fall back to a filesystem check.
106
- const pkgs = inspection.packages;
107
- if (Array.isArray(pkgs)) {
108
- if (pkgs.some((p) => p.name === id))
109
- return true;
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
+ };
110
285
  }
111
- // file lookup as a backstop
112
- const rel = id.startsWith('@') ? id.split('/')[1] ?? '' : id;
113
- return dirExists(inspection.projectRoot, `packages/${rel}`);
286
+ return staleId('package', id);
114
287
  }
115
288
  function normalizeRel(p) {
116
289
  return p.split(/[\\/]/).join('/').replace(/^\.\//, '');
117
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
+ }
118
297
  function checkSymbolReference(projectRoot, ref, graph) {
119
298
  const sym = ref.symbol ?? '';
120
299
  if (!sym) {
121
300
  return {
122
- outcome: ReferenceCheckOutcome.Unknown,
301
+ outcome: ReferenceCheckOutcome.Invalid,
123
302
  confidence: SymbolConfidence.Unknown,
124
- message: 'Symbol reference has no `symbol` field.',
303
+ failure: ReferenceFailure.Malformed,
304
+ message: 'malformed reference: symbol reference missing required field `symbol`.',
125
305
  };
126
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;
127
312
  // Graph-resolved, cross-file path (preferred when a graph is supplied).
128
313
  // A single-file AST scan cannot tell a *moved* symbol from a deleted one;
129
314
  // the graph's global symbol index can, so consult it first.
130
315
  if (graph) {
131
316
  const decl = graph
132
- .findSymbol(sym)
317
+ .findSymbol(lookup)
133
318
  .map((n) => n.path)
134
319
  .filter((p) => typeof p === 'string' && p.length > 0)
135
320
  .map(normalizeRel);
136
321
  if (decl.length > 0) {
137
322
  if (ref.path) {
138
323
  const target = normalizeRel(ref.path);
139
- 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)) {
140
336
  return {
141
337
  outcome: ReferenceCheckOutcome.Ok,
142
338
  confidence: SymbolConfidence.Exact,
143
339
  message: `\`${sym}\` resolves to ${ref.path} (graph).`,
144
340
  };
145
341
  }
146
- // 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.
147
347
  return {
148
- outcome: ReferenceCheckOutcome.Stale,
149
- confidence: SymbolConfidence.Missing,
150
- message: `Symbol \`${sym}\` is no longer in ${ref.path}; the graph resolves it to ${decl
151
- .slice(0, 3)
152
- .join(', ')}.`,
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}\`.`,
153
362
  };
154
363
  }
155
- // No file pin, but the graph resolved it cross-file.
156
- return {
157
- outcome: ReferenceCheckOutcome.Ok,
158
- confidence: SymbolConfidence.Probable,
159
- message: `\`${sym}\` resolves via the code graph to ${decl.slice(0, 3).join(', ')}.`,
160
- };
161
364
  }
162
- // Absent from the graph. The graph may be partial (locals, unindexed
163
- // languages), so fall through to the AST/text backstop when a file is
164
- // pinned; otherwise we cannot verify.
165
- if (!ref.path) {
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.
166
369
  return {
167
370
  outcome: ReferenceCheckOutcome.Unknown,
168
371
  confidence: SymbolConfidence.Unknown,
372
+ failure: ReferenceFailure.Unverifiable,
169
373
  message: `Symbol reference \`${sym}\` has no file pin and is absent from the code graph; stale-check cannot verify.`,
170
374
  };
171
375
  }
172
376
  }
173
- const file = ref.path ? nodePath.join(projectRoot, ref.path) : null;
174
- if (file) {
175
- if (!existsSync(file)) {
176
- return {
177
- outcome: ReferenceCheckOutcome.Missing,
178
- confidence: SymbolConfidence.Missing,
179
- message: `Referenced file does not exist: ${ref.path}`,
180
- };
181
- }
182
- // AST-backed resolution (falls back to text-scan if parse fails).
183
- try {
184
- const res = resolveSymbolInFile(file, sym);
185
- switch (res.resolution) {
186
- case SymbolResolution.ExactExport:
187
- return {
188
- outcome: ReferenceCheckOutcome.Ok,
189
- confidence: SymbolConfidence.Exact,
190
- message: res.message,
191
- };
192
- case SymbolResolution.ExactLocal:
193
- case SymbolResolution.ExactReExport:
194
- return {
195
- outcome: ReferenceCheckOutcome.Ok,
196
- confidence: SymbolConfidence.Exact,
197
- message: res.message,
198
- };
199
- case SymbolResolution.ProbableText:
200
- return {
201
- outcome: ReferenceCheckOutcome.Ok,
202
- confidence: SymbolConfidence.Probable,
203
- message: res.message,
204
- };
205
- case SymbolResolution.Missing:
206
- return {
207
- outcome: ReferenceCheckOutcome.Stale,
208
- confidence: SymbolConfidence.Missing,
209
- message: res.message,
210
- };
211
- default:
212
- return {
213
- outcome: ReferenceCheckOutcome.Unknown,
214
- confidence: SymbolConfidence.Unknown,
215
- message: res.message,
216
- };
217
- }
218
- }
219
- catch {
220
- // Fallback to text scan.
221
- try {
222
- const text = readFileSync(file, 'utf8');
223
- const declRe = new RegExp(`(export\\s+(?:async\\s+)?(?:function|class|interface|enum|type|const|let|var)\\s+|class\\s+|function\\s+)${escapeRe(sym)}\\b`);
224
- if (declRe.test(text)) {
225
- return {
226
- outcome: ReferenceCheckOutcome.Ok,
227
- confidence: SymbolConfidence.Exact,
228
- message: `Found declaration of \`${sym}\` in ${ref.path}.`,
229
- };
230
- }
231
- if (text.includes(sym)) {
232
- return {
233
- outcome: ReferenceCheckOutcome.Ok,
234
- confidence: SymbolConfidence.Probable,
235
- message: `\`${sym}\` appears in ${ref.path}, but not as an exported declaration.`,
236
- };
237
- }
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:
238
421
  return {
239
422
  outcome: ReferenceCheckOutcome.Stale,
240
423
  confidence: SymbolConfidence.Missing,
241
- message: `Symbol \`${sym}\` not found in ${ref.path}.`,
424
+ failure: ReferenceFailure.AnchorMissing,
425
+ message: res.message + via,
426
+ ...(res.suggestedSymbol ? { suggestedSymbol: res.suggestedSymbol } : {}),
242
427
  };
243
- }
244
- catch {
428
+ default:
245
429
  return {
246
430
  outcome: ReferenceCheckOutcome.Unknown,
247
431
  confidence: SymbolConfidence.Unknown,
248
- 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}.`,
448
+ };
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.`,
249
455
  };
250
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
+ };
251
471
  }
252
472
  }
253
- // No file pinned — best-effort confidence is `unknown`.
254
- return {
255
- outcome: ReferenceCheckOutcome.Unknown,
256
- confidence: SymbolConfidence.Unknown,
257
- message: `Symbol reference \`${sym}\` has no file pin; stale-check cannot verify.`,
258
- };
259
473
  }
260
474
  function escapeRe(s) {
261
475
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
262
476
  }
263
- 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) {
264
547
  const projectRoot = inspection.projectRoot;
265
548
  switch (ref.kind) {
266
549
  case 'file': {
@@ -279,6 +562,7 @@ function checkReference(inspection, ref, graph) {
279
562
  }
280
563
  return {
281
564
  outcome: ReferenceCheckOutcome.Stale,
565
+ failure: ReferenceFailure.PathMissing,
282
566
  message: `Directory missing: ${ref.path}`,
283
567
  suggestion: 'Move the directory or update the knowledge reference.',
284
568
  };
@@ -291,98 +575,78 @@ function checkReference(inspection, ref, graph) {
291
575
  const id = ref.id ?? ref.command ?? '';
292
576
  if (!id)
293
577
  return missingField('command', 'id or command');
294
- if (commandExistsInInspection(inspection, id)) {
295
- return { outcome: ReferenceCheckOutcome.Ok, message: `Command available: ${id}` };
296
- }
297
- return {
298
- outcome: ReferenceCheckOutcome.Stale,
299
- message: `Command not registered: ${id}`,
300
- suggestion: 'Register the command in the command catalog or update the reference.',
301
- };
578
+ return checkCommandString(inspection, id, 'Command');
302
579
  }
303
580
  case 'template': {
304
581
  if (!ref.id)
305
582
  return missingField('template', 'id');
306
- if (templateExists(inspection, ref.id)) {
307
- return { outcome: ReferenceCheckOutcome.Ok, message: `Template exists: ${ref.id}` };
308
- }
309
- return staleId('template', ref.id);
583
+ return checkRegisteredId(inspection, 'template', ref.id, 'Template');
310
584
  }
311
585
  case 'playbook': {
312
586
  if (!ref.id)
313
587
  return missingField('playbook', 'id');
314
- if (playbookExists(inspection, ref.id)) {
315
- return { outcome: ReferenceCheckOutcome.Ok, message: `Playbook exists: ${ref.id}` };
316
- }
317
- return staleId('playbook', ref.id);
588
+ return checkRegisteredId(inspection, 'playbook', ref.id, 'Playbook');
318
589
  }
319
590
  case 'construct': {
320
591
  if (!ref.id)
321
592
  return missingField('construct', 'id');
322
- if (constructExists(inspection, ref.id)) {
323
- return { outcome: ReferenceCheckOutcome.Ok, message: `Construct exists: ${ref.id}` };
324
- }
325
- return staleId('construct', ref.id);
593
+ return checkRegisteredId(inspection, 'construct', ref.id, 'Construct');
326
594
  }
327
595
  case 'helper': {
328
596
  if (!ref.id)
329
597
  return missingField('helper', 'id');
330
- if (helperExists(ref.id)) {
331
- return { outcome: ReferenceCheckOutcome.Ok, message: `Helper exists: ${ref.id}` };
332
- }
333
- return staleId('helper', ref.id);
598
+ return checkRegisteredId(inspection, 'helper', ref.id, 'Helper');
334
599
  }
335
600
  case 'policy': {
336
601
  if (!ref.id)
337
602
  return missingField('policy', 'id');
338
- if (policyExists(inspection, ref.id)) {
339
- return { outcome: ReferenceCheckOutcome.Ok, message: `Policy exists: ${ref.id}` };
340
- }
341
- return staleId('policy', ref.id);
603
+ return checkRegisteredId(inspection, 'policy', ref.id, 'Policy');
342
604
  }
343
605
  case 'boundary-rule': {
344
606
  if (!ref.id)
345
607
  return missingField('boundary-rule', 'id');
346
- if (boundaryRuleExists(inspection, ref.id)) {
347
- return { outcome: ReferenceCheckOutcome.Ok, message: `Boundary rule exists: ${ref.id}` };
348
- }
349
- return staleId('boundary-rule', ref.id);
608
+ return checkRegisteredId(inspection, 'boundary-rule', ref.id, 'Boundary rule');
350
609
  }
351
610
  case 'path-convention': {
352
611
  if (!ref.id)
353
612
  return missingField('path-convention', 'id');
354
- if (pathConventionExists(inspection, ref.id)) {
355
- return { outcome: ReferenceCheckOutcome.Ok, message: `Path convention exists: ${ref.id}` };
356
- }
357
- return staleId('path-convention', ref.id);
613
+ return checkRegisteredId(inspection, 'path-convention', ref.id, 'Path convention');
358
614
  }
359
615
  case 'package': {
360
616
  if (!ref.id)
361
617
  return missingField('package', 'id');
362
- if (packageExists(inspection, ref.id)) {
363
- return { outcome: ReferenceCheckOutcome.Ok, message: `Package exists: ${ref.id}` };
364
- }
365
- return staleId('package', ref.id);
618
+ return checkPackageReference(inspection, ref.id);
366
619
  }
367
620
  case 'url': {
368
621
  // We never fetch URLs. Mark them unknown unless we can resolve to a
369
622
  // local docs file.
370
623
  return {
371
624
  outcome: ReferenceCheckOutcome.Unknown,
625
+ failure: ReferenceFailure.Unverifiable,
372
626
  message: 'URL references are not verified (no network).',
373
627
  };
374
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
+ };
375
637
  }
376
638
  }
377
639
  function missingField(kind, field) {
378
640
  return {
379
- outcome: ReferenceCheckOutcome.Unknown,
380
- 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}\`.`,
381
644
  };
382
645
  }
383
646
  function staleFile(rel) {
384
647
  return {
385
648
  outcome: ReferenceCheckOutcome.Stale,
649
+ failure: ReferenceFailure.PathMissing,
386
650
  message: `File missing: ${rel}`,
387
651
  suggestion: 'Restore the file or run `shrk knowledge rename-file <old> <new> --dry-run`.',
388
652
  };
@@ -390,29 +654,237 @@ function staleFile(rel) {
390
654
  function staleId(kind, id) {
391
655
  return {
392
656
  outcome: ReferenceCheckOutcome.Stale,
657
+ failure: ReferenceFailure.IdUnregistered,
393
658
  message: `${kind} not found: ${id}`,
394
659
  suggestion: 'Register the target or remove the reference.',
395
660
  };
396
661
  }
397
- 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) {
398
697
  if (changed.length === 0)
399
698
  return true;
400
- const changedSet = new Set(changed.map((c) => c.split(/[\\/]/).join('/')));
401
- for (const ref of entry.references ?? []) {
402
- if (ref.path && changedSet.has(ref.path.split(/[\\/]/).join('/')))
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)))
403
709
  return true;
404
710
  }
405
- for (const anchor of entry.anchors ?? []) {
406
- if (anchor.path && changedSet.has(anchor.path.split(/[\\/]/).join('/')))
711
+ for (const anchor of subject.anchors ?? []) {
712
+ if (pathTouched(anchor.path, changedList))
407
713
  return true;
408
714
  }
409
715
  return false;
410
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
+ }
411
867
  export function buildKnowledgeStaleReport(inspection, options = {}) {
412
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);
413
873
  const referenceChecks = [];
414
874
  const anchorChecks = [];
415
- 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();
416
888
  let totalReferences = 0;
417
889
  let totalAnchors = 0;
418
890
  // Lazy symbol → file index, built on first stale-symbol need.
@@ -439,108 +911,180 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
439
911
  dirBasenameIndex = buildBasenameDirIndex(inspection.projectRoot);
440
912
  return dirBasenameIndex;
441
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
+ };
442
1014
  for (const entry of inspection.knowledgeEntries) {
443
- 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)) {
444
1021
  continue;
445
1022
  }
446
- 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) {
447
1028
  totalReferences += 1;
448
- const result = checkReference(inspection, ref, options.graph);
449
- const outcome = result.outcome;
450
- if (outcome === ReferenceCheckOutcome.Ok)
1029
+ const check = checkOne(entry.id, ref);
1030
+ if (check.outcome === ReferenceCheckOutcome.Ok)
451
1031
  counts.ok += 1;
452
- else if (outcome === ReferenceCheckOutcome.Stale)
1032
+ else if (check.outcome === ReferenceCheckOutcome.Stale)
453
1033
  counts.stale += 1;
454
- else if (outcome === ReferenceCheckOutcome.Missing)
1034
+ else if (check.outcome === ReferenceCheckOutcome.Missing)
455
1035
  counts.missing += 1;
1036
+ else if (check.outcome === ReferenceCheckOutcome.Invalid)
1037
+ counts.invalid += 1;
456
1038
  else
457
1039
  counts.unknown += 1;
458
- const check = {
459
- entryId: entry.id,
460
- reference: ref,
461
- outcome,
462
- message: result.message,
463
- ...(result.confidence ? { symbolConfidence: result.confidence } : {}),
464
- ...(result.suggestion ? { suggestion: result.suggestion } : {}),
465
- };
466
- const isStaleOrMissing = outcome === ReferenceCheckOutcome.Stale || outcome === ReferenceCheckOutcome.Missing;
467
- // Symbol rename detection. Strict mode: emit `replaceWith.path`
468
- // only for the single unambiguous candidate. Wide mode also
469
- // emits scored candidate lists for the multi-candidate cases that
470
- // strict silently drops.
471
- if (isStaleOrMissing && ref.kind === 'symbol' && ref.symbol) {
472
- const all = (getSymbolIndex().get(ref.symbol) ?? []).filter((p) => p !== ref.path);
473
- if (all.length === 1) {
474
- check.replaceWith = {
475
- path: all[0],
476
- rationale: `\`${ref.symbol}\` is exported from \`${all[0]}\` — sole candidate.`,
477
- strategy: RenameStrategy.Strict,
478
- };
479
- }
480
- else if (strategy === RenameStrategy.Wide && all.length > 1) {
481
- check.replaceWith = buildWideReplacement({
482
- stalePath: ref.path ?? '',
483
- paths: all,
484
- kindLabel: `symbol \`${ref.symbol}\``,
485
- });
486
- }
487
- }
488
- // File rename detection (directory move, basename match).
489
- if (isStaleOrMissing && ref.kind === 'file' && ref.path && !check.replaceWith) {
490
- const indexed = getFileBasenameIndex();
491
- const uniq = pickUniqueRenameCandidate(ref.path, indexed);
492
- if (uniq) {
493
- check.replaceWith = {
494
- path: uniq,
495
- rationale: `File basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\` (likely directory rename).`,
496
- strategy: RenameStrategy.Strict,
497
- };
498
- }
499
- else if (strategy === RenameStrategy.Wide) {
500
- const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
501
- if (all.length > 0) {
502
- check.replaceWith = buildWideReplacement({
503
- stalePath: ref.path,
504
- paths: all,
505
- kindLabel: `file \`${nodePath.basename(ref.path)}\``,
506
- });
507
- }
508
- }
509
- }
510
- // Directory rename detection.
511
- if (isStaleOrMissing && ref.kind === 'directory' && ref.path && !check.replaceWith) {
512
- const indexed = getDirBasenameIndex();
513
- const uniq = pickUniqueRenameCandidate(ref.path, indexed);
514
- if (uniq) {
515
- check.replaceWith = {
516
- path: uniq,
517
- rationale: `Directory basename \`${nodePath.basename(ref.path)}\` resolves uniquely to \`${uniq}\`.`,
518
- strategy: RenameStrategy.Strict,
519
- };
520
- }
521
- else if (strategy === RenameStrategy.Wide) {
522
- const all = (indexed.get(nodePath.basename(ref.path)) ?? []).filter((p) => p !== ref.path);
523
- if (all.length > 0) {
524
- check.replaceWith = buildWideReplacement({
525
- stalePath: ref.path,
526
- paths: all,
527
- kindLabel: `directory \`${nodePath.basename(ref.path)}\``,
528
- });
529
- }
530
- }
1040
+ if (isCheckableOutcome(check.outcome)) {
1041
+ checkable += 1;
1042
+ if (isFailingOutcome(check.outcome))
1043
+ failing += 1;
531
1044
  }
532
1045
  referenceChecks.push(check);
533
1046
  }
534
- for (const anchor of entry.anchors ?? []) {
1047
+ for (const anchor of anchors) {
535
1048
  totalAnchors += 1;
536
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
+ }
537
1060
  anchorChecks.push({
538
1061
  entryId: entry.id,
539
1062
  anchor,
540
1063
  outcome: inspected.outcome,
541
1064
  message: inspected.message,
1065
+ ...(failure ? { failure } : {}),
542
1066
  });
543
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));
544
1088
  }
545
1089
  // Content-similarity boost: when multiple wide replacements in the
546
1090
  // SAME entry name the same candidate path, raise that candidate's score
@@ -548,6 +1092,129 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
548
1092
  // followed" case where a single per-reference signal is weak but the
549
1093
  // aggregate is strong.
550
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
+ }
551
1218
  return {
552
1219
  schema: KNOWLEDGE_STALE_SCHEMA,
553
1220
  entries: inspection.knowledgeEntries.length,
@@ -556,6 +1223,18 @@ export function buildKnowledgeStaleReport(inspection, options = {}) {
556
1223
  counts,
557
1224
  referenceChecks,
558
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 } : {}),
559
1238
  };
560
1239
  }
561
1240
  /**
@@ -872,51 +1551,77 @@ function checkAnchor(inspection, anchor, graph) {
872
1551
  switch (anchor.kind) {
873
1552
  case 'file':
874
1553
  if (!anchor.path) {
875
- 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
+ };
876
1559
  }
877
1560
  if (fileExists(inspection.projectRoot, anchor.path)) {
878
1561
  return { outcome: ReferenceCheckOutcome.Ok, message: `anchor file exists: ${anchor.path}` };
879
1562
  }
880
- 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
+ };
881
1568
  case 'symbol':
882
1569
  return checkSymbolAnchor(inspection, anchor, graph);
883
- case 'command':
884
- if (anchor.targetId && commandExistsInInspection(inspection, anchor.targetId)) {
885
- return { outcome: ReferenceCheckOutcome.Ok, message: `command exists: ${anchor.targetId}` };
1570
+ case 'command': {
1571
+ if (!anchor.targetId) {
1572
+ return { outcome: ReferenceCheckOutcome.Stale, message: 'command anchor unresolved: ?' };
886
1573
  }
887
- 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
+ }
888
1577
  case 'construct':
889
- if (anchor.targetId && constructExists(inspection, anchor.targetId)) {
890
- return { outcome: ReferenceCheckOutcome.Ok, message: `construct exists: ${anchor.targetId}` };
891
- }
892
- return { outcome: ReferenceCheckOutcome.Stale, message: `construct anchor unresolved: ${anchor.targetId ?? '?'}` };
1578
+ return checkAnchorId(inspection, 'construct', anchor.targetId);
893
1579
  case 'template':
894
- if (anchor.targetId && templateExists(inspection, anchor.targetId)) {
895
- return { outcome: ReferenceCheckOutcome.Ok, message: `template exists: ${anchor.targetId}` };
896
- }
897
- return { outcome: ReferenceCheckOutcome.Stale, message: `template anchor unresolved: ${anchor.targetId ?? '?'}` };
1580
+ return checkAnchorId(inspection, 'template', anchor.targetId);
898
1581
  case 'helper':
899
- if (anchor.targetId && helperExists(anchor.targetId)) {
900
- return { outcome: ReferenceCheckOutcome.Ok, message: `helper exists: ${anchor.targetId}` };
901
- }
902
- return { outcome: ReferenceCheckOutcome.Stale, message: `helper anchor unresolved: ${anchor.targetId ?? '?'}` };
1582
+ return checkAnchorId(inspection, 'helper', anchor.targetId);
903
1583
  case 'playbook':
904
- if (anchor.targetId && playbookExists(inspection, anchor.targetId)) {
905
- return { outcome: ReferenceCheckOutcome.Ok, message: `playbook exists: ${anchor.targetId}` };
906
- }
907
- return { outcome: ReferenceCheckOutcome.Stale, message: `playbook anchor unresolved: ${anchor.targetId ?? '?'}` };
1584
+ return checkAnchorId(inspection, 'playbook', anchor.targetId);
908
1585
  case 'policy':
909
- if (anchor.targetId && policyExists(inspection, anchor.targetId)) {
910
- return { outcome: ReferenceCheckOutcome.Ok, message: `policy exists: ${anchor.targetId}` };
911
- }
912
- return { outcome: ReferenceCheckOutcome.Stale, message: `policy anchor unresolved: ${anchor.targetId ?? '?'}` };
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
+ };
913
1593
  }
914
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
+ }
915
1620
  function checkSymbolAnchor(inspection, anchor, graph) {
916
1621
  const r = checkSymbolReference(inspection.projectRoot, {
917
1622
  kind: 'symbol',
918
1623
  symbol: anchor.symbol,
919
1624
  ...(anchor.path ? { path: anchor.path } : {}),
920
1625
  }, graph);
921
- return { outcome: r.outcome, message: r.message };
1626
+ return { outcome: r.outcome, message: r.message, ...(r.failure ? { failure: r.failure } : {}) };
922
1627
  }