@shrkcrft/cli 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 (510) hide show
  1. package/dist/asset-preview/apply-asset-preview.js +1 -1
  2. package/dist/authoring/authoring-kit.d.ts +9 -1
  3. package/dist/authoring/authoring-kit.d.ts.map +1 -1
  4. package/dist/authoring/authoring-kit.js +26 -13
  5. package/dist/bootstrap/unlinked-workspace-dependency.d.ts +11 -0
  6. package/dist/bootstrap/unlinked-workspace-dependency.d.ts.map +1 -0
  7. package/dist/bootstrap/unlinked-workspace-dependency.js +99 -0
  8. package/dist/command-registry.d.ts +100 -1
  9. package/dist/command-registry.d.ts.map +1 -1
  10. package/dist/command-registry.js +130 -3
  11. package/dist/commands/api-diff.command.d.ts.map +1 -1
  12. package/dist/commands/api-diff.command.js +12 -0
  13. package/dist/commands/apply.command.d.ts.map +1 -1
  14. package/dist/commands/apply.command.js +3 -0
  15. package/dist/commands/arch.command.d.ts.map +1 -1
  16. package/dist/commands/arch.command.js +26 -7
  17. package/dist/commands/architecture.command.d.ts.map +1 -1
  18. package/dist/commands/architecture.command.js +8 -2
  19. package/dist/commands/baseline.command.d.ts +76 -0
  20. package/dist/commands/baseline.command.d.ts.map +1 -1
  21. package/dist/commands/baseline.command.js +599 -75
  22. package/dist/commands/biome.command.d.ts.map +1 -1
  23. package/dist/commands/biome.command.js +20 -0
  24. package/dist/commands/boundaries.command.d.ts +9 -1
  25. package/dist/commands/boundaries.command.d.ts.map +1 -1
  26. package/dist/commands/boundaries.command.js +88 -20
  27. package/dist/commands/bundle.command.d.ts.map +1 -1
  28. package/dist/commands/bundle.command.js +57 -11
  29. package/dist/commands/cache-align.command.d.ts.map +1 -1
  30. package/dist/commands/cache-align.command.js +3 -1
  31. package/dist/commands/changelog-data.d.ts.map +1 -1
  32. package/dist/commands/changelog-data.js +164 -0
  33. package/dist/commands/changes.command.d.ts.map +1 -1
  34. package/dist/commands/changes.command.js +15 -0
  35. package/dist/commands/check.command.d.ts.map +1 -1
  36. package/dist/commands/check.command.js +1260 -324
  37. package/dist/commands/checks.command.d.ts.map +1 -1
  38. package/dist/commands/checks.command.js +188 -40
  39. package/dist/commands/ci.command.d.ts.map +1 -1
  40. package/dist/commands/ci.command.js +35 -2
  41. package/dist/commands/code-intel.command.d.ts.map +1 -1
  42. package/dist/commands/code-intel.command.js +8 -1
  43. package/dist/commands/command-catalog.d.ts +23 -1
  44. package/dist/commands/command-catalog.d.ts.map +1 -1
  45. package/dist/commands/command-catalog.js +152 -35
  46. package/dist/commands/commands.command.d.ts +21 -3
  47. package/dist/commands/commands.command.d.ts.map +1 -1
  48. package/dist/commands/commands.command.js +172 -27
  49. package/dist/commands/compress.command.d.ts.map +1 -1
  50. package/dist/commands/compress.command.js +4 -0
  51. package/dist/commands/constructs.command.d.ts.map +1 -1
  52. package/dist/commands/constructs.command.js +50 -12
  53. package/dist/commands/context.command.d.ts.map +1 -1
  54. package/dist/commands/context.command.js +40 -5
  55. package/dist/commands/contract-templates.command.d.ts.map +1 -1
  56. package/dist/commands/contract-templates.command.js +9 -1
  57. package/dist/commands/contract.command.d.ts.map +1 -1
  58. package/dist/commands/contract.command.js +35 -0
  59. package/dist/commands/conventions.command.d.ts.map +1 -1
  60. package/dist/commands/conventions.command.js +189 -29
  61. package/dist/commands/coverage.command.d.ts.map +1 -1
  62. package/dist/commands/coverage.command.js +9 -0
  63. package/dist/commands/daily.commands.d.ts +0 -1
  64. package/dist/commands/daily.commands.d.ts.map +1 -1
  65. package/dist/commands/daily.commands.js +12 -76
  66. package/dist/commands/dashboard.command.d.ts.map +1 -1
  67. package/dist/commands/dashboard.command.js +10 -0
  68. package/dist/commands/dev.command.d.ts.map +1 -1
  69. package/dist/commands/dev.command.js +45 -2
  70. package/dist/commands/diagnostics.command.d.ts +0 -2
  71. package/dist/commands/diagnostics.command.d.ts.map +1 -1
  72. package/dist/commands/diagnostics.command.js +5 -78
  73. package/dist/commands/diff-check.command.d.ts +6 -4
  74. package/dist/commands/diff-check.command.d.ts.map +1 -1
  75. package/dist/commands/diff-check.command.js +190 -103
  76. package/dist/commands/docs-references.command.d.ts +20 -0
  77. package/dist/commands/docs-references.command.d.ts.map +1 -0
  78. package/dist/commands/docs-references.command.js +520 -0
  79. package/dist/commands/doctor.command.d.ts.map +1 -1
  80. package/dist/commands/doctor.command.js +60 -5
  81. package/dist/commands/drift.command.d.ts.map +1 -1
  82. package/dist/commands/drift.command.js +17 -4
  83. package/dist/commands/eslint.command.d.ts.map +1 -1
  84. package/dist/commands/eslint.command.js +20 -0
  85. package/dist/commands/export.command.d.ts.map +1 -1
  86. package/dist/commands/export.command.js +23 -1
  87. package/dist/commands/feedback-dispatch.command.d.ts.map +1 -1
  88. package/dist/commands/feedback-dispatch.command.js +29 -0
  89. package/dist/commands/feedback.command.d.ts.map +1 -1
  90. package/dist/commands/feedback.command.js +6 -1
  91. package/dist/commands/finish.command.d.ts.map +1 -1
  92. package/dist/commands/finish.command.js +32 -8
  93. package/dist/commands/fix.command.d.ts.map +1 -1
  94. package/dist/commands/fix.command.js +19 -0
  95. package/dist/commands/gate.command.d.ts +6 -1
  96. package/dist/commands/gate.command.d.ts.map +1 -1
  97. package/dist/commands/gate.command.js +50 -124
  98. package/dist/commands/gates.command.d.ts +54 -0
  99. package/dist/commands/gates.command.d.ts.map +1 -1
  100. package/dist/commands/gates.command.js +1314 -64
  101. package/dist/commands/gen.command.d.ts.map +1 -1
  102. package/dist/commands/gen.command.js +4 -0
  103. package/dist/commands/generated.command.d.ts +62 -0
  104. package/dist/commands/generated.command.d.ts.map +1 -1
  105. package/dist/commands/generated.command.js +639 -156
  106. package/dist/commands/graph-code-subverbs.d.ts +15 -0
  107. package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
  108. package/dist/commands/graph-code-subverbs.js +203 -25
  109. package/dist/commands/graph.command.d.ts.map +1 -1
  110. package/dist/commands/graph.command.js +79 -37
  111. package/dist/commands/help.command.d.ts +30 -6
  112. package/dist/commands/help.command.d.ts.map +1 -1
  113. package/dist/commands/help.command.js +323 -199
  114. package/dist/commands/helper.command.d.ts +1 -0
  115. package/dist/commands/helper.command.d.ts.map +1 -1
  116. package/dist/commands/helper.command.js +195 -56
  117. package/dist/commands/impact.command.d.ts.map +1 -1
  118. package/dist/commands/impact.command.js +115 -12
  119. package/dist/commands/infer.command.d.ts.map +1 -1
  120. package/dist/commands/infer.command.js +20 -45
  121. package/dist/commands/ingest.command.d.ts +0 -1
  122. package/dist/commands/ingest.command.d.ts.map +1 -1
  123. package/dist/commands/ingest.command.js +25 -41
  124. package/dist/commands/knowledge-author.command.d.ts +0 -3
  125. package/dist/commands/knowledge-author.command.d.ts.map +1 -1
  126. package/dist/commands/knowledge-author.command.js +10 -23
  127. package/dist/commands/knowledge.command.d.ts.map +1 -1
  128. package/dist/commands/knowledge.command.js +469 -129
  129. package/dist/commands/languages.command.d.ts.map +1 -1
  130. package/dist/commands/languages.command.js +27 -0
  131. package/dist/commands/lint.command.d.ts.map +1 -1
  132. package/dist/commands/lint.command.js +3 -0
  133. package/dist/commands/mcp.command.d.ts +18 -0
  134. package/dist/commands/mcp.command.d.ts.map +1 -1
  135. package/dist/commands/mcp.command.js +33 -8
  136. package/dist/commands/onboard.command.d.ts.map +1 -1
  137. package/dist/commands/onboard.command.js +20 -0
  138. package/dist/commands/owners.command.d.ts.map +1 -1
  139. package/dist/commands/owners.command.js +14 -2
  140. package/dist/commands/packs-new.d.ts +13 -1
  141. package/dist/commands/packs-new.d.ts.map +1 -1
  142. package/dist/commands/packs-new.js +500 -335
  143. package/dist/commands/packs.command.d.ts.map +1 -1
  144. package/dist/commands/packs.command.js +378 -36
  145. package/dist/commands/paths.command.d.ts.map +1 -1
  146. package/dist/commands/paths.command.js +9 -1
  147. package/dist/commands/pipelines.command.d.ts.map +1 -1
  148. package/dist/commands/pipelines.command.js +7 -1
  149. package/dist/commands/plan-check.command.d.ts.map +1 -1
  150. package/dist/commands/plan-check.command.js +3 -0
  151. package/dist/commands/plan.command.d.ts.map +1 -1
  152. package/dist/commands/plan.command.js +3 -0
  153. package/dist/commands/playbooks.command.d.ts.map +1 -1
  154. package/dist/commands/playbooks.command.js +7 -2
  155. package/dist/commands/policy-lint.command.d.ts +19 -2
  156. package/dist/commands/policy-lint.command.d.ts.map +1 -1
  157. package/dist/commands/policy-lint.command.js +224 -65
  158. package/dist/commands/policy.command.d.ts.map +1 -1
  159. package/dist/commands/policy.command.js +6 -1
  160. package/dist/commands/pr.command.d.ts.map +1 -1
  161. package/dist/commands/pr.command.js +14 -0
  162. package/dist/commands/presets.command.d.ts.map +1 -1
  163. package/dist/commands/presets.command.js +12 -27
  164. package/dist/commands/profiles.command.d.ts.map +1 -1
  165. package/dist/commands/profiles.command.js +94 -26
  166. package/dist/commands/provenance.command.js +1 -1
  167. package/dist/commands/quality.command.d.ts.map +1 -1
  168. package/dist/commands/quality.command.js +129 -33
  169. package/dist/commands/recommend.command.d.ts +20 -4
  170. package/dist/commands/recommend.command.d.ts.map +1 -1
  171. package/dist/commands/recommend.command.js +172 -278
  172. package/dist/commands/registrations.command.d.ts.map +1 -1
  173. package/dist/commands/registrations.command.js +138 -13
  174. package/dist/commands/registry-lifecycle-run.d.ts +14 -0
  175. package/dist/commands/registry-lifecycle-run.d.ts.map +1 -0
  176. package/dist/commands/registry-lifecycle-run.js +336 -0
  177. package/dist/commands/registry.command.d.ts.map +1 -1
  178. package/dist/commands/registry.command.js +235 -62
  179. package/dist/commands/release.command.d.ts.map +1 -1
  180. package/dist/commands/release.command.js +99 -20
  181. package/dist/commands/report.command.d.ts.map +1 -1
  182. package/dist/commands/report.command.js +38 -2
  183. package/dist/commands/reuse-coverage.command.d.ts +23 -0
  184. package/dist/commands/reuse-coverage.command.d.ts.map +1 -0
  185. package/dist/commands/reuse-coverage.command.js +536 -0
  186. package/dist/commands/reuse.command.d.ts +3 -18
  187. package/dist/commands/reuse.command.d.ts.map +1 -1
  188. package/dist/commands/reuse.command.js +395 -244
  189. package/dist/commands/review.command.d.ts.map +1 -1
  190. package/dist/commands/review.command.js +33 -1
  191. package/dist/commands/rounds.command.d.ts.map +1 -1
  192. package/dist/commands/rounds.command.js +5 -0
  193. package/dist/commands/safety.command.d.ts.map +1 -1
  194. package/dist/commands/safety.command.js +9 -0
  195. package/dist/commands/scaffolds.command.d.ts.map +1 -1
  196. package/dist/commands/scaffolds.command.js +97 -25
  197. package/dist/commands/search.command.d.ts +0 -8
  198. package/dist/commands/search.command.d.ts.map +1 -1
  199. package/dist/commands/search.command.js +136 -28
  200. package/dist/commands/self-config-xrefs.command.d.ts +24 -0
  201. package/dist/commands/self-config-xrefs.command.d.ts.map +1 -0
  202. package/dist/commands/self-config-xrefs.command.js +170 -0
  203. package/dist/commands/self-config.command.d.ts.map +1 -1
  204. package/dist/commands/self-config.command.js +261 -65
  205. package/dist/commands/smart-context.command.d.ts.map +1 -1
  206. package/dist/commands/smart-context.command.js +39 -3
  207. package/dist/commands/spec.command.d.ts.map +1 -1
  208. package/dist/commands/spec.command.js +3 -0
  209. package/dist/commands/stats.command.d.ts.map +1 -1
  210. package/dist/commands/stats.command.js +3 -0
  211. package/dist/commands/surface.command.d.ts +6 -1
  212. package/dist/commands/surface.command.d.ts.map +1 -1
  213. package/dist/commands/surface.command.js +256 -54
  214. package/dist/commands/task-context.command.js +1 -1
  215. package/dist/commands/task.command.d.ts.map +1 -1
  216. package/dist/commands/task.command.js +22 -1
  217. package/dist/commands/templates.command.d.ts +4 -0
  218. package/dist/commands/templates.command.d.ts.map +1 -1
  219. package/dist/commands/templates.command.js +109 -22
  220. package/dist/commands/test.command.d.ts.map +1 -1
  221. package/dist/commands/test.command.js +161 -33
  222. package/dist/commands/tests.command.d.ts.map +1 -1
  223. package/dist/commands/tests.command.js +29 -3
  224. package/dist/commands/trace.command.d.ts.map +1 -1
  225. package/dist/commands/trace.command.js +29 -4
  226. package/dist/commands/watch.command.d.ts.map +1 -1
  227. package/dist/commands/watch.command.js +3 -0
  228. package/dist/commands/why.command.d.ts.map +1 -1
  229. package/dist/commands/why.command.js +3 -0
  230. package/dist/commands/wiring.command.d.ts +18 -4
  231. package/dist/commands/wiring.command.d.ts.map +1 -1
  232. package/dist/commands/wiring.command.js +362 -74
  233. package/dist/dashboard/code-intelligence-data.js +4 -4
  234. package/dist/diff/deleted-orphan-coverage.d.ts +32 -0
  235. package/dist/diff/deleted-orphan-coverage.d.ts.map +1 -0
  236. package/dist/diff/deleted-orphan-coverage.js +107 -0
  237. package/dist/diff/deleted-orphan-scope-notes.d.ts +23 -0
  238. package/dist/diff/deleted-orphan-scope-notes.d.ts.map +1 -0
  239. package/dist/diff/deleted-orphan-scope-notes.js +49 -0
  240. package/dist/diff/deleted-orphans.d.ts +32 -0
  241. package/dist/diff/deleted-orphans.d.ts.map +1 -1
  242. package/dist/diff/deleted-orphans.js +42 -2
  243. package/dist/dispatch/closest-match.d.ts +15 -0
  244. package/dist/dispatch/closest-match.d.ts.map +1 -0
  245. package/dist/dispatch/closest-match.js +47 -0
  246. package/dist/dispatch/global-flags.d.ts +94 -0
  247. package/dist/dispatch/global-flags.d.ts.map +1 -0
  248. package/dist/dispatch/global-flags.js +123 -0
  249. package/dist/dispatch/guard-invocation-input.d.ts +24 -0
  250. package/dist/dispatch/guard-invocation-input.d.ts.map +1 -0
  251. package/dist/dispatch/guard-invocation-input.js +1 -0
  252. package/dist/dispatch/guard-invocation.d.ts +33 -0
  253. package/dist/dispatch/guard-invocation.d.ts.map +1 -0
  254. package/dist/dispatch/guard-invocation.js +183 -0
  255. package/dist/dispatch/help-intercept.d.ts +22 -0
  256. package/dist/dispatch/help-intercept.d.ts.map +1 -0
  257. package/dist/dispatch/help-intercept.js +42 -0
  258. package/dist/dispatch/i-declared-invocation.d.ts +17 -0
  259. package/dist/dispatch/i-declared-invocation.d.ts.map +1 -0
  260. package/dist/dispatch/i-declared-invocation.js +1 -0
  261. package/dist/dispatch/i-unknown-flag-refusal-input.d.ts +28 -0
  262. package/dist/dispatch/i-unknown-flag-refusal-input.d.ts.map +1 -0
  263. package/dist/dispatch/i-unknown-flag-refusal-input.js +1 -0
  264. package/dist/dispatch/invocation-rejection-kind.d.ts +15 -0
  265. package/dist/dispatch/invocation-rejection-kind.d.ts.map +1 -0
  266. package/dist/dispatch/invocation-rejection-kind.js +15 -0
  267. package/dist/dispatch/invocation-rejection.d.ts +18 -0
  268. package/dist/dispatch/invocation-rejection.d.ts.map +1 -0
  269. package/dist/dispatch/invocation-rejection.js +1 -0
  270. package/dist/dispatch/judge-invocation.d.ts +24 -0
  271. package/dist/dispatch/judge-invocation.d.ts.map +1 -0
  272. package/dist/dispatch/judge-invocation.js +50 -0
  273. package/dist/dispatch/positional-mode.d.ts +18 -0
  274. package/dist/dispatch/positional-mode.d.ts.map +1 -0
  275. package/dist/dispatch/positional-mode.js +18 -0
  276. package/dist/dispatch/read-tracking-map.d.ts +28 -0
  277. package/dist/dispatch/read-tracking-map.d.ts.map +1 -0
  278. package/dist/dispatch/read-tracking-map.js +58 -0
  279. package/dist/dispatch/refuse-sibling-valve-flags.d.ts +30 -0
  280. package/dist/dispatch/refuse-sibling-valve-flags.d.ts.map +1 -0
  281. package/dist/dispatch/refuse-sibling-valve-flags.js +65 -0
  282. package/dist/dispatch/subverb-spec.d.ts +26 -0
  283. package/dist/dispatch/subverb-spec.d.ts.map +1 -0
  284. package/dist/dispatch/subverb-spec.js +1 -0
  285. package/dist/dispatch/undocumented-flag-reads.d.ts +22 -0
  286. package/dist/dispatch/undocumented-flag-reads.d.ts.map +1 -0
  287. package/dist/dispatch/undocumented-flag-reads.js +283 -0
  288. package/dist/dispatch/unknown-flag-refusal-mode.d.ts +13 -0
  289. package/dist/dispatch/unknown-flag-refusal-mode.d.ts.map +1 -0
  290. package/dist/dispatch/unknown-flag-refusal-mode.js +13 -0
  291. package/dist/dispatch/unknown-flag-refusal.d.ts +22 -0
  292. package/dist/dispatch/unknown-flag-refusal.d.ts.map +1 -0
  293. package/dist/dispatch/unknown-flag-refusal.js +57 -0
  294. package/dist/dispatch/unread-flags.d.ts +152 -0
  295. package/dist/dispatch/unread-flags.d.ts.map +1 -0
  296. package/dist/dispatch/unread-flags.js +295 -0
  297. package/dist/dispatch/verdict-valve-flag.d.ts +21 -0
  298. package/dist/dispatch/verdict-valve-flag.d.ts.map +1 -0
  299. package/dist/dispatch/verdict-valve-flag.js +21 -0
  300. package/dist/dispatch/walk-declared-subverbs.d.ts +14 -0
  301. package/dist/dispatch/walk-declared-subverbs.d.ts.map +1 -0
  302. package/dist/dispatch/walk-declared-subverbs.js +28 -0
  303. package/dist/exit-codes.d.ts +41 -3
  304. package/dist/exit-codes.d.ts.map +1 -1
  305. package/dist/exit-codes.js +153 -14
  306. package/dist/finish/run-finish.d.ts +72 -5
  307. package/dist/finish/run-finish.d.ts.map +1 -1
  308. package/dist/finish/run-finish.js +453 -104
  309. package/dist/gates/accepted-empty-note.d.ts +10 -0
  310. package/dist/gates/accepted-empty-note.d.ts.map +1 -0
  311. package/dist/gates/accepted-empty-note.js +11 -0
  312. package/dist/gates/allow-empty.d.ts +25 -0
  313. package/dist/gates/allow-empty.d.ts.map +1 -0
  314. package/dist/gates/allow-empty.js +27 -0
  315. package/dist/gates/asset-doctor-failing-units.d.ts +16 -0
  316. package/dist/gates/asset-doctor-failing-units.d.ts.map +1 -0
  317. package/dist/gates/asset-doctor-failing-units.js +14 -0
  318. package/dist/gates/asset-doctor-failure-line.d.ts +16 -0
  319. package/dist/gates/asset-doctor-failure-line.d.ts.map +1 -0
  320. package/dist/gates/asset-doctor-failure-line.js +28 -0
  321. package/dist/gates/empty-rule-advice-lines.d.ts +15 -0
  322. package/dist/gates/empty-rule-advice-lines.d.ts.map +1 -0
  323. package/dist/gates/empty-rule-advice-lines.js +20 -0
  324. package/dist/gates/gate-envelope.d.ts +85 -8
  325. package/dist/gates/gate-envelope.d.ts.map +1 -1
  326. package/dist/gates/gate-envelope.js +56 -7
  327. package/dist/gates/gate-rule-globs.d.ts +67 -0
  328. package/dist/gates/gate-rule-globs.d.ts.map +1 -0
  329. package/dist/gates/gate-rule-globs.js +139 -0
  330. package/dist/gates/gate-rule-view.d.ts +25 -6
  331. package/dist/gates/gate-rule-view.d.ts.map +1 -1
  332. package/dist/gates/gate-rule-view.js +105 -77
  333. package/dist/gates/i-coverage-dead-glob.d.ts +19 -0
  334. package/dist/gates/i-coverage-dead-glob.d.ts.map +1 -0
  335. package/dist/gates/i-coverage-dead-glob.js +1 -0
  336. package/dist/gates/i-coverage-negation.d.ts +13 -0
  337. package/dist/gates/i-coverage-negation.d.ts.map +1 -0
  338. package/dist/gates/i-coverage-negation.js +1 -0
  339. package/dist/gates/i-registration-query-settle-input.d.ts +18 -0
  340. package/dist/gates/i-registration-query-settle-input.d.ts.map +1 -0
  341. package/dist/gates/i-registration-query-settle-input.js +1 -0
  342. package/dist/gates/i-unit-state-note-row.d.ts +16 -0
  343. package/dist/gates/i-unit-state-note-row.d.ts.map +1 -0
  344. package/dist/gates/i-unit-state-note-row.js +1 -0
  345. package/dist/gates/i-unit-state-notes.d.ts +32 -0
  346. package/dist/gates/i-unit-state-notes.d.ts.map +1 -0
  347. package/dist/gates/i-unit-state-notes.js +1 -0
  348. package/dist/gates/incomplete-registry-inventory.d.ts +19 -0
  349. package/dist/gates/incomplete-registry-inventory.d.ts.map +1 -0
  350. package/dist/gates/incomplete-registry-inventory.js +63 -0
  351. package/dist/gates/measure-registration-roles.d.ts +10 -0
  352. package/dist/gates/measure-registration-roles.d.ts.map +1 -0
  353. package/dist/gates/measure-registration-roles.js +9 -0
  354. package/dist/gates/plane-verdict.d.ts +14 -0
  355. package/dist/gates/plane-verdict.d.ts.map +1 -0
  356. package/dist/gates/plane-verdict.js +22 -0
  357. package/dist/gates/qualify-clean-for-units.d.ts +12 -0
  358. package/dist/gates/qualify-clean-for-units.d.ts.map +1 -0
  359. package/dist/gates/qualify-clean-for-units.js +18 -0
  360. package/dist/gates/registration-graph-verdict.d.ts +34 -0
  361. package/dist/gates/registration-graph-verdict.d.ts.map +1 -0
  362. package/dist/gates/registration-graph-verdict.js +70 -0
  363. package/dist/gates/registration-roles.d.ts +9 -0
  364. package/dist/gates/registration-roles.d.ts.map +1 -0
  365. package/dist/gates/registration-roles.js +1 -0
  366. package/dist/gates/registry-liveness.d.ts +11 -0
  367. package/dist/gates/registry-liveness.d.ts.map +1 -0
  368. package/dist/gates/registry-liveness.js +13 -0
  369. package/dist/gates/rule-coverage.d.ts +225 -4
  370. package/dist/gates/rule-coverage.d.ts.map +1 -1
  371. package/dist/gates/rule-coverage.js +610 -43
  372. package/dist/gates/run-gate-planes.d.ts +58 -0
  373. package/dist/gates/run-gate-planes.d.ts.map +1 -0
  374. package/dist/gates/run-gate-planes.js +403 -0
  375. package/dist/gates/scaffold-selftest.d.ts +56 -0
  376. package/dist/gates/scaffold-selftest.d.ts.map +1 -0
  377. package/dist/gates/scaffold-selftest.js +119 -0
  378. package/dist/gates/seam-rejected-rules.d.ts +28 -0
  379. package/dist/gates/seam-rejected-rules.d.ts.map +1 -0
  380. package/dist/gates/seam-rejected-rules.js +95 -0
  381. package/dist/gates/self-test-check.d.ts +29 -0
  382. package/dist/gates/self-test-check.d.ts.map +1 -0
  383. package/dist/gates/self-test-check.js +1 -0
  384. package/dist/gates/self-test-subject.d.ts +28 -0
  385. package/dist/gates/self-test-subject.d.ts.map +1 -0
  386. package/dist/gates/self-test-subject.js +1 -0
  387. package/dist/gates/self-test.d.ts +20 -0
  388. package/dist/gates/self-test.d.ts.map +1 -0
  389. package/dist/gates/self-test.js +82 -0
  390. package/dist/gates/settle-verdict.d.ts +14 -0
  391. package/dist/gates/settle-verdict.d.ts.map +1 -0
  392. package/dist/gates/settle-verdict.js +13 -0
  393. package/dist/gates/settled-verdict.d.ts +8 -0
  394. package/dist/gates/settled-verdict.d.ts.map +1 -0
  395. package/dist/gates/settled-verdict.js +1 -0
  396. package/dist/gates/template-registry-coverage.d.ts +18 -0
  397. package/dist/gates/template-registry-coverage.d.ts.map +1 -0
  398. package/dist/gates/template-registry-coverage.js +24 -0
  399. package/dist/gates/unit-state-notes.d.ts +40 -0
  400. package/dist/gates/unit-state-notes.d.ts.map +1 -0
  401. package/dist/gates/unit-state-notes.js +71 -0
  402. package/dist/gates/verdict-line.d.ts +18 -0
  403. package/dist/gates/verdict-line.d.ts.map +1 -0
  404. package/dist/gates/verdict-line.js +40 -0
  405. package/dist/graph/graph-reuse-lookup.d.ts +10 -0
  406. package/dist/graph/graph-reuse-lookup.d.ts.map +1 -0
  407. package/dist/graph/graph-reuse-lookup.js +41 -0
  408. package/dist/graph/index-behind-hint.d.ts +11 -0
  409. package/dist/graph/index-behind-hint.d.ts.map +1 -0
  410. package/dist/graph/index-behind-hint.js +27 -0
  411. package/dist/knowledge/knowledge-stale-gate-flags.d.ts +3 -0
  412. package/dist/knowledge/knowledge-stale-gate-flags.d.ts.map +1 -0
  413. package/dist/knowledge/knowledge-stale-gate-flags.js +1 -0
  414. package/dist/knowledge/knowledge-stale-gate-input.d.ts +3 -0
  415. package/dist/knowledge/knowledge-stale-gate-input.d.ts.map +1 -0
  416. package/dist/knowledge/knowledge-stale-gate-input.js +1 -0
  417. package/dist/knowledge/knowledge-stale-gate-result.d.ts +3 -0
  418. package/dist/knowledge/knowledge-stale-gate-result.d.ts.map +1 -0
  419. package/dist/knowledge/knowledge-stale-gate-result.js +1 -0
  420. package/dist/knowledge/knowledge-stale-gate.d.ts +10 -0
  421. package/dist/knowledge/knowledge-stale-gate.d.ts.map +1 -0
  422. package/dist/knowledge/knowledge-stale-gate.js +9 -0
  423. package/dist/main.d.ts.map +1 -1
  424. package/dist/main.js +204 -83
  425. package/dist/output/failure-hints.d.ts.map +1 -1
  426. package/dist/output/failure-hints.js +11 -9
  427. package/dist/output/rejected-entries-note.d.ts +57 -0
  428. package/dist/output/rejected-entries-note.d.ts.map +1 -0
  429. package/dist/output/rejected-entries-note.js +103 -0
  430. package/dist/output/stdout-is-pipe.d.ts +13 -0
  431. package/dist/output/stdout-is-pipe.d.ts.map +1 -0
  432. package/dist/output/stdout-is-pipe.js +21 -0
  433. package/dist/packs/stale-build-warning.d.ts +11 -0
  434. package/dist/packs/stale-build-warning.d.ts.map +1 -0
  435. package/dist/packs/stale-build-warning.js +23 -0
  436. package/dist/quality/run-quality.d.ts +123 -0
  437. package/dist/quality/run-quality.d.ts.map +1 -0
  438. package/dist/quality/run-quality.js +442 -0
  439. package/dist/schemas/json-schemas.d.ts +184 -3
  440. package/dist/schemas/json-schemas.d.ts.map +1 -1
  441. package/dist/schemas/json-schemas.js +113 -2
  442. package/dist/shrk.d.ts +3 -0
  443. package/dist/shrk.d.ts.map +1 -0
  444. package/dist/shrk.js +30 -0
  445. package/dist/surface/audience-applicability.d.ts +26 -0
  446. package/dist/surface/audience-applicability.d.ts.map +1 -0
  447. package/dist/surface/audience-applicability.js +60 -0
  448. package/dist/surface/catalog-command-safety.d.ts +18 -0
  449. package/dist/surface/catalog-command-safety.d.ts.map +1 -0
  450. package/dist/surface/catalog-command-safety.js +62 -0
  451. package/dist/surface/cli-command-resolver.d.ts +21 -0
  452. package/dist/surface/cli-command-resolver.d.ts.map +1 -0
  453. package/dist/surface/cli-command-resolver.js +87 -0
  454. package/dist/surface/command-dispatch-kind.d.ts +20 -0
  455. package/dist/surface/command-dispatch-kind.d.ts.map +1 -0
  456. package/dist/surface/command-dispatch-kind.js +20 -0
  457. package/dist/surface/command-index-entry.d.ts +40 -0
  458. package/dist/surface/command-index-entry.d.ts.map +1 -0
  459. package/dist/surface/command-index-entry.js +1 -0
  460. package/dist/surface/command-index.d.ts +77 -0
  461. package/dist/surface/command-index.d.ts.map +1 -0
  462. package/dist/surface/command-index.js +323 -0
  463. package/dist/surface/i-command-index.d.ts +22 -0
  464. package/dist/surface/i-command-index.d.ts.map +1 -0
  465. package/dist/surface/i-command-index.js +1 -0
  466. package/dist/surface/i-command-string-context.d.ts +20 -0
  467. package/dist/surface/i-command-string-context.d.ts.map +1 -0
  468. package/dist/surface/i-command-string-context.js +1 -0
  469. package/dist/surface/i-surface-denial.d.ts +13 -0
  470. package/dist/surface/i-surface-denial.d.ts.map +1 -0
  471. package/dist/surface/i-surface-denial.js +1 -0
  472. package/dist/surface/load-surface-context.d.ts +17 -1
  473. package/dist/surface/load-surface-context.d.ts.map +1 -1
  474. package/dist/surface/load-surface-context.js +48 -10
  475. package/dist/surface/not-enabled-error.d.ts +38 -4
  476. package/dist/surface/not-enabled-error.d.ts.map +1 -1
  477. package/dist/surface/not-enabled-error.js +96 -6
  478. package/dist/surface/profiles.d.ts +7 -0
  479. package/dist/surface/profiles.d.ts.map +1 -1
  480. package/dist/surface/resolve-command-string.d.ts +10 -0
  481. package/dist/surface/resolve-command-string.d.ts.map +1 -0
  482. package/dist/surface/resolve-command-string.js +572 -0
  483. package/dist/surface/surface-config-writer.d.ts +7 -2
  484. package/dist/surface/surface-config-writer.d.ts.map +1 -1
  485. package/dist/surface/surface-config-writer.js +25 -6
  486. package/dist/surface/surface-layer.d.ts +11 -0
  487. package/dist/surface/surface-layer.d.ts.map +1 -0
  488. package/dist/surface/surface-layer.js +11 -0
  489. package/dist/surface/surface-refusal-reason.d.ts +16 -0
  490. package/dist/surface/surface-refusal-reason.d.ts.map +1 -0
  491. package/dist/surface/surface-refusal-reason.js +16 -0
  492. package/dist/surface/surface-selector.d.ts +26 -0
  493. package/dist/surface/surface-selector.d.ts.map +1 -0
  494. package/dist/surface/surface-selector.js +42 -0
  495. package/dist/surface/surface-summary.d.ts +61 -18
  496. package/dist/surface/surface-summary.d.ts.map +1 -1
  497. package/dist/surface/surface-summary.js +147 -60
  498. package/dist/surface/tier.d.ts +85 -22
  499. package/dist/surface/tier.d.ts.map +1 -1
  500. package/dist/surface/tier.js +132 -30
  501. package/dist/usage/usage-log.d.ts +6 -1
  502. package/dist/usage/usage-log.d.ts.map +1 -1
  503. package/dist/usage/usage-log.js +7 -2
  504. package/dist/validation/run-validation-loop.d.ts +9 -0
  505. package/dist/validation/run-validation-loop.d.ts.map +1 -1
  506. package/dist/validation/run-validation-loop.js +14 -11
  507. package/dist/validation/typecheck-emitted.d.ts +8 -9
  508. package/dist/validation/typecheck-emitted.d.ts.map +1 -1
  509. package/dist/validation/typecheck-emitted.js +17 -97
  510. package/package.json +34 -34
