@mmnto/totem 1.118.1 → 1.120.0

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 (118) hide show
  1. package/dist/artifacts/admission.d.ts +12 -12
  2. package/dist/artifacts/verdict.d.ts +16 -16
  3. package/dist/compile-manifest.d.ts +89 -0
  4. package/dist/compile-manifest.d.ts.map +1 -1
  5. package/dist/compile-manifest.js +180 -31
  6. package/dist/compile-manifest.js.map +1 -1
  7. package/dist/compile-manifest.test.js +169 -1
  8. package/dist/compile-manifest.test.js.map +1 -1
  9. package/dist/compile-smoke-gate.d.ts +23 -0
  10. package/dist/compile-smoke-gate.d.ts.map +1 -1
  11. package/dist/compile-smoke-gate.js +139 -8
  12. package/dist/compile-smoke-gate.js.map +1 -1
  13. package/dist/compile-smoke-gate.test.js +431 -0
  14. package/dist/compile-smoke-gate.test.js.map +1 -1
  15. package/dist/compiler-schema.d.ts +1067 -0
  16. package/dist/compiler-schema.d.ts.map +1 -1
  17. package/dist/compiler-schema.js +204 -1
  18. package/dist/compiler-schema.js.map +1 -1
  19. package/dist/compiler-schema.test.js +92 -0
  20. package/dist/compiler-schema.test.js.map +1 -1
  21. package/dist/compiler.d.ts +2 -6
  22. package/dist/compiler.d.ts.map +1 -1
  23. package/dist/compiler.js +6 -17
  24. package/dist/compiler.js.map +1 -1
  25. package/dist/freeze.d.ts +3 -3
  26. package/dist/index.d.ts +12 -3
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +6 -2
  29. package/dist/index.js.map +1 -1
  30. package/dist/ledger.d.ts +12 -12
  31. package/dist/recurrence-stats.d.ts +10 -10
  32. package/dist/regex-safety/apply-rules-bounded.d.ts +11 -0
  33. package/dist/regex-safety/apply-rules-bounded.d.ts.map +1 -1
  34. package/dist/regex-safety/apply-rules-bounded.js +59 -5
  35. package/dist/regex-safety/apply-rules-bounded.js.map +1 -1
  36. package/dist/regex-validation.d.ts +7 -0
  37. package/dist/regex-validation.d.ts.map +1 -0
  38. package/dist/regex-validation.js +32 -0
  39. package/dist/regex-validation.js.map +1 -0
  40. package/dist/rule-engine.d.ts +11 -1
  41. package/dist/rule-engine.d.ts.map +1 -1
  42. package/dist/rule-engine.js +125 -18
  43. package/dist/rule-engine.js.map +1 -1
  44. package/dist/spine/authored-rule.d.ts +687 -263
  45. package/dist/spine/authored-rule.d.ts.map +1 -1
  46. package/dist/spine/authored-rule.js +113 -27
  47. package/dist/spine/authored-rule.js.map +1 -1
  48. package/dist/spine/authored-rule.test.js +170 -4
  49. package/dist/spine/authored-rule.test.js.map +1 -1
  50. package/dist/spine/authoring-ledger.d.ts +83 -24
  51. package/dist/spine/authoring-ledger.d.ts.map +1 -1
  52. package/dist/spine/authoring-ledger.js +46 -25
  53. package/dist/spine/authoring-ledger.js.map +1 -1
  54. package/dist/spine/authoring-ledger.test.js +20 -7
  55. package/dist/spine/authoring-ledger.test.js.map +1 -1
  56. package/dist/spine/candidate-rule.d.ts +18 -1
  57. package/dist/spine/candidate-rule.d.ts.map +1 -1
  58. package/dist/spine/candidate-rule.js.map +1 -1
  59. package/dist/spine/cert-corpus-seed.d.ts +14 -14
  60. package/dist/spine/compile.d.ts +4 -1
  61. package/dist/spine/compile.d.ts.map +1 -1
  62. package/dist/spine/compile.js +50 -3
  63. package/dist/spine/compile.js.map +1 -1
  64. package/dist/spine/compile.test.js +189 -23
  65. package/dist/spine/compile.test.js.map +1 -1
  66. package/dist/spine/frozen-split.d.ts +18 -18
  67. package/dist/spine/ledgers.d.ts +62 -62
  68. package/dist/spine/lf-normalize.d.ts +9 -0
  69. package/dist/spine/lf-normalize.d.ts.map +1 -0
  70. package/dist/spine/lf-normalize.js +38 -0
  71. package/dist/spine/lf-normalize.js.map +1 -0
  72. package/dist/spine/preimage-differential.d.ts +3 -1
  73. package/dist/spine/preimage-differential.d.ts.map +1 -1
  74. package/dist/spine/preimage-differential.js +9 -6
  75. package/dist/spine/preimage-differential.js.map +1 -1
  76. package/dist/spine/preimage-differential.test.js +59 -0
  77. package/dist/spine/preimage-differential.test.js.map +1 -1
  78. package/dist/spine/record-exemplars.fixture.d.ts +5 -0
  79. package/dist/spine/record-exemplars.fixture.d.ts.map +1 -0
  80. package/dist/spine/record-exemplars.fixture.js +61 -0
  81. package/dist/spine/record-exemplars.fixture.js.map +1 -0
  82. package/dist/spine/record-lower.d.ts +90 -0
  83. package/dist/spine/record-lower.d.ts.map +1 -0
  84. package/dist/spine/record-lower.js +418 -0
  85. package/dist/spine/record-lower.js.map +1 -0
  86. package/dist/spine/record-lower.test.d.ts +2 -0
  87. package/dist/spine/record-lower.test.d.ts.map +1 -0
  88. package/dist/spine/record-lower.test.js +562 -0
  89. package/dist/spine/record-lower.test.js.map +1 -0
  90. package/dist/spine/record-runtime.d.ts +215 -0
  91. package/dist/spine/record-runtime.d.ts.map +1 -0
  92. package/dist/spine/record-runtime.js +330 -0
  93. package/dist/spine/record-runtime.js.map +1 -0
  94. package/dist/spine/record-runtime.test.d.ts +2 -0
  95. package/dist/spine/record-runtime.test.d.ts.map +1 -0
  96. package/dist/spine/record-runtime.test.js +814 -0
  97. package/dist/spine/record-runtime.test.js.map +1 -0
  98. package/dist/spine/rule-record.d.ts +864 -0
  99. package/dist/spine/rule-record.d.ts.map +1 -0
  100. package/dist/spine/rule-record.js +781 -0
  101. package/dist/spine/rule-record.js.map +1 -0
  102. package/dist/spine/rule-record.test.d.ts +2 -0
  103. package/dist/spine/rule-record.test.d.ts.map +1 -0
  104. package/dist/spine/rule-record.test.js +1262 -0
  105. package/dist/spine/rule-record.test.js.map +1 -0
  106. package/dist/spine/windtunnel-firing.d.ts.map +1 -1
  107. package/dist/spine/windtunnel-firing.js +28 -1
  108. package/dist/spine/windtunnel-firing.js.map +1 -1
  109. package/dist/spine/windtunnel-lock.d.ts +26 -26
  110. package/dist/stage4-verifier.d.ts.map +1 -1
  111. package/dist/stage4-verifier.js +61 -17
  112. package/dist/stage4-verifier.js.map +1 -1
  113. package/dist/sys/glob.d.ts +30 -0
  114. package/dist/sys/glob.d.ts.map +1 -1
  115. package/dist/sys/glob.js +62 -1
  116. package/dist/sys/glob.js.map +1 -1
  117. package/dist/types.d.ts +1 -1
  118. package/package.json +1 -1
