import type { CompiledRule, CompilerOutput, NonCompilableReasonCode, RegexValidation } from './compiler-schema.js'; import type { RuleTestResult } from './rule-tester.js'; import type { Stage4VerificationResult } from './stage4-verifier.js'; export interface LessonInput { index: number; heading: string; body: string; hash: string; } /** * Machine-readable skip reasons. Threaded through `CompileLessonResult` so * downstream consumers (totem doctor, Layer 4 fallthrough reporting per ADR-088) * can distinguish why a lesson produced no rule without string-matching * human-readable messages. * * mmnto-ai/totem#1481 aligned this internal type 1:1 with the persisted * `NonCompilableReasonCode` enum so ledger writers can pass the code through * without a mapping table. `'non-compilable'` renamed to `'out-of-scope'`, * `'security-verify-rejected'` renamed to `'security-rule-rejected'`, and * four producer-facing codes joined: `'no-pattern-generated'`, * `'pattern-syntax-invalid'`, `'pattern-zero-match'`, `'no-pattern-found'`. * Fresh compile runs MUST NOT emit `'legacy-unknown'`; that sentinel exists * solely for migrating pre-#1481 2-tuples. */ export type CompileLessonReasonCode = Exclude; /** * Single event inside a lesson's compile pipeline. Appended to a per-lesson * `trace` array on every pipeline step (generate / verify / retry / result) * and surfaced via `CompileLessonResult.trace` for the CLI `--verbose` * renderer (mmnto-ai/totem#1482). * * `layer` numbers align with the ADR-088 staging (1 = manual, 2 = example- * based, 3 = Layer 3 LLM with verify-retry). Consumers MUST tolerate unknown * layer numbers so a future ADR-088 phase (dedicated Layer 1 / Layer 2 * telemetry) can emit without breaking the renderer. * * `patternHash` is a stable 16-character sha256 prefix of the emitted * pattern, included only on `generate` events. Callers use it to correlate * retries ("this retry produced the same pattern") without forwarding the * pattern string itself. * * `reasonCode` is only set on the terminal `result` event when the lesson * skipped. A compiled or failed lesson omits the field. */ export interface LayerTraceEvent { layer: number; action: 'generate' | 'verify' | 'retry' | 'result'; outcome: string; patternHash?: string; reasonCode?: Exclude; } export type CompileLessonResult = { status: 'compiled'; rule: CompiledRule; trace?: LayerTraceEvent[]; } | { status: 'skipped'; hash: string; reason?: string; reasonCode: CompileLessonReasonCode; trace?: LayerTraceEvent[]; } | { status: 'failed'; trace?: LayerTraceEvent[]; } | { status: 'noop'; trace?: LayerTraceEvent[]; }; export interface CompileLessonCallbacks { onWarn?: (heading: string, message: string) => void; onDim?: (heading: string, message: string) => void; /** * Fires when the declared-severity override (mmnto-ai/totem#1656) actually * changed the emitted severity. CLI callers use this to write telemetry * records tagged `type: 'severity-override'` for prompt-tuning feedback — * frequent fires mean the prompt directive is drifting. Absent = core runs * without telemetry plumbing. */ onSeverityOverride?: (lesson: { heading: string; hash: string; }, event: { from: 'error' | 'warning' | undefined; to: 'error' | 'warning'; }) => void; /** * Fires when the declared-scope override (mmnto-ai/totem#1665) actually * changed the emitted `fileGlobs`. CLI callers use this to write telemetry * records tagged `type: 'scope-override'` for prompt-tuning feedback — * frequent fires mean the LLM is drifting on Scope preservation. Absent = * core runs without telemetry plumbing. Mirrors `onSeverityOverride` * shape and discipline from #1656. */ onScopeOverride?: (lesson: { heading: string; hash: string; }, event: { from: string[] | undefined; to: string[]; }) => void; /** * Fires after Stage 4 verification returns a result (mmnto-ai/totem#1682). * CLI callers use this to write telemetry records tagged * `type: 'stage4-verify'` to `.totem/temp/telemetry.jsonl`. Absent = core * runs without telemetry plumbing. The result mirrors the discriminated * union returned by `verifyAgainstCodebase`; see ADR-091 §"Stage 4" for * the four-outcome contract. Telemetry path-redaction (per * mmnto-ai/totem#1644 precedent) is the caller's responsibility — the * verifier returns repo-relative paths, but the telemetry writer must * apply the `>` redaction for any path outside the * repo root. */ onStage4Outcome?: (lesson: { heading: string; hash: string; }, result: Stage4VerificationResult) => void; } export interface CompileLessonDeps { parseCompilerResponse: (response: string) => CompilerOutput | null; /** * Invoke the LLM. The optional second parameter `systemPrompt` carries the * persistent compiler template separately from the per-lesson user prompt * so the orchestrator can mark it as a cache target (mmnto/totem#1291 * Phase 3). When the wrapper threads systemPrompt through to a caching- * capable provider (Anthropic), repeat calls within the TTL window read * from prompt cache instead of paying full input-token cost. * * Backward compatible: callers that ignore the second parameter and * receive a wrapper-prepended single string still work — just without * the cache benefit. */ runOrchestrator: (prompt: string, systemPrompt?: string) => Promise; existingByHash: Map; callbacks?: CompileLessonCallbacks; /** Optional: specialized system prompt for Pipeline 3 (Bad/Good example-based compilation). */ pipeline3Prompt?: string; /** * Optional telemetry-driven directive prepended to the Pipeline 2 USER prompt * (not the system prompt). Used by `totem compile --upgrade ` * (mmnto/totem#1131) to nudge Sonnet toward an ast-grep structural pattern * when the existing rule is firing in non-code contexts. Has no effect on * Pipeline 1 (manual) or Pipeline 3 (example-based) compilation. * * Note: this lives in the user prompt rather than the system prompt because * it's per-lesson (specific to one rule's telemetry). Putting it in the * system prompt would invalidate the cache on every --upgrade call. */ telemetryPrefix?: string; /** * Assert that this compile is producing a security rule. Per ADR-088 * Decision 3 (Layer 3 zero-tolerance), security rules that fail the smoke * gate are rejected outright with no retry. Reserved for callers that know * the source pack context (e.g., compiling lessons from a pack scoped * `@mmnto/pack-agent-security` or any pack whose manifest carries an * immutable severity contract). Today's `totem lesson compile` at repo * level does not set this; a future pack-build command will. Defaults to * false; security zero-tolerance is gated on an affirmative caller assertion. */ securityContext?: boolean; /** * ADR-091 Stage 4 Verify-Against-Codebase verifier (mmnto-ai/totem#1682). * When provided, runs after Layer 3 verify-retry produces a compiled rule * (Pipeline 2 / Pipeline 3 only — Pipeline 1 manual rules bypass Stage 4 * because they are human-authored). The callback returns a discriminated * `Stage4VerificationResult` and `compileLesson` mutates the rule in-place * per the four-outcome contract: * * - `'no-matches'` → `status: 'untested-against-codebase'` * - `'out-of-scope'` → `status: 'archived'`, `archivedReason` * cites the offending paths, archive * timestamp set * - `'in-scope-bad-example'` → `confidence: 'high'` (status remains * unset / 'active') * - `'candidate-debt'` → force `severity: 'warning'`, log the * candidate-debt sites via `onWarn` * * Absent (undefined) means Stage 4 does not run. The compiled rule keeps * its Layer 3 zero-trust shape (`unverified: true`, status absent = * active). Callers that don't have filesystem access (cloud compile * worker, packs without consumer codebase) leave this absent; consumer- * side `totem lint` runs the verifier later via the * `pending-verification` flow shipped in T3 (mmnto-ai/totem#1684). */ verifyStage4?: (rule: CompiledRule) => Promise; } /** * Compile-time validation for ast-grep patterns (#1062, #1339). * * Two layers: * 1. Heuristic fast-path — reject empty patterns, multi-root string * patterns (statement boundaries outside braces/parens), and * compound object patterns missing the required `rule` key. * Gives fast, human-readable error messages for the common cases. * 2. Parser-based check (#1339, #1654) — actually invoke ast-grep's rule * compiler via `parse(lang, '').root().findAll(pattern)` for every * Lang resolved from the rule's `fileGlobs`. The pattern is accepted * if it parses under any of those Langs; rejected with the last * Lang's error message if all of them fail. This catches single-line * patterns that look balanced but fail semantic validation — e.g. * `.option("--no-$FLAG", $$$REST)` (floating member call with no * receiver) or `catch($E) { $$$ }` (bare catch clause that can only * exist inside a try statement) — and equally importantly, surfaces * grammar-mismatch failures when a pattern would parse under TSX * but not under the rule's actual target grammar (e.g. a Rust * `ResMut` pattern that TSX accepts as a JSX element * but Rust rejects as a malformed type expression). * * Language choice: when `fileGlobs` resolves to one or more registered * languages (e.g., `**\/*.rs` → Rust, `**\/*.ts` → TypeScript), the pattern * is validated under every such grammar and accepted if any one accepts * it. When `fileGlobs` is absent or no glob carries a registered * extension, falls back to `Lang.Tsx` — the most permissive parser * available (superset of TypeScript plus JSX) — so unscoped pre-1.16 * rules retain their legacy validation behavior. Empty source (`''`) * keeps the call cheap — ast-grep compiles the pattern into a rule * before iterating any AST, so we see the rule-compile error even though * there's nothing to match against. * * Lite-build safety: this function is only called from compile flows * (buildCompiledRule / buildManualRule), which require an orchestrator * and therefore never run in the Lite binary. The esbuild alias swaps * `@ast-grep/napi` for the WASM shim in Lite builds, but since this * function is dead code there, the shim's `ensureInit()` requirement * is never triggered. The parser call is additionally wrapped in * try/catch so any surprise error (uninitialized engine, native-binding * failure) degrades conservatively to `valid: false` rather than * crashing the compile command. */ export declare function validateAstGrepPattern(pattern: string | Record, fileGlobs?: readonly string[]): RegexValidation; /** * Options controlling how `buildCompiledRule` validates its input before * emitting a `CompiledRule`. The smoke gate is opt-in so Pipeline 1 (manual) * callers and ad-hoc test callers keep their existing behaviour unchanged; * Pipeline 2 (LLM) and Pipeline 3 (example-based) opt in explicitly in the * compileLesson flow. */ export interface BuildCompiledRuleOptions { /** * When true, the smoke gate runs after validation and before rule emission. * Missing badExample or zero-match badExample both reject the rule with a * rejectReason that names the gate. When false (default), the gate is * skipped entirely - backward compatible. */ enforceSmokeGate?: boolean; /** * Optional badExample override. When supplied, takes precedence over * `parsed.badExample`. Pipeline 3 uses this to reuse its Bad snippet as the * smoke-gate target without relying on the LLM to echo the snippet back in * the structured output. */ badExampleOverride?: string; /** * Optional goodExample override (mmnto-ai/totem#1580). When supplied, * takes precedence over `parsed.goodExample`. Pipeline 3 uses this to * reuse its Good snippet as the over-matching check target without * relying on the LLM to echo the snippet back. */ goodExampleOverride?: string; /** * Optional declared-severity override (mmnto-ai/totem#1656). When * supplied, takes precedence over `parsed.severity` regardless of * LLM emission. Sourced from the lesson body's `**Severity:** error` * / `Severity: warning` prose convention. `buildCompiledRule` * reports the override event in `BuildRuleResult.severityOverride` * when the override actually changes the emitted severity, so CLI * callers can record telemetry for prompt-tuning feedback. */ declaredSeverityOverride?: 'error' | 'warning'; /** * Optional lesson body for declared-scope override (mmnto-ai/totem#1665). * When supplied AND the body declares a `**Scope:**` line, the parsed * source-Scope glob list takes precedence over `parsed.fileGlobs` * regardless of LLM emission. Author-declared intent always wins; #1626's * test-contract auto-include heuristic only applies when source omits Scope. * `buildCompiledRule` reports the override event in * `BuildRuleResult.scopeOverride` when the override actually changes the * emitted globs, so CLI callers can record telemetry for prompt-tuning * feedback (mirrors `declaredSeverityOverride` from #1656). */ lessonBody?: string; } /** * Build a CompiledRule from parsed compiler output. * Returns { rule, rejectReason } so callers can report why a rule was rejected. */ export declare function buildCompiledRule(parsed: CompilerOutput, lesson: { hash: string; heading: string; }, existingByHash: Map, options?: BuildCompiledRuleOptions): BuildRuleResult; export interface BuildRuleResult { rule: CompiledRule | null; rejectReason?: string; /** * Populated when `declaredSeverityOverride` actually changed the emitted * severity (mmnto-ai/totem#1656). Absent when no override was supplied, or * when the override matched the LLM's emission. CLI callers use this as a * telemetry signal for prompt-tuning feedback — frequent overrides mean * the prompt directive is drifting and the LLM needs a stronger signal. */ severityOverride?: { from: 'error' | 'warning' | undefined; to: 'error' | 'warning'; }; /** * Populated when source-declared `**Scope:**` actually changed the emitted * `fileGlobs` (mmnto-ai/totem#1665). Absent when no `lessonBody` was * supplied, when the body declared no Scope, or when the LLM emission * already matched the source declaration. CLI callers use this as a * telemetry signal for prompt-tuning feedback — frequent overrides mean * the LLM is dropping or hallucinating Scope entries. */ scopeOverride?: { from: string[] | undefined; to: string[]; }; } /** * Derive a virtual file path that satisfies a rule's fileGlobs. * Used to construct test fixtures where glob matching is active. */ export declare function deriveVirtualFilePath(rule: CompiledRule): string; /** * Verify a compiled rule against inline Example Hit/Miss lines. * Returns null if no examples exist or engine is `'ast'` (Tree-sitter). * Tree-sitter rules are skipped because `testRule`'s non-`ast-grep` branch * runs the regex pipeline (`applyRulesToAdditions`), which does not handle * S-expression queries. Regex and ast-grep rules both flow through to * `testRule`. mmnto-ai/totem#1699. * Returns RuleTestResult if verification was run. */ export declare function verifyRuleExamples(rule: CompiledRule, body: string): RuleTestResult | null; export declare function formatExampleFailure(result: RuleTestResult): string; /** * Build a CompiledRule from a lesson's manually specified pattern. * Returns { rule, rejectReason } so callers can report why a pattern was rejected. */ export declare function buildManualRule(lesson: LessonInput, existingByHash: Map): BuildRuleResult; /** * Security-context signal for the missing-Example-Hit check. Either the * compile orchestrator asserts the pack is security-scoped * (`deps.securityContext === true`) OR the rule under construction already * carries `immutable: true` (set by the pack manifest, ADR-089). Both * paths trigger the zero-tolerance reject per ADR-088 Decision 3. * * The LLM-emitted `CompilerOutput` does not currently carry an `immutable` * field (packs set it at pack-merge time), so the second signal only * engages on the Pipeline 1 manual-rule path where `buildManualRule` * could synthesize an immutable rule directly. A future change that * threads `immutable` through `CompilerOutput` can wire Pipeline 2/3 * into this helper without touching the call sites. */ export declare function isSecurityContext(deps: CompileLessonDeps, rule?: { immutable?: boolean; } | null): boolean; /** * Compile a single lesson into a rule. * Handles both manual patterns (zero LLM) and LLM-compiled patterns. * Pure business logic — no UI, no I/O, no process.exit. */ export declare function compileLesson(lesson: LessonInput, compilerPrompt: string, deps: CompileLessonDeps): Promise; //# sourceMappingURL=compile-lesson.d.ts.map