/** * Deterministic structural post-checks (mmnto-ai/totem#2103, strategy#474 slice 4). * * Zero-LLM, caller-side checks over a finished run artifact. Each check is a * {@link PostCheckRule} that returns a verdict at a STATIC enforcement tier: * - `decidable` — a structural fact that can be mechanically resolved (a cited * file exists, a line is in range, output parses against a schema). These * MAY gate: a decidable `fail` rejects the run. * - `sensor` — a softer signal (claim support, summary faithfulness). These * NEVER gate; they are telemetry only. * * The load-bearing invariant (ADR-109 — blurring decidable gates with soft * sensors trains users to bypass the gate): `isRejected` is true IFF some * `decidable` rule returned `fail`. A `sensor` finding can never set it — and * critically (mmnto-ai/totem#2103 codex review), a rule that THROWS is a fail at * the rule's OWN declared tier, never silently upgraded to decidable. A sensor * rule that throws must not reject the run. * * Verdict aggregation is a pure script, never an LLM (Tenet 9). This module is * in-memory only (no persisted shape) → plain TS interfaces, not Zod. */ import type { RunArtifact } from './schema.js'; /** Whether a rule's verdict may gate (`decidable`) or is telemetry only (`sensor`). */ export type EnforcementTier = 'decidable' | 'sensor'; /** A single rule's outcome. `abstain` = the rule did not apply to this artifact's content (honest-absent), distinct from `pass`. */ export type CheckVerdict = 'pass' | 'fail' | 'abstain'; /** * What a rule's `evaluate` returns: the verdict plus a human-facing reason * (every finding explains itself — Tenet 4, no silent degradation) and optional * structured context for the eval harness. */ export interface CheckResult { verdict: CheckVerdict; message: string; context?: Record; } /** A finding as recorded in the report: a {@link CheckResult} stamped with the rule's identity + static tier. */ export interface PostCheckFinding extends CheckResult { ruleName: string; tier: EnforcementTier; } /** The aggregate report. `isRejected` is owned solely by {@link evaluatePostChecks}. */ export interface PostCheckReport { findings: PostCheckFinding[]; /** True IFF a `decidable` finding has verdict `fail`. Nothing else may set it. */ isRejected: boolean; } /** * The minimum override-memory interface a review rule consumes (mmnto-ai/totem#2103 * OQ2 — this slice owns the CHECK, mmnto-ai/totem#2105 owns the STORE). #2103 defines * only this read shape; no persisted schema, no file reader, no durable identity * format beyond what the injected set supplies. Absent ⇒ the override rule abstains. */ export interface OverrideSet { /** * Identities of recorded (still-rejected) overrides whose anchored span * reappears verbatim in `outputContent`. Empty ⇒ none reappeared. The * anchored-span KEY FORMAT lives entirely in the store (mmnto-ai/totem#2105); * #2103 only asks "did any reappear" and never constructs a key (OQ2 boundary). */ reappearsIn(outputContent: string): readonly string[]; } /** Per-invocation context. `configRoot` anchors citation `filePath` resolution (schema: absent `sourceRepo` ⇒ run's own repo). */ export interface PostCheckContext { configRoot: string; /** Test seam — override the file read. Production callers omit it (defaults to a real fs read). */ readFile?: (absPath: string) => string | undefined; /** Injected by the caller; empty/absent until mmnto-ai/totem#2105 supplies a store. */ overrideMemory?: OverrideSet; } /** A structural post-check. `tier` is STATIC (never derived from a verdict). `appliesTo` sees the whole artifact so it can key on caller AND admission class. */ export interface PostCheckRule { name: string; tier: EnforcementTier; /** * Whether this rule applies to the artifact. MUST be a pure, non-throwing * predicate — the engine wraps only `evaluate` (a rule throw → a tier-preserving * fail), so a throwing `appliesTo` would escape as an unhandled engine fault * (CR review). All built-in predicates are trivially safe; third-party rules * must honor this. */ appliesTo(artifact: RunArtifact): boolean; evaluate(artifact: RunArtifact, ctx: PostCheckContext): CheckResult | Promise; } /** * The run's caller identity (`spec` / `review` / …) for caller-scoped rule * targeting. Prefers the explicit `runMetadata.caller` (mmnto-ai/totem#2102), falling * back to a `taskProfile` alias for slice-1/2 artifacts that predate it. An * UNKNOWN profile returns `undefined` — caller-scoped rules then abstain loudly, * never guess. */ export declare function resolveCaller(artifact: RunArtifact): string | undefined; /** * Run every applicable rule and aggregate. Pure, deterministic, zero-LLM. * * A rule that THROWS yields a `fail` at the rule's OWN declared tier (a sensor * throw is a sensor fail — it must not reject; mmnto-ai/totem#2103 codex review). * Only a genuine engine bug (not a rule fault) escapes this loop as an * exception — engine-integrity faults are never laundered into a finding. */ export declare function evaluatePostChecks(artifact: RunArtifact, rules: readonly PostCheckRule[], ctx: PostCheckContext): Promise; //# sourceMappingURL=post-checks.d.ts.map