@shrkcrft/boundaries 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 (229) 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 +10 -8
  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 +49 -1
  35. package/dist/extract/extract-tokens.d.ts.map +1 -1
  36. package/dist/extract/extract-tokens.js +283 -25
  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 +50 -0
  41. package/dist/extract/import-edges.d.ts.map +1 -0
  42. package/dist/extract/import-edges.js +170 -0
  43. package/dist/extract/inspect-source.d.ts +38 -3
  44. package/dist/extract/inspect-source.d.ts.map +1 -1
  45. package/dist/extract/inspect-source.js +56 -8
  46. package/dist/extract/parse-imports.d.ts +80 -0
  47. package/dist/extract/parse-imports.d.ts.map +1 -0
  48. package/dist/extract/parse-imports.js +254 -0
  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 +20 -2
  56. package/dist/generated/check-provenance.d.ts.map +1 -1
  57. package/dist/generated/check-provenance.js +27 -6
  58. package/dist/generated/read-regen-tree.d.ts +16 -0
  59. package/dist/generated/read-regen-tree.d.ts.map +1 -0
  60. package/dist/generated/read-regen-tree.js +61 -0
  61. package/dist/generated/scan-generated.d.ts +57 -3
  62. package/dist/generated/scan-generated.d.ts.map +1 -1
  63. package/dist/generated/scan-generated.js +111 -11
  64. package/dist/index.d.ts +51 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +54 -0
  67. package/dist/model/boundary-intended-empty-line.d.ts +10 -0
  68. package/dist/model/boundary-intended-empty-line.d.ts.map +1 -0
  69. package/dist/model/boundary-intended-empty-line.js +11 -0
  70. package/dist/model/boundary-markable-list.d.ts +15 -0
  71. package/dist/model/boundary-markable-list.d.ts.map +1 -0
  72. package/dist/model/boundary-markable-list.js +15 -0
  73. package/dist/model/boundary-rule-input-keys.d.ts +11 -0
  74. package/dist/model/boundary-rule-input-keys.d.ts.map +1 -0
  75. package/dist/model/boundary-rule-input-keys.js +29 -0
  76. package/dist/model/boundary-rule-input.d.ts +23 -0
  77. package/dist/model/boundary-rule-input.d.ts.map +1 -0
  78. package/dist/model/boundary-rule-input.js +1 -0
  79. package/dist/model/boundary-rule-key-problems.d.ts +11 -0
  80. package/dist/model/boundary-rule-key-problems.d.ts.map +1 -0
  81. package/dist/model/boundary-rule-key-problems.js +36 -0
  82. package/dist/model/boundary-rule-marker-problems.d.ts +26 -0
  83. package/dist/model/boundary-rule-marker-problems.d.ts.map +1 -0
  84. package/dist/model/boundary-rule-marker-problems.js +94 -0
  85. package/dist/model/boundary-rule-scope.d.ts +88 -0
  86. package/dist/model/boundary-rule-scope.d.ts.map +1 -0
  87. package/dist/model/boundary-rule-scope.js +175 -0
  88. package/dist/model/boundary-rule.d.ts +120 -5
  89. package/dist/model/boundary-rule.d.ts.map +1 -1
  90. package/dist/model/boundary-rule.js +148 -1
  91. package/dist/model/boundary-unit-problem-issue.d.ts +9 -0
  92. package/dist/model/boundary-unit-problem-issue.d.ts.map +1 -0
  93. package/dist/model/boundary-unit-problem-issue.js +16 -0
  94. package/dist/model/normalize-boundary-rule.d.ts +21 -0
  95. package/dist/model/normalize-boundary-rule.d.ts.map +1 -0
  96. package/dist/model/normalize-boundary-rule.js +62 -0
  97. package/dist/policy/evaluate-policy.d.ts +80 -3
  98. package/dist/policy/evaluate-policy.d.ts.map +1 -1
  99. package/dist/policy/evaluate-policy.js +152 -19
  100. package/dist/policy/i-policy-rule-liveness.d.ts +15 -0
  101. package/dist/policy/i-policy-rule-liveness.d.ts.map +1 -0
  102. package/dist/policy/i-policy-rule-liveness.js +1 -0
  103. package/dist/policy/run-policy.d.ts +1 -1
  104. package/dist/policy/run-policy.d.ts.map +1 -1
  105. package/dist/policy/run-policy.js +171 -21
  106. package/dist/registry/load-boundary-rules.d.ts +29 -1
  107. package/dist/registry/load-boundary-rules.d.ts.map +1 -1
  108. package/dist/registry/load-boundary-rules.js +38 -8
  109. package/dist/scan/glob.d.ts +90 -0
  110. package/dist/scan/glob.d.ts.map +1 -1
  111. package/dist/scan/glob.js +205 -0
  112. package/dist/scan/i-glob-unit-measure.d.ts +22 -0
  113. package/dist/scan/i-glob-unit-measure.d.ts.map +1 -0
  114. package/dist/scan/i-glob-unit-measure.js +1 -0
  115. package/dist/scan/import-pattern.d.ts +109 -0
  116. package/dist/scan/import-pattern.d.ts.map +1 -0
  117. package/dist/scan/import-pattern.js +191 -0
  118. package/dist/scan/node-builtin-package-names.d.ts +13 -0
  119. package/dist/scan/node-builtin-package-names.d.ts.map +1 -0
  120. package/dist/scan/node-builtin-package-names.js +25 -0
  121. package/dist/scan/scan-imports.d.ts +36 -3
  122. package/dist/scan/scan-imports.d.ts.map +1 -1
  123. package/dist/scan/scan-imports.js +114 -45
  124. package/dist/util/blank-run-hazard-finding.d.ts +34 -0
  125. package/dist/util/blank-run-hazard-finding.d.ts.map +1 -0
  126. package/dist/util/blank-run-hazard-finding.js +1 -0
  127. package/dist/util/blank-run-hazard.d.ts +29 -0
  128. package/dist/util/blank-run-hazard.d.ts.map +1 -0
  129. package/dist/util/blank-run-hazard.js +450 -0
  130. package/dist/util/dead-glob-units.d.ts +46 -0
  131. package/dist/util/dead-glob-units.d.ts.map +1 -0
  132. package/dist/util/dead-glob-units.js +116 -0
  133. package/dist/util/glob-list-liveness-input.d.ts +31 -0
  134. package/dist/util/glob-list-liveness-input.d.ts.map +1 -0
  135. package/dist/util/glob-list-liveness-input.js +65 -0
  136. package/dist/util/i-dead-glob-unit.d.ts +24 -0
  137. package/dist/util/i-dead-glob-unit.d.ts.map +1 -0
  138. package/dist/util/i-dead-glob-unit.js +1 -0
  139. package/dist/util/i-glob-list-units.d.ts +25 -0
  140. package/dist/util/i-glob-list-units.d.ts.map +1 -0
  141. package/dist/util/i-glob-list-units.js +1 -0
  142. package/dist/util/i-glob-liveness-list.d.ts +23 -0
  143. package/dist/util/i-glob-liveness-list.d.ts.map +1 -0
  144. package/dist/util/i-glob-liveness-list.js +1 -0
  145. package/dist/util/i-glob-liveness-request.d.ts +11 -0
  146. package/dist/util/i-glob-liveness-request.d.ts.map +1 -0
  147. package/dist/util/i-glob-liveness-request.js +1 -0
  148. package/dist/util/i-glob-negation.d.ts +13 -0
  149. package/dist/util/i-glob-negation.d.ts.map +1 -0
  150. package/dist/util/i-glob-negation.js +1 -0
  151. package/dist/util/matched-files.d.ts +22 -0
  152. package/dist/util/matched-files.d.ts.map +1 -0
  153. package/dist/util/matched-files.js +1 -0
  154. package/dist/util/negation-cause.d.ts +15 -0
  155. package/dist/util/negation-cause.d.ts.map +1 -0
  156. package/dist/util/negation-cause.js +21 -0
  157. package/dist/util/plane-scan-exclude-dirs.d.ts +17 -0
  158. package/dist/util/plane-scan-exclude-dirs.d.ts.map +1 -0
  159. package/dist/util/plane-scan-exclude-dirs.js +22 -0
  160. package/dist/util/read-glob-list-liveness.d.ts +20 -0
  161. package/dist/util/read-glob-list-liveness.d.ts.map +1 -0
  162. package/dist/util/read-glob-list-liveness.js +20 -0
  163. package/dist/util/read-scope-coverage.d.ts +90 -0
  164. package/dist/util/read-scope-coverage.d.ts.map +1 -0
  165. package/dist/util/read-scope-coverage.js +173 -0
  166. package/dist/util/read-scope.d.ts +14 -0
  167. package/dist/util/read-scope.d.ts.map +1 -0
  168. package/dist/util/read-scope.js +1 -0
  169. package/dist/util/read-selected-files.d.ts +16 -0
  170. package/dist/util/read-selected-files.d.ts.map +1 -0
  171. package/dist/util/read-selected-files.js +25 -0
  172. package/dist/util/settle-glob-lists.d.ts +12 -0
  173. package/dist/util/settle-glob-lists.d.ts.map +1 -0
  174. package/dist/util/settle-glob-lists.js +13 -0
  175. package/dist/util/unread-file-reason.d.ts +26 -0
  176. package/dist/util/unread-file-reason.d.ts.map +1 -0
  177. package/dist/util/unread-file-reason.js +26 -0
  178. package/dist/util/unread-file.d.ts +10 -0
  179. package/dist/util/unread-file.d.ts.map +1 -0
  180. package/dist/util/unread-file.js +1 -0
  181. package/dist/util/walk-files.d.ts +90 -7
  182. package/dist/util/walk-files.d.ts.map +1 -1
  183. package/dist/util/walk-files.js +206 -19
  184. package/dist/wiring/evaluate-wiring.d.ts +88 -7
  185. package/dist/wiring/evaluate-wiring.d.ts.map +1 -1
  186. package/dist/wiring/evaluate-wiring.js +276 -16
  187. package/dist/wiring/explain-wiring.d.ts +56 -14
  188. package/dist/wiring/explain-wiring.d.ts.map +1 -1
  189. package/dist/wiring/explain-wiring.js +136 -10
  190. package/dist/wiring/i-idiom-role-coverage.d.ts +24 -0
  191. package/dist/wiring/i-idiom-role-coverage.d.ts.map +1 -0
  192. package/dist/wiring/i-idiom-role-coverage.js +1 -0
  193. package/dist/wiring/i-registration-query-verdict.d.ts +38 -0
  194. package/dist/wiring/i-registration-query-verdict.d.ts.map +1 -0
  195. package/dist/wiring/i-registration-query-verdict.js +1 -0
  196. package/dist/wiring/i-registration-roles.d.ts +71 -0
  197. package/dist/wiring/i-registration-roles.d.ts.map +1 -0
  198. package/dist/wiring/i-registration-roles.js +1 -0
  199. package/dist/wiring/measure-idiom-role-coverage.d.ts +12 -0
  200. package/dist/wiring/measure-idiom-role-coverage.d.ts.map +1 -0
  201. package/dist/wiring/measure-idiom-role-coverage.js +20 -0
  202. package/dist/wiring/measure-registration-roles.d.ts +25 -0
  203. package/dist/wiring/measure-registration-roles.d.ts.map +1 -0
  204. package/dist/wiring/measure-registration-roles.js +124 -0
  205. package/dist/wiring/plan-wiring-fix.d.ts +16 -1
  206. package/dist/wiring/plan-wiring-fix.d.ts.map +1 -1
  207. package/dist/wiring/plan-wiring-fix.js +177 -4
  208. package/dist/wiring/registration-graph.d.ts +13 -1
  209. package/dist/wiring/registration-graph.d.ts.map +1 -1
  210. package/dist/wiring/registration-graph.js +22 -6
  211. package/dist/wiring/registration-query-verdict.d.ts +23 -0
  212. package/dist/wiring/registration-query-verdict.d.ts.map +1 -0
  213. package/dist/wiring/registration-query-verdict.js +118 -0
  214. package/dist/wiring/registry-query.d.ts +9 -1
  215. package/dist/wiring/registry-query.d.ts.map +1 -1
  216. package/dist/wiring/registry-query.js +17 -6
  217. package/dist/wiring/scan-wiring-files.d.ts +5 -3
  218. package/dist/wiring/scan-wiring-files.d.ts.map +1 -1
  219. package/dist/wiring/scan-wiring-files.js +55 -14
  220. package/dist/wiring/sink-imports.d.ts +95 -0
  221. package/dist/wiring/sink-imports.d.ts.map +1 -0
  222. package/dist/wiring/sink-imports.js +124 -0
  223. package/dist/wiring/trace-literal.d.ts +6 -0
  224. package/dist/wiring/trace-literal.d.ts.map +1 -1
  225. package/dist/wiring/trace-literal.js +5 -2
  226. package/dist/wiring/wiring-labeled-sources.d.ts +12 -0
  227. package/dist/wiring/wiring-labeled-sources.d.ts.map +1 -0
  228. package/dist/wiring/wiring-labeled-sources.js +20 -0
  229. package/package.json +2 -2