@@ -1,15 +1,52 @@
1
- import { buildImportHygieneReport, filterViolationsToChangedScope, gitShowFile, inspectSharkcraft, resolveChangedFiles, resolveProjectConfig, } from '@shrkcrft/inspector';
2
- import { existsSync } from 'node:fs';
3
- import * as nodePath from 'node:path';
4
- import { buildRegistrationGraph, evaluateBoundaries, loadTsconfigPaths, providedTokensFromEntries, registrationTouchesChanged, registrationUnprovided, runPolicyLint, runWiring, scanImports, } from '@shrkcrft/boundaries';
1
+ import { boundaryRuleSourceFiles, buildImportHygieneReport, describeBoundaryConfiguration, gitShowFile, importHygieneCoverage, importHygieneSubjects, inspectSharkcraft, isProjectConfigAbsent, resolveChangedFiles, resolveProjectConfig, runBoundaryCheck, } from '@shrkcrft/inspector';
2
+ import { coverageShortfall, settleRuleStatus, } from '@shrkcrft/core';
3
+ import { buildRegistrationGraph, measureIdiomRoleCoverage, planeScanExcludeDirs, providedTokensFromEntries, registrationTouchesChanged, registrationUnprovidedVerdict, runPolicyLint, runWiring, unreadDirectoryContains, } from '@shrkcrft/boundaries';
5
4
  import { computeDeletedOrphans } from "../diff/deleted-orphans.js";
