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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (694) hide show
  1. package/dist/acceptance-replay.js +1 -1
  2. package/dist/agent-contract-templates.js +6 -6
  3. package/dist/agent-contract.d.ts.map +1 -1
  4. package/dist/agent-contract.js +13 -8
  5. package/dist/agent-orchestration.js +2 -2
  6. package/dist/agent-task-prep.d.ts +16 -0
  7. package/dist/agent-task-prep.d.ts.map +1 -1
  8. package/dist/agent-task-prep.js +51 -4
  9. package/dist/ai-readiness.d.ts.map +1 -1
  10. package/dist/ai-readiness.js +21 -3
  11. package/dist/architecture-map.d.ts +16 -0
  12. package/dist/architecture-map.d.ts.map +1 -1
  13. package/dist/architecture-map.js +42 -26
  14. package/dist/area-explore.d.ts +2 -2
  15. package/dist/area-explore.d.ts.map +1 -1
  16. package/dist/area-explore.js +36 -16
  17. package/dist/area-map.d.ts +56 -11
  18. package/dist/area-map.d.ts.map +1 -1
  19. package/dist/area-map.js +106 -25
  20. package/dist/asset-doctor-proposed-exit.d.ts +21 -0
  21. package/dist/asset-doctor-proposed-exit.d.ts.map +1 -0
  22. package/dist/asset-doctor-proposed-exit.js +22 -0
  23. package/dist/asset-provenance.js +1 -1
  24. package/dist/boundaries-changed-only.d.ts +24 -1
  25. package/dist/boundaries-changed-only.d.ts.map +1 -1
  26. package/dist/boundaries-changed-only.js +12 -2
  27. package/dist/boundary-check-options.model.d.ts +40 -0
  28. package/dist/boundary-check-options.model.d.ts.map +1 -0
  29. package/dist/boundary-check-options.model.js +1 -0
  30. package/dist/boundary-check-result.model.d.ts +89 -0
  31. package/dist/boundary-check-result.model.d.ts.map +1 -0
  32. package/dist/boundary-check-result.model.js +1 -0
  33. package/dist/boundary-configuration-status.d.ts +38 -0
  34. package/dist/boundary-configuration-status.d.ts.map +1 -0
  35. package/dist/boundary-configuration-status.js +118 -0
  36. package/dist/boundary-configuration-status.model.d.ts +37 -0
  37. package/dist/boundary-configuration-status.model.d.ts.map +1 -0
  38. package/dist/boundary-configuration-status.model.js +1 -0
  39. package/dist/boundary-load-issue.model.d.ts +30 -0
  40. package/dist/boundary-load-issue.model.d.ts.map +1 -0
  41. package/dist/boundary-load-issue.model.js +1 -0
  42. package/dist/boundary-rule-check.model.d.ts +44 -0
  43. package/dist/boundary-rule-check.model.d.ts.map +1 -0
  44. package/dist/boundary-rule-check.model.js +1 -0
  45. package/dist/boundary-rule-invalidation.model.d.ts +31 -0
  46. package/dist/boundary-rule-invalidation.model.d.ts.map +1 -0
  47. package/dist/boundary-rule-invalidation.model.js +1 -0
  48. package/dist/boundary-rule-set-diff.model.d.ts +75 -0
  49. package/dist/boundary-rule-set-diff.model.d.ts.map +1 -0
  50. package/dist/boundary-rule-set-diff.model.js +1 -0
  51. package/dist/boundary-rule-sources.d.ts +28 -0
  52. package/dist/boundary-rule-sources.d.ts.map +1 -0
  53. package/dist/boundary-rule-sources.js +110 -0
  54. package/dist/boundary-stale-exception-finding.model.d.ts +12 -0
  55. package/dist/boundary-stale-exception-finding.model.d.ts.map +1 -0
  56. package/dist/boundary-stale-exception-finding.model.js +1 -0
  57. package/dist/boundary-unit-liveness.d.ts +35 -0
  58. package/dist/boundary-unit-liveness.d.ts.map +1 -0
  59. package/dist/boundary-unit-liveness.js +71 -0
  60. package/dist/change-intent.d.ts +3 -0
  61. package/dist/change-intent.d.ts.map +1 -1
  62. package/dist/change-intent.js +61 -27
  63. package/dist/changes-summary.d.ts.map +1 -1
  64. package/dist/changes-summary.js +19 -9
  65. package/dist/ci-integrity-report.js +1 -1
  66. package/dist/ci-predict.d.ts.map +1 -1
  67. package/dist/ci-predict.js +38 -5
  68. package/dist/code-intelligence-doctor.d.ts +40 -0
  69. package/dist/code-intelligence-doctor.d.ts.map +1 -1
  70. package/dist/code-intelligence-doctor.js +86 -12
  71. package/dist/command-recommender.d.ts +45 -2
  72. package/dist/command-recommender.d.ts.map +1 -1
  73. package/dist/command-recommender.js +41 -263
  74. package/dist/command-resolution-status.d.ts +44 -0
  75. package/dist/command-resolution-status.d.ts.map +1 -0
  76. package/dist/command-resolution-status.js +44 -0
  77. package/dist/command-safety-level.d.ts +15 -0
  78. package/dist/command-safety-level.d.ts.map +1 -0
  79. package/dist/command-safety-level.js +43 -0
  80. package/dist/command-taxonomy.js +1 -1
  81. package/dist/compliance-evidence.js +1 -1
  82. package/dist/compliance-profiles.js +1 -1
  83. package/dist/construct-registry.d.ts +40 -0
  84. package/dist/construct-registry.d.ts.map +1 -1
  85. package/dist/construct-registry.js +186 -82
  86. package/dist/context-tuning.d.ts +4 -0
  87. package/dist/context-tuning.d.ts.map +1 -1
  88. package/dist/context-tuning.js +10 -7
  89. package/dist/contract-template-registry.d.ts +10 -0
  90. package/dist/contract-template-registry.d.ts.map +1 -1
  91. package/dist/contract-template-registry.js +59 -33
  92. package/dist/contribution-kind.d.ts +52 -0
  93. package/dist/contribution-kind.d.ts.map +1 -0
  94. package/dist/contribution-kind.js +52 -0
  95. package/dist/contribution-load-failures.d.ts +106 -0
  96. package/dist/contribution-load-failures.d.ts.map +1 -0
  97. package/dist/contribution-load-failures.js +575 -0
  98. package/dist/contributions-report.d.ts +28 -0
  99. package/dist/contributions-report.d.ts.map +1 -0
  100. package/dist/contributions-report.js +242 -0
  101. package/dist/convention-registry.d.ts +22 -0
  102. package/dist/convention-registry.d.ts.map +1 -1
  103. package/dist/convention-registry.js +79 -39
  104. package/dist/coverage-report.d.ts.map +1 -1
  105. package/dist/coverage-report.js +6 -6
  106. package/dist/custom-checks.d.ts +60 -1
  107. package/dist/custom-checks.d.ts.map +1 -1
  108. package/dist/custom-checks.js +159 -6
  109. package/dist/dashboard/dashboard-data.d.ts +6 -0
  110. package/dist/dashboard/dashboard-data.d.ts.map +1 -1
  111. package/dist/dashboard/dashboard-data.js +99 -8
  112. package/dist/dashboard/dashboard-knowledge.d.ts.map +1 -1
  113. package/dist/dashboard/dashboard-knowledge.js +14 -2
  114. package/dist/decision-records.d.ts +20 -0
  115. package/dist/decision-records.d.ts.map +1 -1
  116. package/dist/decision-records.js +68 -20
  117. package/dist/declared-cross-references.d.ts +61 -0
  118. package/dist/declared-cross-references.d.ts.map +1 -0
  119. package/dist/declared-cross-references.js +550 -0
  120. package/dist/declared-gate-plane-rules.d.ts +24 -0
  121. package/dist/declared-gate-plane-rules.d.ts.map +1 -0
  122. package/dist/declared-gate-plane-rules.js +39 -0
  123. package/dist/declared-reference-coverage.d.ts +27 -0
  124. package/dist/declared-reference-coverage.d.ts.map +1 -0
  125. package/dist/declared-reference-coverage.js +50 -0
  126. package/dist/declared-xref-status.d.ts +19 -0
  127. package/dist/declared-xref-status.d.ts.map +1 -0
  128. package/dist/declared-xref-status.js +19 -0
  129. package/dist/delegate-pack-recipes.d.ts +13 -2
  130. package/dist/delegate-pack-recipes.d.ts.map +1 -1
  131. package/dist/delegate-pack-recipes.js +45 -14
  132. package/dist/demo-package.js +2 -2
  133. package/dist/demo-script.js +2 -2
  134. package/dist/demo-workflow.js +3 -3
  135. package/dist/dev-cycle.js +1 -1
  136. package/dist/dev-session.js +1 -1
  137. package/dist/doc-references.d.ts +80 -0
  138. package/dist/doc-references.d.ts.map +1 -0
  139. package/dist/doc-references.js +269 -0
  140. package/dist/doctor-result.d.ts +8 -0
  141. package/dist/doctor-result.d.ts.map +1 -1
  142. package/dist/doctor-verdict-kind.d.ts +19 -0
  143. package/dist/doctor-verdict-kind.d.ts.map +1 -0
  144. package/dist/doctor-verdict-kind.js +19 -0
  145. package/dist/doctor-verdict.d.ts +18 -0
  146. package/dist/doctor-verdict.d.ts.map +1 -0
  147. package/dist/doctor-verdict.js +28 -0
  148. package/dist/drift.d.ts +17 -0
  149. package/dist/drift.d.ts.map +1 -1
  150. package/dist/drift.js +40 -10
  151. package/dist/entrypoint-matrix.d.ts.map +1 -1
  152. package/dist/entrypoint-matrix.js +4 -1
  153. package/dist/feedback-ingestion.d.ts +28 -2
  154. package/dist/feedback-ingestion.d.ts.map +1 -1
  155. package/dist/feedback-ingestion.js +68 -19
  156. package/dist/fix-preview.d.ts.map +1 -1
  157. package/dist/fix-preview.js +9 -5
  158. package/dist/framework-extractor-outcomes.d.ts +27 -0
  159. package/dist/framework-extractor-outcomes.d.ts.map +1 -0
  160. package/dist/framework-extractor-outcomes.js +83 -0
  161. package/dist/fuzzy-impact.d.ts.map +1 -1
  162. package/dist/fuzzy-impact.js +12 -21
  163. package/dist/healing-plan.d.ts.map +1 -1
  164. package/dist/healing-plan.js +3 -4
  165. package/dist/helper-catalog.d.ts +25 -0
  166. package/dist/helper-catalog.d.ts.map +1 -0
  167. package/dist/helper-catalog.js +91 -0
  168. package/dist/helper-registry.d.ts.map +1 -1
  169. package/dist/helper-registry.js +17 -1
  170. package/dist/helper-view.d.ts +33 -0
  171. package/dist/helper-view.d.ts.map +1 -0
  172. package/dist/helper-view.js +1 -0
  173. package/dist/i-asset-doctor-flags.d.ts +16 -0
  174. package/dist/i-asset-doctor-flags.d.ts.map +1 -0
  175. package/dist/i-asset-doctor-flags.js +1 -0
  176. package/dist/i-command-resolution.d.ts +22 -0
  177. package/dist/i-command-resolution.d.ts.map +1 -0
  178. package/dist/i-command-resolution.js +1 -0
  179. package/dist/i-command-resolve-options.d.ts +24 -0
  180. package/dist/i-command-resolve-options.d.ts.map +1 -0
  181. package/dist/i-command-resolve-options.js +1 -0
  182. package/dist/i-command-safety.d.ts +13 -0
  183. package/dist/i-command-safety.d.ts.map +1 -0
  184. package/dist/i-command-safety.js +1 -0
  185. package/dist/i-contribution-accepted-entry.d.ts +18 -0
  186. package/dist/i-contribution-accepted-entry.d.ts.map +1 -0
  187. package/dist/i-contribution-accepted-entry.js +1 -0
  188. package/dist/i-contribution-entry-rejection.d.ts +21 -0
  189. package/dist/i-contribution-entry-rejection.d.ts.map +1 -0
  190. package/dist/i-contribution-entry-rejection.js +1 -0
  191. package/dist/i-contribution-file-issue.d.ts +18 -0
  192. package/dist/i-contribution-file-issue.d.ts.map +1 -0
  193. package/dist/i-contribution-file-issue.js +1 -0
  194. package/dist/i-contribution-file-report.d.ts +40 -0
  195. package/dist/i-contribution-file-report.d.ts.map +1 -0
  196. package/dist/i-contribution-file-report.js +1 -0
  197. package/dist/i-contribution-file-validation.d.ts +24 -0
  198. package/dist/i-contribution-file-validation.d.ts.map +1 -0
  199. package/dist/i-contribution-file-validation.js +1 -0
  200. package/dist/i-contributions-report.d.ts +31 -0
  201. package/dist/i-contributions-report.d.ts.map +1 -0
  202. package/dist/i-contributions-report.js +1 -0
  203. package/dist/i-declared-xref-field.d.ts +25 -0
  204. package/dist/i-declared-xref-field.d.ts.map +1 -0
  205. package/dist/i-declared-xref-field.js +1 -0
  206. package/dist/i-declared-xref-issue.d.ts +25 -0
  207. package/dist/i-declared-xref-issue.d.ts.map +1 -0
  208. package/dist/i-declared-xref-issue.js +1 -0
  209. package/dist/i-declared-xref-report.d.ts +48 -0
  210. package/dist/i-declared-xref-report.d.ts.map +1 -0
  211. package/dist/i-declared-xref-report.js +1 -0
  212. package/dist/i-declared-xref-row.d.ts +29 -0
  213. package/dist/i-declared-xref-row.d.ts.map +1 -0
  214. package/dist/i-declared-xref-row.js +1 -0
  215. package/dist/i-doctor-verdict.d.ts +10 -0
  216. package/dist/i-doctor-verdict.d.ts.map +1 -0
  217. package/dist/i-doctor-verdict.js +1 -0
  218. package/dist/i-kind-outcomes.d.ts +13 -0
  219. package/dist/i-kind-outcomes.d.ts.map +1 -0
  220. package/dist/i-kind-outcomes.js +1 -0
  221. package/dist/i-pack-doctor-verdict.d.ts +15 -0
  222. package/dist/i-pack-doctor-verdict.d.ts.map +1 -0
  223. package/dist/i-pack-doctor-verdict.js +1 -0
  224. package/dist/i-probed-id-field.d.ts +17 -0
  225. package/dist/i-probed-id-field.d.ts.map +1 -0
  226. package/dist/i-probed-id-field.js +1 -0
  227. package/dist/i-reference-kind-declaration.d.ts +38 -0
  228. package/dist/i-reference-kind-declaration.d.ts.map +1 -0
  229. package/dist/i-reference-kind-declaration.js +1 -0
  230. package/dist/i-reference-warm-options.d.ts +21 -0
  231. package/dist/i-reference-warm-options.d.ts.map +1 -0
  232. package/dist/i-reference-warm-options.js +1 -0
  233. package/dist/i-registry-outcomes.d.ts +16 -0
  234. package/dist/i-registry-outcomes.d.ts.map +1 -0
  235. package/dist/i-registry-outcomes.js +1 -0
  236. package/dist/i-scaffold-pattern-doctor-report.d.ts +35 -0
  237. package/dist/i-scaffold-pattern-doctor-report.d.ts.map +1 -0
  238. package/dist/i-scaffold-pattern-doctor-report.js +1 -0
  239. package/dist/i-search-tuning-key-probe.d.ts +30 -0
  240. package/dist/i-search-tuning-key-probe.d.ts.map +1 -0
  241. package/dist/i-search-tuning-key-probe.js +1 -0
  242. package/dist/i-search-tuning-lint-issue.d.ts +61 -0
  243. package/dist/i-search-tuning-lint-issue.d.ts.map +1 -0
  244. package/dist/i-search-tuning-lint-issue.js +1 -0
  245. package/dist/i-search-tuning-lint-report.d.ts +38 -0
  246. package/dist/i-search-tuning-lint-report.d.ts.map +1 -0
  247. package/dist/i-search-tuning-lint-report.js +1 -0
  248. package/dist/i-unresolvable-reference-scan.d.ts +14 -0
  249. package/dist/i-unresolvable-reference-scan.d.ts.map +1 -0
  250. package/dist/i-unresolvable-reference-scan.js +1 -0
  251. package/dist/i-unresolvable-reference.d.ts +29 -0
  252. package/dist/i-unresolvable-reference.d.ts.map +1 -0
  253. package/dist/i-unresolvable-reference.js +1 -0
  254. package/dist/i-workspace-profile-payload.d.ts +12 -0
  255. package/dist/i-workspace-profile-payload.d.ts.map +1 -0
  256. package/dist/i-workspace-profile-payload.js +1 -0
  257. package/dist/impact-analysis.d.ts +15 -0
  258. package/dist/impact-analysis.d.ts.map +1 -1
  259. package/dist/impact-analysis.js +58 -13
  260. package/dist/impact-render.d.ts.map +1 -1
  261. package/dist/impact-render.js +22 -0
  262. package/dist/import-hygiene.d.ts +35 -1
  263. package/dist/import-hygiene.d.ts.map +1 -1
  264. package/dist/import-hygiene.js +107 -13
  265. package/dist/index.d.ts +145 -0
  266. package/dist/index.d.ts.map +1 -1
  267. package/dist/index.js +176 -0
  268. package/dist/inspection-discovery.d.ts +45 -0
  269. package/dist/inspection-discovery.d.ts.map +1 -0
  270. package/dist/inspection-discovery.js +70 -0
  271. package/dist/knowledge-advisory-code.d.ts +17 -0
  272. package/dist/knowledge-advisory-code.d.ts.map +1 -0
  273. package/dist/knowledge-advisory-code.js +17 -0
  274. package/dist/knowledge-aged-entry.d.ts +10 -0
  275. package/dist/knowledge-aged-entry.d.ts.map +1 -0
  276. package/dist/knowledge-aged-entry.js +1 -0
  277. package/dist/knowledge-authoring.d.ts +24 -1
  278. package/dist/knowledge-authoring.d.ts.map +1 -1
  279. package/dist/knowledge-authoring.js +46 -7
  280. package/dist/knowledge-entry-verdict-record.d.ts +22 -0
  281. package/dist/knowledge-entry-verdict-record.d.ts.map +1 -0
  282. package/dist/knowledge-entry-verdict-record.js +1 -0
  283. package/dist/knowledge-entry-verdict.d.ts +17 -0
  284. package/dist/knowledge-entry-verdict.d.ts.map +1 -0
  285. package/dist/knowledge-entry-verdict.js +17 -0
  286. package/dist/knowledge-graph.d.ts +6 -0
  287. package/dist/knowledge-graph.d.ts.map +1 -1
  288. package/dist/knowledge-graph.js +18 -7
  289. package/dist/knowledge-kind-bucket.d.ts +19 -0
  290. package/dist/knowledge-kind-bucket.d.ts.map +1 -0
  291. package/dist/knowledge-kind-bucket.js +1 -0
  292. package/dist/knowledge-load-failure.d.ts +23 -0
  293. package/dist/knowledge-load-failure.d.ts.map +1 -0
  294. package/dist/knowledge-load-failure.js +1 -0
  295. package/dist/knowledge-reference-kind-bucket.d.ts +11 -0
  296. package/dist/knowledge-reference-kind-bucket.d.ts.map +1 -0
  297. package/dist/knowledge-reference-kind-bucket.js +1 -0
  298. package/dist/knowledge-stale-advisory.d.ts +14 -0
  299. package/dist/knowledge-stale-advisory.d.ts.map +1 -0
  300. package/dist/knowledge-stale-advisory.js +1 -0
  301. package/dist/knowledge-stale-coverage.d.ts +19 -0
  302. package/dist/knowledge-stale-coverage.d.ts.map +1 -0
  303. package/dist/knowledge-stale-coverage.js +1 -0
  304. package/dist/knowledge-stale-gate-flags.d.ts +33 -0
  305. package/dist/knowledge-stale-gate-flags.d.ts.map +1 -0
  306. package/dist/knowledge-stale-gate-flags.js +1 -0
  307. package/dist/knowledge-stale-gate-input.d.ts +46 -0
  308. package/dist/knowledge-stale-gate-input.d.ts.map +1 -0
  309. package/dist/knowledge-stale-gate-input.js +1 -0
  310. package/dist/knowledge-stale-gate-result.d.ts +33 -0
  311. package/dist/knowledge-stale-gate-result.d.ts.map +1 -0
  312. package/dist/knowledge-stale-gate-result.js +1 -0
  313. package/dist/knowledge-stale-gate-rule.d.ts +33 -0
  314. package/dist/knowledge-stale-gate-rule.d.ts.map +1 -0
  315. package/dist/knowledge-stale-gate-rule.js +1 -0
  316. package/dist/knowledge-stale-gate-violation.d.ts +13 -0
  317. package/dist/knowledge-stale-gate-violation.d.ts.map +1 -0
  318. package/dist/knowledge-stale-gate-violation.js +1 -0
  319. package/dist/knowledge-stale-gate.d.ts +83 -0
  320. package/dist/knowledge-stale-gate.d.ts.map +1 -0
  321. package/dist/knowledge-stale-gate.js +429 -0
  322. package/dist/knowledge-stale-quality-gate.d.ts +26 -0
  323. package/dist/knowledge-stale-quality-gate.d.ts.map +1 -0
  324. package/dist/knowledge-stale-quality-gate.js +110 -0
  325. package/dist/knowledge-stale.d.ts +122 -9
  326. package/dist/knowledge-stale.d.ts.map +1 -1
  327. package/dist/knowledge-stale.js +1002 -316
  328. package/dist/knowledge-unverifiable-reason.d.ts +12 -0
  329. package/dist/knowledge-unverifiable-reason.d.ts.map +1 -0
  330. package/dist/knowledge-unverifiable-reason.js +12 -0
  331. package/dist/loader-diagnostics.d.ts +14 -2
  332. package/dist/loader-diagnostics.d.ts.map +1 -1
  333. package/dist/match-terms.d.ts +48 -0
  334. package/dist/match-terms.d.ts.map +1 -0
  335. package/dist/match-terms.js +155 -0
  336. package/dist/migration-profile-registry.d.ts +10 -0
  337. package/dist/migration-profile-registry.d.ts.map +1 -1
  338. package/dist/migration-profile-registry.js +57 -34
  339. package/dist/migration-readiness.d.ts.map +1 -1
  340. package/dist/migration-readiness.js +1 -3
  341. package/dist/nearest-id.d.ts +14 -0
  342. package/dist/nearest-id.d.ts.map +1 -0
  343. package/dist/nearest-id.js +12 -0
  344. package/dist/pack-asset-freshness.d.ts +65 -0
  345. package/dist/pack-asset-freshness.d.ts.map +1 -0
  346. package/dist/pack-asset-freshness.js +340 -0
  347. package/dist/pack-author-ux.d.ts +24 -3
  348. package/dist/pack-author-ux.d.ts.map +1 -1
  349. package/dist/pack-author-ux.js +87 -93
  350. package/dist/pack-contributions-inventory.d.ts +79 -39
  351. package/dist/pack-contributions-inventory.d.ts.map +1 -1
  352. package/dist/pack-contributions-inventory.js +443 -286
  353. package/dist/pack-doctor-verdict.d.ts +29 -0
  354. package/dist/pack-doctor-verdict.d.ts.map +1 -0
  355. package/dist/pack-doctor-verdict.js +45 -0
  356. package/dist/pack-doctor.d.ts +74 -1
  357. package/dist/pack-doctor.d.ts.map +1 -1
  358. package/dist/pack-doctor.js +281 -1
  359. package/dist/pack-helper-plan.d.ts +24 -0
  360. package/dist/pack-helper-plan.d.ts.map +1 -0
  361. package/dist/pack-helper-plan.js +77 -0
  362. package/dist/pack-helper-registry.d.ts +14 -0
  363. package/dist/pack-helper-registry.d.ts.map +1 -1
  364. package/dist/pack-helper-registry.js +76 -37
  365. package/dist/pack-manifest-reader.d.ts +8 -0
  366. package/dist/pack-manifest-reader.d.ts.map +1 -0
  367. package/dist/pack-manifest-reader.js +79 -0
  368. package/dist/pack-release-check.d.ts +14 -1
  369. package/dist/pack-release-check.d.ts.map +1 -1
  370. package/dist/pack-release-check.js +23 -1
  371. package/dist/pack-signature-status.d.ts +18 -6
  372. package/dist/pack-signature-status.d.ts.map +1 -1
  373. package/dist/pack-signature-status.js +59 -105
  374. package/dist/pack-test-runner.d.ts +27 -5
  375. package/dist/pack-test-runner.d.ts.map +1 -1
  376. package/dist/pack-test-runner.js +91 -35
  377. package/dist/pack-typecheck.d.ts +8 -0
  378. package/dist/pack-typecheck.d.ts.map +1 -0
  379. package/dist/pack-typecheck.js +76 -0
  380. package/dist/plan-review.d.ts +2 -0
  381. package/dist/plan-review.d.ts.map +1 -1
  382. package/dist/plan-review.js +17 -5
  383. package/dist/plan-simulation.d.ts.map +1 -1
  384. package/dist/plan-simulation.js +16 -5
  385. package/dist/playbook-load-issue.d.ts +16 -0
  386. package/dist/playbook-load-issue.d.ts.map +1 -0
  387. package/dist/playbook-load-issue.js +1 -0
  388. package/dist/playbook-registry.d.ts +32 -1
  389. package/dist/playbook-registry.d.ts.map +1 -1
  390. package/dist/playbook-registry.js +144 -55
  391. package/dist/policy-declaration.d.ts +18 -0
  392. package/dist/policy-declaration.d.ts.map +1 -0
  393. package/dist/policy-declaration.js +1 -0
  394. package/dist/policy-engine.d.ts +8 -0
  395. package/dist/policy-engine.d.ts.map +1 -1
  396. package/dist/policy-engine.js +26 -9
  397. package/dist/policy-overrides.d.ts.map +1 -1
  398. package/dist/policy-overrides.js +4 -4
  399. package/dist/policy-registry.d.ts +42 -0
  400. package/dist/policy-registry.d.ts.map +1 -0
  401. package/dist/policy-registry.js +131 -0
  402. package/dist/probed-id-fields.d.ts +21 -0
  403. package/dist/probed-id-fields.d.ts.map +1 -0
  404. package/dist/probed-id-fields.js +25 -0
  405. package/dist/probed-id-source.d.ts +11 -0
  406. package/dist/probed-id-source.d.ts.map +1 -0
  407. package/dist/probed-id-source.js +11 -0
  408. package/dist/profile-registry.d.ts +25 -2
  409. package/dist/profile-registry.d.ts.map +1 -1
  410. package/dist/profile-registry.js +59 -6
  411. package/dist/quality-gate-examination.d.ts +15 -0
  412. package/dist/quality-gate-examination.d.ts.map +1 -0
  413. package/dist/quality-gate-examination.js +15 -0
  414. package/dist/quality-gate-row-status.d.ts +19 -0
  415. package/dist/quality-gate-row-status.d.ts.map +1 -0
  416. package/dist/quality-gate-row-status.js +28 -0
  417. package/dist/quality-gate-status.d.ts +16 -0
  418. package/dist/quality-gate-status.d.ts.map +1 -0
  419. package/dist/quality-gate-status.js +16 -0
  420. package/dist/quality-html.d.ts.map +1 -1
  421. package/dist/quality-html.js +14 -3
  422. package/dist/quality-report-coverage.d.ts +26 -0
  423. package/dist/quality-report-coverage.d.ts.map +1 -0
  424. package/dist/quality-report-coverage.js +48 -0
  425. package/dist/quality-report.d.ts +28 -1
  426. package/dist/quality-report.d.ts.map +1 -1
  427. package/dist/quality-report.js +263 -35
  428. package/dist/query-intent-kind.d.ts +18 -0
  429. package/dist/query-intent-kind.d.ts.map +1 -0
  430. package/dist/query-intent-kind.js +18 -0
  431. package/dist/query-intent-result.d.ts +23 -0
  432. package/dist/query-intent-result.d.ts.map +1 -0
  433. package/dist/query-intent-result.js +1 -0
  434. package/dist/query-intent.d.ts +18 -0
  435. package/dist/query-intent.d.ts.map +1 -0
  436. package/dist/query-intent.js +336 -0
  437. package/dist/query-resolver.d.ts +18 -0
  438. package/dist/query-resolver.d.ts.map +1 -1
  439. package/dist/query-resolver.js +15 -12
  440. package/dist/ranked-recommendations.d.ts +36 -0
  441. package/dist/ranked-recommendations.d.ts.map +1 -0
  442. package/dist/ranked-recommendations.js +1 -0
  443. package/dist/ranker-explainability.d.ts.map +1 -1
  444. package/dist/ranker-explainability.js +38 -10
  445. package/dist/recommend-recipe.d.ts +17 -0
  446. package/dist/recommend-recipe.d.ts.map +1 -0
  447. package/dist/recommend-recipe.js +1 -0
  448. package/dist/recommend-recipes.d.ts +9 -0
  449. package/dist/recommend-recipes.d.ts.map +1 -0
  450. package/dist/recommend-recipes.js +106 -0
  451. package/dist/recommendation-candidate.d.ts +30 -0
  452. package/dist/recommendation-candidate.d.ts.map +1 -0
  453. package/dist/recommendation-candidate.js +1 -0
  454. package/dist/recommendation-confidence.d.ts +27 -0
  455. package/dist/recommendation-confidence.d.ts.map +1 -0
  456. package/dist/recommendation-confidence.js +1 -0
  457. package/dist/recommendation-ranking-options.d.ts +24 -0
  458. package/dist/recommendation-ranking-options.d.ts.map +1 -0
  459. package/dist/recommendation-ranking-options.js +1 -0
  460. package/dist/recommendation-ranking.d.ts +50 -0
  461. package/dist/recommendation-ranking.d.ts.map +1 -0
  462. package/dist/recommendation-ranking.js +527 -0
  463. package/dist/recommendation-source.d.ts +23 -0
  464. package/dist/recommendation-source.d.ts.map +1 -0
  465. package/dist/recommendation-source.js +23 -0
  466. package/dist/recommendation-suppression.d.ts +14 -0
  467. package/dist/recommendation-suppression.d.ts.map +1 -0
  468. package/dist/recommendation-suppression.js +14 -0
  469. package/dist/reference-asset-kind.d.ts +14 -0
  470. package/dist/reference-asset-kind.d.ts.map +1 -0
  471. package/dist/reference-asset-kind.js +14 -0
  472. package/dist/reference-content-check.d.ts +32 -0
  473. package/dist/reference-content-check.d.ts.map +1 -0
  474. package/dist/reference-content-check.js +143 -0
  475. package/dist/reference-failure.d.ts +31 -0
  476. package/dist/reference-failure.d.ts.map +1 -0
  477. package/dist/reference-failure.js +31 -0
  478. package/dist/reference-id-status.d.ts +15 -0
  479. package/dist/reference-id-status.d.ts.map +1 -0
  480. package/dist/reference-id-status.js +15 -0
  481. package/dist/reference-kind-declarations.d.ts +42 -0
  482. package/dist/reference-kind-declarations.d.ts.map +1 -0
  483. package/dist/reference-kind-declarations.js +142 -0
  484. package/dist/reference-lookup.d.ts +6 -0
  485. package/dist/reference-lookup.d.ts.map +1 -1
  486. package/dist/reference-lookup.js +13 -5
  487. package/dist/reference-registry.d.ts +210 -0
  488. package/dist/reference-registry.d.ts.map +1 -0
  489. package/dist/reference-registry.js +395 -0
  490. package/dist/reference-subject.d.ts +20 -0
  491. package/dist/reference-subject.d.ts.map +1 -0
  492. package/dist/reference-subject.js +1 -0
  493. package/dist/registration-hint-discovery-status.d.ts +25 -0
  494. package/dist/registration-hint-discovery-status.d.ts.map +1 -0
  495. package/dist/registration-hint-discovery-status.js +25 -0
  496. package/dist/registration-hint-registry.d.ts +80 -0
  497. package/dist/registration-hint-registry.d.ts.map +1 -1
  498. package/dist/registration-hint-registry.js +356 -138
  499. package/dist/registry-lifecycle.d.ts +200 -19
  500. package/dist/registry-lifecycle.d.ts.map +1 -1
  501. package/dist/registry-lifecycle.js +526 -136
  502. package/dist/release-readiness.d.ts.map +1 -1
  503. package/dist/release-readiness.js +10 -7
  504. package/dist/release-smoke.d.ts.map +1 -1
  505. package/dist/release-smoke.js +11 -7
  506. package/dist/report-site.d.ts.map +1 -1
  507. package/dist/report-site.js +25 -7
  508. package/dist/repository-intelligence.d.ts.map +1 -1
  509. package/dist/repository-intelligence.js +3 -2
  510. package/dist/repository-knowledge-model.d.ts.map +1 -1
  511. package/dist/repository-knowledge-model.js +18 -5
  512. package/dist/resolve-project-config.d.ts +64 -0
  513. package/dist/resolve-project-config.d.ts.map +1 -1
  514. package/dist/resolve-project-config.js +258 -32
  515. package/dist/reuse/compute-reuse-coverage.d.ts +29 -0
  516. package/dist/reuse/compute-reuse-coverage.d.ts.map +1 -0
  517. package/dist/reuse/compute-reuse-coverage.js +198 -0
  518. package/dist/reuse/rank-reuse-candidates.d.ts +38 -0
  519. package/dist/reuse/rank-reuse-candidates.d.ts.map +1 -0
  520. package/dist/reuse/rank-reuse-candidates.js +212 -0
  521. package/dist/reuse/rank-reuse-options.d.ts +20 -0
  522. package/dist/reuse/rank-reuse-options.d.ts.map +1 -0
  523. package/dist/reuse/rank-reuse-options.js +1 -0
  524. package/dist/reuse/rank-reuse-suggestions.d.ts +15 -0
  525. package/dist/reuse/rank-reuse-suggestions.d.ts.map +1 -0
  526. package/dist/reuse/rank-reuse-suggestions.js +31 -0
  527. package/dist/reuse/resolve-curated-reuse.d.ts +43 -0
  528. package/dist/reuse/resolve-curated-reuse.d.ts.map +1 -0
  529. package/dist/reuse/resolve-curated-reuse.js +115 -0
  530. package/dist/reuse/reuse-candidate.d.ts +29 -0
  531. package/dist/reuse/reuse-candidate.d.ts.map +1 -0
  532. package/dist/reuse/reuse-candidate.js +1 -0
  533. package/dist/reuse/reuse-coverage-report.d.ts +74 -0
  534. package/dist/reuse/reuse-coverage-report.d.ts.map +1 -0
  535. package/dist/reuse/reuse-coverage-report.js +1 -0
  536. package/dist/reuse/reuse-curated-coverage.d.ts +36 -0
  537. package/dist/reuse/reuse-curated-coverage.d.ts.map +1 -0
  538. package/dist/reuse/reuse-curated-coverage.js +1 -0
  539. package/dist/reuse/reuse-curated-resolution.d.ts +36 -0
  540. package/dist/reuse/reuse-curated-resolution.d.ts.map +1 -0
  541. package/dist/reuse/reuse-curated-resolution.js +1 -0
  542. package/dist/reuse/reuse-curated-status.d.ts +21 -0
  543. package/dist/reuse/reuse-curated-status.d.ts.map +1 -0
  544. package/dist/reuse/reuse-curated-status.js +21 -0
  545. package/dist/reuse/reuse-match-detail.d.ts +15 -0
  546. package/dist/reuse/reuse-match-detail.d.ts.map +1 -0
  547. package/dist/reuse/reuse-match-detail.js +1 -0
  548. package/dist/reuse/reuse-ranking.d.ts +37 -0
  549. package/dist/reuse/reuse-ranking.d.ts.map +1 -0
  550. package/dist/reuse/reuse-ranking.js +1 -0
  551. package/dist/reuse/reuse-suggestion.d.ts +19 -0
  552. package/dist/reuse/reuse-suggestion.d.ts.map +1 -0
  553. package/dist/reuse/reuse-suggestion.js +1 -0
  554. package/dist/reuse/reuse-symbol-lookup.d.ts +28 -0
  555. package/dist/reuse/reuse-symbol-lookup.d.ts.map +1 -0
  556. package/dist/reuse/reuse-symbol-lookup.js +1 -0
  557. package/dist/reuse/reuse-tokenize.d.ts +11 -0
  558. package/dist/reuse/reuse-tokenize.d.ts.map +1 -0
  559. package/dist/reuse/reuse-tokenize.js +26 -0
  560. package/dist/reuse/score-reuse-name.d.ts +16 -0
  561. package/dist/reuse/score-reuse-name.d.ts.map +1 -0
  562. package/dist/reuse/score-reuse-name.js +35 -0
  563. package/dist/reuse/score-reuse-primitive.d.ts +33 -0
  564. package/dist/reuse/score-reuse-primitive.d.ts.map +1 -0
  565. package/dist/reuse/score-reuse-primitive.js +92 -0
  566. package/dist/review-packet.d.ts +2 -0
  567. package/dist/review-packet.d.ts.map +1 -1
  568. package/dist/review-packet.js +13 -6
  569. package/dist/role-views.d.ts.map +1 -1
  570. package/dist/role-views.js +16 -16
  571. package/dist/routing-recommends-channels.d.ts +24 -0
  572. package/dist/routing-recommends-channels.d.ts.map +1 -0
  573. package/dist/routing-recommends-channels.js +19 -0
  574. package/dist/run-boundary-check.d.ts +118 -0
  575. package/dist/run-boundary-check.d.ts.map +1 -0
  576. package/dist/run-boundary-check.js +592 -0
  577. package/dist/safety-audit.js +1 -1
  578. package/dist/scaffold-coverage.js +1 -1
  579. package/dist/scaffold-pattern-doctor-report.d.ts +19 -0
  580. package/dist/scaffold-pattern-doctor-report.d.ts.map +1 -0
  581. package/dist/scaffold-pattern-doctor-report.js +54 -0
  582. package/dist/scaffold-patterns.d.ts +75 -1
  583. package/dist/scaffold-patterns.d.ts.map +1 -1
  584. package/dist/scaffold-patterns.js +375 -84
  585. package/dist/schema-inventory.d.ts.map +1 -1
  586. package/dist/schema-inventory.js +4 -3
  587. package/dist/search-document-id.d.ts +52 -0
  588. package/dist/search-document-id.d.ts.map +1 -0
  589. package/dist/search-document-id.js +72 -0
  590. package/dist/search-document-prefix.d.ts +7 -0
  591. package/dist/search-document-prefix.d.ts.map +1 -0
  592. package/dist/search-document-prefix.js +1 -0
  593. package/dist/search-index.d.ts.map +1 -1
  594. package/dist/search-index.js +20 -23
  595. package/dist/search-tuning-explain.d.ts +6 -0
  596. package/dist/search-tuning-explain.d.ts.map +1 -1
  597. package/dist/search-tuning-explain.js +19 -17
  598. package/dist/search-tuning-key-probes.d.ts +16 -0
  599. package/dist/search-tuning-key-probes.d.ts.map +1 -0
  600. package/dist/search-tuning-key-probes.js +65 -0
  601. package/dist/search-tuning-key-resolution.d.ts +25 -0
  602. package/dist/search-tuning-key-resolution.d.ts.map +1 -0
  603. package/dist/search-tuning-key-resolution.js +1 -0
  604. package/dist/search-tuning-key-resolver.d.ts +4 -0
  605. package/dist/search-tuning-key-resolver.d.ts.map +1 -0
  606. package/dist/search-tuning-key-resolver.js +107 -0
  607. package/dist/search-tuning-key-status.d.ts +24 -0
  608. package/dist/search-tuning-key-status.d.ts.map +1 -0
  609. package/dist/search-tuning-key-status.js +24 -0
  610. package/dist/search-tuning-lint.d.ts +9 -0
  611. package/dist/search-tuning-lint.d.ts.map +1 -0
  612. package/dist/search-tuning-lint.js +468 -0
  613. package/dist/search-tuning-registry.d.ts +43 -0
  614. package/dist/search-tuning-registry.d.ts.map +1 -1
  615. package/dist/search-tuning-registry.js +214 -49
  616. package/dist/self-audit.d.ts +12 -0
  617. package/dist/self-audit.d.ts.map +1 -1
  618. package/dist/self-audit.js +36 -10
  619. package/dist/self-config-doctor-v2.d.ts +145 -3
  620. package/dist/self-config-doctor-v2.d.ts.map +1 -1
  621. package/dist/self-config-doctor-v2.js +1329 -255
  622. package/dist/self-config-doctor.d.ts +20 -1
  623. package/dist/self-config-doctor.d.ts.map +1 -1
  624. package/dist/self-config-doctor.js +65 -309
  625. package/dist/settled-unit-states.d.ts +9 -0
  626. package/dist/settled-unit-states.d.ts.map +1 -0
  627. package/dist/settled-unit-states.js +14 -0
  628. package/dist/sharkcraft-inspector.d.ts +47 -1
  629. package/dist/sharkcraft-inspector.d.ts.map +1 -1
  630. package/dist/sharkcraft-inspector.js +278 -29
  631. package/dist/spec/spec-evidence.d.ts.map +1 -1
  632. package/dist/spec/spec-evidence.js +14 -20
  633. package/dist/split-identifier.d.ts +21 -0
  634. package/dist/split-identifier.d.ts.map +1 -0
  635. package/dist/split-identifier.js +27 -0
  636. package/dist/start-here.d.ts.map +1 -1
  637. package/dist/start-here.js +1 -2
  638. package/dist/substitute-placeholders.d.ts +8 -0
  639. package/dist/substitute-placeholders.d.ts.map +1 -0
  640. package/dist/substitute-placeholders.js +9 -0
  641. package/dist/symbol-index.d.ts +33 -0
  642. package/dist/symbol-index.d.ts.map +1 -1
  643. package/dist/symbol-index.js +241 -3
  644. package/dist/symbol-member-entry.d.ts +26 -0
  645. package/dist/symbol-member-entry.d.ts.map +1 -0
  646. package/dist/symbol-member-entry.js +1 -0
  647. package/dist/symbol-member-kind.d.ts +14 -0
  648. package/dist/symbol-member-kind.d.ts.map +1 -0
  649. package/dist/symbol-member-kind.js +14 -0
  650. package/dist/task-packet.d.ts.map +1 -1
  651. package/dist/task-packet.js +52 -9
  652. package/dist/task-ranker.d.ts +8 -0
  653. package/dist/task-ranker.d.ts.map +1 -1
  654. package/dist/task-ranker.js +25 -13
  655. package/dist/task-risk.js +1 -1
  656. package/dist/task-routing-hint-registry.d.ts +20 -1
  657. package/dist/task-routing-hint-registry.d.ts.map +1 -1
  658. package/dist/task-routing-hint-registry.js +186 -42
  659. package/dist/template-drift.d.ts.map +1 -1
  660. package/dist/template-drift.js +30 -9
  661. package/dist/template-lint.d.ts.map +1 -1
  662. package/dist/template-lint.js +160 -2
  663. package/dist/term-query.d.ts +10 -0
  664. package/dist/term-query.d.ts.map +1 -0
  665. package/dist/term-query.js +1 -0
  666. package/dist/test-definitions.d.ts +6 -3
  667. package/dist/test-definitions.d.ts.map +1 -1
  668. package/dist/test-runner.d.ts +95 -10
  669. package/dist/test-runner.d.ts.map +1 -1
  670. package/dist/test-runner.js +309 -318
  671. package/dist/tuning-query-tokens.d.ts +29 -0
  672. package/dist/tuning-query-tokens.d.ts.map +1 -0
  673. package/dist/tuning-query-tokens.js +51 -0
  674. package/dist/typecheck-files.d.ts +36 -0
  675. package/dist/typecheck-files.d.ts.map +1 -0
  676. package/dist/typecheck-files.js +158 -0
  677. package/dist/uncertainty.js +1 -1
  678. package/dist/universal-search.d.ts.map +1 -1
  679. package/dist/universal-search.js +5 -3
  680. package/dist/unregistered-exports.d.ts +19 -0
  681. package/dist/unregistered-exports.d.ts.map +1 -0
  682. package/dist/unregistered-exports.js +141 -0
  683. package/dist/unresolvable-reason.d.ts +11 -0
  684. package/dist/unresolvable-reason.d.ts.map +1 -0
  685. package/dist/unresolvable-reason.js +11 -0
  686. package/dist/upgrade-advisor.d.ts.map +1 -1
  687. package/dist/upgrade-advisor.js +16 -9
  688. package/dist/validate-contribution-file.d.ts +4 -0
  689. package/dist/validate-contribution-file.d.ts.map +1 -0
  690. package/dist/validate-contribution-file.js +228 -0
  691. package/dist/why-file.d.ts +3 -0
  692. package/dist/why-file.d.ts.map +1 -1
  693. package/dist/why-file.js +7 -3
  694. package/package.json +17 -17