@@ -11,7 +11,18 @@
11
11
  * comment syntax differs from the C/JS family (e.g. `#` comments) is scanned as
12
12
  * plain code.
13
13
  */
14
- /** Index of the closing quote of the string starting at `start` (handles escapes). */
14
+ /**
15
+ * Index of the closing quote of the string starting at `start` (handles escapes).
16
+ *
17
+ * A `'…'` or `"…"` literal ends at an unescaped NEWLINE (JS semantics — only a
18
+ * template literal spans lines): the returned index is then the last character
19
+ * before the newline. Without this a quote the lexer could not recognise as
20
+ * code — the `'` inside a regex literal such as `/[&<>"']/g`, an apostrophe in
21
+ * prose — swallowed the rest of the FILE as one phantom string, hiding every
22
+ * real construct after it (round 11, 6.1a: a real `import()` 60 lines later
23
+ * vanished). Bounding the mis-lex to one line is strictly more accurate for
24
+ * every caller: zone lexing, the policy plane and array extraction alike.
25
+ */
15
26
  export declare function skipString(content: string, start: number): number;
16
27
  /**
17
28
  * If a comment starts at `start`, the index of its LAST character; otherwise
@@ -1 +1 @@
1
- {"version":3,"file":"scan-literals.d.ts","sourceRoot":"","sources":["../../src/extract/scan-literals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAYlE;AAED,iDAAiD;AACjD,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAO7D;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,qFAAqF;AACrF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AA0CD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAgC7E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ7D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAsB7D"}
1
+ {"version":3,"file":"scan-literals.d.ts","sourceRoot":"","sources":["../../src/extract/scan-literals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAYjE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAYlE;AAED,iDAAiD;AACjD,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAO7D;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,qFAAqF;AACrF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AA0CD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAgC7E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ7D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAsB7D"}
@@ -11,9 +11,21 @@
11
11
  * comment syntax differs from the C/JS family (e.g. `#` comments) is scanned as
12
12
  * plain code.
13
13
  */