5
+ import { deletedOrphanCoverage } from "../diff/deleted-orphan-coverage.js";
6
6
  import { ExitCode } from "../exit-codes.js";
7
+ import { buildGateEnvelope } from "../gates/gate-envelope.js";
8
+ import { seamRejectedRules } from "../gates/seam-rejected-rules.js";
9
+ import { unitStateNotes } from "../gates/unit-state-notes.js";
10
+ import { DEAD_SELECTOR_CAUSES } from '@shrkcrft/core';
7
11
  export const FINISH_SCHEMA = 'sharkcraft.finish/v1';
8
12
  /** Files in the boundary/import/wiring domain — a change outside it evaluates nothing there. */
9
13
  const CODE_FILE = /\.(?:m|c)?[jt]sx?$/;
10
14
  function codeFilesOf(files) {
11
15
  return files.filter((f) => CODE_FILE.test(f));
12
16
  }
17
+ /**
18
+ * A sub-gate's REPORTED status, settled against its coverage — the ONE place
19
+ * finish derives `partial`, through core's one rule (`settleRuleStatus`: a
20
+ * `passed` whose coverage has a shortfall is `partial`). Finish's sub-gates say
21
+ * `pass`, so the status is spoken in the envelope's vocabulary for the call.
22
+ * The fold, the text renderer and --json all read the settled gate; none
23
+ * re-derives it. (The gate envelope re-derives the same word through the same
24
+ * core function when a `partial` gate is handed to it as `passed`.)
25
+ */
26
+ function settleFinishGate(g) {
27
+ const { shortfall: _producerShortfall, ...rest } = g;
28
+ // Every record the sub-gate settled — `coverage`, then `extraCoverage` —
29
+ // so a gap in any of them is `partial` and named (round 13 review).
30
+ const records = [...(g.coverage !== undefined ? [g.coverage] : []), ...(g.extraCoverage ?? [])];
31
+ // Over several records a gap names its subject (the idiom), or two dead
32
+ // roles would read as the same sentence twice.
33
+ const labelled = records.length > 1;
34
+ const gaps = records.flatMap((c) => {
35
+ const s = coverageShortfall(c);
36
+ if (s === undefined)
37
+ return [];
38
+ return [{ record: c, shortfall: labelled && c.subject !== undefined ? `${c.subject}: ${s}` : s }];
39
+ });
40
+ const first = gaps[0];
41
+ if (first === undefined)
42
+ return rest;
43
+ const settled = settleRuleStatus(g.status === 'pass' ? 'passed' : g.status, first.record);
44
+ return {
45
+ ...rest,
46
+ ...(settled === 'partial' ? { status: 'partial' } : {}),
47
+ shortfall: gaps.map((x) => x.shortfall).join('; '),
48
+ };
49
+ }
13
50
  /** The base ref the unprovided gate diffs against to spot removed providers. */
