/** * Compile-time smoke gate for compiled rules (ADR-087). * * Runs a freshly built `CompiledRule` against its own `badExample` snippet * using the same engine entry points that the runtime uses (`matchAstGrepPattern` * for ast-grep, `new RegExp` for regex). If the rule cannot match its own * bad example, something structurally wrong happened between the LLM output * and the persisted rule shape — either the pattern does not compile against * the snippet, or the snippet does not exercise the pattern the prompt * promised. Either way, the rule has no business in compiled-rules.json. * * The gate is intentionally a thin wrapper: its entire purpose is to * guarantee that a rule passing here will also fire at runtime on identical * input. Any divergence between "smoke gate happy" and "runtime happy" is a * bug in this module, not a rule authoring problem. * * Prop 310 § Design 8 PASS TWO (mmnto-ai/totem#2678): the match predicate is * "the target matched AND the required context is ABSENT", so a gate that ran * only pass one WAS that divergence — a `requires:`-bearing record's `good` * example keeps the target and adds the companion, so the gate read it as * over-matching and no such record could pass `totem rule test` or the ADR-112 * §4 preimage differential. The gate therefore evaluates the requirement with * the RUNTIME'S OWN evaluator (`requiresSuppressesMatch`, `spine/record-runtime.ts`) * — the same function `rule-engine.ts` and `regex-safety/apply-rules-bounded.ts` * call. One evaluator, never a second implementation of § Design 8 living here * (Tenet 20: derive or couple, never mirror). Legacy rules carry no `requires` * and are byte-identical by construction — every call site is guarded on * `rule.requires !== undefined` so no legacy rule pays a second test. * * Borrowing the evaluator means borrowing its PRECONDITIONS: `runSmokeGate` runs * `assertNoTornRecordRules` on every rule, and adds * `assertRequiresPatternsSafe` / `assertNoAstGrepLineScope` for a * `requires:`-bearing one, before evaluating it — two of these at each regex * dispatcher, all three at the ast dispatcher, in each case at invocation * altitude. Without them the gate ACCEPTED rules the runtime refuses — a torn * rule, an unsafe-but-compilable `requires.pattern` that backtracks * catastrophically, and `ast-grep` + `requires.scope: line` — which is the same * gate-vs-runtime divergence this module exists to rule out. * * Not wired to Pipeline 1 (manual) rules in mmnto/totem#1408 - a dry-run * sweep lands in a follow-up ticket before the Pipeline 1 gate flips on. */ import type { CompiledRule } from './compiler-schema.js'; export interface SmokeGateResult { /** True when the rule produced at least one match against the snippet. */ matched: boolean; /** Number of matches the engine reported. Zero when `matched` is false. */ matchCount: number; /** * When matched is false and the engine refused to execute (invalid regex, * ast-grep runtime throw, missing engine fields), this carries the first * line of the error so the caller can build a human-readable rejectReason. * Absent when matched is true, and absent when matched is false due to the * snippet simply not containing anything the pattern would match. */ reason?: string; } /** * Run the smoke gate for a compiled rule against an arbitrary snippet. * Callers interpret `matched === true` based on the snippet's role: * - badExample check: under-matching when matched is false → reject * - goodExample check (mmnto-ai/totem#1580): over-matching when matched * is true → reject * * This function is intentionally role-agnostic and only reports what the * engine says; the accept/reject decision belongs to the caller. */ export declare function runSmokeGate(rule: CompiledRule, snippet: string): SmokeGateResult; //# sourceMappingURL=compile-smoke-gate.d.ts.map