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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/baseline/compute-baseline.d.ts +8 -0
  2. package/dist/baseline/compute-baseline.d.ts.map +1 -1
  3. package/dist/baseline/compute-baseline.js +8 -7
  4. package/dist/baseline/diff-baseline.d.ts +28 -0
  5. package/dist/baseline/diff-baseline.d.ts.map +1 -1
  6. package/dist/baseline/diff-baseline.js +29 -0
  7. package/dist/evaluate/boundary-unit-finding.d.ts +10 -0
  8. package/dist/evaluate/boundary-unit-finding.d.ts.map +1 -0
  9. package/dist/evaluate/boundary-unit-finding.js +21 -0
  10. package/dist/evaluate/boundary-unit-kind.d.ts +7 -0
  11. package/dist/evaluate/boundary-unit-kind.d.ts.map +1 -0
  12. package/dist/evaluate/boundary-unit-kind.js +14 -0
  13. package/dist/evaluate/evaluate-boundaries.d.ts +230 -3
  14. package/dist/evaluate/evaluate-boundaries.d.ts.map +1 -1
  15. package/dist/evaluate/evaluate-boundaries.js +481 -53
  16. package/dist/evaluate/i-boundary-rule-settle-input.d.ts +27 -0
  17. package/dist/evaluate/i-boundary-rule-settle-input.d.ts.map +1 -0
  18. package/dist/evaluate/i-boundary-rule-settle-input.js +1 -0
  19. package/dist/evaluate/i-boundary-rule-settlement.d.ts +23 -0
  20. package/dist/evaluate/i-boundary-rule-settlement.d.ts.map +1 -0
  21. package/dist/evaluate/i-boundary-rule-settlement.js +1 -0
  22. package/dist/evaluate/i-boundary-unit-finding.d.ts +22 -0
  23. package/dist/evaluate/i-boundary-unit-finding.d.ts.map +1 -0
  24. package/dist/evaluate/i-boundary-unit-finding.js +1 -0
  25. package/dist/evaluate/settle-boundary-rule.d.ts +23 -0
  26. package/dist/evaluate/settle-boundary-rule.d.ts.map +1 -0
  27. package/dist/evaluate/settle-boundary-rule.js +73 -0
  28. package/dist/evaluate/with-boundary-rule-settlement.d.ts +11 -0
  29. package/dist/evaluate/with-boundary-rule-settlement.d.ts.map +1 -0
  30. package/dist/evaluate/with-boundary-rule-settlement.js +44 -0
  31. package/dist/extract/code-zones.d.ts +95 -3
  32. package/dist/extract/code-zones.d.ts.map +1 -1
  33. package/dist/extract/code-zones.js +313 -4
  34. package/dist/extract/extract-tokens.d.ts +30 -0
  35. package/dist/extract/extract-tokens.d.ts.map +1 -1
  36. package/dist/extract/extract-tokens.js +233 -27
  37. package/dist/extract/i-labeled-source.d.ts +12 -0
  38. package/dist/extract/i-labeled-source.d.ts.map +1 -0
  39. package/dist/extract/i-labeled-source.js +1 -0
  40. package/dist/extract/import-edges.d.ts +1 -0
  41. package/dist/extract/import-edges.d.ts.map +1 -1
  42. package/dist/extract/import-edges.js +20 -7
  43. package/dist/extract/inspect-source.d.ts +32 -3
  44. package/dist/extract/inspect-source.d.ts.map +1 -1
  45. package/dist/extract/inspect-source.js +50 -7
  46. package/dist/extract/parse-imports.d.ts +34 -12
  47. package/dist/extract/parse-imports.d.ts.map +1 -1
  48. package/dist/extract/parse-imports.js +119 -20
  49. package/dist/extract/scan-literals.d.ts +12 -1
  50. package/dist/extract/scan-literals.d.ts.map +1 -1
  51. package/dist/extract/scan-literals.js +15 -1
  52. package/dist/extract/source-liveness-request.d.ts +24 -0
  53. package/dist/extract/source-liveness-request.d.ts.map +1 -0
  54. package/dist/extract/source-liveness-request.js +45 -0
  55. package/dist/generated/check-provenance.d.ts.map +1 -1
  56. package/dist/generated/check-provenance.js +3 -1
  57. package/dist/generated/read-regen-tree.d.ts +16 -0
  58. package/dist/generated/read-regen-tree.d.ts.map +1 -0
  59. package/dist/generated/read-regen-tree.js +61 -0
  60. package/dist/generated/scan-generated.d.ts +8 -0
  61. package/dist/generated/scan-generated.d.ts.map +1 -1
  62. package/dist/generated/scan-generated.js +25 -10
  63. package/dist/index.d.ts +48 -0
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +51 -0
  66. package/dist/model/boundary-intended-empty-line.d.ts +10 -0
  67. package/dist/model/boundary-intended-empty-line.d.ts.map +1 -0
  68. package/dist/model/boundary-intended-empty-line.js +11 -0
  69. package/dist/model/boundary-markable-list.d.ts +15 -0
  70. package/dist/model/boundary-markable-list.d.ts.map +1 -0
  71. package/dist/model/boundary-markable-list.js +15 -0
  72. package/dist/model/boundary-rule-input-keys.d.ts +11 -0
  73. package/dist/model/boundary-rule-input-keys.d.ts.map +1 -0
  74. package/dist/model/boundary-rule-input-keys.js +29 -0
  75. package/dist/model/boundary-rule-input.d.ts +23 -0
  76. package/dist/model/boundary-rule-input.d.ts.map +1 -0
  77. package/dist/model/boundary-rule-input.js +1 -0
  78. package/dist/model/boundary-rule-key-problems.d.ts +11 -0
  79. package/dist/model/boundary-rule-key-problems.d.ts.map +1 -0
  80. package/dist/model/boundary-rule-key-problems.js +36 -0
  81. package/dist/model/boundary-rule-marker-problems.d.ts +26 -0
  82. package/dist/model/boundary-rule-marker-problems.d.ts.map +1 -0
  83. package/dist/model/boundary-rule-marker-problems.js +94 -0
  84. package/dist/model/boundary-rule-scope.d.ts +88 -0
  85. package/dist/model/boundary-rule-scope.d.ts.map +1 -0
  86. package/dist/model/boundary-rule-scope.js +175 -0
  87. package/dist/model/boundary-rule.d.ts +120 -5
  88. package/dist/model/boundary-rule.d.ts.map +1 -1
  89. package/dist/model/boundary-rule.js +148 -1
  90. package/dist/model/boundary-unit-problem-issue.d.ts +9 -0
  91. package/dist/model/boundary-unit-problem-issue.d.ts.map +1 -0
  92. package/dist/model/boundary-unit-problem-issue.js +16 -0
  93. package/dist/model/normalize-boundary-rule.d.ts +21 -0
  94. package/dist/model/normalize-boundary-rule.d.ts.map +1 -0
  95. package/dist/model/normalize-boundary-rule.js +62 -0
  96. package/dist/policy/evaluate-policy.d.ts +80 -3
  97. package/dist/policy/evaluate-policy.d.ts.map +1 -1
  98. package/dist/policy/evaluate-policy.js +152 -19
  99. package/dist/policy/i-policy-rule-liveness.d.ts +15 -0
  100. package/dist/policy/i-policy-rule-liveness.d.ts.map +1 -0
  101. package/dist/policy/i-policy-rule-liveness.js +1 -0
  102. package/dist/policy/run-policy.d.ts +1 -1
  103. package/dist/policy/run-policy.d.ts.map +1 -1
  104. package/dist/policy/run-policy.js +171 -21
  105. package/dist/registry/load-boundary-rules.d.ts +29 -1
  106. package/dist/registry/load-boundary-rules.d.ts.map +1 -1
  107. package/dist/registry/load-boundary-rules.js +38 -8
  108. package/dist/scan/glob.d.ts +90 -0
  109. package/dist/scan/glob.d.ts.map +1 -1
  110. package/dist/scan/glob.js +205 -0
  111. package/dist/scan/i-glob-unit-measure.d.ts +22 -0
  112. package/dist/scan/i-glob-unit-measure.d.ts.map +1 -0
  113. package/dist/scan/i-glob-unit-measure.js +1 -0
  114. package/dist/scan/import-pattern.d.ts +109 -0
  115. package/dist/scan/import-pattern.d.ts.map +1 -0
  116. package/dist/scan/import-pattern.js +191 -0
  117. package/dist/scan/node-builtin-package-names.d.ts +13 -0
  118. package/dist/scan/node-builtin-package-names.d.ts.map +1 -0
  119. package/dist/scan/node-builtin-package-names.js +25 -0
  120. package/dist/scan/scan-imports.d.ts +36 -3
  121. package/dist/scan/scan-imports.d.ts.map +1 -1
  122. package/dist/scan/scan-imports.js +114 -45
  123. package/dist/util/blank-run-hazard-finding.d.ts +34 -0
  124. package/dist/util/blank-run-hazard-finding.d.ts.map +1 -0
  125. package/dist/util/blank-run-hazard-finding.js +1 -0
  126. package/dist/util/blank-run-hazard.d.ts +29 -0
  127. package/dist/util/blank-run-hazard.d.ts.map +1 -0
  128. package/dist/util/blank-run-hazard.js +450 -0
  129. package/dist/util/dead-glob-units.d.ts +46 -0
  130. package/dist/util/dead-glob-units.d.ts.map +1 -0
  131. package/dist/util/dead-glob-units.js +116 -0
  132. package/dist/util/glob-list-liveness-input.d.ts +31 -0
  133. package/dist/util/glob-list-liveness-input.d.ts.map +1 -0
  134. package/dist/util/glob-list-liveness-input.js +65 -0
  135. package/dist/util/i-dead-glob-unit.d.ts +24 -0
  136. package/dist/util/i-dead-glob-unit.d.ts.map +1 -0
  137. package/dist/util/i-dead-glob-unit.js +1 -0
  138. package/dist/util/i-glob-list-units.d.ts +25 -0
  139. package/dist/util/i-glob-list-units.d.ts.map +1 -0
  140. package/dist/util/i-glob-list-units.js +1 -0
  141. package/dist/util/i-glob-liveness-list.d.ts +23 -0
  142. package/dist/util/i-glob-liveness-list.d.ts.map +1 -0
  143. package/dist/util/i-glob-liveness-list.js +1 -0
  144. package/dist/util/i-glob-liveness-request.d.ts +11 -0
  145. package/dist/util/i-glob-liveness-request.d.ts.map +1 -0
  146. package/dist/util/i-glob-liveness-request.js +1 -0
  147. package/dist/util/i-glob-negation.d.ts +13 -0
  148. package/dist/util/i-glob-negation.d.ts.map +1 -0
  149. package/dist/util/i-glob-negation.js +1 -0
  150. package/dist/util/matched-files.d.ts +22 -0
  151. package/dist/util/matched-files.d.ts.map +1 -0
  152. package/dist/util/matched-files.js +1 -0
  153. package/dist/util/negation-cause.d.ts +15 -0
  154. package/dist/util/negation-cause.d.ts.map +1 -0
  155. package/dist/util/negation-cause.js +21 -0
  156. package/dist/util/plane-scan-exclude-dirs.d.ts +17 -0
  157. package/dist/util/plane-scan-exclude-dirs.d.ts.map +1 -0
  158. package/dist/util/plane-scan-exclude-dirs.js +22 -0
  159. package/dist/util/read-glob-list-liveness.d.ts +20 -0
  160. package/dist/util/read-glob-list-liveness.d.ts.map +1 -0
  161. package/dist/util/read-glob-list-liveness.js +20 -0
  162. package/dist/util/read-scope-coverage.d.ts +90 -0
  163. package/dist/util/read-scope-coverage.d.ts.map +1 -0
  164. package/dist/util/read-scope-coverage.js +173 -0
  165. package/dist/util/read-scope.d.ts +14 -0
  166. package/dist/util/read-scope.d.ts.map +1 -0
  167. package/dist/util/read-scope.js +1 -0
  168. package/dist/util/read-selected-files.d.ts +16 -0
  169. package/dist/util/read-selected-files.d.ts.map +1 -0
  170. package/dist/util/read-selected-files.js +25 -0
  171. package/dist/util/settle-glob-lists.d.ts +12 -0
  172. package/dist/util/settle-glob-lists.d.ts.map +1 -0
  173. package/dist/util/settle-glob-lists.js +13 -0
  174. package/dist/util/unread-file-reason.d.ts +26 -0
  175. package/dist/util/unread-file-reason.d.ts.map +1 -0
  176. package/dist/util/unread-file-reason.js +26 -0
  177. package/dist/util/unread-file.d.ts +10 -0
  178. package/dist/util/unread-file.d.ts.map +1 -0
  179. package/dist/util/unread-file.js +1 -0
  180. package/dist/util/walk-files.d.ts +74 -10
  181. package/dist/util/walk-files.d.ts.map +1 -1
  182. package/dist/util/walk-files.js +137 -31
  183. package/dist/wiring/evaluate-wiring.d.ts +79 -5
  184. package/dist/wiring/evaluate-wiring.d.ts.map +1 -1
  185. package/dist/wiring/evaluate-wiring.js +267 -12
  186. package/dist/wiring/explain-wiring.d.ts +49 -5
  187. package/dist/wiring/explain-wiring.d.ts.map +1 -1
  188. package/dist/wiring/explain-wiring.js +118 -10
  189. package/dist/wiring/i-idiom-role-coverage.d.ts +24 -0
  190. package/dist/wiring/i-idiom-role-coverage.d.ts.map +1 -0
  191. package/dist/wiring/i-idiom-role-coverage.js +1 -0
  192. package/dist/wiring/i-registration-query-verdict.d.ts +38 -0
  193. package/dist/wiring/i-registration-query-verdict.d.ts.map +1 -0
  194. package/dist/wiring/i-registration-query-verdict.js +1 -0
  195. package/dist/wiring/i-registration-roles.d.ts +71 -0
  196. package/dist/wiring/i-registration-roles.d.ts.map +1 -0
  197. package/dist/wiring/i-registration-roles.js +1 -0
  198. package/dist/wiring/measure-idiom-role-coverage.d.ts +12 -0
  199. package/dist/wiring/measure-idiom-role-coverage.d.ts.map +1 -0
  200. package/dist/wiring/measure-idiom-role-coverage.js +20 -0
  201. package/dist/wiring/measure-registration-roles.d.ts +25 -0
  202. package/dist/wiring/measure-registration-roles.d.ts.map +1 -0
  203. package/dist/wiring/measure-registration-roles.js +124 -0
  204. package/dist/wiring/plan-wiring-fix.js +2 -2
  205. package/dist/wiring/registration-graph.d.ts +12 -0
  206. package/dist/wiring/registration-graph.d.ts.map +1 -1
  207. package/dist/wiring/registration-graph.js +16 -5
  208. package/dist/wiring/registration-query-verdict.d.ts +23 -0
  209. package/dist/wiring/registration-query-verdict.d.ts.map +1 -0
  210. package/dist/wiring/registration-query-verdict.js +118 -0
  211. package/dist/wiring/registry-query.d.ts +8 -0
  212. package/dist/wiring/registry-query.d.ts.map +1 -1
  213. package/dist/wiring/registry-query.js +12 -5
  214. package/dist/wiring/scan-wiring-files.d.ts +4 -2
  215. package/dist/wiring/scan-wiring-files.d.ts.map +1 -1
  216. package/dist/wiring/scan-wiring-files.js +52 -15
  217. package/dist/wiring/sink-imports.d.ts +11 -0
  218. package/dist/wiring/sink-imports.d.ts.map +1 -1
  219. package/dist/wiring/sink-imports.js +11 -2
  220. package/dist/wiring/trace-literal.d.ts +6 -0
  221. package/dist/wiring/trace-literal.d.ts.map +1 -1
  222. package/dist/wiring/trace-literal.js +5 -2
  223. package/dist/wiring/wiring-labeled-sources.d.ts +12 -0
  224. package/dist/wiring/wiring-labeled-sources.d.ts.map +1 -0
  225. package/dist/wiring/wiring-labeled-sources.js +20 -0
  226. package/package.json +2 -2