14
51
  function baseRefFor(input) {
15
52
  if (input.mode === 'files')
@@ -43,49 +80,30 @@ export async function runFinishGates(input) {
43
80
  const { cwd } = input;
44
81
  const changed = resolveChangedFiles(input.scope);
45
82
  const changedFiles = changed.files;
46
- // The boundary/import/wiring engines only reason about code files. A change to
47
- // non-code files (docs, JSON, config) evaluates NOTHING in those gates, so they
48
- // must SKIP, not trivially pass — otherwise a markdown-only change paints green
49
- // ("evaluated nothing" must read as not-verified, never a `0`).
50
- const codeChanged = codeFilesOf(changedFiles);
51
83
  const gates = [];
52
- // ── boundaries (changed-only) ────────────────────────────────────────
84
+ // ── boundaries (changed-only, with rule escalation) ──────────────────
53
85
  const inspection = await inspectSharkcraft({ cwd });
54
- const boundaryRules = inspection.boundaryRegistry.list();
55
- if (boundaryRules.length === 0) {
56
- gates.push(skip('boundaries', 'no boundary rules configured'));
57
- }
58
- else if (codeChanged.length === 0) {
59
- gates.push(skip('boundaries', 'no code files in changed scope'));
60
- }
61
- else {
62
- const scan = scanImports({ projectRoot: cwd });
63
- const tsconfigPaths = loadTsconfigPaths(cwd);
64
- const evalResult = evaluateBoundaries(scan, boundaryRules, {
65
- ...(tsconfigPaths.aliases.size > 0 ? { tsconfigPaths } : {}),
66
- });
67
- const filtered = filterViolationsToChangedScope(evalResult.violations, input.scope);
68
- const errors = filtered.includedViolations.filter((v) => v.severity === 'error');
69
- const warnings = filtered.includedViolations.filter((v) => v.severity === 'warning');
70
- gates.push({
71
- name: 'boundaries',
72
- status: errors.length > 0 ? 'fail' : 'pass',
73
- detail: `${errors.length} error(s), ${warnings.length} warning(s) across ${changedFiles.length} changed file(s)`,
74
- errors: errors.length,
75
- warnings: warnings.length,
76
- items: [...errors, ...warnings].map((v) => ({
77
- file: v.file,
78
- line: v.line,
79
- message: `[${v.severity}] ${v.ruleId}: ${v.message}`,
80
- })),
81
- });
82
- }
86
+ gates.push(boundariesGate(inspection, { mode: changed.mode, files: changedFiles }));
87
+ // The boundary/import engines only reason about code files, and a file that
88
+ // DEFINES rules (a boundaryFiles entry, the config) is a rule source, not a
89
+ // subject: a changed rule file escalates its rules above; it is never
90
+ // "scanned" as if it were the change under review. A change to non-code files
91
+ // evaluates NOTHING here, so these gates SKIP rather than trivially pass.
92
+ const ruleSources = new Set(boundaryRuleSourceFiles(inspection));
93
+ const codeChanged = codeFilesOf(changedFiles).filter((f) => !ruleSources.has(f));
83
94
  // ── import hygiene (changed-only) ────────────────────────────────────
95
+ // Hygiene reads existing .ts/.tsx sources only (THE subject list the engine
96
+ // itself scans): a deleted file, a .js edit or a test fixture put nothing in
97
+ // front of it, and a pass over nothing is not a pass.
98
+ const hygieneFiles = importHygieneSubjects(cwd, codeChanged);
84
99
  if (codeChanged.length === 0) {
85
100
  gates.push(skip('imports', 'no code files in changed scope'));
86
101
  }
102
+ else if (hygieneFiles.length === 0) {
103
+ gates.push(skip('imports', `no existing .ts/.tsx source in the changed scope (${codeChanged.length} changed code file(s))`));
104
+ }
87
105
  else {
88
- const report = buildImportHygieneReport(cwd, { files: codeChanged });
106
+ const report = buildImportHygieneReport(cwd, { files: hygieneFiles });
89
107
  const errors = report.counts?.['error'] ?? (report.verdict === 'errors' ? report.findings.length : 0);
90
108
  const warnings = report.counts?.['warning'] ?? (report.verdict === 'warnings' ? report.findings.length : 0);
91
109
  // Only the findings that actually DRIVE the verdict become "failing items".
@@ -97,9 +115,12 @@ export async function runFinishGates(input) {
97
115
  gates.push({
98
116
  name: 'imports',
99
117
  status: report.verdict === 'errors' ? 'fail' : 'pass',
100
- detail: `verdict=${report.verdict} (${driving.length} of ${report.findings.length} finding(s) drive it)`,
118
+ detail: `verdict=${report.verdict} (${driving.length} of ${report.findings.length} finding(s) drive it) across ${hygieneFiles.length} changed source file(s)`,
101
119
  errors,
102
120
  warnings,
121
+ // THE hygiene coverage fold: an unreadable changed source was never
122
+ // checked, so a clean result over it settles this sub-gate `partial` (2).
123
+ coverage: importHygieneCoverage(report, 'changed source files'),
103
124
  items: driving.map((f) => ({
104
125
  file: f.file,
105
126
  line: f.line,
@@ -114,7 +135,9 @@ export async function runFinishGates(input) {
114
135
  // A MALFORMED config in a real sharkcraft project is a fail (the wiring/
115
136
  // policy gates can't be trusted). The mere ABSENCE of a sharkcraft/ folder
116
137
  // is not — those gates simply don't apply, so skip them without failing.
117
- const isSharkcraftProject = existsSync(nodePath.join(cwd, 'sharkcraft'));
138
+ // THE "no config at all" answer (`isProjectConfigAbsent`: the loader found
139
+ // no sharkcraft/ folder) — the quality report's plane row reads it too.
140
+ const isSharkcraftProject = !isProjectConfigAbsent(loaded.error);
118
141
  if (isSharkcraftProject)
119
142
  configError = loaded.error.message;
120
143
  const detail = isSharkcraftProject
@@ -125,7 +148,17 @@ export async function runFinishGates(input) {
125
148
  gates.push(skip('policy', detail));
126
149
  }
127
150
  else {
128
- gates.push(wiringGate(cwd, loaded.value.config.wiringRules ?? [], changedFiles), unprovidedGate(cwd, loaded.value.config.registrationGraph ?? [], changedFiles, baseRefFor(input)), policyGate(cwd, loaded.value.config.policyRules ?? [], changedFiles));
151
+ // Every plane sub-gate walks THE plane scan scope (`planeScanExcludeDirs`),
152
+ // the same tree its verb (`check wiring`, `wiring unprovided`,
153
+ // `policy-lint`) walks for the same rule.
154
+ const excludeDirs = planeScanExcludeDirs(cwd, loaded.value.sharkcraftDir);
155
+ // A pack rule the merge seam rejected never runs, whatever changed: its
156
+ // sub-gate FAILS naming it, as `gates check` fails the same errored row
157
+ // (round 12 review, R12-X1) — never a "Safe to finish" over a rule that
158
+ // was silently dropped.
159
+ const rejected = seamRejectedRules(loaded.value, ['wiring', 'registration', 'policy']);
160
+ const rejectedOn = (plane) => rejected.filter((r) => r.type === plane);
161
+ gates.push(withSeamRejections(wiringGate(cwd, loaded.value.config.wiringRules ?? [], changedFiles, excludeDirs), rejectedOn('wiring')), withSeamRejections(unprovidedGate(cwd, loaded.value.config.registrationGraph ?? [], changedFiles, excludeDirs, baseRefFor(input)), rejectedOn('registration')), withSeamRejections(policyGate(cwd, loaded.value.config.policyRules ?? [], changedFiles, excludeDirs), rejectedOn('policy')));
129
162
  }
130
163
  // ── deleted-orphans (write-safety) ───────────────────────────────────
131
164
  gates.push(await orphansGate(input));
@@ -133,46 +166,106 @@ export async function runFinishGates(input) {
133
166
  gates.push(await archGate(cwd, changedFiles));
134
167
  // ── impact summary (informational, best-effort) ──────────────────────
135
168
  const impact = await impactSummary(cwd, changedFiles);
136
- // ── fold into one honest 0/1/2 verdict ───────────────────────────────
137
- // Only NON-advisory ("deciding") gates decide the verdict. A deciding fail (or
138
- // a config that could not load) is `1`. Otherwise, if NO deciding gate actually
139
- // evaluated anything (all skipped), the run verified nothing → `2` (never a
140
- // green `0`). Only when at least one deciding gate ran over a real scope and
141
- // none failed is it a true `0`.
142
- const deciding = gates.filter((g) => !g.advisory);
169
+ // ── settle each sub-gate's reported status, once ─────────────────────
170
+ // A `pass` whose coverage has a shortfall is `partial` derived HERE, so the
171
+ // text renderer and --json show the same word and the fold below reads it.
172
+ const settledGates = gates.map(settleFinishGate);
173
+ // ── fold into one honest 0/1/2 verdict through the shared envelope ──
174
+ // Only NON-advisory ("deciding") gates decide the verdict, and only the ones
175
+ // that evaluated something become rows: a skipped sub-gate is narrowing (its
176
+ // domain had nothing in the changeset), not a gap. The envelope settles the
177
+ // proposal on every row's coverage and on the run's — so a partial gate is 2,
178
+ // and a run where NO deciding gate evaluated anything (run coverage expected
179
+ // 0) is 2 unless `--allow-empty` accepted it. A deciding fail (or a config
180
+ // that could not load) is 1.
181
+ const deciding = settledGates.filter((g) => !g.advisory);
182
+ const evaluatedDeciding = deciding.filter((g) => g.status !== 'skipped');
143
183
  const failed = deciding.filter((g) => g.status === 'fail');
144
- const anyEvaluated = deciding.some((g) => g.status === 'pass');
145
- const warnings = gates.reduce((n, g) => n + g.warnings, 0);
146
- let verdict;
147
- let exit;
148
- if (failed.length > 0 || configError) {
149
- verdict = 'fail';
150
- exit = ExitCode.Failure;
151
- }
152
- else if (!anyEvaluated) {
153
- verdict = 'not-verified';
154
- exit = ExitCode.NotVerified;
155
- }
156
- else {
157
- verdict = 'pass';
158
- exit = ExitCode.VerifiedPass;
184
+ const rows = evaluatedDeciding.flatMap((g) => [
185
+ {
186
+ id: g.name,
187
+ type: 'finish',
188
+ // A settled `partial` is handed over as `passed` with its coverage: the
189
+ // builder re-derives `partial` through the same core `settleRuleStatus`.
190
+ status: g.status === 'fail' ? 'failed' : 'passed',
191
+ severity: 'error',
192
+ counts: { errors: g.errors, warnings: g.warnings },
193
+ violations: g.items.slice(0, 50).map((i) => ({
194
+ id: i.message,
195
+ ...(i.file ? { file: i.file } : {}),
196
+ ...(i.line !== undefined ? { line: i.line } : {}),
197
+ })),
198
+ coverage: g.coverage ?? { unit: `${g.name} sub-gate runs`, expected: 1, examined: 1 },
199
+ },
200
+ // A sub-gate's further records (round 13 review: the unprovided sub-gate's
201
+ // other idioms' role records and acceptances) — each its own row, so the
202
+ // one settle names every gap and prints every acceptance.
203
+ ...(g.extraCoverage ?? []).map((c, i) => ({
204
+ id: `${g.name}: ${c.subject ?? `record ${i + 2}`}${c.acceptedBy !== undefined ? ' (accepted)' : ''}`,
205
+ type: 'finish',
206
+ status: 'passed',
207
+ severity: 'error',
208
+ counts: {},
209
+ violations: [],
210
+ coverage: c,
211
+ })),
212
+ ]);
213
+ if (configError) {
214
+ rows.push({
215
+ id: 'config',
216
+ type: 'finish',
217
+ status: 'error',
218
+ severity: 'error',
219
+ counts: {},
220
+ violations: [],
221
+ error: `sharkcraft.config.ts did not load: ${configError}`,
222
+ coverage: { unit: 'config files', expected: 1, examined: 0, reason: 'failed to load' },
223
+ });
159
224
  }
160
- const skipped = gates.filter((g) => g.status === 'skipped').map((g) => g.name);
161
- const advisoryWarned = gates.filter((g) => g.advisory && g.warnings > 0).map((g) => g.name);
225
+ const runCoverage = {
226
+ unit: 'deciding sub-gates',
227
+ expected: evaluatedDeciding.length,
228
+ examined: evaluatedDeciding.length,
229
+ ...(evaluatedDeciding.length === 0
230
+ ? {
231
+ reason: changedFiles.length === 0
232
+ ? 'nothing changed'
233
+ : 'no deciding gate had anything in the changed scope to evaluate',
234
+ }
235
+ : {}),
236
+ ...(input.emptyValve ? input.emptyValve(evaluatedDeciding.length) : {}),
237
+ };
238
+ const proposed = failed.length > 0 || configError ? ExitCode.Failure : ExitCode.VerifiedPass;
239
+ const gate = buildGateEnvelope('finish', proposed, rows, runCoverage);
240
+ const exit = gate.exit;
241
+ const verdict = gate.verdict === 'pass' ? 'pass' : gate.verdict === 'fail' ? 'fail' : 'not-verified';
242
+ const anyEvaluated = evaluatedDeciding.length > 0;
243
+ const warnings = settledGates.reduce((n, g) => n + g.warnings, 0);
244
+ const skipped = settledGates.filter((g) => g.status === 'skipped').map((g) => g.name);
245
+ const advisoryWarned = settledGates.filter((g) => g.advisory && g.warnings > 0).map((g) => g.name);
246
+ const gateShortfalls = gate.shortfalls;
162
247
  const summary = verdict === 'fail'
163
248
  ? `Not safe to finish: ${configError ? 'config failed to load; ' : ''}${failed.map((g) => `${g.name} (${g.errors} error(s))`).join(', ') || 'see gates'}.`
164
- : verdict === 'not-verified'
165
- ? `Not verified: no gate evaluated the changed scope${changedFiles.length === 0 ? ' (nothing changed)' : ' (nothing in it is gate-relevant)'} — this is NOT a pass. Re-run over a scope with code changes, or gate explicitly.`
166
- : `Safe to finish: every applicable gate passed${warnings > 0 ? ` (${warnings} non-blocking warning(s)${advisoryWarned.length > 0 ? ` — see ${advisoryWarned.join(', ')}` : ''})` : ''}${skipped.length > 0 ? `; skipped: ${skipped.join(', ')}` : ''}.`;
249
+ : verdict === 'not-verified' && anyEvaluated
250
+ ? `Not verified: ${gateShortfalls.slice(0, 3).join('; ')}${gateShortfalls.length > 3 ? `; (+${gateShortfalls.length - 3} more)` : ''} — this is NOT a pass.`
251
+ : verdict === 'not-verified'
252
+ ? `Not verified: no gate evaluated the changed scope${changedFiles.length === 0 ? ' (nothing changed)' : ' (nothing in it is gate-relevant)'} — this is NOT a pass. Re-run over a scope with code changes, or gate explicitly.`
253
+ : !anyEvaluated
254
+ ? 'Nothing to finish: no deciding gate had anything to evaluate — accepted explicitly.'
255
+ : `Safe to finish: every applicable gate passed${warnings > 0 ? ` (${warnings} non-blocking warning(s)${advisoryWarned.length > 0 ? ` — see ${advisoryWarned.join(', ')}` : ''})` : ''}${skipped.length > 0 ? `; skipped: ${skipped.join(', ')}` : ''}.`;
167
256
  const nextAction = verdict === 'fail'
168
257
  ? 'Fix every failing gate item (each carries file:line), then re-run `shrk finish`.'
169
258
  : verdict === 'not-verified'
170
- ? 'Nothing was verified — do not treat this as done.'
171
- : 'Safe to declare done.';
259
+ ? anyEvaluated
260
+ ? 'Part of the changed scope was not verified (see "Not verified" above) — do not treat this as done.'
261
+ : 'Nothing was verified — do not treat this as done.'
262
+ : anyEvaluated
263
+ ? 'Safe to declare done.'
264
+ : 'Nothing was gated — the empty changeset was accepted with --allow-empty.';
172
265
  return {
173
266
  schema: FINISH_SCHEMA,
174
267
  scope: { mode: input.mode, files: changedFiles, fileCount: changedFiles.length },
175
- gates,
268
+ gates: settledGates,
176
269
  impact,
177
270
  verdict,
178
271
  exit,
@@ -180,6 +273,7 @@ export async function runFinishGates(input) {
180
273
  ...(configError ? { configError } : {}),
181
274
  summary,
182
275
  nextAction,
276
+ gate,
183
277
  };
184
278
  }
185
279
  function skip(name, detail) {
@@ -189,12 +283,199 @@ function skip(name, detail) {
189
283
  function advisorySkip(name, detail) {
190
284
  return { name, status: 'skipped', detail, errors: 0, warnings: 0, items: [], advisory: true };
191
285
  }
192
- function wiringGate(cwd, rules, changedFiles) {
286
+ /**
287
+ * A plane sub-gate with the pack rules the merge seam REJECTED folded in: each
288
+ * is a failing item (it never ran), so the sub-gate fails — skipped or not —
289
+ * exactly as `gates check` fails the same errored rows (round 12 review,
290
+ * R12-X1). Unchanged when there are none.
291
+ */
292
+ function withSeamRejections(gate, rejected) {
293
+ if (rejected.length === 0)
294
+ return gate;
295
+ const note = `${rejected.length} pack rule(s) rejected at the pack-plane merge seam — NOT evaluated`;
296
+ return {
297
+ ...gate,
298
+ status: 'fail',
299
+ detail: gate.status === 'skipped' ? note : `${gate.detail}, ${note}`,
300
+ errors: gate.errors + rejected.length,
301
+ items: [
302
+ ...gate.items,
303
+ ...rejected.map((r) => ({ message: `rejected pack rule ${r.id}: ${r.error ?? 'failed validation'}` })),
304
+ ],
305
+ };
306
+ }
307
+ /**
308
+ * One sub-gate's coverage over the rules its changeset SELECTED, folded from
309
+ * each rule's ENGINE coverage through core's `coverageShortfall` — the rule
310
+ * `check wiring --changed-only` / `policy-lint --changed-only` / `check
311
+ * boundaries --changed-only` settle their exit on — so a rule that passed over
312
+ * part of its scope, or examined nothing at all, is unexamined here exactly as
313
+ * those verbs count it. Returns the record and the ` — NOT VERIFIED: <rule>:
314
+ * <shortfall>` detail suffix. `diff-check` folds its boundaries row with it too.
315
+ */
316
+ export function ruleSetCoverage(unit, rules, reason) {
317
+ const ruleShortfalls = rules.flatMap((r) => {
318
+ const s = coverageShortfall(r.coverage);
319
+ return s === undefined ? [] : [{ id: r.ruleId, shortfall: s }];
320
+ });
321
+ const coverage = {
322
+ unit,
323
+ expected: rules.length,
324
+ examined: rules.length - ruleShortfalls.length,
325
+ ...(ruleShortfalls.length > 0
326
+ ? {
327
+ unexamined: ruleShortfalls.slice(0, 20).map((r) => r.id),
328
+ unexaminedTotal: ruleShortfalls.length,
329
+ reason,
330
+ }
331
+ : {}),
332
+ };
333
+ const first = ruleShortfalls[0];
334
+ const notVerified = first
335
+ ? ` — NOT VERIFIED: ${first.id}: ${first.shortfall}${ruleShortfalls.length > 1 ? ` (+${ruleShortfalls.length - 1} more)` : ''}`
336
+ : '';
337
+ return { coverage, notVerified };
338
+ }
339
+ /** A rule that matched nothing under `failOnEmpty`, as a sub-gate item. */
340
+ function emptyFailureItem(s) {
341
+ return { message: `[${s.severity}] ${s.ruleId}: matched nothing (failOnEmpty) — ${s.reason}` };
342
+ }
343
+ /**
344
+ * The boundaries sub-gate — through THE boundary orchestrator, the call `check
345
+ * boundaries --changed-only`, `diff-check` and the MCP tools make (round 11).
346
+ *
347
+ * It closes the two greens closing#d reproduced:
348
+ * - a rule EDIT (the changeset touches a rule source, the config or
349
+ * tsconfig) escalates those rules to a whole-tree evaluation: the
350
+ * violations the edit created in untouched files are this changeset's,
351
+ * never "legacy";
352
+ * - a change no rule GOVERNS evaluated nothing, so the gate SKIPS — it used to
353
+ * report `pass` from an error count of zero over files no rule looks at.
354
+ * Otherwise it carries the selected rules' coverage, so a rule whose scope glob
355
+ * went dead settles it `partial`.
356
+ */
357
+ function boundariesGate(inspection, changed) {
358
+ const loadIssues = inspection.boundaryLoadIssues ?? [];
359
+ if (inspection.boundaryRegistry.size() === 0 && loadIssues.length === 0) {
360
+ const why = describeBoundaryConfiguration(inspection).diagnostics[0];
361
+ return skip('boundaries', `no boundary rules configured${why ? ` — ${why}` : ''}`);
362
+ }
363
+ if (changed.files.length === 0 && loadIssues.length === 0) {
364
+ return skip('boundaries', 'nothing in the changed scope');
365
+ }
366
+ const r = runBoundaryCheck(inspection, { changed });
367
+ if (r.selectedRuleIds.length === 0 && r.loadIssues.length === 0) {
368
+ return skip('boundaries', `no changed source file is governed by a boundary rule (${changed.files.length} changed file(s))`);
369
+ }
370
+ const errors = r.violations.filter((v) => v.severity === 'error');
371
+ const warnings = r.violations.filter((v) => v.severity === 'warning');
372
+ const failedOnEmpty = r.rules.filter((x) => x.failedOnEmpty === true);
373
+ const escalated = r.changed?.escalatedRuleIds ?? [];
374
+ const governed = r.changed?.governedFiles.length ?? 0;
375
+ const { coverage, notVerified } = ruleSetCoverage('boundary rules', r.rules.map((x) => ({ ruleId: x.ruleId, coverage: x.coverage })), 'passed over part of their scope or checked nothing');
376
+ const detailParts = [
377
+ `${errors.length} error(s), ${warnings.length} warning(s) across ${governed} governed changed file(s)`,
378
+ ];
379
+ if (escalated.length > 0) {
380
+ const why = [...new Set(r.changed?.escalation.reasons.map((x) => x.file) ?? [])].join(', ');
381
+ detailParts.push(`${escalated.length} rule(s) escalated (${why} changed) — evaluated repo-wide`);
382
+ }
383
+ if (r.loadIssues.length > 0)
384
+ detailParts.push(`${r.loadIssues.length} errored rule(s)`);
385
+ if (r.staleExceptions.length > 0)
386
+ detailParts.push(`${r.staleExceptions.length} stale exception(s)`);
387
+ if (failedOnEmpty.length > 0)
388
+ detailParts.push(`${failedOnEmpty.length} matched nothing (failOnEmpty)`);
389
+ // Round 13 (lane B): dead selector units and went-live expectEmpty markers
390
+ // are ADVISORY on finish — named in the detail and listed in `notes`, never
391
+ // items (items are the envelope's violations) and never a failure: finish
392
+ // runs without --fail-on-dead-units. It used to report `pass` in silence
393
+ // while `check boundaries` withheld its ✓ over the same tree.
394
+ // A PACK marker that went live is INFO (round 13 review) — `check
395
+ // boundaries` prints it in its own INFO block; it never counts as advisory.
396
+ const advisory = [
397
+ ...r.deadUnits.map((d) => `[advisory] dead selector unit [${d.unit}] ${d.ruleId}: ${d.selector} — ${d.reason}`),
398
+ ...r.wentLive.map((u) => `${u.packageName !== undefined ? '[info]' : '[advisory]'} [${u.unit}] ${u.ruleId}: ${u.selector} — ${u.reason}`),
399
+ ];
400
+ const localWentLive = r.wentLive.filter((u) => u.packageName === undefined).length;
401
+ const packWentLive = r.wentLive.length - localWentLive;
402
+ // Each rule's expectEmpty acceptance (settle record B) — its own envelope row
403
+ // through `extraCoverage`, so finish's ONE settle prints every acceptance at
404
+ // exit 0 (round 13 review: they were dropped in silence — the sub-gate's
405
+ // single `coverage` is the rule-set fold, which cannot carry them).
406
+ const acceptances = r.rules.flatMap((x) => x.detail.unitAcceptance !== undefined
407
+ ? [{ ...x.detail.unitAcceptance, subject: x.detail.unitAcceptance.subject ?? x.ruleId }]
408
+ : []);
409
+ if (r.deadUnits.length > 0)
410
+ detailParts.push(`${r.deadUnits.length} dead selector unit(s) (advisory)`);
411
+ if (localWentLive > 0)
412
+ detailParts.push(`${localWentLive} expectEmpty marker(s) went live (advisory)`);
413
+ if (packWentLive > 0)
414
+ detailParts.push(`${packWentLive} pack expectEmpty marker(s) went live (INFO)`);
415
+ if (r.intendedEmpty.length > 0)
416
+ detailParts.push(`${r.intendedEmpty.length} expectEmpty unit(s) intended empty`);
417
+ return {
418
+ name: 'boundaries',
419
+ // The orchestrator's proposal is THE boundary verdict: 1 = something failed.
420
+ status: r.proposedExit === ExitCode.Failure ? 'fail' : 'pass',
421
+ detail: `${detailParts.join('; ')}${notVerified}`,
422
+ errors: errors.length + r.loadIssues.length + r.staleExceptions.length + failedOnEmpty.length,
423
+ warnings: warnings.length,
424
+ coverage,
425
+ ...(acceptances.length > 0 ? { extraCoverage: acceptances } : {}),
426
+ ...(advisory.length > 0 ? { notes: advisory } : {}),
427
+ items: [
428
+ ...[...errors, ...warnings].map((v) => ({
429
+ file: v.file,
430
+ line: v.line,
431
+ message: `[${v.severity}] ${v.ruleId}: ${v.message}`,
432
+ })),
433
+ ...r.loadIssues.map((i) => ({
434
+ file: i.file,
435
+ message: `[error] ${i.ruleId ?? i.kind}: ${i.issues.join('; ')} — NOT evaluated`,
436
+ })),
437
+ ...r.staleExceptions.map((s) => ({ file: s.file, message: `[error] ${s.ruleId}: ${s.message}` })),
438
+ ...failedOnEmpty.map((x) => emptyFailureItem({ ruleId: x.ruleId, reason: x.skipReason ?? 'no governed file', severity: x.severity })),
439
+ ],
440
+ };
441
+ }
442
+ /**
443
+ * A plane sub-gate's per-rule expectEmpty acceptances and unit notes (round 13
444
+ * review): the acceptances ride beside the rule-set fold as `extraCoverage`
445
+ * (one envelope row each, as the boundaries sub-gate does), so finish's ONE
446
+ * settle prints every one at exit 0 — `check wiring` / `policy-lint` printed
447
+ * them while finish dropped them in silence. A dead glob of a rule that still
448
+ * matched and a LOCAL marker whose target appeared are ADVISORY notes; a pack
449
+ * marker is `[info]` — worded by THE shared unit-state renderer. Never a
450
+ * failure: finish runs without --fail-on-dead-units.
451
+ */
452
+ function planeUnitFields(rules, emptyIds) {
453
+ const extra = rules.flatMap((r) => r.unitAcceptance !== undefined ? [{ ...r.unitAcceptance, subject: r.unitAcceptance.subject ?? r.ruleId }] : []);
454
+ const n = unitStateNotes(rules.map((r) => ({
455
+ id: r.ruleId,
456
+ ...(r.unitLiveness !== undefined ? { unitLiveness: r.unitLiveness } : {}),
457
+ reportedEmpty: emptyIds.has(r.ruleId),
458
+ })));
459
+ const notes = [
460
+ ...n.deadLines.map((l) => `[advisory] dead selector unit ${l} — ${DEAD_SELECTOR_CAUSES}`),
461
+ ...n.localWentLiveLines.map((l) => `[advisory] ${l}`),
462
+ ...n.packWentLiveLines.map((l) => `[info] ${l}`),
463
+ ];
464
+ return { ...(extra.length > 0 ? { extraCoverage: extra } : {}), ...(notes.length > 0 ? { notes } : {}) };
465
+ }
466
+ function wiringGate(cwd, rules, changedFiles, excludeDirs) {
193
467
  if (rules.length === 0)
194
468
  return skip('wiring', 'no wiring rules configured');
195
- const report = runWiring(cwd, rules, { changedOnly: true, changedFiles });
196
- if (report.evaluated === 0) {
197
- return skip('wiring', `${report.rules.length} rule(s) configured but none matched the changed scope`);
469
+ const report = runWiring(cwd, rules, { changedOnly: true, changedFiles, excludeDirs });
470
+ // Skip ONLY when the changeset selected no rule (no rule's footprint
471
+ // intersects it) deliberate narrowing. A SELECTED rule that examined
472
+ // nothing (its source globs matched no files) is not narrowing: it falls
473
+ // through, carries its coverage, and settles this sub-gate `partial` (2) or
474
+ // `fail` (1, an error-severity failOnEmpty rule) — exactly where `check
475
+ // wiring --changed-only` reads 2 / 1. (Skipping on `evaluated === 0` read
476
+ // "Safe to finish", exit 0, over a rule that had checked nothing.)
477
+ if (report.rules.length === 0) {
478
+ return skip('wiring', `${rules.length} rule(s) configured, none in the changed scope`);
198
479
  }
199
480
  const errors = report.violations.filter((v) => v.severity === 'error');
200
481
  const warnings = report.violations.filter((v) => v.severity === 'warning');
@@ -202,12 +483,23 @@ function wiringGate(cwd, rules, changedFiles) {
202
483
  // rule-level error but NO violation — it must FAIL the gate, never read as a
203
484
  // silent green ("loud, never silent green").
204
485
  const diag = report.diagnostics;
486
+ // A rule that matched nothing under failOnEmpty carries no violation either:
487
+ // it is counted and listed so a failing sub-gate names what failed.
488
+ const emptyFailed = report.skipped.filter((s) => s.failed);
489
+ // Round 11: what this sub-gate examined — every selected rule's engine
490
+ // coverage through core's shortfall rule, so a rule that passed over part of
491
+ // its scope (a subset rule whose declared selector never produced some
492
+ // registered tokens) or checked nothing keeps this sub-gate from counting as
493
+ // a pass (the settle step reports it `partial` and the composite is 2).
494
+ const { coverage, notVerified } = ruleSetCoverage('wiring rules', report.rules, 'passed over part of their scope or checked nothing');
205
495
  return {
496
+ ...planeUnitFields(report.rules, new Set(report.skipped.map((s) => s.ruleId))),
206
497
  name: 'wiring',
207
498
  status: report.verdict === 'errors' || diag.length > 0 ? 'fail' : 'pass',
208
- detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}`,
209
- errors: errors.length + diag.length,
210
- warnings: warnings.length,
499
+ detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}${emptyFailed.length > 0 ? `, ${emptyFailed.length} matched nothing (failOnEmpty)` : ''}${notVerified}`,
500
+ errors: errors.length + diag.length + emptyFailed.filter((s) => s.severity === 'error').length,
501
+ warnings: warnings.length + emptyFailed.filter((s) => s.severity === 'warning').length,
502
+ coverage,
211
503
  items: [
212
504
  ...report.violations.map((v) => ({
213
505
  file: v.file,
@@ -215,25 +507,40 @@ function wiringGate(cwd, rules, changedFiles) {
215
507
  message: `[${v.severity}] ${v.ruleId}: "${v.token}" ${v.direction === 'registered-missing' ? 'registered but not declared' : 'declared but not registered'}`,
216
508
  })),
217
509
  ...diag.map((d) => ({ message: `misconfigured rule: ${d}` })),
510
+ ...emptyFailed.map(emptyFailureItem),
218
511
  ],
219
512
  };
220
513
  }
221
- function policyGate(cwd, rules, changedFiles) {
514
+ function policyGate(cwd, rules, changedFiles, excludeDirs) {
222
515
  if (rules.length === 0)
223
516
  return skip('policy', 'no policy rules configured');
224
- const report = runPolicyLint(cwd, rules, { changedOnly: true, changedFiles });
225
- if (report.evaluated === 0) {
226
- return skip('policy', `${report.rules.length} rule(s) configured but none matched the changed scope`);
517
+ // The SharkCraft asset dir holds the rule definitions themselves (which can
518
+ // self-match): pruned through THE plane scan scope, exactly as `policy-lint`.
519
+ const report = runPolicyLint(cwd, rules, { changedOnly: true, changedFiles, excludeDirs });
520
+ // Same shape as the wiring sub-gate: skip ONLY when no rule is in the
521
+ // changed scope. The engine already narrows a rule out when the change put
522
+ // no content in front of it (only deleted files, or files with nothing on its
523
+ // surface), so a rule still SELECTED here that scanned nothing left real
524
+ // scope unexamined: it settles this sub-gate `partial` (2), or `fail` (1)
525
+ // under failOnEmpty — exactly where `policy-lint --changed-only` reads 2 / 1.
526
+ if (report.rules.length === 0) {
527
+ return skip('policy', `${rules.length} rule(s) configured, none with content in the changed scope`);
227
528
  }
228
529
  const errors = report.findings.filter((f) => f.severity === 'error');
229
530
  const warnings = report.findings.filter((f) => f.severity === 'warning');
230
531
  const diag = report.diagnostics;
532
+ // `policy-lint` fails on ANY failOnEmpty skip (its proposed exit is 1), so
533
+ // this sub-gate fails on one too, and counts it as an error.
534
+ const emptyFailed = report.skipped.filter((s) => s.failed);
535
+ const { coverage, notVerified } = ruleSetCoverage('policy rules', report.rules, 'examined only part of their scope, scanned nothing, or could not run');
231
536
  return {
537
+ ...planeUnitFields(report.rules, new Set(report.skipped.map((s) => s.ruleId))),
232
538
  name: 'policy',
233
- status: report.verdict === 'errors' || diag.length > 0 ? 'fail' : 'pass',
234
- detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}`,
235
- errors: errors.length + diag.length,
539
+ status: report.verdict === 'errors' || diag.length > 0 || emptyFailed.length > 0 ? 'fail' : 'pass',
540
+ detail: `${report.evaluated}/${report.rules.length} rule(s) evaluated — ${errors.length} error(s), ${warnings.length} warning(s)${diag.length > 0 ? `, ${diag.length} misconfigured` : ''}${emptyFailed.length > 0 ? `, ${emptyFailed.length} matched nothing (failOnEmpty)` : ''}${notVerified}`,
541
+ errors: errors.length + diag.length + emptyFailed.length,
236
542
  warnings: warnings.length,
543
+ coverage,
237
544
  items: [
238
545
  ...report.findings.map((f) => ({
239
546
  file: f.file,
@@ -241,6 +548,7 @@ function policyGate(cwd, rules, changedFiles) {
241
548
  message: `[${f.severity}] ${f.ruleId}: ${f.message ?? ''}`.trim(),
242
549
  })),
243
550
  ...diag.map((d) => ({ message: `misconfigured rule: ${d}` })),
551
+ ...emptyFailed.map(emptyFailureItem),
244
552
  ],
245
553
  };
246
554
  }
@@ -252,10 +560,10 @@ function policyGate(cwd, rules, changedFiles) {
252
560
  * fails) when no idioms are configured or the change touched no wiring — so a
253
561
  * repo that never modeled its DI is not penalized.
254
562
  */
255
- function unprovidedGate(cwd, idioms, changedFiles, baseRef) {
563
+ function unprovidedGate(cwd, idioms, changedFiles, excludeDirs, baseRef) {
256
564
  if (idioms.length === 0)
257
565
  return skip('unprovided', 'no registration idioms configured');
258
- const graph = buildRegistrationGraph(cwd, idioms);
566
+ const graph = buildRegistrationGraph(cwd, idioms, { excludeDirs });
259
567
  const diag = graph.diagnostics;
260
568
  // Two ways THIS change can leave a token unprovided:
261
569
  // (1) a declared/injected site added in a changed file with no provider —
@@ -263,32 +571,65 @@ function unprovidedGate(cwd, idioms, changedFiles, baseRef) {
263
571
  // (2) the last PROVIDER removed from a changed file — which leaves NO site in
264
572
  // the changed file, so (1) structurally can't see it. Recover it by
265
573
  // diffing the base content of the changed files (providerRegressions).
266
- const scoped = registrationUnprovided(graph, changedFiles);
267
574
  const regressions = baseRef ? providerRegressions(cwd, idioms, graph, changedFiles, baseRef) : [];
268
- const byToken = new Map();
269
- for (const u of [...scoped, ...regressions])
270
- if (!byToken.has(u.token))
271
- byToken.set(u.token, u);
272
- const unprovided = [...byToken.values()].sort((a, b) => a.token.localeCompare(b.token));
575
+ // Settled against what the graph READ, through the one boundaries helper
576
+ // `wiring unprovided` and MCP `get_wiring_graph` use. A file an idiom
577
+ // matched that the reader could not read (over the read cap) holds sites the
578
+ // graph never saw: a token whose provider could sit there is `unproven` —
579
+ // not a failure, and never a pass (its coverage names the token and file).
580
+ // Round 13 (P4): and against what every idiom's ROLES examined, from THE
581
+ // role authority `gates check` reads — a declared role that matched no file
582
+ // printed `✓ unprovided pass` here while `gates check` said NOT VERIFIED.
583
+ const verdict = registrationUnprovidedVerdict(graph, idioms, measureIdiomRoleCoverage(cwd, idioms, excludeDirs), changedFiles, regressions);
584
+ const unprovided = verdict.findings;
585
+ const candidates = unprovided.length + verdict.unproven.length;
586
+ // A CHANGED unread file is scope this change put in front of the gate, so it
587
+ // keeps the gate from skipping — so does a changed file beneath a directory
588
+ // the reader could not list.
589
+ const unreadChanged = (graph.readScope?.unread ?? []).filter((u) => changedFiles.includes(u.path) || changedFiles.some((f) => unreadDirectoryContains(u, f)));
273
590
  // Skip (evaluated nothing) only when the change touched no registration site
274
591
  // AND removed no provider AND has no misconfigured idiom — never a silent pass.
275
- if (!registrationTouchesChanged(graph, changedFiles) && unprovided.length === 0 && diag.length === 0) {
592
+ if (!registrationTouchesChanged(graph, changedFiles) &&
593
+ candidates === 0 &&
594
+ diag.length === 0 &&
595
+ unreadChanged.length === 0) {
276
596
  return skip('unprovided', 'no registration sites in the changed scope');
277
597
  }
598
+ // The most specific record: the demoted tokens (their reason names the
599
+ // unread file), else the first record with a gap — the unread idiom files,
600
+ // or an idiom role that examined nothing (round 13, P4) — else an
601
+ // expectEmpty acceptance (printed at 0), else the first. A full record
602
+ // picked over a gap would settle `pass` over a dead role.
603
+ const coverage = verdict.coverage.find((c) => c.subject === 'unprovided') ??
604
+ verdict.coverage.find((c) => coverageShortfall(c) !== undefined) ??
605
+ verdict.coverage.find((c) => c.acceptedBy !== undefined) ??
606
+ verdict.coverage[0];
607
+ // Every OTHER record rides beside it (round 13 review) — one per idiom: a
608
+ // second idiom's acceptance or dead role is never dropped.
609
+ const extraCoverage = verdict.coverage.filter((c) => c !== coverage);
610
+ const siteOf = (u) => {
611
+ const site = u.declared[0] ?? u.consumed[0];
612
+ return site ? { file: site.file, line: site.line } : {};
613
+ };
278
614
  return {
279
615
  name: 'unprovided',
280
616
  status: unprovided.length > 0 || diag.length > 0 ? 'fail' : 'pass',
281
- detail: `${unprovided.length} unprovided token(s) attributable to the change${diag.length > 0 ? `, ${diag.length} misconfigured idiom(s)` : ''}`,
617
+ detail: `${unprovided.length} unprovided token(s) attributable to the change` +
618
+ `${verdict.unproven.length > 0 ? `, ${verdict.unproven.length} not verified (a provider may sit in an unread file)` : ''}` +
619
+ `${diag.length > 0 ? `, ${diag.length} misconfigured idiom(s)` : ''}`,
282
620
  errors: unprovided.length + diag.length,
283
621
  warnings: 0,
622
+ ...(coverage ? { coverage } : {}),
623
+ ...(extraCoverage.length > 0 ? { extraCoverage } : {}),
284
624
  items: [
285
- ...unprovided.map((u) => {
286
- const site = u.declared[0] ?? u.consumed[0];
287
- return {
288
- ...(site ? { file: site.file, line: site.line } : {}),
289
- message: `"${u.token}" declared/injected but never provided (silent at runtime)`,
290
- };
291
- }),
625
+ ...unprovided.map((u) => ({
626
+ ...siteOf(u),
627
+ message: `"${u.token}" declared/injected but never provided (silent at runtime)`,
628
+ })),
629
+ ...verdict.unproven.map((u) => ({
630
+ ...siteOf(u),
631
+ message: `"${u.token}" has no provider among the files read — NOT VERIFIED (a provider may sit in an unread file)`,
632
+ })),
292
633
  ...diag.map((d) => ({ message: `misconfigured idiom: ${d}` })),
293
634
  ],
294
635
  };
@@ -376,6 +717,13 @@ async function orphansGate(input) {
376
717
  }
377
718
  if (scan.deleted.length === 0)
378
719
  return skip('orphans', `nothing deleted (vs ${scan.ref})`);
720
+ // What the orphan query examined — the ONE authority `check orphans` settles
721
+ // on: deleted files the index knows, over an index current for every
722
+ // surviving importer. A delete of nothing the graph indexes (a README, a
723
+ // `dist/*.js`) evaluates nothing here, so the sub-gate skips.
724
+ const coverage = deletedOrphanCoverage(scan);
725
+ if (coverage.expected === 0)
726
+ return skip('orphans', coverage.reason ?? 'no deleted code files');
379
727
  const orphans = scan.report?.orphans ?? [];
380
728
  return {
381
729
  name: 'orphans',
@@ -383,6 +731,7 @@ async function orphansGate(input) {
383
731
  detail: `${scan.deleted.length} deleted file(s) (vs ${scan.ref}) — ${orphans.length} surviving importer(s)`,
384
732
  errors: orphans.length,
385
733
  warnings: 0,
734
+ coverage,
386
735
  items: orphans.map((o) => ({
387
736
  file: o.path ?? o.id,
388
737
  ...(typeof o.line === 'number' ? { line: o.line } : {}),