@@ -0,0 +1,215 @@
1
+ import type { CompiledRule } from '../compiler-schema.js';
2
+ /**
3
+ * The Prop 310 compiled homes, as a runtime-readable list. A rule carrying ANY
4
+ * of them came from the record path; a rule carrying NONE is legacy.
5
+ *
6
+ * DERIVED, never mirrored (Tenet 20): the discriminator is the presence of the
7
+ * § Design 12 homes themselves, not a separate `dialect:` marker field — a marker
8
+ * would be a second, drift-capable statement of what the fields already say, and
9
+ * § Design 4's key space is closed against inventing one.
10
+ *
11
+ * The derivation is SAFE because nothing but the record path can write these
12
+ * fields: they were added to `CompiledRuleSchema` by this slice, and the only
13
+ * other writer (the legacy lesson-compile actuator) is under the standing
14
+ * `rule-compilation` freeze and does not regenerate. The byte-identity guard
15
+ * asserts the empirical half — zero of the shipped 485 rules carries any of them.
16
+ */
17
+ export declare const RECORD_COMPILED_HOME_KEYS: readonly ["excludeGlobs", "requires", "examples", "language", "verificationShadow", "recoveryHint", "curation"];
18
+ /**
19
+ * True when a compiled rule came from the Prop 310 record path — i.e. when the
20
+ * § Design 7 dialect and its two-array scope rule apply to it. False for every
21
+ * legacy/mined rule, which keeps the shipped matcher unchanged.
22
+ *
23
+ * The test is `examples !== undefined` ALONE, deliberately narrower than "any
24
+ * § Design 12 home" (falsification round, 2026-08-21). `examples` is the EXACT
25
+ * discriminator, not merely a sufficient one:
26
+ * - § Design 5 makes it non-omittable and min-1, so EVERY record carries it;
27
+ * - `compileRuleRecord` emits it unconditionally, so every lowered rule has it;
28
+ * - it is the one home no other construct can be present without.
29
+ *
30
+ * The wider seven-key disjunction was demonstrated to be UNSAFE in the other
31
+ * direction: a legacy-shaped rule that ever gained just a `recoveryHint` — a
32
+ * plausible future addition to the mined path, and a purely cosmetic one — would
33
+ * silently flip from the shipped tree-wide matcher to the record dialect's
34
+ * root-only reading of the same `*.ts` glob, narrowing its scope with no signal.
35
+ * Keying on the field that MEANS "this is a record" removes that coupling
36
+ * between a diagnostic field and a matcher semantic.
37
+ *
38
+ * `RECORD_COMPILED_HOME_KEYS` remains the full home set for the corpus guard
39
+ * (which asserts the legacy manifest carries NONE of them — a stronger claim
40
+ * than this predicate needs, and the right one for a freeze check).
41
+ */
42
+ export declare function isRecordPathRule(rule: Pick<CompiledRule, 'examples'>): boolean;
43
+ /**
44
+ * The exemplar lines a rule's Stage-4 / doctor readers treat as "the authored bad
45
+ * example", single-homed (Tenet 20).
46
+ *
47
+ * Prop 310 slice 3 discharges the slice-2 pin: a record rule's authored preimages
48
+ * live in `examples[i].bad` — the § Design 10 EDITABLE home — and are NOT mirrored
49
+ * onto the legacy `badExample` field, so every reader of that field would otherwise
50
+ * read a record rule as having no bad example at all. Both shipped readers now ask
51
+ * this one function instead:
52
+ *
53
+ * - Stage 4 (`lineMatchesBadExample`) — a matched line equal to any of these
54
+ * classifies `in-scope-bad-example` rather than `candidate-debt`;
55
+ * - `totem doctor`'s `no-badExample` grandfathering reason — an empty result is
56
+ * exactly the old `!badExample || badExample.trim() === ''` test.
57
+ *
58
+ * BLANK LINES ARE DROPPED, which is what makes both of those byte-identical for
59
+ * legacy rules: Stage 4 already refuses to match an empty trimmed line, and a
60
+ * whitespace-only `badExample` must still count as absent for doctor.
61
+ */
62
+ export declare function ruleBadExampleLines(rule: Pick<CompiledRule, 'badExample' | 'examples'>): string[];
63
+ /**
64
+ * The POSTIMAGE twin of `ruleBadExampleLines`, on the identical contract: record
65
+ * path → every `examples[i].good`, legacy → `goodExample`, blank lines dropped.
66
+ *
67
+ * `totem doctor`'s `no-goodExample` grandfathering reason reads it. Without the
68
+ * twin, a record rule cleared the `no-badExample` reason and then tripped
69
+ * `no-goodExample` on the very same absence — the record's exemplars live in
70
+ * `examples`, and § Design 10 makes that the editable home for BOTH sides of the
71
+ * pair, so a reader that single-homes one side and not the other reports half a
72
+ * truth. An empty result is exactly the old
73
+ * `!goodExample || goodExample.trim() === ''` test.
74
+ */
75
+ export declare function ruleGoodExampleLines(rule: Pick<CompiledRule, 'goodExample' | 'examples'>): string[];
76
+ /**
77
+ * Exactly the fields the scope predicates READ. Structural rather than the whole
78
+ * `CompiledRule` so callers holding a projection — Stage 4's `classifyFile`
79
+ * takes one — pass it directly instead of casting a narrower object back to the
80
+ * full type. A cast there would have been a lie the compiler stopped checking.
81
+ */
82
+ export type RuleScopeFields = Pick<CompiledRule, 'fileGlobs' | 'excludeGlobs' | 'examples'>;
83
+ /**
84
+ * § Design 12 — fail loud on a TORN record rule: one carrying a Prop 310
85
+ * compiled home but no `examples`.
86
+ *
87
+ * Such a rule cannot exist downstream of the lowering (`examples` is min-1 at
88
+ * parse and emitted unconditionally) nor in the frozen legacy corpus (which
89
+ * carries no home at all), so this only ever catches a hand-edited manifest or
90
+ * a bypassed producer. Left unchecked, `isRecordPathRule` would class it LEGACY
91
+ * and its `excludeGlobs` and `requires` would be dropped — a scope silently
92
+ * widened and a requirement silently unevaluated. The drop direction is toward
93
+ * flagging, so it is not fail-open, but § Design 12's ban on accepting a
94
+ * construct and dropping it is a ban on the SILENCE, not just on the unsafe
95
+ * direction. Fail loud instead.
96
+ *
97
+ * Called at dispatcher altitude, once per invocation — the same altitude as the
98
+ * tree-sitter `requires` backstop, and for the same reason: a per-file check
99
+ * would re-report the same defect on every file.
100
+ */
101
+ export declare function assertNoTornRecordRules(rules: readonly CompiledRule[]): void;
102
+ /**
103
+ * § Design 7 — the two-array scope rule for a RECORD-path rule:
104
+ * `positiveMatch && !excludeMatch`, both arrays evaluated under the normative
105
+ * dialect (`matchesRecordGlob`).
106
+ *
107
+ * `fileGlobs` is min-1 at parse, so the empty-positive case cannot arise from a
108
+ * well-formed record; a hand-edited manifest that produced one would match
109
+ * NOTHING here rather than everything. That direction is deliberate — the
110
+ * shipped `fileMatchesGlobs` treats "no positive globs" as include-everything,
111
+ * which for a record-path rule would silently WIDEN a scope the grammar requires
112
+ * to be declared. A rule that fires nowhere is visible; one that fires everywhere
113
+ * is the fail-open class § Design 2 exists to kill.
114
+ */
115
+ export declare function recordScopeMatchesFile(filePath: string, fileGlobs: readonly string[] | undefined, excludeGlobs: readonly string[] | undefined): boolean;
116
+ /**
117
+ * The ONE scope predicate the rule engine asks: does this rule apply to this
118
+ * file? Record-path rules get § Design 7's dialect + two-array rule; every other
119
+ * rule gets the shipped `fileMatchesGlobs` predicate verbatim, including its
120
+ * "unscoped rule applies to everything" behaviour.
121
+ *
122
+ * Single-homed on purpose: the engine had four separate copies of the
123
+ * `rule.fileGlobs && rule.fileGlobs.length > 0 ? fileMatchesGlobs(...) : true`
124
+ * expression, and four copies of a dialect fork is how a rule silently gets one
125
+ * scope at dispatch and a different one at the fail-loud guard.
126
+ */
127
+ export declare function ruleAppliesToFile(rule: RuleScopeFields, filePath: string): boolean;
128
+ /**
129
+ * The text the § Design 8 scopes resolve to at a target-match locus L.
130
+ *
131
+ * `file` is a LAZY resolver returning `null` when the file cannot be read: the
132
+ * caller owns the IO, a `line`-scope requirement never triggers a read, and the
133
+ * unreadable case gets an explicit, documented direction below rather than an
134
+ * exception thrown from inside a matcher.
135
+ */
136
+ export interface RequiresScopeText {
137
+ /** L's line — the text of the line the target matched on. */
138
+ line: string;
139
+ /** The whole file containing L, or `null` when unreadable. */
140
+ file: () => string | null;
141
+ }
142
+ /**
143
+ * Path-containment check for the § Design 8 whole-file readers.
144
+ *
145
+ * The paths handed to these readers come out of a DIFF, which is
146
+ * attacker-shaped input on any path where a lint runs over untrusted contributed
147
+ * changes — a `../` prefix would otherwise read outside the repo. `path.relative`
148
+ * rather than `startsWith` on purpose: the string form admits the
149
+ * sibling-directory bypass (`/app-secrets` passing a `/app` prefix test), which
150
+ * is the same reasoning the shipped ast-path containment checks use.
151
+ *
152
+ * An out-of-root path is reported as UNREADABLE, not as an error: the requirement
153
+ * is then unmet and the rule FIRES, preserving the fail-toward-flagging direction
154
+ * every other read failure on this path takes.
155
+ */
156
+ export declare function isInsideRoot(root: string, filePath: string): boolean;
157
+ /**
158
+ * § Design 8 — gate every `requires.pattern` in the loaded manifest through the
159
+ * SAME safe-regex2 check the compile path applies, before any of them is
160
+ * evaluated.
161
+ *
162
+ * The lowering already gates this, so a pattern reaching here unsafe means the
163
+ * manifest was hand-edited or written by a bypassed producer. Left ungated it is
164
+ * a live ReDoS surface, and a worse one than the target pattern's: at
165
+ * `scope: file` the requirement runs against WHOLE-FILE text, unbounded, on the
166
+ * pre-commit path. `validateRegex` is the same single-homed check the compile
167
+ * gate uses, so the two can never drift.
168
+ *
169
+ * Dispatcher altitude, once per invocation — the same reason as
170
+ * `assertNoTornRecordRules`: a per-match check would re-report one defect on
171
+ * every line of every file.
172
+ */
173
+ export declare function assertRequiresPatternsSafe(rules: readonly CompiledRule[]): void;
174
+ /**
175
+ * § Design 8 — fail loud on `ast-grep` + `requires.scope: line`.
176
+ *
177
+ * The lowering REJECTS this combination (an ast-grep match is a span, and
178
+ * reading it as its start line makes the verdict depend on source formatting),
179
+ * but a hand-edited manifest can still carry it, and the ast dispatcher would
180
+ * silently evaluate `match.lineText` — shipping exactly the formatting-dependent
181
+ * semantic the ruling refused. Mirrors the tree-sitter `requires` backstop:
182
+ * unreachable from any compiled record, unreachable from the legacy corpus,
183
+ * live only against a torn manifest.
184
+ */
185
+ export declare function assertNoAstGrepLineScope(rules: readonly CompiledRule[]): void;
186
+ /**
187
+ * § Design 8 — pass two. Returns TRUE when the required context is PRESENT
188
+ * within the declared scope containing L, i.e. when the rule must stay SILENT.
189
+ * The rule fires iff the target matched AND this returns false.
190
+ *
191
+ * `requires.pattern` is a regex evaluated TEXTUALLY, independent of the target
192
+ * engine — "the requirement is a context check, not a second matcher" — so an
193
+ * ast-grep rule's requirement is still a plain regex over the scope text.
194
+ *
195
+ * UNREADABLE FILE (`scope: file` only) ⇒ context ABSENT ⇒ the rule FIRES. The
196
+ * safe direction for a lint gate is toward flagging: a false positive is visible
197
+ * and disputable, a suppressed real violation is silent. This mirrors the shipped
198
+ * Rust test-span exemption, whose read failure likewise yields no exemption
199
+ * (`rule-engine.ts`: "the exemption fails toward flagging, never toward
200
+ * suppression").
201
+ *
202
+ * A `requires.pattern` that will not compile means the compile-stage gate was
203
+ * bypassed or the manifest was hand-edited. Fail LOUD — the same treatment
204
+ * `applyRulesToAdditions` gives an uncompilable `rule.pattern`; silently treating
205
+ * it as "context absent" would turn a broken rule into a firing one.
206
+ */
207
+ export declare function requiresContextPresent(rule: CompiledRule, requires: NonNullable<CompiledRule['requires']>, text: RequiresScopeText): boolean;
208
+ /**
209
+ * § Design 8 applied to one target match: TRUE when the rule must stay silent at
210
+ * this locus because its required context is present. Always false for a rule
211
+ * carrying no `requires` block — which is every legacy rule, so the shipped
212
+ * evaluation path is untouched by construction.
213
+ */
214
+ export declare function requiresSuppressesMatch(rule: CompiledRule, text: RequiresScopeText): boolean;
215
+ //# sourceMappingURL=record-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-runtime.d.ts","sourceRoot":"","sources":["../../src/spine/record-runtime.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAK1D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB,iHAQc,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG,OAAO,CAE9E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,UAAU,CAAC,GAAG,MAAM,EAAE,CAIjG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,GAAG,UAAU,CAAC,GACnD,MAAM,EAAE,CAMV;AAkBD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,cAAc,GAAG,UAAU,CAAC,CAAC;AAE5F;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CAY5E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACxC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAC1C,OAAO,CAKT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQlF;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,IAAI,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CAC3B;AAUD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIpE;AAUD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CAU/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CAS7E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,WAAW,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,EAC/C,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAqBT;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAG5F"}
@@ -0,0 +1,330 @@
1
+ // ─── Prop 310 V1 record grammar — RUNTIME EVALUATION (slice 2 of the V1 build) ─
2
+ //
3
+ // The § Design 12 lowering says what a construct BECOMES on the compiled rule;
4
+ // this module says what it MEANS when `totem lint` runs. Two constructs need a
5
+ // runtime semantic the shipped engine does not have:
6
+ //
7
+ // § Design 7 — the two-array scope rule. A file is in scope iff it matches some
8
+ // `fileGlobs` entry AND matches no `excludeGlobs` entry. Exclusion
9
+ // is STRUCTURAL: entries are positive-form globs applied as
10
+ // exclusions, never `!`-negation (a parse error in the grammar).
11
+ // § Design 8 — the `requires:` two-pass. On a target match at locus L the rule
12
+ // FIRES iff `requires.pattern` does NOT match within the declared
13
+ // scope containing L.
14
+ //
15
+ // FREEZE / NON-REGRESSION BOUNDARY (binding): the legacy corpus (485 compiled
16
+ // rules, `.totem/compiled-rules.json`, frozen by `rule-compilation`) must keep
17
+ // matching BYTE-FOR-BYTE. Every function here is therefore gated on
18
+ // `isRecordPathRule`, and the mined-path byte-identity guard in
19
+ // `record-lower.test.ts` proves zero of the 485 legacy rules can reach a record
20
+ // branch. Nothing in this module runs `sanitizeFileGlobs`, and it never imports
21
+ // the frozen actuator.
22
+ //
23
+ // Purity: no IO, no clock, no module state. The `requires` evaluator takes the
24
+ // file text as a LAZY resolver so the caller owns the read (and so a `line`-scope
25
+ // requirement never forces one).
26
+ import * as path from 'node:path';
27
+ import { TotemParseError } from '../errors.js';
28
+ import { validateRegex } from '../regex-validation.js';
29
+ import { BoundedRegexCache, fileMatchesGlobs, matchesRecordGlob } from '../sys/glob.js';
30
+ /**
31
+ * The Prop 310 compiled homes, as a runtime-readable list. A rule carrying ANY
32
+ * of them came from the record path; a rule carrying NONE is legacy.
33
+ *
34
+ * DERIVED, never mirrored (Tenet 20): the discriminator is the presence of the
35
+ * § Design 12 homes themselves, not a separate `dialect:` marker field — a marker
36
+ * would be a second, drift-capable statement of what the fields already say, and
37
+ * § Design 4's key space is closed against inventing one.
38
+ *
39
+ * The derivation is SAFE because nothing but the record path can write these
40
+ * fields: they were added to `CompiledRuleSchema` by this slice, and the only
41
+ * other writer (the legacy lesson-compile actuator) is under the standing
42
+ * `rule-compilation` freeze and does not regenerate. The byte-identity guard
43
+ * asserts the empirical half — zero of the shipped 485 rules carries any of them.
44
+ */
45
+ export const RECORD_COMPILED_HOME_KEYS = [
46
+ 'excludeGlobs',
47
+ 'requires',
48
+ 'examples',
49
+ 'language',
50
+ 'verificationShadow',
51
+ 'recoveryHint',
52
+ 'curation',
53
+ ];
54
+ /**
55
+ * True when a compiled rule came from the Prop 310 record path — i.e. when the
56
+ * § Design 7 dialect and its two-array scope rule apply to it. False for every
57
+ * legacy/mined rule, which keeps the shipped matcher unchanged.
58
+ *
59
+ * The test is `examples !== undefined` ALONE, deliberately narrower than "any
60
+ * § Design 12 home" (falsification round, 2026-08-21). `examples` is the EXACT
61
+ * discriminator, not merely a sufficient one:
62
+ * - § Design 5 makes it non-omittable and min-1, so EVERY record carries it;
63
+ * - `compileRuleRecord` emits it unconditionally, so every lowered rule has it;
64
+ * - it is the one home no other construct can be present without.
65
+ *
66
+ * The wider seven-key disjunction was demonstrated to be UNSAFE in the other
67
+ * direction: a legacy-shaped rule that ever gained just a `recoveryHint` — a
68
+ * plausible future addition to the mined path, and a purely cosmetic one — would
69
+ * silently flip from the shipped tree-wide matcher to the record dialect's
70
+ * root-only reading of the same `*.ts` glob, narrowing its scope with no signal.
71
+ * Keying on the field that MEANS "this is a record" removes that coupling
72
+ * between a diagnostic field and a matcher semantic.
73
+ *
74
+ * `RECORD_COMPILED_HOME_KEYS` remains the full home set for the corpus guard
75
+ * (which asserts the legacy manifest carries NONE of them — a stronger claim
76
+ * than this predicate needs, and the right one for a freeze check).
77
+ */
78
+ export function isRecordPathRule(rule) {
79
+ return rule.examples !== undefined;
80
+ }
81
+ /**
82
+ * The exemplar lines a rule's Stage-4 / doctor readers treat as "the authored bad
83
+ * example", single-homed (Tenet 20).
84
+ *
85
+ * Prop 310 slice 3 discharges the slice-2 pin: a record rule's authored preimages
86
+ * live in `examples[i].bad` — the § Design 10 EDITABLE home — and are NOT mirrored
87
+ * onto the legacy `badExample` field, so every reader of that field would otherwise
88
+ * read a record rule as having no bad example at all. Both shipped readers now ask
89
+ * this one function instead:
90
+ *
91
+ * - Stage 4 (`lineMatchesBadExample`) — a matched line equal to any of these
92
+ * classifies `in-scope-bad-example` rather than `candidate-debt`;
93
+ * - `totem doctor`'s `no-badExample` grandfathering reason — an empty result is
94
+ * exactly the old `!badExample || badExample.trim() === ''` test.
95
+ *
96
+ * BLANK LINES ARE DROPPED, which is what makes both of those byte-identical for
97
+ * legacy rules: Stage 4 already refuses to match an empty trimmed line, and a
98
+ * whitespace-only `badExample` must still count as absent for doctor.
99
+ */
100
+ export function ruleBadExampleLines(rule) {
101
+ return exemplarLines(isRecordPathRule(rule) ? (rule.examples ?? []).map((e) => e.bad) : maybeBlock(rule.badExample));
102
+ }
103
+ /**
104
+ * The POSTIMAGE twin of `ruleBadExampleLines`, on the identical contract: record
105
+ * path → every `examples[i].good`, legacy → `goodExample`, blank lines dropped.
106
+ *
107
+ * `totem doctor`'s `no-goodExample` grandfathering reason reads it. Without the
108
+ * twin, a record rule cleared the `no-badExample` reason and then tripped
109
+ * `no-goodExample` on the very same absence — the record's exemplars live in
110
+ * `examples`, and § Design 10 makes that the editable home for BOTH sides of the
111
+ * pair, so a reader that single-homes one side and not the other reports half a
112
+ * truth. An empty result is exactly the old
113
+ * `!goodExample || goodExample.trim() === ''` test.
114
+ */
115
+ export function ruleGoodExampleLines(rule) {
116
+ return exemplarLines(isRecordPathRule(rule)
117
+ ? (rule.examples ?? []).map((e) => e.good)
118
+ : maybeBlock(rule.goodExample));
119
+ }
120
+ /** A present block as a one-element list, an absent one as none. */
121
+ function maybeBlock(block) {
122
+ return block === undefined ? [] : [block];
123
+ }
124
+ /**
125
+ * Split exemplar blocks into lines and DROP the blank ones — the shared half of
126
+ * both readers' contract. The blank-drop is what makes them byte-identical to the
127
+ * shipped behaviour for legacy rules: Stage 4 already refuses to match an empty
128
+ * trimmed line, and a whitespace-only exemplar field must still count as absent
129
+ * for doctor.
130
+ */
131
+ function exemplarLines(blocks) {
132
+ return blocks.flatMap((block) => block.split(/\r?\n/)).filter((line) => line.trim().length > 0);
133
+ }
134
+ /**
135
+ * § Design 12 — fail loud on a TORN record rule: one carrying a Prop 310
136
+ * compiled home but no `examples`.
137
+ *
138
+ * Such a rule cannot exist downstream of the lowering (`examples` is min-1 at
139
+ * parse and emitted unconditionally) nor in the frozen legacy corpus (which
140
+ * carries no home at all), so this only ever catches a hand-edited manifest or
141
+ * a bypassed producer. Left unchecked, `isRecordPathRule` would class it LEGACY
142
+ * and its `excludeGlobs` and `requires` would be dropped — a scope silently
143
+ * widened and a requirement silently unevaluated. The drop direction is toward
144
+ * flagging, so it is not fail-open, but § Design 12's ban on accepting a
145
+ * construct and dropping it is a ban on the SILENCE, not just on the unsafe
146
+ * direction. Fail loud instead.
147
+ *
148
+ * Called at dispatcher altitude, once per invocation — the same altitude as the
149
+ * tree-sitter `requires` backstop, and for the same reason: a per-file check
150
+ * would re-report the same defect on every file.
151
+ */
152
+ export function assertNoTornRecordRules(rules) {
153
+ const torn = rules.find((rule) => rule.examples === undefined &&
154
+ RECORD_COMPILED_HOME_KEYS.some((key) => rule[key] !== undefined));
155
+ if (torn === undefined)
156
+ return;
157
+ const homes = RECORD_COMPILED_HOME_KEYS.filter((key) => torn[key] !== undefined);
158
+ throw new TotemParseError(`Rule ${torn.lessonHash} (${torn.lessonHeading}) carries Prop 310 record field(s) [${homes.join(', ')}] but no \`examples\`, so it can be classified neither as a record rule nor as a legacy one.`, 'Prop 310 § Design 5 makes `examples` non-omittable and the record lowering always emits it, so this manifest was hand-edited or written by a bypassed producer. Re-compile the rule record. Left unclassified, its `excludeGlobs` would be ignored and its `requires` never evaluated.');
159
+ }
160
+ /**
161
+ * § Design 7 — the two-array scope rule for a RECORD-path rule:
162
+ * `positiveMatch && !excludeMatch`, both arrays evaluated under the normative
163
+ * dialect (`matchesRecordGlob`).
164
+ *
165
+ * `fileGlobs` is min-1 at parse, so the empty-positive case cannot arise from a
166
+ * well-formed record; a hand-edited manifest that produced one would match
167
+ * NOTHING here rather than everything. That direction is deliberate — the
168
+ * shipped `fileMatchesGlobs` treats "no positive globs" as include-everything,
169
+ * which for a record-path rule would silently WIDEN a scope the grammar requires
170
+ * to be declared. A rule that fires nowhere is visible; one that fires everywhere
171
+ * is the fail-open class § Design 2 exists to kill.
172
+ */
173
+ export function recordScopeMatchesFile(filePath, fileGlobs, excludeGlobs) {
174
+ const positives = fileGlobs ?? [];
175
+ if (!positives.some((glob) => matchesRecordGlob(filePath, glob)))
176
+ return false;
177
+ if (excludeGlobs === undefined)
178
+ return true;
179
+ return !excludeGlobs.some((glob) => matchesRecordGlob(filePath, glob));
180
+ }
181
+ /**
182
+ * The ONE scope predicate the rule engine asks: does this rule apply to this
183
+ * file? Record-path rules get § Design 7's dialect + two-array rule; every other
184
+ * rule gets the shipped `fileMatchesGlobs` predicate verbatim, including its
185
+ * "unscoped rule applies to everything" behaviour.
186
+ *
187
+ * Single-homed on purpose: the engine had four separate copies of the
188
+ * `rule.fileGlobs && rule.fileGlobs.length > 0 ? fileMatchesGlobs(...) : true`
189
+ * expression, and four copies of a dialect fork is how a rule silently gets one
190
+ * scope at dispatch and a different one at the fail-loud guard.
191
+ */
192
+ export function ruleAppliesToFile(rule, filePath) {
193
+ if (isRecordPathRule(rule)) {
194
+ return recordScopeMatchesFile(filePath, rule.fileGlobs, rule.excludeGlobs);
195
+ }
196
+ if (rule.fileGlobs && rule.fileGlobs.length > 0) {
197
+ return fileMatchesGlobs(filePath, rule.fileGlobs);
198
+ }
199
+ return true;
200
+ }
201
+ /**
202
+ * Compiled `requires.pattern` instances, LRU-bounded and shared across every
203
+ * dispatcher. Keyed by pattern SOURCE, so two rules declaring the same
204
+ * requirement share one compilation and a manifest reload does not leak.
205
+ */
206
+ const REQUIRES_PATTERN_CACHE_CAPACITY = 512;
207
+ const requiresPatternCache = new BoundedRegexCache(REQUIRES_PATTERN_CACHE_CAPACITY);
208
+ /**
209
+ * Path-containment check for the § Design 8 whole-file readers.
210
+ *
211
+ * The paths handed to these readers come out of a DIFF, which is
212
+ * attacker-shaped input on any path where a lint runs over untrusted contributed
213
+ * changes — a `../` prefix would otherwise read outside the repo. `path.relative`
214
+ * rather than `startsWith` on purpose: the string form admits the
215
+ * sibling-directory bypass (`/app-secrets` passing a `/app` prefix test), which
216
+ * is the same reasoning the shipped ast-path containment checks use.
217
+ *
218
+ * An out-of-root path is reported as UNREADABLE, not as an error: the requirement
219
+ * is then unmet and the rule FIRES, preserving the fail-toward-flagging direction
220
+ * every other read failure on this path takes.
221
+ */
222
+ export function isInsideRoot(root, filePath) {
223
+ const resolvedRoot = path.resolve(root);
224
+ const relative = path.relative(resolvedRoot, path.resolve(resolvedRoot, filePath));
225
+ return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);
226
+ }
227
+ /** Evaluate a compiled requirement against the declared § Design 8 scope. */
228
+ function matchesInScope(re, scope, text) {
229
+ if (scope === 'line')
230
+ return re.test(text.line);
231
+ const fileText = text.file();
232
+ if (fileText === null)
233
+ return false;
234
+ return re.test(fileText);
235
+ }
236
+ /**
237
+ * § Design 8 — gate every `requires.pattern` in the loaded manifest through the
238
+ * SAME safe-regex2 check the compile path applies, before any of them is
239
+ * evaluated.
240
+ *
241
+ * The lowering already gates this, so a pattern reaching here unsafe means the
242
+ * manifest was hand-edited or written by a bypassed producer. Left ungated it is
243
+ * a live ReDoS surface, and a worse one than the target pattern's: at
244
+ * `scope: file` the requirement runs against WHOLE-FILE text, unbounded, on the
245
+ * pre-commit path. `validateRegex` is the same single-homed check the compile
246
+ * gate uses, so the two can never drift.
247
+ *
248
+ * Dispatcher altitude, once per invocation — the same reason as
249
+ * `assertNoTornRecordRules`: a per-match check would re-report one defect on
250
+ * every line of every file.
251
+ */
252
+ export function assertRequiresPatternsSafe(rules) {
253
+ for (const rule of rules) {
254
+ if (rule.requires === undefined)
255
+ continue;
256
+ const validation = validateRegex(rule.requires.pattern);
257
+ if (validation.valid)
258
+ continue;
259
+ throw new TotemParseError(`Rule ${rule.lessonHash} (${rule.lessonHeading}) has an unusable \`requires.pattern\` (${validation.reason ?? 'invalid regex pattern'}) and cannot be evaluated (Prop 310 § Design 8).`, `The record-path compile gate runs this exact safe-regex2 check, so a pattern that fails it here means the manifest was hand-edited or written by a bypassed producer. Re-compile the rule record. At \`scope: file\` this pattern would run against whole-file text. Pattern: ${JSON.stringify(rule.requires.pattern)}`);
260
+ }
261
+ }
262
+ /**
263
+ * § Design 8 — fail loud on `ast-grep` + `requires.scope: line`.
264
+ *
265
+ * The lowering REJECTS this combination (an ast-grep match is a span, and
266
+ * reading it as its start line makes the verdict depend on source formatting),
267
+ * but a hand-edited manifest can still carry it, and the ast dispatcher would
268
+ * silently evaluate `match.lineText` — shipping exactly the formatting-dependent
269
+ * semantic the ruling refused. Mirrors the tree-sitter `requires` backstop:
270
+ * unreachable from any compiled record, unreachable from the legacy corpus,
271
+ * live only against a torn manifest.
272
+ */
273
+ export function assertNoAstGrepLineScope(rules) {
274
+ const offender = rules.find((rule) => rule.engine === 'ast-grep' && rule.requires?.scope === 'line');
275
+ if (offender === undefined)
276
+ return;
277
+ throw new TotemParseError(`Rule ${offender.lessonHash} (${offender.lessonHeading}) carries \`requires.scope: line\` on the \`ast-grep\` engine, a combination the record lowering rejects.`, "Prop 310 § Design 8: an ast-grep match is a SPAN, and reading it as its start line makes the verdict depend on source formatting rather than on the rule. No rule record can compile to this shape, so the manifest was hand-edited or written by a bypassed producer. Use `scope: file`, move the requirement into the payload's own `not:`/`has:` combinators, or wait for the reserved `block` unit.");
278
+ }
279
+ /**
280
+ * § Design 8 — pass two. Returns TRUE when the required context is PRESENT
281
+ * within the declared scope containing L, i.e. when the rule must stay SILENT.
282
+ * The rule fires iff the target matched AND this returns false.
283
+ *
284
+ * `requires.pattern` is a regex evaluated TEXTUALLY, independent of the target
285
+ * engine — "the requirement is a context check, not a second matcher" — so an
286
+ * ast-grep rule's requirement is still a plain regex over the scope text.
287
+ *
288
+ * UNREADABLE FILE (`scope: file` only) ⇒ context ABSENT ⇒ the rule FIRES. The
289
+ * safe direction for a lint gate is toward flagging: a false positive is visible
290
+ * and disputable, a suppressed real violation is silent. This mirrors the shipped
291
+ * Rust test-span exemption, whose read failure likewise yields no exemption
292
+ * (`rule-engine.ts`: "the exemption fails toward flagging, never toward
293
+ * suppression").
294
+ *
295
+ * A `requires.pattern` that will not compile means the compile-stage gate was
296
+ * bypassed or the manifest was hand-edited. Fail LOUD — the same treatment
297
+ * `applyRulesToAdditions` gives an uncompilable `rule.pattern`; silently treating
298
+ * it as "context absent" would turn a broken rule into a firing one.
299
+ */
300
+ export function requiresContextPresent(rule, requires, text) {
301
+ // Compiled once per distinct pattern, not once per MATCH: this runs on every
302
+ // target hit of every requires-carrying rule, and at `scope: file` against
303
+ // whole-file text. The pattern set is manifest-bounded, and the LRU bound keeps
304
+ // a long-lived process (the MCP server, a watch loop) from growing a map keyed
305
+ // by rule content.
306
+ let re = requiresPatternCache.get(requires.pattern);
307
+ if (re !== undefined) {
308
+ return matchesInScope(re, requires.scope, text);
309
+ }
310
+ try {
311
+ re = new RegExp(requires.pattern);
312
+ requiresPatternCache.set(requires.pattern, re);
313
+ }
314
+ catch (err) {
315
+ throw new TotemParseError(`Rule ${rule.lessonHash} has an invalid \`requires.pattern\` and cannot be evaluated (Prop 310 § Design 8).`, `The record-path compile gate validates this pattern with the same safe-regex2 check it applies to the target, so an invalid one here means the manifest was hand-edited or written by a bypassed producer. Re-compile the rule record. Pattern: ${JSON.stringify(requires.pattern)}`, err);
316
+ }
317
+ return matchesInScope(re, requires.scope, text);
318
+ }
319
+ /**
320
+ * § Design 8 applied to one target match: TRUE when the rule must stay silent at
321
+ * this locus because its required context is present. Always false for a rule
322
+ * carrying no `requires` block — which is every legacy rule, so the shipped
323
+ * evaluation path is untouched by construction.
324
+ */
325
+ export function requiresSuppressesMatch(rule, text) {
326
+ if (rule.requires === undefined)
327
+ return false;
328
+ return requiresContextPresent(rule, rule.requires, text);
329
+ }
330
+ //# sourceMappingURL=record-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-runtime.js","sourceRoot":"","sources":["../../src/spine/record-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=record-runtime.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-runtime.test.d.ts","sourceRoot":"","sources":["../../src/spine/record-runtime.test.ts"],"names":[],"mappings":""}