@@ -2,8 +2,9 @@ import { createHash } from 'node:crypto';
2
2
  import { statSync } from 'node:fs';
3
3
  import * as nodePath from 'node:path';
4
4
  import { resolveSourceGlobs } from '@shrkcrft/core';
5
- import { matchesAny } from "../scan/glob.js";
5
+ import { globListSelects } from "../scan/glob.js";
6
6
  import { readMatchingFiles, walkMatching } from "../util/walk-files.js";
7
+ import { readScopeOfLists } from "../util/read-scope-coverage.js";
7
8
  import { collectSourceSites } from "./evaluate-wiring.js";
8
9
  export const REGISTRATION_GRAPH_SCHEMA = 'sharkcraft.registration-graph/v1';
9
10
  function sortSites(sites) {
@@ -28,12 +29,14 @@ function registrationGlobs(idioms) {
28
29
  * diagnostic.
29
30
  */
30
31
  export function buildRegistrationGraph(projectRoot, idioms, options = {}) {
31
- const cache = readMatchingFiles(projectRoot, registrationGlobs(idioms), new Set(options.excludeDirs ?? []));
32
- const entries = [...cache.entries()].map(([path, content]) => ({
32
+ const matched = readMatchingFiles(projectRoot, registrationGlobs(idioms), new Set(options.excludeDirs ?? []));
33
+ const entries = [...matched.files.entries()].map(([path, content]) => ({
33
34
  path,
34
35
  content,
35
36
  }));
36
- const filesFor = (source) => entries.filter((f) => matchesAny(f.path, resolveSourceGlobs(source)));
37
+ // The walk is the POSITIVE union of every role of every idiom; each role
38
+ // selects its own files, so one role's `!x` never hides another role's file.
39
+ const filesFor = (source) => entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(source)));
37
40
  const declared = new Map();
38
41
  const provided = new Map();
39
42
  const consumed = new Map();
@@ -60,11 +63,15 @@ export function buildRegistrationGraph(projectRoot, idioms, options = {}) {
60
63
  provided: sortSites(provided.get(token) ?? []),
61
64
  consumed: sortSites(consumed.get(token) ?? []),
62
65
  }));
66
+ // Read scope PER ROLE LIST (one file counted once): an unread file every
67
+ // role that could reach it EXCLUDES is out of scope, not a gap.
68
+ const scope = readScopeOfLists(matched, idioms.flatMap((i) => [i.declared, i.provided, i.consumed].map((s) => resolveSourceGlobs(s))));
63
69
  return {
64
70
  schema: REGISTRATION_GRAPH_SCHEMA,
65
71
  idioms: idioms.map((i) => i.name),
66
72
  tokens,
67
73
  diagnostics,
74
+ ...(scope.unread.length > 0 ? { readScope: scope } : {}),
68
75
  };
69
76
  }
70
77
  /**
@@ -75,6 +82,10 @@ export function buildRegistrationGraph(projectRoot, idioms, options = {}) {
75
82
  * persisted registration-graph cache — a source edit shifts the signature even
76
83
  * when no reindex has run, so a stale wiring verdict is impossible. Walk-and-stat
77
84
  * only (no file reads), so it stays far cheaper than a full build. Never throws.
85
+ *
86
+ * It signs the POSITIVE union walk, not each role's selected set: a file a
87
+ * role's `!` excludes still shifts the signature. That over-inclusion can only
88
+ * cause a spurious cache miss — the safe direction — never a stale verdict.
78
89
  */
79
90
  export function registrationGraphSignature(projectRoot, idioms, options = {}) {
80
91
  const files = walkMatching(projectRoot, registrationGlobs(idioms), new Set(options.excludeDirs ?? [])).sort();
@@ -101,7 +112,7 @@ export function registrationGraphSignature(projectRoot, idioms, options = {}) {
101
112
  export function providedTokensFromEntries(idioms, entries) {
102
113
  const tokens = new Set();
103
114
  for (const idiom of idioms) {
104
- const files = entries.filter((f) => matchesAny(f.path, resolveSourceGlobs(idiom.provided)));
115
+ const files = entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(idiom.provided)));
105
116
  for (const s of collectSourceSites(idiom.provided, files).sites)
106
117
  tokens.add(s.token);
107
118
  }
@@ -0,0 +1,23 @@
1
+ import { type IRegistrationIdiom, type IVerdictCoverage } from '@shrkcrft/core';
2
+ import type { IIdiomRoleCoverage } from './i-idiom-role-coverage.js';
3
+ import type { IRegistrationQueryVerdict } from './i-registration-query-verdict.js';
4
+ import { type IOrphanRegistration, type IRegistrationGraph, type IUnprovidedToken } from './registration-graph.js';
5
+ /**
6
+ * THE registration graph's read-scope record: the idiom files the reader could
7
+ * not read, named (`examined 3 of 4 files, 1 over the 1MB read cap: …`), or
8
+ * `undefined` when it read every file its idioms matched. `wiring unprovided |
9
+ * orphans`, finish's unprovided sub-gate and MCP `get_wiring_graph` all read
10
+ * it here, so the verb and its siblings cannot disagree about what was read.
11
+ */
12
+ export declare function registrationGraphCoverage(graph: IRegistrationGraph): IVerdictCoverage | undefined;
13
+ /**
14
+ * `wiring unprovided` settled against what the graph read AND what every
15
+ * idiom's roles examined (`roles`: `measureIdiomRoleCoverage`, THE role
16
+ * authority — required, so no surface can forget the fold). `extra` adds
17
+ * candidates found another way (finish's base-ref provider regressions); they
18
+ * are merged by token and sorted, as finish always did.
19
+ */
20
+ export declare function registrationUnprovidedVerdict(graph: IRegistrationGraph, idioms: readonly IRegistrationIdiom[], roles: readonly IIdiomRoleCoverage[], changedFiles?: readonly string[], extra?: readonly IUnprovidedToken[]): IRegistrationQueryVerdict<IUnprovidedToken>;
21
+ /** `wiring orphans` settled against what the graph read and what every idiom's roles examined. */
22
+ export declare function registrationOrphansVerdict(graph: IRegistrationGraph, idioms: readonly IRegistrationIdiom[], roles: readonly IIdiomRoleCoverage[], changedFiles?: readonly string[]): IRegistrationQueryVerdict<IOrphanRegistration>;
23
+ //# sourceMappingURL=registration-query-verdict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration-query-verdict.d.ts","sourceRoot":"","sources":["../../src/wiring/registration-query-verdict.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAKjC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,SAAS,CASjG;AAwFD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,EAChC,KAAK,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAClC,yBAAyB,CAAC,gBAAgB,CAAC,CAQ7C;AAED,kGAAkG;AAClG,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAC/B,yBAAyB,CAAC,mBAAmB,CAAC,CAUhD"}
@@ -0,0 +1,118 @@
1
+ import { coverageShortfall, resolveSourceGlobs, ruleVerdictRecords, } from '@shrkcrft/core';
2
+ import { describeUnread, readScopeCoverage, unreadMatching } from "../util/read-scope-coverage.js";
3
+ import { registrationOrphans, registrationUnprovided, } from "./registration-graph.js";
4
+ /** At most this many demoted tokens ride on one coverage record. */
5
+ const TOKEN_LABEL_CAP = 20;
6
+ /**
7
+ * THE registration graph's read-scope record: the idiom files the reader could
8
+ * not read, named (`examined 3 of 4 files, 1 over the 1MB read cap: …`), or
9
+ * `undefined` when it read every file its idioms matched. `wiring unprovided |
10
+ * orphans`, finish's unprovided sub-gate and MCP `get_wiring_graph` all read
11
+ * it here, so the verb and its siblings cannot disagree about what was read.
12
+ */
13
+ export function registrationGraphCoverage(graph) {
14
+ if (!graph.readScope || graph.readScope.unread.length === 0)
15
+ return undefined;
16
+ return {
17
+ ...readScopeCoverage({ unit: 'registration idioms', expected: graph.idioms.length, examined: graph.idioms.length }, graph.readScope),
18
+ subject: 'registration graph',
19
+ };
20
+ }
21
+ /**
22
+ * Every idiom's role records, as an absence query settles them (round 13, P4):
23
+ * the role authority's coverage (`examined 2 of 3 roles … declared (0 files)`)
24
+ * plus its expectEmpty acceptance, folded by core's `ruleVerdictRecords` and
25
+ * subject-prefixed with the idiom. A role record that is only the idiom's read
26
+ * gap is dropped while the graph's own read-scope record is carried — that
27
+ * record already names every unread idiom file (a union over idioms); its
28
+ * acceptance is kept, so a record B is never dropped silently.
29
+ */
30
+ function roleRecords(graph, roles) {
31
+ const graphNamesUnread = registrationGraphCoverage(graph) !== undefined;
32
+ return roles.flatMap((r) => {
33
+ const records = graphNamesUnread && r.readGap
34
+ ? r.unitAcceptance
35
+ ? [r.unitAcceptance]
36
+ : []
37
+ : ruleVerdictRecords(r.coverage, r.unitAcceptance);
38
+ return records.map((c) => ({ ...c, subject: c.subject ?? r.idiom }));
39
+ });
40
+ }
41
+ /**
42
+ * THE rule for an absence claim over an incomplete read. The graph merges
43
+ * tokens across idioms, so a site of the missing role in ANY idiom satisfies a
44
+ * token. When an unread file matches any idiom's `missingRole` globs, every
45
+ * candidate could be refuted there: all are demoted to `unproven`, named in a
46
+ * second coverage record. Otherwise the candidates are findings.
47
+ *
48
+ * Round 13 (P4): every idiom's ROLE coverage is carried too, always — the
49
+ * graph's read scope said nothing about a role whose globs matched no file, so
50
+ * "every declared token has a provider" printed a ✓ over a declared role that
51
+ * examined nothing. A dead role never demotes a candidate (a finding stays a
52
+ * finding, as on `gates check`); it keeps a clean answer from being a pass.
53
+ */
54
+ function absenceVerdict(graph, idioms, roles, missingRole, candidates, unit, subject) {
55
+ const unread = graph.readScope?.unread ?? [];
56
+ // Per idiom's role LIST, never a flattened union: a negation subtracts from
57
+ // its own list only, so one idiom's `!x` cannot hide a refuting file from
58
+ // another idiom that reads it. Deduped by path, sorted.
59
+ const refutingByPath = new Map();
60
+ for (const i of unread.length > 0 ? idioms : []) {
61
+ for (const u of unreadMatching(unread, resolveSourceGlobs(i[missingRole]))) {
62
+ if (!refutingByPath.has(u.path))
63
+ refutingByPath.set(u.path, u);
64
+ }
65
+ }
66
+ const refuting = [...refutingByPath.values()].sort((a, b) => a.path.localeCompare(b.path));
67
+ const unproven = refuting.length > 0 ? candidates : [];
68
+ const fileRecord = registrationGraphCoverage(graph);
69
+ const coverage = [
70
+ ...(fileRecord ? [fileRecord] : []),
71
+ ...roleRecords(graph, roles),
72
+ ...(unproven.length > 0
73
+ ? [
74
+ {
75
+ unit,
76
+ expected: unproven.length,
77
+ examined: 0,
78
+ unexamined: unproven.slice(0, TOKEN_LABEL_CAP).map((t) => t.token),
79
+ unexaminedTotal: unproven.length,
80
+ reason: `may be ${missingRole} in a file the reader did not read — ${describeUnread(refuting)}`,
81
+ subject,
82
+ },
83
+ ]
84
+ : []),
85
+ ];
86
+ return {
87
+ findings: refuting.length > 0 ? [] : candidates,
88
+ unproven,
89
+ coverage,
90
+ shortfalls: coverage.flatMap((c) => {
91
+ const s = coverageShortfall(c);
92
+ return s === undefined ? [] : [s];
93
+ }),
94
+ unread,
95
+ };
96
+ }
97
+ /**
98
+ * `wiring unprovided` settled against what the graph read AND what every
99
+ * idiom's roles examined (`roles`: `measureIdiomRoleCoverage`, THE role
100
+ * authority — required, so no surface can forget the fold). `extra` adds
101
+ * candidates found another way (finish's base-ref provider regressions); they
102
+ * are merged by token and sorted, as finish always did.
103
+ */
104
+ export function registrationUnprovidedVerdict(graph, idioms, roles, changedFiles, extra) {
105
+ let candidates = registrationUnprovided(graph, changedFiles);
106
+ if (extra !== undefined) {
107
+ const byToken = new Map();
108
+ for (const u of [...candidates, ...extra])
109
+ if (!byToken.has(u.token))
110
+ byToken.set(u.token, u);
111
+ candidates = [...byToken.values()].sort((a, b) => a.token.localeCompare(b.token));
112
+ }
113
+ return absenceVerdict(graph, idioms, roles, 'provided', candidates, 'unprovided tokens', 'unprovided');
114
+ }
115
+ /** `wiring orphans` settled against what the graph read and what every idiom's roles examined. */
116
+ export function registrationOrphansVerdict(graph, idioms, roles, changedFiles) {
117
+ return absenceVerdict(graph, idioms, roles, 'consumed', registrationOrphans(graph, changedFiles), 'orphan registrations', 'orphans');
118
+ }
@@ -1,4 +1,5 @@
1
1
  import { type IRegistryDeclaration } from '@shrkcrft/core';
2
+ import type { IReadScope } from '../util/read-scope.js';
2
3
  export declare const REGISTRY_SCHEMA: "sharkcraft.registry-inventory/v1";
3
4
  /** Where an id was found (project-relative path + 1-based line). */
4
5
  export interface IRegistrySite {
@@ -22,6 +23,13 @@ export interface IRegistryInventory {
22
23
  readonly entries: readonly IRegistryEntry[];
23
24
  /** Misconfiguration messages (bad regex / no capture group / bad source). */
24
25
  readonly diagnostics: readonly string[];
26
+ /**
27
+ * The SOURCE side's read scope: declaration files read, and matched ones the
28
+ * reader did not read (over the read cap, or unreadable). With any unread
29
+ * the inventory is incomplete: an absent id, "no duplicates" or an empty
30
+ * inventory proves nothing.
31
+ */
32
+ readonly readScope: IReadScope;
25
33
  }
26
34
  export interface IScanRegistryOptions {
27
35
  /** Project-relative directories to prune from the walk. */
@@ -1 +1 @@
1
- {"version":3,"file":"registry-query.d.ts","sourceRoot":"","sources":["../../src/wiring/registry-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAK/E,eAAO,MAAM,eAAe,EAAG,kCAA2C,CAAC;AAE3E,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACnD;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,eAAe,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,GAAE,oBAAyB,GACjC,kBAAkB,CAkDpB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjF;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEnG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,kBAAkB,GAAG,SAAS,cAAc,EAAE,CAE3F"}
1
+ {"version":3,"file":"registry-query.d.ts","sourceRoot":"","sources":["../../src/wiring/registry-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,eAAO,MAAM,eAAe,EAAG,kCAA2C,CAAC;AAE3E,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACnD;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,eAAe,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,GAAE,oBAAyB,GACjC,kBAAkB,CAwDpB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjF;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEnG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,kBAAkB,GAAG,SAAS,cAAc,EAAE,CAE3F"}
@@ -1,6 +1,7 @@
1
1
  import { resolveSourceGlobs } from '@shrkcrft/core';
2
- import { matchesAny } from "../scan/glob.js";
2
+ import { globListSelects } from "../scan/glob.js";
3
3
  import { readMatchingFiles } from "../util/walk-files.js";
4
+ import { unreadMatching } from "../util/read-scope-coverage.js";
4
5
  import { collectSourceSites } from "./evaluate-wiring.js";
5
6
  export const REGISTRY_SCHEMA = 'sharkcraft.registry-inventory/v1';
6
7
  function sortSites(sites) {
@@ -18,16 +19,18 @@ export function scanRegistry(projectRoot, decl, options = {}) {
18
19
  ...(decl.consumer ? resolveSourceGlobs(decl.consumer) : []),
19
20
  ]),
20
21
  ];
21
- const cache = readMatchingFiles(projectRoot, globs, new Set(options.excludeDirs ?? []));
22
- const entries = [...cache.entries()].map(([path, content]) => ({ path, content }));
22
+ const matched = readMatchingFiles(projectRoot, globs, new Set(options.excludeDirs ?? []));
23
+ const entries = [...matched.files.entries()].map(([path, content]) => ({ path, content }));
23
24
  const diagnostics = [];
24
- const sourceFiles = entries.filter((f) => matchesAny(f.path, resolveSourceGlobs(decl.source)));
25
+ // The walk above is the POSITIVE union of both sides; each side selects its
26
+ // own files, so the source's `!` never hides a consumer file (or vice versa).
27
+ const sourceFiles = entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(decl.source)));
25
28
  const declared = collectSourceSites(decl.source, sourceFiles);
26
29
  if (declared.error)
27
30
  diagnostics.push(`registry "${decl.name}" source: ${declared.error}`);
28
31
  const consumerByToken = new Map();
29
32
  if (decl.consumer) {
30
- const consumerFiles = entries.filter((f) => matchesAny(f.path, resolveSourceGlobs(decl.consumer)));
33
+ const consumerFiles = entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(decl.consumer)));
31
34
  const consumed = collectSourceSites(decl.consumer, consumerFiles);
32
35
  if (consumed.error)
33
36
  diagnostics.push(`registry "${decl.name}" consumer: ${consumed.error}`);
@@ -57,6 +60,10 @@ export function scanRegistry(projectRoot, decl, options = {}) {
57
60
  ...(decl.description ? { description: decl.description } : {}),
58
61
  entries: entriesOut,
59
62
  diagnostics,
63
+ readScope: {
64
+ read: sourceFiles.length,
65
+ unread: unreadMatching(matched.unread, resolveSourceGlobs(decl.source)),
66
+ },
60
67
  };
61
68
  }
62
69
  /** True if `id` is declared in the registry (alias-blind exact membership). */
@@ -12,8 +12,10 @@ export interface IRunWiringOptions {
12
12
  }
13
13
  /**
14
14
  * Filesystem-backed wiring run: walks the project once, reads only the files a
15
- * rule references (skipping oversized files), and evaluates every (selected)
16
- * rule. Pure-engine output; the only IO is the read-only tree walk + reads.
15
+ * rule references, and evaluates every (selected) rule. A matched file the
16
+ * reader could not read (over the read cap) is handed to the engine as
17
+ * unread, so the rule's coverage names it instead of passing over it.
18
+ * Pure-engine output; the only IO is the read-only tree walk + reads.
17
19
  */
18
20
  export declare function runWiring(projectRoot: string, rules: readonly IWiringRule[], options?: IRunWiringOptions): IWiringReport;
19
21
  //# sourceMappingURL=scan-wiring-files.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scan-wiring-files.d.ts","sourceRoot":"","sources":["../../src/wiring/scan-wiring-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAItE,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAO9B,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,OAAO,GAAE,iBAAsB,GAC9B,aAAa,CAqCf"}
1
+ {"version":3,"file":"scan-wiring-files.d.ts","sourceRoot":"","sources":["../../src/wiring/scan-wiring-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAQ3F,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAQ9B,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,OAAO,GAAE,iBAAsB,GAC9B,aAAa,CAsEf"}
@@ -1,29 +1,49 @@
1
- import { resolveSourceGlobs } from '@shrkcrft/core';
2
- import { matchesAny } from "../scan/glob.js";
1
+ import { normalizeWiringRule, resolveSourceGlobs } from '@shrkcrft/core';
2
+ import { globListSelects } from "../scan/glob.js";
3
3
  import { readMatchingFiles } from "../util/walk-files.js";
4
+ import { unreadMatching } from "../util/read-scope-coverage.js";
5
+ import { globListUnits } from "../util/dead-glob-units.js";
6
+ import { settleGlobLists } from "../util/settle-glob-lists.js";
7
+ import { sourceLivenessRequest } from "../extract/source-liveness-request.js";
4
8
  import { loadTsconfigPaths } from "../scan/tsconfig-aliases.js";
5
- import { evaluateWiring, wiringGlobsOf, } from "./evaluate-wiring.js";
9
+ import { evaluateWiring, wiringGlobsOf, wiringSourcesOf, } from "./evaluate-wiring.js";
10
+ import { wiringLabeledSources } from "./wiring-labeled-sources.js";
6
11
  /** All file globs a rule references: every side / every hop. */
7
12
  function ruleGlobs(rule) {
8
13
  return wiringGlobsOf(rule);
9
14
  }
10
15
  /**
11
16
  * Filesystem-backed wiring run: walks the project once, reads only the files a
12
- * rule references (skipping oversized files), and evaluates every (selected)
13
- * rule. Pure-engine output; the only IO is the read-only tree walk + reads.
17
+ * rule references, and evaluates every (selected) rule. A matched file the
18
+ * reader could not read (over the read cap) is handed to the engine as
19
+ * unread, so the rule's coverage names it instead of passing over it.
20
+ * Pure-engine output; the only IO is the read-only tree walk + reads.
14
21
  */
15
22
  export function runWiring(projectRoot, rules, options = {}) {
16
- let selected = rules;
23
+ // The engine entry normalises idempotently (round 13): a loaded rule comes
24
+ // back equal, a hand-built `{ pattern, expectEmpty }` entry becomes a glob
25
+ // plus a marker. A MALFORMED entry stays on the authored rule, which
26
+ // `evaluateWiring` reports as misconfigured — no glob reader here sees it.
27
+ const malformed = new Set();
28
+ let selected = rules.map((r) => {
29
+ const n = normalizeWiringRule(r);
30
+ if (n.ok)
31
+ return n.value;
32
+ malformed.add(r);
33
+ return r;
34
+ });
17
35
  if (options.only && options.only.length > 0) {
18
36
  const ids = new Set(options.only);
19
37
  selected = selected.filter((r) => ids.has(r.id));
20
38
  }
21
39
  if (options.changedOnly) {
22
40
  const changed = options.changedFiles ?? [];
23
- selected = selected.filter((r) => {
24
- const globs = ruleGlobs(r);
25
- return changed.some((c) => matchesAny(c, globs));
26
- });
41
+ // Per SOURCE, never through the flattened union: a negation subtracts from
42
+ // its own list only, so one side's `!x` cannot hide a changed file another
43
+ // side reads, and a change touching only excluded files selects nothing.
44
+ // A malformed rule has no footprint to prove it out of scope: it stays in.
45
+ selected = selected.filter((r) => malformed.has(r) ||
46
+ wiringSourcesOf(r).some((s) => changed.some((c) => globListSelects(c, resolveSourceGlobs(s)))));
27
47
  }
28
48
  if (selected.length === 0) {
29
49
  return {
@@ -33,14 +53,31 @@ export function runWiring(projectRoot, rules, options = {}) {
33
53
  diagnostics: [],
34
54
  skipped: [],
35
55
  evaluated: 0,
56
+ acceptedEmpty: 0,
36
57
  verdict: 'pass',
37
58
  };
38
59
  }
39
- // Union of all globs across selected rules → one tree walk, cached reads.
40
- const allGlobs = [...new Set(selected.flatMap(ruleGlobs))];
41
- const cache = readMatchingFiles(projectRoot, allGlobs, new Set(options.excludeDirs ?? []));
42
- const entries = [...cache.entries()].map(([path, content]) => ({ path, content }));
60
+ // Union of all globs across selected rules → one POSITIVE tree walk, cached
61
+ // reads; each source then selects its own files from it (`globListSelects`).
62
+ const allGlobs = [...new Set(selected.filter((r) => !malformed.has(r)).flatMap(ruleGlobs))];
63
+ const excludeDirs = options.excludeDirs ?? [];
64
+ const matched = readMatchingFiles(projectRoot, allGlobs, new Set(excludeDirs));
65
+ const entries = [...matched.files.entries()].map(([path, content]) => ({ path, content }));
66
+ const walked = entries.map((f) => f.path);
67
+ // THE one dead-unit decision (`globListUnits`, the one `gates coverage` and
68
+ // `policy-lint` read) over this run's positive walk.
69
+ const unitsOf = (globs) => globListUnits(walked, matched.unread, globs);
43
70
  // The tsconfig paths let an `import-edges` source resolve alias specifiers the
44
71
  // way the compiler does — the same map `check boundaries` resolves with.
45
- return evaluateWiring(selected, (source) => entries.filter((f) => matchesAny(f.path, resolveSourceGlobs(source))), { tsconfigPaths: loadTsconfigPaths(projectRoot) });
72
+ return evaluateWiring(selected, (source) => entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(source))), { tsconfigPaths: loadTsconfigPaths(projectRoot) }, (source) => unreadMatching(matched.unread, resolveSourceGlobs(source)),
73
+ // Which negations emptied a source's list, so its skip reason says so (R12-DOC-2).
74
+ (source) => {
75
+ const units = unitsOf(resolveSourceGlobs(source));
76
+ return units.allExcluded ? units.negations : undefined;
77
+ },
78
+ // Every source's glob units settled with their `expectEmpty` markers
79
+ // (round 13) — THE per-source request `gates coverage` builds too
80
+ // (`sourceLivenessRequest` over the engine's own side labels), so `check
81
+ // wiring` and `gates coverage` accept, and word, one planned glob alike.
82
+ (rule) => settleGlobLists(sourceLivenessRequest(projectRoot, wiringLabeledSources(rule), excludeDirs, rule.id, unitsOf)));
46
83
  }
@@ -31,6 +31,17 @@ export interface ISinkBinding {
31
31
  /** Module specifier, for an import binding. */
32
32
  readonly specifier?: string;
33
33
  }
34
+ /**
35
+ * Top-level value/type declarations — the names a file binds itself.
36
+ *
37
+ * Every whitespace run is single-line (`[ \t]`). The old form opened with
38
+ * `(?:^|\n)\s*`: from every newline of a long blank block, `\s*` ran across all
39
+ * the lines below it and backtracked through each — O(lines × run) per block
40
+ * (`findBlankRunHazards` flags that shape). Modifiers and the declared name sit
41
+ * on the declaration's own line in real TS, so the matches are the same
42
+ * (`r75-blank-run-linearity.test.ts` holds old ≡ new over every source file).
43
+ */
44
+ export declare const LOCAL_DECL: RegExp;
34
45
  /**
35
46
  * Every value name bound in the file: imports plus its own top-level
36
47
  * declarations.
@@ -1 +1 @@
1
- {"version":3,"file":"sink-imports.d.ts","sourceRoot":"","sources":["../../src/wiring/sink-imports.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAE/F,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAsBnE;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,eAAe,GAAG,SAAS,CA6B7B;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7E;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,SAAS,CAe9B"}
1
+ {"version":3,"file":"sink-imports.d.ts","sourceRoot":"","sources":["../../src/wiring/sink-imports.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAE/F,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,QAC0I,CAAC;AAElK;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAsBnE;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,eAAe,GAAG,SAAS,CA6B7B;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7E;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,SAAS,CAe9B"}
@@ -1,7 +1,16 @@
1
1
  import { lineOf } from "../extract/scan-literals.js";
2
2
  import { parseImportStatements } from "../extract/parse-imports.js";
3
- /** Top-level value/type declarations — the names a file binds itself. */
4
- const LOCAL_DECL = /(?:^|\n)\s*(?:export\s+)?(?:declare\s+)?(?:default\s+)?(?:const|let|var|function\s*\*?|class|enum|interface|type)\s+([A-Za-z_$][\w$]*)/g;
3
+ /**
4
+ * Top-level value/type declarations — the names a file binds itself.
5
+ *
6
+ * Every whitespace run is single-line (`[ \t]`). The old form opened with
7
+ * `(?:^|\n)\s*`: from every newline of a long blank block, `\s*` ran across all
8
+ * the lines below it and backtracked through each — O(lines × run) per block
9
+ * (`findBlankRunHazards` flags that shape). Modifiers and the declared name sit
10
+ * on the declaration's own line in real TS, so the matches are the same
11
+ * (`r75-blank-run-linearity.test.ts` holds old ≡ new over every source file).
12
+ */
13
+ export const LOCAL_DECL = /(?:^|\n)[ \t]*(?:export[ \t]+)?(?:declare[ \t]+)?(?:default[ \t]+)?(?:const|let|var|function(?:[ \t]*\*)?|class|enum|interface|type)[ \t]+([A-Za-z_$][\w$]*)/g;
5
14
  /**
6
15
  * Every value name bound in the file: imports plus its own top-level
7
16
  * declarations.
@@ -1,3 +1,4 @@
1
+ import type { IUnreadFile } from '../util/unread-file.js';
1
2
  export declare const TRACE_SCHEMA: "sharkcraft.trace/v1";
2
3
  /** Default source globs scanned when `trace` is given no `--glob`. */
3
4
  export declare const TRACE_DEFAULT_GLOBS: readonly string[];
@@ -41,6 +42,11 @@ export interface ITraceReport {
41
42
  readonly byRole: Readonly<Record<TraceRole, readonly ITraceSite[]>>;
42
43
  /** Const names found bound to the literal (`const NAME = 'literal'`), if any. */
43
44
  readonly aliases: readonly string[];
45
+ /**
46
+ * Matched files the reader did not read (over the read cap, or unreadable),
47
+ * so a site inside one is missing from `byRole`. Set only when non-empty.
48
+ */
49
+ readonly unread?: readonly IUnreadFile[];
44
50
  }
45
51
  export interface ITraceOptions {
46
52
  /** Override the default source globs. */
@@ -1 +1 @@
1
- {"version":3,"file":"trace-literal.d.ts","sourceRoot":"","sources":["../../src/wiring/trace-literal.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,EAAG,qBAA8B,CAAC;AAE3D,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAShD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,oBAAY,SAAS;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,SAAS,cAAc;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AA6ED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAuFd"}
1
+ {"version":3,"file":"trace-literal.d.ts","sourceRoot":"","sources":["../../src/wiring/trace-literal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,eAAO,MAAM,YAAY,EAAG,qBAA8B,CAAC;AAE3D,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAShD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,oBAAY,SAAS;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,SAAS,cAAc;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AA6ED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,YAAY,CA0Fd"}
@@ -1,4 +1,4 @@
1
- import { readMatchingFiles } from "../util/walk-files.js";
1
+ import { readSelectedFiles } from "../util/read-selected-files.js";
2
2
  export const TRACE_SCHEMA = 'sharkcraft.trace/v1';
3
3
  /** Default source globs scanned when `trace` is given no `--glob`. */
4
4
  export const TRACE_DEFAULT_GLOBS = [
@@ -125,7 +125,9 @@ const ALIAS_NAME = /^[A-Z][\w$]*$/;
125
125
  export function traceLiteral(projectRoot, literal, options = {}) {
126
126
  const globs = options.globs && options.globs.length > 0 ? options.globs : TRACE_DEFAULT_GLOBS;
127
127
  const exclude = new Set(options.excludeDirs ?? []);
128
- const cache = readMatchingFiles(projectRoot, globs, exclude);
128
+ // One glob list (the caller's `--glob`s, or the defaults): a `!` excludes.
129
+ const matched = readSelectedFiles(projectRoot, globs, exclude);
130
+ const cache = matched.files;
129
131
  const sites = [];
130
132
  const aliasNames = new Set();
131
133
  // Exact quoted-literal match: the closing quote immediately follows, so the
@@ -205,5 +207,6 @@ export function traceLiteral(projectRoot, literal, options = {}) {
205
207
  [TraceRole.Reference]: sortSites(byRole[TraceRole.Reference]),
206
208
  },
207
209
  aliases: [...aliasNames].sort(),
210
+ ...(matched.unread.length > 0 ? { unread: matched.unread } : {}),
208
211
  };
209
212
  }
@@ -0,0 +1,12 @@
1
+ import type { IWiringRule } from '@shrkcrft/core';
2
+ import type { ILabeledSource } from '../extract/i-labeled-source.js';
3
+ /**
4
+ * THE enumeration of a wiring rule's sources, each named by its side —
5
+ * `declared`, `registered` (or `registered[i]` when a rule unions several
6
+ * sinks), `chain[i]`. `gates coverage` / `gates explain` (the CLI's
7
+ * `gateRuleLabeledSources`) and the wiring engine's liveness settle
8
+ * (`runWiring`) both read it, so a marker or a dead glob is named by one side
9
+ * label on every surface.
10
+ */
11
+ export declare function wiringLabeledSources(rule: IWiringRule): readonly ILabeledSource[];
12
+ //# sourceMappingURL=wiring-labeled-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiring-labeled-sources.d.ts","sourceRoot":"","sources":["../../src/wiring/wiring-labeled-sources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,cAAc,EAAE,CAWjF"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * THE enumeration of a wiring rule's sources, each named by its side —
3
+ * `declared`, `registered` (or `registered[i]` when a rule unions several
4
+ * sinks), `chain[i]`. `gates coverage` / `gates explain` (the CLI's
5
+ * `gateRuleLabeledSources`) and the wiring engine's liveness settle
6
+ * (`runWiring`) both read it, so a marker or a dead glob is named by one side
7
+ * label on every surface.
8
+ */
9
+ export function wiringLabeledSources(rule) {
10
+ const registered = Array.isArray(rule.registered)
11
+ ? rule.registered
12
+ : rule.registered
13
+ ? [rule.registered]
14
+ : [];
15
+ return [
16
+ ...(rule.declared ? [{ label: 'declared', source: rule.declared }] : []),
17
+ ...registered.map((source, i) => ({ label: registered.length > 1 ? `registered[${i}]` : 'registered', source })),
18
+ ...(rule.chain ?? []).map((source, i) => ({ label: `chain[${i}]`, source })),
19
+ ];
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/boundaries",
3
- "version": "0.1.0-alpha.30",
3
+ "version": "0.1.0-alpha.31",
4
4
  "description": "SharkCraft boundary rules: detect when a repository violates its own architecture (forbidden imports across folder/package/layer boundaries).",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -43,7 +43,7 @@
43
43
  "typecheck": "tsc --noEmit -p tsconfig.json"
44
44
  },
45
45
  "dependencies": {
46
- "@shrkcrft/core": "^0.1.0-alpha.30"
46
+ "@shrkcrft/core": "^0.1.0-alpha.31"
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"