@@ -8,15 +8,28 @@
8
8
  * Ignored: generated files (.generated.*, .d.ts), files containing
9
9
  * `@shrkcrft lifecycle-ignore <reason>`. Sites with `@shrkcrft lifecycle-managed-by <name>`
10
10
  * are reported under `ignored` with the managed-by reference.
11
+ *
12
+ * Bounded, and honest about the bound (round 11):
13
+ * - candidates are SORTED by project-relative path, so `offset` / `limit`
14
+ * page through them deterministically and `nextOffset` always reaches the
15
+ * remainder a cap, the budget or a signal left unexamined;
16
+ * - the wall-clock budget is checked between files AND inside each file
17
+ * (after the strip, after the declaration scan, before every register), so
18
+ * one pathological file cannot run past it unbounded. A file over its own
19
+ * sub-budget is abandoned whole and NAMED in `overBudgetFiles`;
20
+ * - every per-file step is linear — no two newline-spanning quantifiers
21
+ * touch in any pattern that runs on the blanked buffer, and the remover
22
+ * lookup is one pass per file instead of one regex per candidate name;
23
+ * - the report carries engine-owned coverage (`files`, `registrations`), and
24
+ * {@link registryLifecycleVerdict} reads nothing else.
11
25
  */
12
- import { readdirSync, readFileSync } from 'node:fs';
13
- import { extname, join, relative } from 'node:path';
26
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
27
+ import { extname, join, relative, sep } from 'node:path';
28
+ import { coverageShortfall } from '@shrkcrft/core';
14
29
  /**
15
30
  * Default directories the walk skips — build artefacts + non-source trees that
16
- * inflate the scan without owning runtime lifecycle. This is the DEFAULT only;
17
- * a repo that genuinely registers code under `tools/`, a non-standard root, etc.
18
- * can override the set via config (`registryLifecycle.skipDirs`) / the engine's
19
- * `skipDirs` input, so a hardcoded exclusion never silently blinds the check.
31
+ * inflate the scan without owning runtime lifecycle. Extend it with
32
+ * `registryLifecycle.skipDirsAdd`; replace it with `registryLifecycle.skipDirs`.
20
33
  */
