@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
@@ -0,0 +1,65 @@
1
+ import { isNegatedGlob, UnitDeadWeight } from '@shrkcrft/core';
2
+ /**
3
+ * THE observation predicate for a gate-plane glob unit (round 13). Every
4
+ * gate-plane reporter — `gates coverage` / `gates try` / `quality`
5
+ * (`buildGateCoverage`), `policy-lint` (`runPolicyLint`), and each plane
6
+ * engine's rule-emptiness check (`settleGlobLists`) — builds its core
7
+ * `settleUnitLiveness` input here, so a glob and its marker are read one way
8
+ * everywhere:
9
+ *
10
+ * unit | exists | live
11
+ * --------------------------------------+--------+------
12
+ * inclusion glob, not dead | yes | yes (`it now matches N file(s)`)
13
+ * inclusion glob, dead, matched > 0 | yes | no (every match excluded by the list's own negations)
14
+ * inclusion glob, dead, matched 0 | no | no (its target does not exist)
15
+ * negation that excludes a file | yes | yes
16
+ * negation that excludes nothing | no | no
17
+ *
18
+ * `exists` is RAW target existence — it decides a MARKED unit (intended-empty,
19
+ * or went-live), so an acceptance never says "does not exist yet" about a glob
20
+ * whose files exist but are all excluded. `live` is the one dead-unit decision
21
+ * (`globListUnits`) negated — it decides an unmarked unit, and whether a
22
+ * went-live unit is effective. A glob in front of an unread file is not dead
23
+ * (`globListUnits`), so it reads live, never intended-empty.
24
+ *
25
+ * Advisory weight: a dead gate glob is listed and withholds the ✓, it moves
26
+ * the exit only under `--fail-on-dead-units`; record B (the acceptance) is
27
+ * emitted for any intended-empty unit.
28
+ */
29
+ export function globListLivenessInput(request) {
30
+ const observations = [];
31
+ for (const l of request.lists) {
32
+ const deadBy = new Map(l.units.dead.map((d) => [d.glob, d]));
33
+ const excludes = new Map(l.units.negations.map((n) => [n.glob, n.excludes]));
34
+ const effective = new Map((l.units.measures ?? []).map((m) => [m.glob, m.effective]));
35
+ for (const glob of new Set(l.globs)) {
36
+ const dead = deadBy.get(glob);
37
+ const negation = isNegatedGlob(glob);
38
+ const exists = dead === undefined || (!dead.negation && dead.matched > 0);
39
+ const live = dead === undefined;
40
+ const count = negation ? excludes.get(glob) : live ? effective.get(glob) : dead?.matched;
41
+ const liveBecause = !exists
42
+ ? undefined
43
+ : negation
44
+ ? `it now excludes ${count ?? 'a'} file${count === 1 || count === undefined ? '' : 's'}`
45
+ : `it now matches ${count ?? 'a'} file${count === 1 || count === undefined ? '' : 's'}`;
46
+ observations.push({
47
+ list: l.list,
48
+ unit: glob,
49
+ label: l.label !== undefined ? `${l.label}: ${glob}` : glob,
50
+ exists,
51
+ live,
52
+ ...(dead !== undefined ? { matched: dead.matched, deadReason: dead.reason } : {}),
53
+ ...(liveBecause !== undefined ? { liveBecause } : {}),
54
+ });
55
+ }
56
+ }
57
+ return {
58
+ ...(request.subject !== undefined ? { subject: request.subject } : {}),
59
+ unitLabel: 'globs',
60
+ weight: UnitDeadWeight.Advisory,
61
+ observations,
62
+ marks: request.marks,
63
+ deadSummary: 'select or exclude nothing',
64
+ };
65
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * One glob of a gate-plane list that does nothing (`globListUnits`).
3
+ *
4
+ * An inclusion glob is dead when it selects nothing that survives the list's
5
+ * negations; a negation is dead when it EXCLUDES nothing from its own list's
6
+ * positive set. The `reason` says which, in the words every surface prints.
7
+ */
8
+ export interface IDeadGlobUnit {
9
+ /** The glob as written (a negation keeps its `!`). */
10
+ readonly glob: string;
11
+ readonly negation: boolean;
12
+ /**
13
+ * Inclusion glob: the files it matched before the list's negations (0, or
14
+ * all of them excluded). Negation: the size of the positive set it failed to
15
+ * touch.
16
+ */
17
+ readonly matched: number;
18
+ /**
19
+ * `matched 0 files`, `matches only files the list's negations exclude (N)`,
20
+ * or `excludes nothing — none of the N file(s) the other globs select match it`.
21
+ */
22
+ readonly reason: string;
23
+ }
24
+ //# sourceMappingURL=i-dead-glob-unit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i-dead-glob-unit.d.ts","sourceRoot":"","sources":["../../src/util/i-dead-glob-unit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { IGlobUnitMeasure } from '../scan/i-glob-unit-measure.js';
2
+ import type { IDeadGlobUnit } from './i-dead-glob-unit.js';
3
+ import type { IGlobNegation } from './i-glob-negation.js';
4
+ /** One glob list's units, judged by `globListUnits`: the dead ones, the live negations, and how many were checked. */
5
+ export interface IGlobListUnits {
6
+ readonly dead: readonly IDeadGlobUnit[];
7
+ /** Negations that exclude at least one file, with the count. */
8
+ readonly negations: readonly IGlobNegation[];
9
+ /** Distinct globs (negations included) examined. */
10
+ readonly checked: number;
11
+ /**
12
+ * True when the list's OWN negations emptied it: its inclusion globs matched
13
+ * at least one file, every one of them is excluded, and no unread entry the
14
+ * list keeps in scope could hold a survivor. A rule over such a list that
15
+ * matched nothing is the author's `!` at work — not a stale selector.
16
+ */
17
+ readonly allExcluded: boolean;
18
+ /**
19
+ * Every distinct glob's measure (`measureGlobList`: an inclusion glob's raw
20
+ * and effective matches, a negation's exclusions) — what a went-live unit's
21
+ * sentence quotes (`it now matches 3 file(s)`). Set by `globListUnits`.
22
+ */
23
+ readonly measures?: readonly IGlobUnitMeasure[];
24
+ }
25
+ //# sourceMappingURL=i-glob-list-units.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i-glob-list-units.d.ts","sourceRoot":"","sources":["../../src/util/i-glob-list-units.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,sHAAsH;AACtH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IACxC,gEAAgE;IAChE,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACjD"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { IGlobListUnits } from './i-glob-list-units.js';
2
+ /**
3
+ * One gate-plane glob list as the liveness settle observes it (round 13).
4
+ *
5
+ * `list` is the marker ledger's `list` for these globs — `files`, `to.files`,
6
+ * `watchFiles`, `generatedGlob`, or a side-qualified `declared.files` when a
7
+ * rule reads several sources (`qualifyListPath`); the marks handed to the
8
+ * settle must be qualified the same way. `units` is the list judged by the one
9
+ * dead-unit decision (`globListUnits`).
10
+ */
11
+ export interface IGlobLivenessList {
12
+ readonly list: string;
13
+ /**
14
+ * Display prefix for each unit of this list. Set when the rule reads more
15
+ * than one list: a unit then prints `<label>: <glob>` — today's dead-glob
16
+ * selector (`declared: src/moved/*.ts`). Absent: the bare glob.
17
+ */
18
+ readonly label?: string;
19
+ /** The globs as the loader normalised them (a negation keeps its `!`). */
20
+ readonly globs: readonly string[];
21
+ readonly units: IGlobListUnits;
22
+ }
23
+ //# sourceMappingURL=i-glob-liveness-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i-glob-liveness-list.d.ts","sourceRoot":"","sources":["../../src/util/i-glob-liveness-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { IUnitMark } from '@shrkcrft/core';
2
+ import type { IGlobLivenessList } from './i-glob-liveness-list.js';
3
+ /** Every glob list one gate rule reads, and its markers — the input of the one gate-plane glob settle (round 13). */
4
+ export interface IGlobLivenessRequest {
5
+ /** The rule id; it becomes the coverage records' `subject`. */
6
+ readonly subject?: string;
7
+ readonly lists: readonly IGlobLivenessList[];
8
+ /** The rule's / sources' `expectEmptyUnits`, qualified exactly like the lists' `list`. */
9
+ readonly marks: readonly IUnitMark[];
10
+ }
11
+ //# sourceMappingURL=i-glob-liveness-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i-glob-liveness-request.d.ts","sourceRoot":"","sources":["../../src/util/i-glob-liveness-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,qHAAqH;AACrH,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC7C,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;CACtC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A LIVE negation of a gate-plane glob list and what it removes: the files its
3
+ * list's inclusion globs select that it takes out of scope (read, or matched
4
+ * but unread). `gates coverage` prints each one, so the scope a `!` narrowed is
5
+ * visible on the surface the author already reads.
6
+ */
7
+ export interface IGlobNegation {
8
+ /** The negation as written (`!src/**\/*.spec.ts`). */
9
+ readonly glob: string;
10
+ /** Files it excludes from the list's positive set (at least 1). */
11
+ readonly excludes: number;
12
+ }
13
+ //# sourceMappingURL=i-glob-negation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i-glob-negation.d.ts","sourceRoot":"","sources":["../../src/util/i-glob-negation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import type { IUnreadFile } from './unread-file.js';
2
+ /**
3
+ * What the one reader returns: every glob-matched file it READ, and every
4
+ * glob-matched file it did NOT read, with the reason.
5
+ *
6
+ * The second half is the point. A reader that drops an over-cap file without
7
+ * saying so turns "a forbidden token in a 1.7MB file" into "examined 1 of 1 ✓":
8
+ * every plane counted its expected scope from what was read, so the dropped
9
+ * file was invisible to every coverage record built on top of it. Returning
10
+ * the unread files next to the read ones means no caller can build coverage
11
+ * without choosing, explicitly, what to do with them.
12
+ */
13
+ export interface IMatchedFiles {
14
+ /**
15
+ * Project-relative POSIX path → content, for every matched file that was
16
+ * read. A fresh map per call, because callers filter and partition it.
17
+ */
18
+ readonly files: Map<string, string>;
19
+ /** Matched files the reader did not read, sorted by path. */
20
+ readonly unread: readonly IUnreadFile[];
21
+ }
22
+ //# sourceMappingURL=matched-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matched-files.d.ts","sourceRoot":"","sources":["../../src/util/matched-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACzC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { IGlobNegation } from './i-glob-negation.js';
2
+ /**
3
+ * THE cause of a list its OWN negations emptied — its inclusion globs matched
4
+ * files and every one was excluded as written — for the policy plane's skip
5
+ * and coverage reasons. Not a stale selector, and never worded as one.
6
+ */
7
+ export declare function allExcludedCause(negations: readonly IGlobNegation[]): string;
8
+ /**
9
+ * THE skip reason of an extraction SOURCE its own negations emptied (round 12
10
+ * review, R12-DOC-2) — the words `gates coverage` prints for the same rule, so
11
+ * `check wiring`, `gates check` and the NOT VERIFIED line no longer say "0
12
+ * files matched the source globs" (a file DID match, then was excluded).
13
+ */
14
+ export declare function emptiedByNegationsReason(negations: readonly IGlobNegation[]): string;
15
+ //# sourceMappingURL=negation-cause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negation-cause.d.ts","sourceRoot":"","sources":["../../src/util/negation-cause.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAO1D;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAEpF"}
@@ -0,0 +1,21 @@
1
+ /** `!src/**\/*.spec.ts (1 file), !docs/drafts/** (3 files)` — each negation with what it excluded. */
2
+ function describeNegations(negations) {
3
+ return negations.map((n) => `${n.glob} (${n.excludes} file${n.excludes === 1 ? '' : 's'})`).join(', ');
4
+ }
5
+ /**
6
+ * THE cause of a list its OWN negations emptied — its inclusion globs matched
7
+ * files and every one was excluded as written — for the policy plane's skip
8
+ * and coverage reasons. Not a stale selector, and never worded as one.
9
+ */
10
+ export function allExcludedCause(negations) {
11
+ return `every file its inclusion globs select is excluded by its own negations (${describeNegations(negations)})`;
12
+ }
13
+ /**
14
+ * THE skip reason of an extraction SOURCE its own negations emptied (round 12
15
+ * review, R12-DOC-2) — the words `gates coverage` prints for the same rule, so
16
+ * `check wiring`, `gates check` and the NOT VERIFIED line no longer say "0
17
+ * files matched the source globs" (a file DID match, then was excluded).
18
+ */
19
+ export function emptiedByNegationsReason(negations) {
20
+ return `matched nothing after its own negations: every file its inclusion globs select is excluded (${describeNegations(negations)})`;
21
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * THE scan scope of every data-defined plane walk: the project-relative
3
+ * directories the walk prunes. Today that is the SharkCraft asset/config dir,
4
+ * when it sits inside the project. Its `.ts` files hold the rule definitions
5
+ * themselves, so a rule would otherwise match (and self-report) its own
6
+ * definition.
7
+ *
8
+ * Every verb and every aggregate that walks a plane reads it here: `policy-lint`,
9
+ * `check wiring`, `wiring explain | unprovided | orphans`, `registry …`,
10
+ * `baseline`, `generated`, `docs references`, `gates check | coverage | try`,
11
+ * `quality`, `finish`, `shrk gate` and MCP. Before round 11 each derived it
12
+ * inline, and several did not derive it at all. So `check wiring` and `gates
13
+ * check` (or `policy-lint` and `shrk gate`) walked different trees for the SAME
14
+ * rule, and one read 1 where the other read 0 ✓.
15
+ */
16
+ export declare function planeScanExcludeDirs(projectRoot: string, sharkcraftDir: string | null | undefined): string[];
17
+ //# sourceMappingURL=plane-scan-exclude-dirs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plane-scan-exclude-dirs.d.ts","sourceRoot":"","sources":["../../src/util/plane-scan-exclude-dirs.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAI5G"}
@@ -0,0 +1,22 @@
1
+ import * as nodePath from 'node:path';
2
+ /**
3
+ * THE scan scope of every data-defined plane walk: the project-relative
4
+ * directories the walk prunes. Today that is the SharkCraft asset/config dir,
5
+ * when it sits inside the project. Its `.ts` files hold the rule definitions
6
+ * themselves, so a rule would otherwise match (and self-report) its own
7
+ * definition.
8
+ *
9
+ * Every verb and every aggregate that walks a plane reads it here: `policy-lint`,
10
+ * `check wiring`, `wiring explain | unprovided | orphans`, `registry …`,
11
+ * `baseline`, `generated`, `docs references`, `gates check | coverage | try`,
12
+ * `quality`, `finish`, `shrk gate` and MCP. Before round 11 each derived it
13
+ * inline, and several did not derive it at all. So `check wiring` and `gates
14
+ * check` (or `policy-lint` and `shrk gate`) walked different trees for the SAME
15
+ * rule, and one read 1 where the other read 0 ✓.
16
+ */
17
+ export function planeScanExcludeDirs(projectRoot, sharkcraftDir) {
18
+ if (!sharkcraftDir)
19
+ return [];
20
+ const rel = nodePath.relative(projectRoot, sharkcraftDir).split(nodePath.sep).join('/');
21
+ return rel && !rel.startsWith('..') ? [rel] : [];
22
+ }
@@ -0,0 +1,20 @@
1
+ import type { ISettledUnitLiveness, IUnitMark } from '@shrkcrft/core';
2
+ import type { IGlobListUnits } from './i-glob-list-units.js';
3
+ /**
4
+ * ONE glob list, judged off the one reader's positive walk
5
+ * (`readGlobListUnits`, memoised under `withFileReadCache`) and settled with
6
+ * its markers (`settleGlobLists`) — for an engine that selects files by a bare
7
+ * list and must decide its rule's emptiness: a registry's / idiom role's /
8
+ * extractor baseline's source `files`, a generated rule's `generatedGlob`, a
9
+ * doc-reference rule's `files` (pass its `dotDirsNamedBy`, so the walk is the
10
+ * one the rule reads). `marks` are the list's own (`list` equal to `list`).
11
+ */
12
+ export declare function readGlobListLiveness(projectRoot: string, list: string, globs: readonly string[], marks: readonly IUnitMark[], options?: {
13
+ readonly subject?: string;
14
+ readonly excludeDirs?: ReadonlySet<string>;
15
+ readonly allowDotDirs?: ReadonlySet<string>;
16
+ }): {
17
+ readonly units: IGlobListUnits;
18
+ readonly liveness: ISettledUnitLiveness;
19
+ };
20
+ //# sourceMappingURL=read-glob-list-liveness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-glob-list-liveness.d.ts","sourceRoot":"","sources":["../../src/util/read-glob-list-liveness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,OAAO,GAAE;IACP,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACxC,GACL;IAAE,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAA;CAAE,CAQ7E"}
@@ -0,0 +1,20 @@
1
+ import { readGlobListUnits } from "./dead-glob-units.js";
2
+ import { settleGlobLists } from "./settle-glob-lists.js";
3
+ /**
4
+ * ONE glob list, judged off the one reader's positive walk
5
+ * (`readGlobListUnits`, memoised under `withFileReadCache`) and settled with
6
+ * its markers (`settleGlobLists`) — for an engine that selects files by a bare
7
+ * list and must decide its rule's emptiness: a registry's / idiom role's /
8
+ * extractor baseline's source `files`, a generated rule's `generatedGlob`, a
9
+ * doc-reference rule's `files` (pass its `dotDirsNamedBy`, so the walk is the
10
+ * one the rule reads). `marks` are the list's own (`list` equal to `list`).
11
+ */
12
+ export function readGlobListLiveness(projectRoot, list, globs, marks, options = {}) {
13
+ const units = readGlobListUnits(projectRoot, globs, options.excludeDirs, options.allowDotDirs);
14
+ const liveness = settleGlobLists({
15
+ ...(options.subject !== undefined ? { subject: options.subject } : {}),
16
+ lists: [{ list, globs, units }],
17
+ marks: marks.filter((m) => m.list === list),
18
+ });
19
+ return { units, liveness };
20
+ }
@@ -0,0 +1,90 @@
1
+ import type { IVerdictCoverage } from '@shrkcrft/core';
2
+ import type { IMatchedFiles } from './matched-files.js';
3
+ import type { IReadScope } from './read-scope.js';
4
+ import type { IUnreadFile } from './unread-file.js';
5
+ /** How a coverage gap names a file the reader skipped for its size. */
6
+ export declare const READ_CAP_REASON = "over the 1MB read cap";
7
+ /** How a coverage gap names a regenerated file skipped for its size. */
8
+ export declare const REGEN_CAP_REASON = "over the 2MB regen read cap";
9
+ /** How a coverage gap names a directory the walk could not list. */
10
+ export declare const UNLISTABLE_DIR_REASON = "unlistable (a directory whose files were never enumerated)";
11
+ /** The one sentence naming why a set of files went unread. */
12
+ export declare function unreadReason(unread: readonly IUnreadFile[]): string;
13
+ /** True for the entry of a directory the walk could not list (its path ends in `/`). */
14
+ export declare function isUnreadDirectory(u: IUnreadFile): boolean;
15
+ /** Is project-relative `path` beneath the unlistable-directory entry `u`? */
16
+ export declare function unreadDirectoryContains(u: IUnreadFile, path: string): boolean;
17
+ /**
18
+ * THE "is this unread entry in front of these globs?" test, for every plane.
19
+ * A file: the list SELECTS its path (`globListSelects` — an inclusion glob
20
+ * matches, no negation does). A directory the walk could not list: an
21
+ * inclusion glob could match something beneath it (`globMayMatchUnder`) and no
22
+ * negation covers ALL of it (`globCoversAllUnder`) — its files were never
23
+ * enumerated, so no rule over that tree may claim it examined them, unless the
24
+ * list itself excludes every one of them.
25
+ */
26
+ export declare function unreadEntryMatches(u: IUnreadFile, globs: readonly string[]): boolean;
27
+ /**
28
+ * The exemption side of {@link unreadEntryMatches}: do `globs` take the WHOLE
29
+ * entry out of scope? A file: the list selects it. A directory: one inclusion
30
+ * glob covers everything beneath it (`globCoversAllUnder`) and no negation
31
+ * could carve part of it back out (`globMayMatchUnder`) — an exemption
32
+ * covering part of an unlistable directory leaves the rest a gap.
33
+ */
34
+ export declare function unreadEntryWhollyMatches(u: IUnreadFile, globs: readonly string[]): boolean;
35
+ /**
36
+ * The unread entries a selector's globs are in front of, optionally only those
37
+ * in a changeset. Sorted by path.
38
+ *
39
+ * Under a changeset, an unlistable directory contributes the CHANGED files
40
+ * beneath it that the globs match (each unreadable: its directory could not be
41
+ * listed) — the changeset is the scope, so the gap is named by file.
42
+ */
43
+ export declare function unreadMatching(unread: readonly IUnreadFile[], globs: readonly string[], changed?: ReadonlySet<string>): IUnreadFile[];
44
+ /**
45
+ * One rule's read scope over a read the caller already did: the read files
46
+ * its globs match, and the unread ones. `changed` narrows both to a changeset,
47
+ * which is deliberate narrowing and not a gap.
48
+ */
49
+ export declare function readScopeOf(matched: IMatchedFiles, globs: readonly string[], changed?: ReadonlySet<string>): IReadScope;
50
+ /** Merge several read scopes (a rule's sides), counting each unread path once. */
51
+ export declare function mergeReadScopes(read: number, scopes: readonly (readonly IUnreadFile[])[]): IReadScope;
52
+ /**
53
+ * The read scope of SEVERAL lists over one read (a registration idiom's three
54
+ * roles, every idiom of a graph): a file counts once when ANY list selects it,
55
+ * and each list's unread entries are taken from that list alone.
56
+ *
57
+ * Never flatten the lists into one and call {@link readScopeOf}: a negation
58
+ * subtracts from its own list only, and a flattened union would let one role's
59
+ * `!x` hide another role's file.
60
+ */
61
+ export declare function readScopeOfLists(matched: IMatchedFiles, lists: readonly (readonly string[])[], changed?: ReadonlySet<string>): IReadScope;
62
+ /**
63
+ * True when a rule's scope holds a file the reader did not read. Such a rule's
64
+ * zero is never "matched nothing": it matched a file it could not examine. So
65
+ * it is PARTIAL (2), never a `failOnEmpty` failure (1), and never a pass.
66
+ */
67
+ export declare function readScopeHasUnread(scope: IReadScope | undefined): boolean;
68
+ /**
69
+ * THE rule for folding the one reader's unread files into a rule's coverage.
70
+ *
71
+ * With no unread file in scope, the plane's own record is returned unchanged.
72
+ * Otherwise the rule examined `read` of the `read + unread` files its globs
73
+ * MATCHED: unit `files`, and each unread path named with its reason
74
+ * (`examined 1 of 2 files, 1 over the 1MB read cap: src/big.ts`).
75
+ *
76
+ * The file record REPLACES the plane's record rather than sitting next to it.
77
+ * Every count the plane derived (tokens, content units, entries, references)
78
+ * came from an incomplete read, so it misses whatever the unread file holds.
79
+ * CLAUDE.md's loud-skip rule applies: a number derived from an incomplete input
80
+ * is reported NOT VERIFIED, never as a number. Any acceptance on the plane
81
+ * record (`registeredExtras`, `expectEmpty`, `--allow-empty`) is dropped with
82
+ * it, because none of them accepted an unread file.
83
+ */
84
+ export declare function readScopeCoverage(plane: IVerdictCoverage, scope: IReadScope | undefined): IVerdictCoverage;
85
+ /**
86
+ * A short human clause for a rule's unread files, for skip reasons and hints:
87
+ * `1 matched file over the 1MB read cap (src/big.ts)`.
88
+ */
89
+ export declare function describeUnread(unread: readonly IUnreadFile[], shown?: number): string;
90
+ //# sourceMappingURL=read-scope-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-scope-coverage.d.ts","sourceRoot":"","sources":["../../src/util/read-scope-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOpD,uEAAuE;AACvE,eAAO,MAAM,eAAe,0BAA2D,CAAC;AAExF,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,gCAAkE,CAAC;AAEhG,oEAAoE;AACpE,eAAO,MAAM,qBAAqB,+DAA+D,CAAC;AAElG,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CASnE;AAED,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAIpF;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAI1F;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC5B,WAAW,EAAE,CAef;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC5B,UAAU,CAMZ;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,SAAS,WAAW,EAAE,CAAC,EAAE,GAAG,UAAU,CAIrG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,EACrC,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC5B,UAAU,CAOZ;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAEzE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,gBAAgB,CAa1G;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,SAAI,GAAG,MAAM,CAIhF"}
@@ -0,0 +1,173 @@
1
+ import { globCoversAllUnder, globListParts, globListSelects, globMayMatchUnder } from "../scan/glob.js";
2
+ import { UnreadFileReason } from "./unread-file-reason.js";
3
+ import { MAX_REGEN_FILE_BYTES, MAX_SCAN_FILE_BYTES } from "./walk-files.js";
4
+ /** At most this many unread paths ride on one coverage record. */
5
+ const UNREAD_LABEL_CAP = 20;
6
+ /** How a coverage gap names a file the reader skipped for its size. */
7
+ export const READ_CAP_REASON = `over the ${MAX_SCAN_FILE_BYTES / 1_000_000}MB read cap`;
8
+ /** How a coverage gap names a regenerated file skipped for its size. */
9
+ export const REGEN_CAP_REASON = `over the ${MAX_REGEN_FILE_BYTES / 1_000_000}MB regen read cap`;
10
+ /** How a coverage gap names a directory the walk could not list. */
11
+ export const UNLISTABLE_DIR_REASON = 'unlistable (a directory whose files were never enumerated)';
12
+ /** The one sentence naming why a set of files went unread. */
13
+ export function unreadReason(unread) {
14
+ const kinds = new Set(unread.map((u) => u.reason));
15
+ const parts = [
16
+ ...(kinds.has(UnreadFileReason.OverReadCap) ? [READ_CAP_REASON] : []),
17
+ ...(kinds.has(UnreadFileReason.OverRegenCap) ? [REGEN_CAP_REASON] : []),
18
+ ...(kinds.has(UnreadFileReason.Unreadable) ? ['unreadable'] : []),
19
+ ...(kinds.has(UnreadFileReason.UnreadableDirectory) ? [UNLISTABLE_DIR_REASON] : []),
20
+ ];
21
+ return parts.length > 0 ? parts.join(' or ') : READ_CAP_REASON;
22
+ }
23
+ /** True for the entry of a directory the walk could not list (its path ends in `/`). */
24
+ export function isUnreadDirectory(u) {
25
+ return u.reason === UnreadFileReason.UnreadableDirectory;
26
+ }
27
+ /** Is project-relative `path` beneath the unlistable-directory entry `u`? */
28
+ export function unreadDirectoryContains(u, path) {
29
+ return isUnreadDirectory(u) && (u.path === './' || path.startsWith(u.path));
30
+ }
31
+ /**
32
+ * THE "is this unread entry in front of these globs?" test, for every plane.
33
+ * A file: the list SELECTS its path (`globListSelects` — an inclusion glob
34
+ * matches, no negation does). A directory the walk could not list: an
35
+ * inclusion glob could match something beneath it (`globMayMatchUnder`) and no
36
+ * negation covers ALL of it (`globCoversAllUnder`) — its files were never
37
+ * enumerated, so no rule over that tree may claim it examined them, unless the
38
+ * list itself excludes every one of them.
39
+ */
40
+ export function unreadEntryMatches(u, globs) {
41
+ if (!isUnreadDirectory(u))
42
+ return globListSelects(u.path, globs);
43
+ const { include, exclude } = globListParts(globs);
44
+ return include.some((g) => globMayMatchUnder(g, u.path)) && !exclude.some((n) => globCoversAllUnder(n, u.path));
45
+ }
46
+ /**
47
+ * The exemption side of {@link unreadEntryMatches}: do `globs` take the WHOLE
48
+ * entry out of scope? A file: the list selects it. A directory: one inclusion
49
+ * glob covers everything beneath it (`globCoversAllUnder`) and no negation
50
+ * could carve part of it back out (`globMayMatchUnder`) — an exemption
51
+ * covering part of an unlistable directory leaves the rest a gap.
52
+ */
53
+ export function unreadEntryWhollyMatches(u, globs) {
54
+ if (!isUnreadDirectory(u))
55
+ return globListSelects(u.path, globs);
56
+ const { include, exclude } = globListParts(globs);
57
+ return include.some((g) => globCoversAllUnder(g, u.path)) && !exclude.some((n) => globMayMatchUnder(n, u.path));
58
+ }
59
+ /**
60
+ * The unread entries a selector's globs are in front of, optionally only those
61
+ * in a changeset. Sorted by path.
62
+ *
63
+ * Under a changeset, an unlistable directory contributes the CHANGED files
64
+ * beneath it that the globs match (each unreadable: its directory could not be
65
+ * listed) — the changeset is the scope, so the gap is named by file.
66
+ */
67
+ export function unreadMatching(unread, globs, changed) {
68
+ if (changed === undefined)
69
+ return unread.filter((u) => unreadEntryMatches(u, globs));
70
+ const out = new Map();
71
+ for (const u of unread) {
72
+ if (!isUnreadDirectory(u)) {
73
+ if (changed.has(u.path) && globListSelects(u.path, globs))
74
+ out.set(u.path, u);
75
+ continue;
76
+ }
77
+ for (const path of changed) {
78
+ if (!out.has(path) && unreadDirectoryContains(u, path) && globListSelects(path, globs)) {
79
+ out.set(path, { path, reason: UnreadFileReason.Unreadable });
80
+ }
81
+ }
82
+ }
83
+ return [...out.values()].sort((a, b) => a.path.localeCompare(b.path));
84
+ }
85
+ /**
86
+ * One rule's read scope over a read the caller already did: the read files
87
+ * its globs match, and the unread ones. `changed` narrows both to a changeset,
88
+ * which is deliberate narrowing and not a gap.
89
+ */
90
+ export function readScopeOf(matched, globs, changed) {
91
+ let read = 0;
92
+ for (const path of matched.files.keys()) {
93
+ if ((changed === undefined || changed.has(path)) && globListSelects(path, globs))
94
+ read += 1;
95
+ }
96
+ return { read, unread: unreadMatching(matched.unread, globs, changed) };
97
+ }
98
+ /** Merge several read scopes (a rule's sides), counting each unread path once. */
99
+ export function mergeReadScopes(read, scopes) {
100
+ const seen = new Map();
101
+ for (const list of scopes)
102
+ for (const u of list)
103
+ if (!seen.has(u.path))
104
+ seen.set(u.path, u);
105
+ return { read, unread: [...seen.values()].sort((a, b) => a.path.localeCompare(b.path)) };
106
+ }
107
+ /**
108
+ * The read scope of SEVERAL lists over one read (a registration idiom's three
109
+ * roles, every idiom of a graph): a file counts once when ANY list selects it,
110
+ * and each list's unread entries are taken from that list alone.
111
+ *
112
+ * Never flatten the lists into one and call {@link readScopeOf}: a negation
113
+ * subtracts from its own list only, and a flattened union would let one role's
114
+ * `!x` hide another role's file.
115
+ */
116
+ export function readScopeOfLists(matched, lists, changed) {
117
+ let read = 0;
118
+ for (const path of matched.files.keys()) {
119
+ if (changed !== undefined && !changed.has(path))
120
+ continue;
121
+ if (lists.some((l) => globListSelects(path, l)))
122
+ read += 1;
123
+ }
124
+ return mergeReadScopes(read, lists.map((l) => unreadMatching(matched.unread, l, changed)));
125
+ }
126
+ /**
127
+ * True when a rule's scope holds a file the reader did not read. Such a rule's
128
+ * zero is never "matched nothing": it matched a file it could not examine. So
129
+ * it is PARTIAL (2), never a `failOnEmpty` failure (1), and never a pass.
130
+ */
131
+ export function readScopeHasUnread(scope) {
132
+ return scope !== undefined && scope.unread.length > 0;
133
+ }
134
+ /**
135
+ * THE rule for folding the one reader's unread files into a rule's coverage.
136
+ *
137
+ * With no unread file in scope, the plane's own record is returned unchanged.
138
+ * Otherwise the rule examined `read` of the `read + unread` files its globs
139
+ * MATCHED: unit `files`, and each unread path named with its reason
140
+ * (`examined 1 of 2 files, 1 over the 1MB read cap: src/big.ts`).
141
+ *
142
+ * The file record REPLACES the plane's record rather than sitting next to it.
143
+ * Every count the plane derived (tokens, content units, entries, references)
144
+ * came from an incomplete read, so it misses whatever the unread file holds.
145
+ * CLAUDE.md's loud-skip rule applies: a number derived from an incomplete input
146
+ * is reported NOT VERIFIED, never as a number. Any acceptance on the plane
147
+ * record (`registeredExtras`, `expectEmpty`, `--allow-empty`) is dropped with
148
+ * it, because none of them accepted an unread file.
149
+ */
150
+ export function readScopeCoverage(plane, scope) {
151
+ if (scope === undefined || scope.unread.length === 0)
152
+ return plane;
153
+ const unread = scope.unread;
154
+ return {
155
+ unit: 'files',
156
+ expected: scope.read + unread.length,
157
+ examined: scope.read,
158
+ unexamined: unread.slice(0, UNREAD_LABEL_CAP).map((u) => u.path),
159
+ unexaminedTotal: unread.length,
160
+ reason: unreadReason(unread),
161
+ ...(plane.root !== undefined ? { root: plane.root } : {}),
162
+ ...(plane.subject !== undefined ? { subject: plane.subject } : {}),
163
+ };
164
+ }
165
+ /**
166
+ * A short human clause for a rule's unread files, for skip reasons and hints:
167
+ * `1 matched file over the 1MB read cap (src/big.ts)`.
168
+ */
169
+ export function describeUnread(unread, shown = 3) {
170
+ const names = unread.slice(0, shown).map((u) => u.path);
171
+ const more = unread.length > shown ? `, +${unread.length - shown} more` : '';
172
+ return `${unread.length} matched file(s) ${unreadReason(unread)} (${names.join(', ')}${more})`;
173
+ }