14
- /** Index of the closing quote of the string starting at `start` (handles escapes). */
14
+ /**
15
+ * Index of the closing quote of the string starting at `start` (handles escapes).
16
+ *
17
+ * A `'…'` or `"…"` literal ends at an unescaped NEWLINE (JS semantics — only a
18
+ * template literal spans lines): the returned index is then the last character
19
+ * before the newline. Without this a quote the lexer could not recognise as
20
+ * code — the `'` inside a regex literal such as `/[&<>"']/g`, an apostrophe in
21
+ * prose — swallowed the rest of the FILE as one phantom string, hiding every
22
+ * real construct after it (round 11, 6.1a: a real `import()` 60 lines later
23
+ * vanished). Bounding the mis-lex to one line is strictly more accurate for
24
+ * every caller: zone lexing, the policy plane and array extraction alike.
25
+ */
15
26
  export function skipString(content, start) {
16
27
  const quote = content[start];
28
+ const singleLine = quote === "'" || quote === '"';
17
29
  for (let i = start + 1; i < content.length; i += 1) {
18
30
  if (content[i] === '\\') {
19
31
  i += 1;
@@ -21,6 +33,8 @@ export function skipString(content, start) {
21
33
  }
22
34
  if (content[i] === quote)
23
35
  return i;
36
+ if (singleLine && content[i] === '\n')
37
+ return i - 1;
24
38
  }
25
39
  return content.length - 1;
26
40
  }
@@ -0,0 +1,24 @@
1
+ import type { IGlobListUnits } from '../util/i-glob-list-units.js';
2
+ import type { IGlobLivenessRequest } from '../util/i-glob-liveness-request.js';
3
+ import type { ILabeledSource } from './i-labeled-source.js';
4
+ /**
5
+ * THE glob lists of a rule's extraction sources as the gate-plane liveness
6
+ * settle reads them (round 13) — every source's `files` and, NEW this round,
7
+ * an `import-edges` source's `to.files` (judged for liveness, so a typo'd fence
8
+ * target is no longer a silent pass, and markable, so a fence to a planned
9
+ * subtree is sayable).
10
+ *
11
+ * Each list is qualified by its side (`declared.files`, `compute.source.to.files`
12
+ * — `qualifyListPath`), and each source's markers the same way
13
+ * (`qualifyUnitMarks`), so one settle spans a rule's every source without two
14
+ * sides' `files` colliding. In a multi-source rule a unit prints `<side>: <glob>`
15
+ * (today's dead-glob selector); `to.files` units print `[<side> ]to.files: <glob>`.
16
+ *
17
+ * `gates coverage` (`buildGateCoverage`), the wiring engine (`runWiring`) and
18
+ * the registration role measurement all build their request here, so the one
19
+ * rule's units read identically on every surface. `unitsOf` lets a caller
20
+ * judge a `files` list off a walk it already did; `to.files` matches resolved
21
+ * import targets, so it is judged off the one reader's own walk of its globs.
22
+ */
23
+ export declare function sourceLivenessRequest(projectRoot: string, sources: readonly ILabeledSource[], excludeDirs?: readonly string[], subject?: string, unitsOf?: (globs: readonly string[]) => IGlobListUnits): IGlobLivenessRequest;
24
+ //# sourceMappingURL=source-liveness-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-liveness-request.d.ts","sourceRoot":"","sources":["../../src/extract/source-liveness-request.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,WAAW,GAAE,SAAS,MAAM,EAAO,EACnC,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,cAAc,GACrD,oBAAoB,CAuBtB"}
@@ -0,0 +1,45 @@
1
+ import { qualifyListPath, qualifyUnitMarks } from '@shrkcrft/core';
2
+ import { readGlobListUnits } from "../util/dead-glob-units.js";
3
+ /**
4
+ * THE glob lists of a rule's extraction sources as the gate-plane liveness
5
+ * settle reads them (round 13) — every source's `files` and, NEW this round,
6
+ * an `import-edges` source's `to.files` (judged for liveness, so a typo'd fence
7
+ * target is no longer a silent pass, and markable, so a fence to a planned
8
+ * subtree is sayable).
9
+ *
10
+ * Each list is qualified by its side (`declared.files`, `compute.source.to.files`
11
+ * — `qualifyListPath`), and each source's markers the same way
12
+ * (`qualifyUnitMarks`), so one settle spans a rule's every source without two
13
+ * sides' `files` colliding. In a multi-source rule a unit prints `<side>: <glob>`
14
+ * (today's dead-glob selector); `to.files` units print `[<side> ]to.files: <glob>`.
15
+ *
16
+ * `gates coverage` (`buildGateCoverage`), the wiring engine (`runWiring`) and
17
+ * the registration role measurement all build their request here, so the one
18
+ * rule's units read identically on every surface. `unitsOf` lets a caller
19
+ * judge a `files` list off a walk it already did; `to.files` matches resolved
20
+ * import targets, so it is judged off the one reader's own walk of its globs.
21
+ */
22
+ export function sourceLivenessRequest(projectRoot, sources, excludeDirs = [], subject, unitsOf) {
23
+ const multi = sources.length > 1;
24
+ const exclude = new Set(excludeDirs);
25
+ const judge = unitsOf ?? ((globs) => readGlobListUnits(projectRoot, globs, exclude));
26
+ const lists = [];
27
+ const marks = [];
28
+ for (const { label, source } of sources) {
29
+ const files = source.files ?? [];
30
+ if (files.length > 0) {
31
+ lists.push({ list: qualifyListPath(label, 'files'), ...(multi ? { label } : {}), globs: files, units: judge(files) });
32
+ }
33
+ const to = source.to?.files ?? [];
34
+ if (to.length > 0) {
35
+ lists.push({
36
+ list: qualifyListPath(label, 'to.files'),
37
+ label: multi ? `${label} to.files` : 'to.files',
38
+ globs: to,
39
+ units: readGlobListUnits(projectRoot, to, exclude),
40
+ });
41
+ }
42
+ marks.push(...qualifyUnitMarks(source.expectEmptyUnits ?? [], label));
43
+ }
44
+ return { ...(subject !== undefined ? { subject } : {}), lists, marks };
45
+ }
@@ -6,7 +6,11 @@ export type ProvenanceFindingKind =
6
6
  /** A file OUTSIDE the glob carries the header — a hand-written file mislabeled. */
7
7
  | 'mislabeled'
8
8
  /** Advisory: the header does not name how to regenerate the file. */
9
- | 'no-regen-pointer';
9
+ | 'no-regen-pointer'
10
+ /** In the tree, owned by no writer and not blessed as hand-maintained. */
11
+ | 'unclassified'
12
+ /** A `handMaintained` bless whose file no longer exists. */
13
+ | 'stale-hand-maintained';
10
14
  export interface IProvenanceFinding {
11
15
  readonly ruleId: string;
12
16
  readonly file: string;
@@ -20,6 +24,20 @@ export interface IProvenanceFinding {
20
24
  * deterministic — never a whole-tree read of every file type.
21
25
  */
22
26
  export declare function deriveOutsideGlobs(generatedGlob: readonly string[]): string[];
27
+ /**
28
+ * Tree-classification results a mixed-tree rule produces alongside its files.
29
+ *
30
+ * Passed in rather than recomputed so the classification a caller already did
31
+ * (see `scanGeneratedFiles`) is the same one reported — two implementations of
32
+ * "which files does this rule own?" is precisely the drift this plane exists
33
+ * to catch.
34
+ */
35
+ export interface IClassificationFindings {
36
+ /** Files under the tree owned by no writer and not blessed. */
37
+ readonly unclassified?: readonly string[];
38
+ /** `handMaintained` patterns currently matching no file. */
39
+ readonly staleHandMaintained?: readonly string[];
40
+ }
23
41
  /**
24
42
  * Check the "this file is generated" header contract.
25
43
  *
@@ -30,7 +48,7 @@ export declare function deriveOutsideGlobs(generatedGlob: readonly string[]): st
30
48
  * `outside` should contain candidate files NOT in `generatedGlob`; pass an empty
31
49
  * map when `forbidOutside` is off.
32
50
  */
33
- export declare function checkProvenanceHeaders(rule: IGeneratedArtifactRule, generated: ReadonlyMap<string, string>, outside: ReadonlyMap<string, string>): {
51
+ export declare function checkProvenanceHeaders(rule: IGeneratedArtifactRule, generated: ReadonlyMap<string, string>, outside: ReadonlyMap<string, string>, classification?: IClassificationFindings): {
34
52
  findings: readonly IProvenanceFinding[];
35
53
  error?: string;
36
54
  };
@@ -1 +1 @@
1
- {"version":3,"file":"check-provenance.d.ts","sourceRoot":"","sources":["../../src/generated/check-provenance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAG7D,0CAA0C;AAC1C,MAAM,MAAM,qBAAqB;AAC/B,qEAAqE;AACnE,gBAAgB;AAClB,mFAAmF;GACjF,YAAY;AACd,qEAAqE;GACnE,kBAAkB,CAAC;AAEvB,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAQD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAO7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,sBAAsB,EAC5B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC;IAAE,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA2D7D"}
1
+ {"version":3,"file":"check-provenance.d.ts","sourceRoot":"","sources":["../../src/generated/check-provenance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAI7D,0CAA0C;AAC1C,MAAM,MAAM,qBAAqB;AAC/B,qEAAqE;AACnE,gBAAgB;AAClB,mFAAmF;GACjF,YAAY;AACd,qEAAqE;GACnE,kBAAkB;AACpB,0EAA0E;GACxE,cAAc;AAChB,4DAA4D;GAC1D,uBAAuB,CAAC;AAE5B,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAQD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAQ7E;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,4DAA4D;IAC5D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,sBAAsB,EAC5B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,cAAc,GAAE,uBAA4B,GAC3C;IAAE,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgF7D"}
@@ -1,3 +1,4 @@
1
+ import { globListWalkGlobs } from "../scan/glob.js";
1
2
  import { safeCompile } from "../util/safe-regex.js";
2
3
  /** The head of a file, as the header contract sees it. */
3
4
  function headOf(content, withinLines) {
@@ -11,7 +12,8 @@ function headOf(content, withinLines) {
11
12
  */
12
13
  export function deriveOutsideGlobs(generatedGlob) {
13
14
  const exts = new Set();
14
- for (const g of generatedGlob) {
15
+ // Inclusion globs only: a `!gen/**/*.hand.ts` names no extension to search.
16
+ for (const g of globListWalkGlobs(generatedGlob)) {
15
17
  const m = /\.([A-Za-z0-9]+)$/.exec(g);
16
18
  if (m)
17
19
  exts.add(m[1]);
@@ -28,16 +30,35 @@ export function deriveOutsideGlobs(generatedGlob) {
28
30
  * `outside` should contain candidate files NOT in `generatedGlob`; pass an empty
29
31
  * map when `forbidOutside` is off.
30
32
  */
31
- export function checkProvenanceHeaders(rule, generated, outside) {
33
+ export function checkProvenanceHeaders(rule, generated, outside, classification = {}) {
34
+ const severity = rule.severity ?? 'error';
35
+ // Classification findings do not depend on the header contract — a mixed tree
36
+ // must be fully accounted for whether or not the rule asserts headers, so
37
+ // these are produced before the early return below.
38
+ const classFindings = [
39
+ ...(classification.unclassified ?? []).map((file) => ({
40
+ ruleId: rule.id,
41
+ file,
42
+ kind: 'unclassified',
43
+ severity,
44
+ message: 'in the generated tree but owned by no `sources[]` glob and not listed in `handMaintained` — classify it',
45
+ })),
46
+ ...(classification.staleHandMaintained ?? []).map((pattern) => ({
47
+ ruleId: rule.id,
48
+ file: pattern,
49
+ kind: 'stale-hand-maintained',
50
+ severity: 'warning',
51
+ message: '`handMaintained` entry matches no file — the blessed file was renamed or deleted',
52
+ })),
53
+ ];
32
54
  const header = rule.provenanceHeader;
33
55
  if (!header)
34
- return { findings: [] };
56
+ return { findings: classFindings };
35
57
  const { re, error } = safeCompile(header.mustMatch, header.flags);
36
58
  if (error || !re)
37
- return { findings: [], error: `provenanceHeader ${error}` };
38
- const severity = rule.severity ?? 'error';
59
+ return { findings: classFindings, error: `provenanceHeader ${error}` };
39
60
  const within = header.withinLines ?? 10;
40
- const findings = [];
61
+ const findings = [...classFindings];
41
62
  const test = (content) => {
42
63
  re.lastIndex = 0;
43
64
  return re.test(headOf(content, within));
@@ -0,0 +1,16 @@
1
+ import type { IMatchedFiles } from '../util/matched-files.js';
2
+ /**
3
+ * Read a REGENERATED temp tree (what a `generatedArtifacts[].regen` wrote into
4
+ * `{TMP}`): every file under `root`, keyed by POSIX path relative to it.
5
+ *
6
+ * Same contract as the one reader (`readMatchingFiles`): a file it did not read
7
+ * (over {@link MAX_REGEN_FILE_BYTES}, or unreadable) is returned in `unread`
8
+ * with its reason, never dropped. It used to be dropped, so a regen that wrote
9
+ * a NEW 2.1MB file read "byte-identical to a fresh regen ✓" while the same
10
+ * file at 100 bytes failed as "regen produces it but it is not committed".
11
+ * The caller decides what an unread output means: one keyed onto a committed
12
+ * file cannot be byte-compared (a coverage gap), one keyed onto nothing
13
+ * committed is drift its path alone proves.
14
+ */
15
+ export declare function readRegenTree(root: string): IMatchedFiles;
16
+ //# sourceMappingURL=read-regen-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-regen-tree.d.ts","sourceRoot":"","sources":["../../src/generated/read-regen-tree.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAuCzD"}
@@ -0,0 +1,61 @@
1
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import * as nodePath from 'node:path';
3
+ import { UnreadFileReason } from "../util/unread-file-reason.js";
4
+ import { MAX_REGEN_FILE_BYTES } from "../util/walk-files.js";
5
+ /**
6
+ * Read a REGENERATED temp tree (what a `generatedArtifacts[].regen` wrote into
7
+ * `{TMP}`): every file under `root`, keyed by POSIX path relative to it.
8
+ *
9
+ * Same contract as the one reader (`readMatchingFiles`): a file it did not read
10
+ * (over {@link MAX_REGEN_FILE_BYTES}, or unreadable) is returned in `unread`
11
+ * with its reason, never dropped. It used to be dropped, so a regen that wrote
12
+ * a NEW 2.1MB file read "byte-identical to a fresh regen ✓" while the same
13
+ * file at 100 bytes failed as "regen produces it but it is not committed".
14
+ * The caller decides what an unread output means: one keyed onto a committed
15
+ * file cannot be byte-compared (a coverage gap), one keyed onto nothing
16
+ * committed is drift its path alone proves.
17
+ */
18
+ export function readRegenTree(root) {
19
+ const files = new Map();
20
+ const unread = [];
21
+ const visit = (abs) => {
22
+ let entries;
23
+ try {
24
+ entries = readdirSync(abs, { withFileTypes: true });
25
+ }
26
+ catch {
27
+ return;
28
+ }
29
+ for (const e of entries) {
30
+ const child = nodePath.join(abs, e.name);
31
+ if (e.isDirectory()) {
32
+ visit(child);
33
+ continue;
34
+ }
35
+ if (!e.isFile())
36
+ continue;
37
+ const rel = nodePath.relative(root, child).split(nodePath.sep).join('/');
38
+ let size;
39
+ try {
40
+ size = statSync(child).size;
41
+ }
42
+ catch {
43
+ unread.push({ path: rel, reason: UnreadFileReason.Unreadable });
44
+ continue;
45
+ }
46
+ if (size > MAX_REGEN_FILE_BYTES) {
47
+ unread.push({ path: rel, reason: UnreadFileReason.OverRegenCap, bytes: size });
48
+ continue;
49
+ }
50
+ try {
51
+ files.set(rel, readFileSync(child, 'utf8'));
52
+ }
53
+ catch {
54
+ unread.push({ path: rel, reason: UnreadFileReason.Unreadable, bytes: size });
55
+ }
56
+ }
57
+ };
58
+ visit(root);
59
+ unread.sort((a, b) => a.path.localeCompare(b.path));
60
+ return { files, unread: Object.freeze(unread) };
61
+ }
@@ -1,17 +1,71 @@
1
1
  import type { IGeneratedArtifactRule } from '@shrkcrft/core';
2
+ import type { IReadScope } from '../util/read-scope.js';
3
+ /** One writer's slice of a mixed generated tree. */
4
+ export interface IGeneratedSourceSlice {
5
+ /** Label for output — the writer's `id`, or `source[<index>]`. */
6
+ readonly label: string;
7
+ /** The regen command that owns this slice. */
8
+ readonly regen: string;
9
+ /** Globs selecting the committed files this writer owns. */
10
+ readonly glob: readonly string[];
11
+ /** Committed files matching this writer's glob (path → content). */
12
+ readonly files: ReadonlyMap<string, string>;
13
+ readonly timeoutMs?: number;
14
+ }
2
15
  /** The committed generated files plus the mislabel-candidate set. */
3
16
  export interface IGeneratedScan {
4
17
  /** Files matching `generatedGlob` (project-relative path → content). */
5
18
  readonly generated: ReadonlyMap<string, string>;
19
+ /**
20
+ * The subset of {@link generated} that is checkable — i.e. everything except
21
+ * the hand-maintained blesses. Header and byte checks run over this.
22
+ */
23
+ readonly checkable: ReadonlyMap<string, string>;
24
+ /** Files excluded by `handMaintained` or by the in-file marker, sorted. */
25
+ readonly handMaintained: readonly string[];
26
+ /** The subset of {@link handMaintained} that declared itself via the marker. */
27
+ readonly markedHandMaintained: readonly string[];
28
+ /**
29
+ * `handMaintained` patterns that currently match NO file. A bless whose file
30
+ * was renamed or deleted is a stale exemption: it silently widens nothing
31
+ * today, but it also documents a file that no longer exists, and the next
32
+ * file to take that name inherits an exemption nobody reviewed.
33
+ */
34
+ readonly staleHandMaintained: readonly string[];
35
+ /**
36
+ * Files under `generatedGlob` owned by NO writer and not blessed. Only
37
+ * populated for multi-writer rules, where the writers partition the tree —
38
+ * a single-writer rule owns everything its glob matches by definition.
39
+ */
40
+ readonly unclassified: readonly string[];
41
+ /** Per-writer slices for a multi-writer rule; empty for a single-writer one. */
42
+ readonly slices: readonly IGeneratedSourceSlice[];
6
43
  /** Candidates for the `forbidOutside` mislabel check (empty when it is off). */
7
44
  readonly outside: ReadonlyMap<string, string>;
8
45
  /** Globs actually used for the outside scan (derived or configured). */
9
46
  readonly outsideGlobs: readonly string[];
47
+ /**
48
+ * The rule's read scope: every file read (committed generated files, plus
49
+ * the outside candidates when `forbidOutside` is on), and every matched file
50
+ * the reader did NOT read (over the read cap, or unreadable). An unread
51
+ * committed file was never header-checked or byte-compared.
52
+ */
53
+ readonly readScope: IReadScope;
10
54
  }
11
55
  /**
12
- * Read the committed side of a generated-artifact rule. Pure filesystem reads —
13
- * the regen command (if any) is orchestrated by the caller, which is also where
14
- * the local-config-only trust decision is enforced.
56
+ * Read + CLASSIFY the committed side of a generated-artifact rule.
57
+ *
58
+ * Classification is the part that makes this usable on a real tree. A blanket
59
+ * "everything under this glob is generated by that one command" holds only in a
60
+ * demo; a real `generated/` dir mixes several writers' output with files that
61
+ * are still hand-maintained. Partitioning the tree first means each writer is
62
+ * compared against its OWN slice (no cross-writer `only-committed` noise), the
63
+ * hand-written files are exempted from the header contract instead of failing
64
+ * it, and anything belonging to neither is surfaced as `unclassified` rather
65
+ * than silently assumed.
66
+ *
67
+ * Pure filesystem reads — the regen commands (if any) are orchestrated by the
68
+ * caller, which is also where the local-config-only trust decision is enforced.
15
69
  */
16
70
  export declare function scanGeneratedFiles(projectRoot: string, rule: IGeneratedArtifactRule, excludeDirs?: readonly string[]): IGeneratedScan;
17
71
  //# sourceMappingURL=scan-generated.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scan-generated.d.ts","sourceRoot":"","sources":["../../src/generated/scan-generated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAK7D,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,wEAAwE;IACxE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,sBAAsB,EAC5B,WAAW,GAAE,SAAS,MAAM,EAAO,GAClC,cAAc,CAmBhB"}
1
+ {"version":3,"file":"scan-generated.d.ts","sourceRoot":"","sources":["../../src/generated/scan-generated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAI7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,oDAAoD;AACpD,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,2EAA2E;IAC3E,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAClD,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,wEAAwE;IACxE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;CAChC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,sBAAsB,EAC5B,WAAW,GAAE,SAAS,MAAM,EAAO,GAClC,cAAc,CAkHhB"}
@@ -1,27 +1,127 @@
1
- import { matchesAny } from "../scan/glob.js";
2
- import { readMatchingFiles } from "../util/walk-files.js";
1
+ import { globListSelects } from "../scan/glob.js";
2
+ import { safeCompile } from "../util/safe-regex.js";
3
+ import { readSelectedFiles } from "../util/read-selected-files.js";
4
+ import { mergeReadScopes, unreadEntryMatches, unreadEntryWhollyMatches } from "../util/read-scope-coverage.js";
3
5
  import { deriveOutsideGlobs } from "./check-provenance.js";
4
6
  /**
5
- * Read the committed side of a generated-artifact rule. Pure filesystem reads —
6
- * the regen command (if any) is orchestrated by the caller, which is also where
7
- * the local-config-only trust decision is enforced.
7
+ * Read + CLASSIFY the committed side of a generated-artifact rule.
8
+ *
9
+ * Classification is the part that makes this usable on a real tree. A blanket
10
+ * "everything under this glob is generated by that one command" holds only in a
11
+ * demo; a real `generated/` dir mixes several writers' output with files that
12
+ * are still hand-maintained. Partitioning the tree first means each writer is
13
+ * compared against its OWN slice (no cross-writer `only-committed` noise), the
14
+ * hand-written files are exempted from the header contract instead of failing
15
+ * it, and anything belonging to neither is surfaced as `unclassified` rather
16
+ * than silently assumed.
17
+ *
18
+ * Pure filesystem reads — the regen commands (if any) are orchestrated by the
19
+ * caller, which is also where the local-config-only trust decision is enforced.
8
20
  */
9
21
  export function scanGeneratedFiles(projectRoot, rule, excludeDirs = []) {
10
22
  const exclude = new Set(excludeDirs);
11
- const generated = readMatchingFiles(projectRoot, rule.generatedGlob, exclude);
23
+ // The files `generatedGlob` SELECTS: a `!` entry (`!gen/**/*.hand.ts`)
24
+ // excludes, so a hand file carved out of the tree is never header-checked.
25
+ const generatedRead = readSelectedFiles(projectRoot, rule.generatedGlob, exclude);
26
+ const generated = generatedRead.files;
27
+ const blessPatterns = rule.handMaintained ?? [];
28
+ // The in-file marker is read from the same head window the header contract
29
+ // uses, so "the top of the file" means one thing for both classifications.
30
+ const markerWindow = rule.provenanceHeader?.withinLines ?? 10;
31
+ const marker = rule.handMaintainedMarker
32
+ ? safeCompile(rule.handMaintainedMarker, rule.handMaintainedMarkerFlags).re
33
+ : undefined;
34
+ const carriesMarker = (content) => {
35
+ if (!marker)
36
+ return false;
37
+ marker.lastIndex = 0;
38
+ return marker.test(content.split('\n').slice(0, Math.max(1, markerWindow)).join('\n'));
39
+ };
40
+ const handMaintained = [];
41
+ const markedHandMaintained = [];
42
+ const checkable = new Map();
43
+ for (const [path, content] of generated) {
44
+ if (blessPatterns.length > 0 && globListSelects(path, blessPatterns)) {
45
+ handMaintained.push(path);
46
+ continue;
47
+ }
48
+ if (carriesMarker(content)) {
49
+ handMaintained.push(path);
50
+ markedHandMaintained.push(path);
51
+ continue;
52
+ }
53
+ checkable.set(path, content);
54
+ }
55
+ handMaintained.sort();
56
+ markedHandMaintained.sort();
57
+ // A bless is pinned to a NAMED file (the schema forbids a wildcard basename),
58
+ // so one that matches nothing points at a file that has been renamed away.
59
+ // A file over the read cap was still MATCHED, so a bless naming it is live.
60
+ // So is one that could match beneath a directory the walk could not list.
61
+ const matchedPaths = [...generated.keys()];
62
+ const staleHandMaintained = blessPatterns
63
+ .filter((pattern) => !matchedPaths.some((p) => globListSelects(p, [pattern])) &&
64
+ !generatedRead.unread.some((u) => unreadEntryMatches(u, [pattern])))
65
+ .sort();
66
+ const slices = (rule.sources ?? []).map((src, i) => {
67
+ const files = new Map();
68
+ for (const [path, content] of checkable) {
69
+ if (globListSelects(path, src.glob))
70
+ files.set(path, content);
71
+ }
72
+ return {
73
+ label: src.id ?? `source[${i}]`,
74
+ regen: src.regen,
75
+ glob: src.glob,
76
+ files,
77
+ ...(src.timeoutMs !== undefined ? { timeoutMs: src.timeoutMs } : {}),
78
+ };
79
+ });
80
+ // Only a multi-writer rule PARTITIONS the tree; a single-writer rule owns
81
+ // everything its glob matched, so nothing there can be unclassified.
82
+ const unclassified = slices.length === 0
83
+ ? []
84
+ : [...checkable.keys()].filter((p) => !slices.some((s) => globListSelects(p, s.glob))).sort();
12
85
  const wantOutside = rule.provenanceHeader?.forbidOutside === true;
13
86
  if (!wantOutside) {
14
- return { generated, outside: new Map(), outsideGlobs: [] };
87
+ return {
88
+ generated,
89
+ checkable,
90
+ handMaintained,
91
+ markedHandMaintained,
92
+ staleHandMaintained,
93
+ unclassified,
94
+ slices,
95
+ outside: new Map(),
96
+ outsideGlobs: [],
97
+ readScope: { read: generated.size, unread: generatedRead.unread },
98
+ };
15
99
  }
16
100
  const outsideGlobs = rule.provenanceHeader?.outsideGlob && rule.provenanceHeader.outsideGlob.length > 0
17
101
  ? [...rule.provenanceHeader.outsideGlob]
18
102
  : deriveOutsideGlobs(rule.generatedGlob);
19
- const all = readMatchingFiles(projectRoot, outsideGlobs, exclude);
103
+ const all = readSelectedFiles(projectRoot, outsideGlobs, exclude);
20
104
  const outside = new Map();
21
- for (const [path, content] of all) {
22
- if (matchesAny(path, rule.generatedGlob))
105
+ for (const [path, content] of all.files) {
106
+ // A file `generatedGlob` excludes is OUTSIDE the generated tree: carrying
107
+ // the "do not edit" header there is exactly a mislabel.
108
+ if (globListSelects(path, rule.generatedGlob))
23
109
  continue;
24
110
  outside.set(path, content);
25
111
  }
26
- return { generated, outside, outsideGlobs };
112
+ // An unlistable directory is "outside" unless the generated glob covers ALL
113
+ // of it (it may hold both); `mergeReadScopes` counts a shared path once.
114
+ const outsideUnread = all.unread.filter((u) => !unreadEntryWhollyMatches(u, rule.generatedGlob));
115
+ return {
116
+ generated,
117
+ checkable,
118
+ handMaintained,
119
+ markedHandMaintained,
120
+ staleHandMaintained,
121
+ unclassified,
122
+ slices,
123
+ outside,
124
+ outsideGlobs,
125
+ readScope: mergeReadScopes(generated.size + outside.size, [generatedRead.unread, outsideUnread]),
126
+ };
27
127
  }
package/dist/index.d.ts CHANGED
@@ -1,24 +1,57 @@
1
1
  export * from './model/boundary-rule.js';
2
+ export * from './model/boundary-rule-scope.js';
3
+ export * from './scan/import-pattern.js';
4
+ export * from './scan/node-builtin-package-names.js';
2
5
  export * from './registry/boundary-registry.js';
3
6
  export * from './registry/load-boundary-rules.js';
4
7
  export * from './scan/glob.js';
8
+ export * from './scan/i-glob-unit-measure.js';
5
9
  export * from './scan/scan-imports.js';
6
10
  export * from './evaluate/evaluate-boundaries.js';
11
+ export * from './model/boundary-markable-list.js';
12
+ export * from './model/boundary-rule-input.js';
13
+ export * from './model/boundary-rule-input-keys.js';
14
+ export * from './model/boundary-rule-key-problems.js';
15
+ export * from './model/boundary-rule-marker-problems.js';
16
+ export * from './model/boundary-unit-problem-issue.js';
17
+ export * from './model/normalize-boundary-rule.js';
18
+ export * from './model/boundary-intended-empty-line.js';
19
+ export * from './evaluate/i-boundary-rule-settle-input.js';
20
+ export * from './evaluate/i-boundary-rule-settlement.js';
21
+ export * from './evaluate/boundary-unit-kind.js';
22
+ export * from './evaluate/i-boundary-unit-finding.js';
23
+ export * from './evaluate/boundary-unit-finding.js';
24
+ export * from './evaluate/settle-boundary-rule.js';
25
+ export * from './evaluate/with-boundary-rule-settlement.js';
7
26
  export * from './scan/tsconfig-aliases.js';
8
27
  export * from './wiring/evaluate-wiring.js';
9
28
  export * from './wiring/scan-wiring-files.js';
10
29
  export * from './wiring/explain-wiring.js';
11
30
  export * from './wiring/registry-query.js';
12
31
  export * from './wiring/registration-graph.js';
32
+ export * from './wiring/i-registration-query-verdict.js';
33
+ export * from './wiring/registration-query-verdict.js';
34
+ export * from './wiring/i-registration-roles.js';
35
+ export * from './wiring/measure-registration-roles.js';
36
+ export * from './wiring/i-idiom-role-coverage.js';
37
+ export * from './wiring/measure-idiom-role-coverage.js';
38
+ export * from './generated/read-regen-tree.js';
13
39
  export * from './wiring/trace-literal.js';
14
40
  export * from './wiring/plan-wiring-fix.js';
41
+ export * from './wiring/sink-imports.js';
15
42
  export * from './policy/extract-templates.js';
16
43
  export * from './policy/evaluate-policy.js';
17
44
  export * from './policy/run-policy.js';
18
45
  export * from './extract/scan-literals.js';
19
46
  export * from './extract/extract-tokens.js';
47
+ export * from './extract/parse-imports.js';
48
+ export * from './extract/import-edges.js';
20
49
  export * from './extract/code-zones.js';
21
50
  export * from './extract/inspect-source.js';
51
+ export * from './extract/i-labeled-source.js';
52
+ export * from './extract/source-liveness-request.js';
53
+ export * from './wiring/wiring-labeled-sources.js';
54
+ export * from './policy/i-policy-rule-liveness.js';
22
55
  export * from './baseline/canonicalize.js';
23
56
  export * from './baseline/json-path-keys.js';
24
57
  export * from './baseline/diff-baseline.js';
@@ -27,5 +60,23 @@ export * from './generated/check-provenance.js';
27
60
  export * from './generated/compare-trees.js';
28
61
  export * from './generated/scan-generated.js';
29
62
  export * from './util/safe-regex.js';
63
+ export * from './util/blank-run-hazard.js';
64
+ export * from './util/blank-run-hazard-finding.js';
30
65
  export * from './util/walk-files.js';
66
+ export * from './util/unread-file-reason.js';
67
+ export * from './util/unread-file.js';
68
+ export * from './util/matched-files.js';
69
+ export * from './util/read-scope.js';
70
+ export * from './util/read-scope-coverage.js';
71
+ export * from './util/read-selected-files.js';
72
+ export * from './util/i-dead-glob-unit.js';
73
+ export * from './util/i-glob-negation.js';
74
+ export * from './util/i-glob-list-units.js';
75
+ export * from './util/dead-glob-units.js';
76
+ export * from './util/i-glob-liveness-list.js';
77
+ export * from './util/i-glob-liveness-request.js';
78
+ export * from './util/glob-list-liveness-input.js';
79
+ export * from './util/settle-glob-lists.js';
80
+ export * from './util/read-glob-list-liveness.js';
81
+ export * from './util/plane-scan-exclude-dirs.js';
31
82
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,sCAAsC,CAAC;AACrD,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mCAAmC,CAAC;AAElD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,kCAAkC,CAAC;AACjD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,oCAAoC,CAAC;AACnD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,kCAAkC,CAAC;AACjD,cAAc,wCAAwC,CAAC;AACvD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAE5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAE1C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC"}