21
34
  export const DEFAULT_REGISTRY_LIFECYCLE_SKIP_DIRS = Object.freeze([
22
35
  'node_modules',
@@ -36,52 +49,102 @@ export const DEFAULT_REGISTRY_LIFECYCLE_SKIP_DIRS = Object.freeze([
36
49
  '.agents',
37
50
  '.github',
38
51
  ]);
52
+ /**
53
+ * The dependency / VCS-adjacent / build-output defaults. A replacing
54
+ * `skipDirs` list that omits one of these makes the scan read vendored or
55
+ * generated code — four false positives from one added exclusion, measured —
56
+ * so dropping one is always reported. (`.git` & co. stay skipped by the
57
+ * dot-entry rule regardless.)
58
+ */
59
+ export const CORE_REGISTRY_LIFECYCLE_SKIP_DIRS = Object.freeze([
60
+ 'node_modules',
61
+ 'dist',
62
+ 'build',
63
+ 'out',
64
+ 'coverage',
65
+ '.sharkcraft',
66
+ ]);
39
67
  const SCAN_EXTENSIONS = new Set(['.ts', '.tsx']);
40
- /** Skip pathological single files — a 200 KB command file has no registry to pair. */
68
+ /**
69
+ * Skip pathological single files — named in `excludedFiles.oversized` AND
70
+ * counted UNEXAMINED in the files coverage: a cap is never a deliberate
71
+ * narrowing (round 11 review: subtracting them from `expected` let a missing
72
+ * remover inside a 361 KB file settle to a clean ✓). The boundaries reader's
73
+ * rule (`readScopeCoverage`: over-cap = unread = a shortfall) — one rule.
74
+ */
41
75
  const MAX_SCAN_FILE_BYTES = 256 * 1024;
42
- /** Hard wall-clock budget so the scan fails loud (partial flush) instead of hanging. */
76
+ /** How the files coverage names the size cap. */
77
+ const LIFECYCLE_READ_CAP = `${MAX_SCAN_FILE_BYTES / 1024} KB`;
78
+ /** Wall-clock budget for a whole run. */
43
79
  const DEFAULT_BUDGET_MS = 15_000;
44
- function isGeneratedFile(file, content) {
45
- if (file.endsWith('.d.ts'))
46
- return true;
47
- if (/\.generated\.(ts|tsx)$/.test(file))
48
- return true;
49
- if (/^\/\/ @generated\b/m.test(content))
50
- return true;
51
- return false;
80
+ /** Default file cap. */
81
+ const DEFAULT_LIMIT = 2000;
82
+ /** Labels named in a coverage record's `unexamined`. */
83
+ const UNEXAMINED_SHOWN = 20;
84
+ /**
85
+ * THE skip-dir authority: the engine, the CLI and `shrk doctor` all call this.
86
+ * `skipDirsAdd` extends; `skipDirs` replaces; a replacement that drops a core
87
+ * default is reported in `droppedDefaults`.
88
+ */
89
+ export function resolveRegistryLifecycleSkipDirs(input) {
90
+ const base = input.skipDirs ?? DEFAULT_REGISTRY_LIFECYCLE_SKIP_DIRS;
91
+ const effective = [...new Set([...base, ...(input.skipDirsAdd ?? [])])].sort();
92
+ const kept = new Set(effective);
93
+ const droppedDefaults = CORE_REGISTRY_LIFECYCLE_SKIP_DIRS.filter((d) => !kept.has(d));
94
+ return { effective, droppedDefaults };
95
+ }
96
+ /** The warning a replacing skip list that drops core defaults earns. One wording, scan and doctor. */
97
+ export function registryLifecycleSkipDirsWarning(droppedDefaults) {
98
+ return (`registryLifecycle.skipDirs replaces the defaults and drops: ${droppedDefaults.join(', ')} — ` +
99
+ 'use skipDirsAdd to extend the default skip set instead');
52
100
  }
53
- function walk(dir, projectRoot, out, skipDirs, deadline) {
54
- // The wall-clock budget must bound the TREE WALK too, not only the per-file
55
- // scan loop — a pathological tree could otherwise eat the whole budget before
56
- // the loop's deadline check ever runs (a25 §2.5). Bail the walk on expiry;
57
- // partial candidates flow through and the caller flags timedOut.
58
- if (deadline !== undefined && Date.now() > deadline)
59
- return;
101
+ function isGeneratedPath(file) {
102
+ return file.endsWith('.d.ts') || /\.generated\.(ts|tsx)$/.test(file);
103
+ }
104
+ function isGeneratedContent(content) {
105
+ return /^\/\/ @generated\b/m.test(content);
106
+ }
107
+ function toPosix(p) {
108
+ return sep === '/' ? p : p.split(sep).join('/');
109
+ }
110
+ function comparePaths(a, b) {
111
+ return a < b ? -1 : a > b ? 1 : 0;
112
+ }
113
+ function isMissing(e) {
114
+ return e?.code === 'ENOENT';
115
+ }
116
+ /** Walk for candidates. Returns `false` when the budget expired before the walk finished. */
117
+ function walkCandidates(dir, out, skipDirs, expired) {
118
+ // The budget bounds DISCOVERY too, not only the per-file loop — a
119
+ // pathological tree could otherwise eat the whole budget first (a25 §2.5).
120
+ if (expired())
121
+ return false;
60
122
  let entries;
61
123
  try {
62
124
  entries = readdirSync(dir, { withFileTypes: true });
63
125
  }
64
126
  catch {
65
- return;
127
+ return true;
66
128
  }
67
129
  for (const e of entries) {
68
- if (deadline !== undefined && Date.now() > deadline)
69
- return;
130
+ if (expired())
131
+ return false;
70
132
  if (skipDirs.has(e.name))
71
133
  continue;
72
- if (e.name.startsWith('.') && e.name !== '.sharkcraft') {
73
- // skip hidden dotfiles
74
- if (e.name !== '.')
75
- continue;
76
- }
134
+ // Hidden entries stay out, except `.sharkcraft` (skipped by default, but a
135
+ // replacing skip list makes it scannable — and is warned about).
136
+ if (e.name.startsWith('.') && e.name !== '.sharkcraft')
137
+ continue;
77
138
  const abs = join(dir, e.name);
78
139
  if (e.isDirectory()) {
79
- walk(abs, projectRoot, out, skipDirs, deadline);
140
+ if (!walkCandidates(abs, out, skipDirs, expired))
141
+ return false;
80
142
  }
81
143
  else if (e.isFile() && SCAN_EXTENSIONS.has(extname(e.name))) {
82
144
  out.push(abs);
83
145
  }
84
146
  }
147
+ return true;
85
148
  }
86
149
  const REGISTER_PATTERNS = Object.freeze([
87
150
  // export function registerX(
@@ -108,6 +171,29 @@ const ACCUMULATION_RE = /\.(?:set|add|push)\s*\(|\[[^\]]+\]\s*=[^=]/;
108
171
  // A teardown-shaped API somewhere in the file — evidence the file is in the
109
172
  // business of removing things, so a missing per-register remover is suspicious.
110
173
  const TEARDOWN_RE = /\b(?:remove|unregister|clear|dispose|unsubscribe)\w*\s*\(|\boff\s*\(/i;
174
+ /**
175
+ * Every callable site `name(` / `name = (` in the blanked code, in ONE pass.
176
+ *
177
+ * Replaces a whole-file regex compiled once PER CANDIDATE NAME whose third
178
+ * alternative, `(?:public|private|protected)?\s*(?:async\s+)?NAME\s*\(`, could
179
+ * start at every offset of a blank run and cost O(run) there: O(run²) per
180
+ * blanked comment, times six candidate names, times every register — 16 s for
181
+ * one 51 KB file. Anchored on `\b` + an identifier, no whitespace quantifier
182
+ * can start a match, so this is linear. It also reports the NAME's offset, so
183
+ * `removerLine` is the method's own line (the old match began at the blank run).
184
+ */
185
+ const CALLABLE_SITE_RE = /\b([A-Za-z_$][\w$]*)\s*(?:=\s*)?\(/g;
186
+ /**
187
+ * Every pattern this module runs on a blanked buffer, for the linearity lock
188
+ * (`r75-registry-lifecycle-scan.test.ts`): each must stay fast on a 64 KiB
189
+ * blank run and clean under `findBlankRunHazards`.
190
+ */
191
+ export const REGISTRY_LIFECYCLE_PATTERNS = Object.freeze({
192
+ register: REGISTER_PATTERNS,
193
+ accumulation: ACCUMULATION_RE,
194
+ teardown: TEARDOWN_RE,
195
+ callableSite: CALLABLE_SITE_RE,
196
+ });
111
197
  /**
112
198
  * Blank out comments and string / template-literal bodies (preserving newlines
113
199
  * so line numbers stay accurate) before scanning for `register*` declarations.
@@ -240,14 +326,29 @@ function findRegistersInFile(content, lineStarts) {
240
326
  re.lastIndex = 0;
241
327
  while ((m = re.exec(content)) !== null) {
242
328
  const name = m[1];
243
- if (seen.has(`${name}@${m.index}`))
329
+ // The NAME's offset, not the match start: the method pattern begins at
330
+ // the preceding `\n`, which used to report the line ABOVE the declaration.
331
+ const at = m.index + m[0].indexOf(name);
332
+ if (seen.has(`${name}@${at}`))
244
333
  continue;
245
- seen.add(`${name}@${m.index}`);
246
- out.push({ name, line: lineAtOffset(lineStarts, m.index) });
334
+ seen.add(`${name}@${at}`);
335
+ out.push({ name, line: lineAtOffset(lineStarts, at) });
247
336
  }
248
337
  }
249
338
  return out;
250
339
  }
340
+ /** First offset of every callable name in the file — built once, looked up per register. */
341
+ function indexCallableSites(code) {
342
+ const first = new Map();
343
+ CALLABLE_SITE_RE.lastIndex = 0;
344
+ let m;
345
+ while ((m = CALLABLE_SITE_RE.exec(code)) !== null) {
346
+ const name = m[1];
347
+ if (!first.has(name))
348
+ first.set(name, m.index);
349
+ }
350
+ return first;
351
+ }
251
352
  function findIgnoreAnnotations(content, registerName) {
252
353
  const ignoreRe = new RegExp(`@shrkcrft\\s+lifecycle-ignore(?:\\s+([^\\n]+))?[\\s\\S]{0,200}?\\b${registerName}\\b`, 'm');
253
354
  const managedRe = new RegExp(`@shrkcrft\\s+lifecycle-managed-by\\s+([\\w.-]+)[\\s\\S]{0,200}?\\b${registerName}\\b`, 'm');
@@ -274,88 +375,142 @@ function expectedRemoverNames(registerName) {
274
375
  `${stem}Off`,
275
376
  ];
276
377
  }
277
- function findRemoverInContent(content, candidates, lineStarts) {
278
- for (const name of candidates) {
279
- const re = new RegExp(`(?:export\\s+)?(?:async\\s+)?function\\s+${name}\\s*\\(|\\b${name}\\s*=\\s*\\(|(?:public|private|protected)?\\s*(?:async\\s+)?${name}\\s*\\(`);
280
- const m = content.match(re);
281
- if (m && typeof m.index === 'number') {
282
- return { name, line: lineAtOffset(lineStarts, m.index) };
283
- }
284
- }
285
- return null;
286
- }
287
- export function buildRegistryLifecycleReport(input) {
378
+ /**
379
+ * Create a scan: discover and sort the candidates, then judge them one
380
+ * {@link IRegistryLifecycleScan.step} at a time. The sync report builder and
381
+ * the signal-aware async runner both drive this — one loop, not two.
382
+ */
383
+ export function createRegistryLifecycleScan(input) {
288
384
  const { projectRoot } = input;
385
+ const clock = input.now ?? (() => Date.now());
289
386
  const scope = input.scope && input.scope.length > 0 ? input.scope : undefined;
290
387
  const changedOnly = input.files !== undefined;
291
- const deadline = Date.now() + (input.budgetMs ?? DEFAULT_BUDGET_MS);
292
- const limit = input.limit ?? 2000;
293
- const skipDirs = new Set(input.skipDirs ?? DEFAULT_REGISTRY_LIFECYCLE_SKIP_DIRS);
294
- // Candidate files: either the explicit changed set (bypassing the walk) or a
295
- // bounded tree walk. The changed set resolves relative paths under the root
296
- // and keeps only scannable extensions.
388
+ const budgetMs = input.budgetMs ?? DEFAULT_BUDGET_MS;
389
+ const perFileBudgetMs = input.perFileBudgetMs ?? Math.max(1000, Math.floor(budgetMs / 4));
390
+ const rawLimit = input.limit ?? DEFAULT_LIMIT;
391
+ const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : 0;
392
+ const requestedOffset = Math.max(0, Math.floor(input.offset ?? 0));
393
+ const skip = resolveRegistryLifecycleSkipDirs({
394
+ ...(input.skipDirs ? { skipDirs: input.skipDirs } : {}),
395
+ ...(input.skipDirsAdd ? { skipDirsAdd: input.skipDirsAdd } : {}),
396
+ });
397
+ const warnings = [];
398
+ if (input.skipDirs !== undefined && skip.droppedDefaults.length > 0) {
399
+ warnings.push(registryLifecycleSkipDirsWarning(skip.droppedDefaults));
400
+ }
401
+ // An existing config that failed to load: the configured skip set was never
402
+ // applied, so this walk is NOT the scope the project asked for. Loud (a
403
+ // warning line) AND settled (a coverage record the verdict reads).
404
+ const loadError = input.configLoadError;
405
+ const configCoverage = loadError
406
+ ? {
407
+ unit: 'config files',
408
+ expected: 1,
409
+ examined: 0,
410
+ ...(loadError.file ? { unexamined: [loadError.file], unexaminedTotal: 1 } : {}),
411
+ reason: 'failed to load, so registryLifecycle.skipDirs / skipDirsAdd were not applied (the scan used the default skip set)',
412
+ }
413
+ : undefined;
414
+ if (loadError) {
415
+ warnings.push('sharkcraft.config.ts failed to load — registryLifecycle.skipDirs / skipDirsAdd not applied, so this scan ' +
416
+ `used the default skip set (${loadError.message}). Run \`shrk doctor\`.`);
417
+ }
418
+ const deadline = clock('start') + budgetMs;
419
+ const walkRoot = changedOnly ? projectRoot : scope ? join(projectRoot, scope) : projectRoot;
420
+ const rel = (abs) => toPosix(relative(projectRoot, abs));
421
+ // Candidates, SORTED by project-relative path: without a stable order a
422
+ // continuation cannot be derived (readdir order is not one).
297
423
  let files;
424
+ let walkComplete = true;
298
425
  if (input.files !== undefined) {
299
- files = input.files
426
+ const abs = input.files
300
427
  .map((f) => (f.startsWith('/') ? f : join(projectRoot, f)))
301
428
  .filter((f) => SCAN_EXTENSIONS.has(extname(f)));
429
+ files = [...new Set(abs)];
302
430
  }
303
431
  else {
304
- const walkRoot = scope ? join(projectRoot, scope) : projectRoot;
305
432
  files = [];
306
- walk(walkRoot, projectRoot, files, skipDirs, deadline);
433
+ walkComplete = walkCandidates(walkRoot, files, new Set(skip.effective), () => clock('walk') >= deadline);
307
434
  }
308
- const scanFiles = files.slice(0, limit);
435
+ const relOf = new Map(files.map((f) => [f, rel(f)]));
436
+ files.sort((a, b) => comparePaths(relOf.get(a), relOf.get(b)));
437
+ const start = Math.min(requestedOffset, files.length);
438
+ const end = limit > 0 ? Math.min(files.length, start + limit) : files.length;
439
+ let cursor = start;
440
+ let finished = false;
441
+ let timedOut = !walkComplete;
442
+ let filesScanned = 0;
443
+ let registersFound = 0;
444
+ let generated = 0;
445
+ let missingCount = 0;
446
+ const oversized = [];
447
+ const unreadable = [];
448
+ const overBudget = [];
309
449
  const matchedPairs = [];
310
450
  const missingRemovers = [];
311
451
  const oneShotBootstrap = [];
312
452
  const ignored = [];
313
- let registersFound = 0;
314
- // The tree walk itself is now deadline-bounded (a25 §2.5), so an exhausted
315
- // budget can leave `scanFiles` empty and the per-file loop below would never
316
- // run to set the flag. Seed it from the deadline so a walk-level timeout is
317
- // reported (NOT-verified `2`) rather than read as a clean zero-registration pass.
318
- let timedOut = Date.now() > deadline;
319
- let filesScanned = 0;
320
- for (const file of scanFiles) {
321
- // Hard wall-clock budget checked between files — the scan is synchronous, so
322
- // a Date.now() deadline is the only interruption point. Flush partial results.
323
- if (Date.now() > deadline) {
324
- timedOut = true;
325
- break;
453
+ function judgeFile(abs, file, fileStart) {
454
+ if (isGeneratedPath(abs))
455
+ return { kind: 'generated' };
456
+ let size;
457
+ try {
458
+ size = statSync(abs).size;
459
+ }
460
+ catch (e) {
461
+ return { kind: isMissing(e) ? 'missing' : 'unreadable' };
326
462
  }
463
+ if (size > MAX_SCAN_FILE_BYTES)
464
+ return { kind: 'oversized' };
327
465
  let content;
328
466
  try {
329
- content = readFileSync(file, 'utf8');
467
+ content = readFileSync(abs, 'utf8');
330
468
  }
331
- catch {
332
- continue;
469
+ catch (e) {
470
+ return { kind: isMissing(e) ? 'missing' : 'unreadable' };
333
471
  }
334
- // Per-file size cap: a huge file (a big command/aggregate module) is a
335
- // catastrophic-backtracking / quadratic hazard and never holds a registry
336
- // that owes a remover. Skip it rather than let one file wedge the scan.
337
- if (content.length > MAX_SCAN_FILE_BYTES)
338
- continue;
339
- if (isGeneratedFile(file, content))
340
- continue;
341
- filesScanned += 1;
472
+ if (isGeneratedContent(content))
473
+ return { kind: 'generated' };
474
+ const fileDeadline = Math.min(deadline, fileStart + perFileBudgetMs);
475
+ const abandon = (phase, t, registersSeen) => ({
476
+ kind: 'over-budget',
477
+ entry: {
478
+ file,
479
+ phase,
480
+ deadline: t >= deadline ? 'run' : 'file',
481
+ elapsedMs: Math.max(0, t - fileStart),
482
+ registersSeen,
483
+ },
484
+ });
342
485
  // Scan declarations on code with comments/strings blanked out; keep the raw
343
486
  // content only for the comment-based @shrkcrft annotations.
344
487
  const code = stripCommentsAndLiterals(content);
488
+ let t = clock('strip');
489
+ if (t >= fileDeadline)
490
+ return abandon('strip', t, 0);
345
491
  const lineStarts = buildLineStarts(code);
346
492
  const registers = findRegistersInFile(code, lineStarts);
493
+ t = clock('registers');
494
+ if (t >= fileDeadline)
495
+ return abandon('registers', t, registers.length);
347
496
  // Per-file lifecycle evidence: only a file that BOTH accumulates removable
348
497
  // state AND has a teardown-shaped API plausibly owes a per-register remover.
349
- const hasAccumulation = ACCUMULATION_RE.test(code);
350
- const hasTeardown = TEARDOWN_RE.test(code);
351
- const ownsLifecycle = hasAccumulation && hasTeardown;
498
+ const ownsLifecycle = ACCUMULATION_RE.test(code) && TEARDOWN_RE.test(code);
499
+ const sites = registers.length > 0 ? indexCallableSites(code) : new Map();
500
+ const annotated = content.includes('@shrkcrft');
501
+ const pairs = [];
502
+ const missing = [];
503
+ const oneShot = [];
504
+ const ign = [];
352
505
  for (const reg of registers) {
353
- registersFound += 1;
354
- const ann = findIgnoreAnnotations(content, reg.name);
506
+ t = clock('register');
507
+ if (t >= fileDeadline)
508
+ return abandon('removers', t, registers.length);
509
+ const ann = annotated ? findIgnoreAnnotations(content, reg.name) : { ignore: false };
355
510
  if (ann.ignore) {
356
- ignored.push({
511
+ ign.push({
357
512
  registerName: reg.name,
358
- file: relative(projectRoot, file),
513
+ file,
359
514
  line: reg.line,
360
515
  reason: ann.reason ?? 'ignored',
361
516
  ...(ann.managedBy ? { managedBy: ann.managedBy } : {}),
@@ -363,88 +518,323 @@ export function buildRegistryLifecycleReport(input) {
363
518
  continue;
364
519
  }
365
520
  const candidates = expectedRemoverNames(reg.name);
366
- const remover = findRemoverInContent(code, candidates, lineStarts);
367
- if (remover) {
368
- matchedPairs.push({
521
+ // Candidate PRIORITY order is preserved: the first expected name with a
522
+ // callable site wins, exactly as the per-name loop it replaces.
523
+ const removerName = candidates.find((n) => sites.has(n));
524
+ if (removerName !== undefined) {
525
+ pairs.push({
369
526
  registerName: reg.name,
370
- removerName: remover.name,
371
- file: relative(projectRoot, file),
527
+ removerName,
528
+ file,
372
529
  registerLine: reg.line,
373
- removerLine: remover.line,
530
+ removerLine: lineAtOffset(lineStarts, sites.get(removerName)),
374
531
  });
375
532
  }
376
533
  else if (ownsLifecycle) {
377
- missingRemovers.push({
534
+ missing.push({
378
535
  registerName: reg.name,
379
536
  expectedRemoverNames: candidates,
380
- file: relative(projectRoot, file),
537
+ file,
381
538
  line: reg.line,
382
539
  suggestion: `Add ${candidates[0]}() / ${candidates[1]}() / ${candidates[2]}() — or annotate with \`@shrkcrft lifecycle-ignore <reason>\` / \`@shrkcrft lifecycle-managed-by <name>\` if cleanup is owned elsewhere.`,
383
540
  });
384
541
  }
385
542
  else {
386
543
  // No teardown-shaped API in the file → one-shot / bootstrap registration.
387
- oneShotBootstrap.push({
544
+ oneShot.push({
388
545
  registerName: reg.name,
389
- file: relative(projectRoot, file),
546
+ file,
390
547
  line: reg.line,
391
548
  reason: 'no teardown-shaped API in file — treated as one-shot bootstrap',
392
549
  });
393
550
  }
394
551
  }
552
+ return { kind: 'judged', registers: registers.length, pairs, missing, oneShot, ignored: ign };
553
+ }
554
+ function step() {
555
+ if (finished)
556
+ return false;
557
+ if (cursor >= end) {
558
+ finished = true;
559
+ return false;
560
+ }
561
+ const fileStart = clock('file');
562
+ if (fileStart >= deadline) {
563
+ timedOut = true;
564
+ finished = true;
565
+ return false;
566
+ }
567
+ const abs = files[cursor];
568
+ const outcome = judgeFile(abs, relOf.get(abs), fileStart);
569
+ switch (outcome.kind) {
570
+ case 'generated':
571
+ generated += 1;
572
+ break;
573
+ case 'missing':
574
+ missingCount += 1;
575
+ break;
576
+ case 'oversized':
577
+ oversized.push(relOf.get(abs));
578
+ break;
579
+ case 'unreadable':
580
+ unreadable.push(relOf.get(abs));
581
+ break;
582
+ case 'over-budget':
583
+ overBudget.push(outcome.entry);
584
+ if (outcome.entry.deadline === 'run') {
585
+ // The RUN's deadline expired inside this file: stop, and leave the
586
+ // cursor ON it so `--offset <nextOffset>` re-scans it.
587
+ timedOut = true;
588
+ finished = true;
589
+ return false;
590
+ }
591
+ break;
592
+ case 'judged':
593
+ filesScanned += 1;
594
+ registersFound += outcome.registers;
595
+ matchedPairs.push(...outcome.pairs);
596
+ missingRemovers.push(...outcome.missing);
597
+ oneShotBootstrap.push(...outcome.oneShot);
598
+ ignored.push(...outcome.ignored);
599
+ break;
600
+ }
601
+ cursor += 1;
602
+ return true;
603
+ }
604
+ function snapshot(options = {}) {
605
+ const interrupted = options.interrupted === true && cursor < end;
606
+ const nextOffset = walkComplete && cursor < files.length ? cursor : undefined;
607
+ const truncated = !walkComplete || cursor < files.length;
608
+ // Deliberate narrowing only: a generated file, and one deleted mid-walk.
609
+ // An over-cap file stays EXPECTED and is named unexamined below.
610
+ const excludedCount = generated + missingCount;
611
+ const expected = Math.max(0, files.length - start - excludedCount);
612
+ const fileOverBudget = overBudget.filter((o) => o.deadline === 'file').map((o) => o.file);
613
+ const remaining = files.slice(cursor).map((f) => relOf.get(f));
614
+ const unexaminedAll = [...new Set([...fileOverBudget, ...unreadable, ...oversized, ...remaining])];
615
+ const reason = coverageReason({
616
+ walkComplete,
617
+ interrupted,
618
+ timedOut,
619
+ capped: truncated,
620
+ budgetMs,
621
+ limit,
622
+ perFileBudgetMs,
623
+ cursor,
624
+ processed: cursor - start,
625
+ overBudgetCount: fileOverBudget.length,
626
+ unreadableCount: unreadable.length,
627
+ oversizedCount: oversized.length,
628
+ });
629
+ const coverage = {
630
+ unit: 'files',
631
+ expected,
632
+ examined: filesScanned,
633
+ root: walkRoot,
634
+ ...(truncated ? { capped: true } : {}),
635
+ ...(unexaminedAll.length > 0
636
+ ? { unexamined: unexaminedAll.slice(0, UNEXAMINED_SHOWN), unexaminedTotal: unexaminedAll.length }
637
+ : {}),
638
+ ...(reason ? { reason } : {}),
639
+ };
640
+ const registrationCoverage = {
641
+ unit: 'registrations',
642
+ expected: registersFound,
643
+ examined: registersFound,
644
+ root: walkRoot,
645
+ ...(registersFound === 0 ? { reason: 'no register* declaration in the examined files' } : {}),
646
+ };
647
+ const recommendations = [];
648
+ if (missingRemovers.length > 0) {
649
+ recommendations.push(`Run \`shrk check registry-lifecycle --json\` and add the matching remover(s), or document why not via a \`@shrkcrft lifecycle-ignore\` / \`@shrkcrft lifecycle-managed-by\` annotation.`);
650
+ }
651
+ const settled = registryLifecycleVerdict({
652
+ missingRemovers,
653
+ coverage,
654
+ registrationCoverage,
655
+ ...(configCoverage ? { configCoverage } : {}),
656
+ });
657
+ return {
658
+ schema: 'sharkcraft.registry-lifecycle/v1',
659
+ filesScanned,
660
+ totalFiles: files.length,
661
+ truncated,
662
+ timedOut,
663
+ interrupted,
664
+ walkComplete,
665
+ changedOnly,
666
+ ...(scope ? { scope } : {}),
667
+ offset: start,
668
+ limit,
669
+ budgetMs,
670
+ perFileBudgetMs,
671
+ candidatesExamined: cursor - start,
672
+ ...(nextOffset !== undefined ? { nextOffset } : {}),
673
+ excludedFiles: { generated, missing: missingCount, oversized: [...oversized] },
674
+ unreadableFiles: [...unreadable],
675
+ overBudgetFiles: [...overBudget],
676
+ skipDirs: skip.effective,
677
+ droppedDefaultSkipDirs: skip.droppedDefaults,
678
+ warnings: [...warnings],
679
+ registersFound,
680
+ matchedPairs: [...matchedPairs],
681
+ missingRemovers: [...missingRemovers],
682
+ oneShotBootstrap: [...oneShotBootstrap],
683
+ ignored: [...ignored],
684
+ recommendations,
685
+ coverage,
686
+ registrationCoverage,
687
+ ...(configCoverage ? { configCoverage } : {}),
688
+ verdict: settled.verdict,
689
+ ...(settled.reason !== undefined ? { verdictReason: settled.reason } : {}),
690
+ };
691
+ }
692
+ return { step, snapshot };
693
+ }
694
+ /** Why the files coverage fell short — the continuation lives here, so the verdict line carries it. */
695
+ function coverageReason(s) {
696
+ if (!s.walkComplete) {
697
+ return `the ${s.budgetMs} ms wall-clock budget expired during the tree walk, before every candidate was found; narrow with --scope <dir> or raise --budget-ms`;
698
+ }
699
+ if (s.interrupted)
700
+ return `interrupted by a signal after ${s.processed} candidate(s); continue with --offset ${s.cursor}`;
701
+ if (s.timedOut) {
702
+ return `the ${s.budgetMs} ms wall-clock budget expired after ${s.processed} candidate(s); continue with --offset ${s.cursor}, or raise --budget-ms`;
703
+ }
704
+ if (s.capped)
705
+ return `--limit ${s.limit} reached; continue with --offset ${s.cursor} (or --limit 0 for an uncapped run)`;
706
+ const parts = [];
707
+ if (s.overBudgetCount > 0)
708
+ parts.push(`over the per-file budget (${s.perFileBudgetMs} ms) and never judged`);
709
+ if (s.unreadableCount > 0)
710
+ parts.push('unreadable');
711
+ if (s.oversizedCount > 0)
712
+ parts.push(`over the ${LIFECYCLE_READ_CAP} lifecycle read cap, so never judged`);
713
+ if (parts.length === 0)
714
+ return undefined;
715
+ // The budget remedy only reaches what a budget stopped; an over-cap file is
716
+ // never judged whatever the budget.
717
+ const remedy = s.overBudgetCount > 0 || s.unreadableCount > 0 ? ' — raise --budget-ms or narrow with --scope <dir>' : '';
718
+ return `${parts.join(' or ')}${remedy}`;
719
+ }
720
+ /**
721
+ * THE lifecycle verdict. Both CLI verbs settle the same coverage records
722
+ * through `buildGateEnvelope`, and the MCP tool reads this — every surface
723
+ * answers from `coverageShortfall`, never a re-derived ternary.
724
+ *
725
+ * - a missing remover in the examined scope → `fail` (a real violation is
726
+ * real however much of the tree was examined);
727
+ * - otherwise any coverage shortfall (a config that failed to load, a cap,
728
+ * an expired budget, a signal, an over-budget or unreadable file, nothing
729
+ * in scope, no registrations) → `not-verified`, with the shortfall as the
730
+ * reason;
731
+ * - otherwise `pass`.
732
+ */
733
+ export function registryLifecycleVerdict(report) {
734
+ if (report.missingRemovers.length > 0) {
735
+ return {
736
+ verdict: 'fail',
737
+ reason: `${report.missingRemovers.length} register* declaration(s) with no matching remover`,
738
+ };
395
739
  }
396
- const recommendations = [];
397
- if (missingRemovers.length > 0) {
398
- recommendations.push(`Run \`shrk check registry-lifecycle --json\` and add the matching remover(s), or document why not via a \`@shrkcrft lifecycle-ignore\` / \`@shrkcrft lifecycle-managed-by\` annotation.`);
399
- }
400
- return {
401
- schema: 'sharkcraft.registry-lifecycle/v1',
402
- filesScanned,
403
- totalFiles: files.length,
404
- truncated: files.length > scanFiles.length || timedOut,
405
- timedOut,
406
- changedOnly,
407
- ...(scope ? { scope } : {}),
408
- registersFound,
409
- matchedPairs,
410
- missingRemovers,
411
- oneShotBootstrap,
412
- ignored,
413
- recommendations,
414
- };
740
+ // The config shortfall FIRST: when the configured scope was never applied,
741
+ // every other count describes the wrong scope.
742
+ const gap = (report.configCoverage ? coverageShortfall(report.configCoverage) : undefined) ??
743
+ coverageShortfall(report.coverage) ??
744
+ coverageShortfall(report.registrationCoverage);
745
+ if (gap !== undefined)
746
+ return { verdict: 'not-verified', reason: gap };
747
+ return { verdict: 'pass' };
415
748
  }
416
- export function renderRegistryLifecycleReportText(report) {
749
+ /** Synchronous scan: drives {@link createRegistryLifecycleScan} to the end. */
750
+ export function buildRegistryLifecycleReport(input) {
751
+ const scan = createRegistryLifecycleScan(input);
752
+ while (scan.step()) {
753
+ // one candidate per step — the budget is checked inside each
754
+ }
755
+ return scan.snapshot();
756
+ }
757
+ /**
758
+ * Signal-aware scan: the same {@link IRegistryLifecycleScan.step}, yielding to
759
+ * the event loop between files so a SIGTERM / SIGINT handler can run. An
760
+ * aborted run returns the partial report with `interrupted: true` and a
761
+ * `nextOffset` that continues it. Latency is bounded by one (linear) file.
762
+ */
763
+ export async function runRegistryLifecycleScan(input, options = {}) {
764
+ const scan = createRegistryLifecycleScan(input);
765
+ for (;;) {
766
+ if (options.signal?.aborted)
767
+ return scan.snapshot({ interrupted: true });
768
+ if (!scan.step())
769
+ return scan.snapshot();
770
+ await new Promise((resolve) => setImmediate(resolve));
771
+ }
772
+ }
773
+ export function renderRegistryLifecycleReportText(report, options = {}) {
774
+ const command = options.command ?? 'shrk check registry-lifecycle';
417
775
  const lines = [];
418
776
  lines.push('=== Registry lifecycle ===');
419
777
  if (report.scope)
420
778
  lines.push(` scope ${report.scope}`);
421
779
  if (report.changedOnly)
422
780
  lines.push(' scope changed-only');
423
- // Loud skip: a changed diff that touches no scannable file was NOT verified —
424
- // never let "scanned nothing" read as a clean pass.
425
- if (report.changedOnly && report.filesScanned === 0) {
426
- lines.push('');
427
- lines.push(' ! 0 files in the changed scope — lifecycle NOT verified (this is not a pass).');
428
- return lines.join('\n') + '\n';
429
- }
430
- if (report.timedOut) {
431
- lines.push(` ! wall-clock budget hit — scanned ${report.filesScanned}/${report.totalFiles} before flushing partial results (re-run with --scope <dir> to finish).`);
432
- }
433
- lines.push(` files scanned ${report.filesScanned}` +
434
- (report.truncated && !report.timedOut
435
- ? ` ! capped (${report.totalFiles} candidates — re-run with --scope <dir> for the rest)`
436
- : ''));
781
+ lines.push(` candidates ${report.totalFiles}` + (report.offset > 0 ? ` (this run starts at offset ${report.offset})` : ''));
782
+ lines.push(` files scanned ${report.filesScanned} of ${report.coverage.expected}`);
437
783
  lines.push(` registers found ${report.registersFound}`);
438
784
  lines.push(` matched pairs ${report.matchedPairs.length}`);
439
785
  lines.push(` missing removers ${report.missingRemovers.length}`);
440
786
  lines.push(` one-shot bootstrap ${report.oneShotBootstrap.length}`);
441
787
  lines.push(` ignored ${report.ignored.length}`);
442
- // Zero registrations in scope means nothing was actually checked — say so
443
- // loudly so it never reads as the green "all removers present" pass (the exit
444
- // code is NOT-verified `2` to match; a25 §1 / §2.5).
445
- if (report.registersFound === 0 && !report.timedOut) {
788
+ const ex = report.excludedFiles;
789
+ if (ex.generated + ex.missing > 0) {
790
+ const parts = [];
791
+ if (ex.generated > 0)
792
+ parts.push(`${ex.generated} generated`);
793
+ if (ex.missing > 0)
794
+ parts.push(`${ex.missing} deleted`);
795
+ lines.push(` excluded ${parts.join(', ')}`);
796
+ }
797
+ // An over-cap file is UNEXAMINED (it is in the coverage's expected scope),
798
+ // never "excluded": listed with the other files that were never judged.
799
+ for (const o of ex.oversized.slice(0, 20)) {
800
+ lines.push(` ! over read cap ${o} — over the ${LIFECYCLE_READ_CAP} lifecycle read cap, never judged`);
801
+ }
802
+ for (const o of report.overBudgetFiles) {
803
+ lines.push(` ! over budget ${o.file} — ${o.phase} phase, ${o.elapsedMs} ms (${o.deadline === 'run' ? "the run's budget" : `the ${report.perFileBudgetMs} ms per-file budget`}); ${o.registersSeen} register(s) seen, none judged`);
804
+ }
805
+ for (const u of report.unreadableFiles.slice(0, 20))
806
+ lines.push(` ! unreadable ${u}`);
807
+ for (const w of report.warnings)
808
+ lines.push(` ! ${w}`);
809
+ // Scope facts — what was NOT examined and how to reach it. The verdict line
810
+ // itself is the caller's (the CLI settles it through the gate envelope).
811
+ const remainingCount = report.totalFiles - (report.offset + report.candidatesExamined);
812
+ const resume = report.nextOffset !== undefined ? `${command} --offset ${report.nextOffset}` : undefined;
813
+ if (report.coverage.expected === 0 && report.walkComplete) {
814
+ lines.push('');
815
+ lines.push(report.changedOnly
816
+ ? ' ! 0 files in the changed scope — nothing to judge.'
817
+ : ' ! 0 candidate files in scope — nothing to judge.');
818
+ }
819
+ else if (report.registersFound === 0 && report.filesScanned > 0 && !report.truncated) {
820
+ lines.push('');
821
+ lines.push(' ! 0 register* declarations in the examined files — nothing to judge.');
822
+ }
823
+ if (!report.walkComplete) {
824
+ lines.push('');
825
+ lines.push(` ! wall-clock budget (${report.budgetMs} ms) expired during the tree walk — ${report.totalFiles} candidate(s) found so far; narrow with --scope <dir> or raise --budget-ms.`);
826
+ }
827
+ else if (report.interrupted && resume) {
828
+ lines.push('');
829
+ lines.push(` ! INTERRUPTED — partial results (scanned ${report.filesScanned}/${report.coverage.expected}); continue with ${resume}`);
830
+ }
831
+ else if (report.timedOut && resume) {
832
+ lines.push('');
833
+ lines.push(` ! wall-clock budget (${report.budgetMs} ms) hit — scanned ${report.filesScanned}/${report.coverage.expected}; continue with ${resume} (or raise --budget-ms)`);
834
+ }
835
+ else if (resume) {
446
836
  lines.push('');
447
- lines.push(' ! 0 registrations found in scope — lifecycle NOT verified (this is not a pass).');
837
+ lines.push(` ! capped — scanned ${report.candidatesExamined} of ${report.totalFiles - report.offset} candidate files (cap --limit ${report.limit}); ${remainingCount} unexamined. Continue: ${resume} (or --limit 0 for an uncapped run)`);
448
838
  }
449
839
  lines.push('');
450
840
  if (report.missingRemovers.length > 0) {