/** * Admission-record contract — the machine-readable disposition a deterministic * review skip leaves behind (mmnto-ai/totem#2473, operator-ruled 2026-08-12). * * The admission phase precedes fan execution: a poll of the diff that resolves * to nothing reviewable is a `not-applicable` ADMISSION VERDICT, never a lane * outcome and never an `InvokeFailureKind` (#2452's taxonomy describes failures * after an invocation was attempted). The record is DISCLOSURE, not * authorization — no skip path ever stamps the push-gate cache — but it is * still a claim about a projection, so it binds the exact observation that * produced it (codex review, 2026-08-14): the normalized diff scope, a hash of * the pre-filter diff bytes, and a canonical hash of the effective selection * policy. Equal bytes under a changed policy are a DIFFERENT observation. * * Store mechanics mirror the verdict store deliberately (one idiom, two * artifact kinds): content-addressed with `createdAt` (observability) AND * `schemaVersion` (writer metadata) excluded — the address is the OBSERVATION * — `wx` create-exclusive writes with EEXIST as logical-identity dedup, * raw-address verification BEFORE schema parsing on load, and a * version-tolerant-within-major reader with a migration registry for future * majors. Version-free addressing has a named backward cost: an OLDER CLI * meeting a NEWER-major record at a shared address reports it via the * explicit newer-major load error below (upgrade guidance), never a generic * corruption message. Resolution by consumers is deterministic and * exact-current: `totem review --covariate` re-derives the CURRENT admission * classification and looks up that exact identity — never wall-clock * arbitration across record families. */ import { z } from 'zod'; /** * The admission schemaVersion WRITTEN by this code. Readers accept any 1.x; * a MAJOR bump requires a migration entry in {@link loadAdmissionRecord} * before the writer ships. */ export declare const ADMISSION_RECORD_SCHEMA_VERSION = "1.0.0"; /** * The closed set of deterministic not-applicable reasons (mmnto-ai/totem#2473 * ruling item 1). Order here is the documentation order; the value is data. */ export declare const NOT_APPLICABLE_REASONS: readonly ["no-diff", "all-non-code", "filtered-empty", "all-generated"]; export type NotApplicableReason = (typeof NOT_APPLICABLE_REASONS)[number]; /** * Normalized diff-scope identity. ALWAYS present on a record — including * `no-diff`, which binds the RESOLVED terminal scope the resolver had reached * when it found nothing (e.g. `branch-vs-base` + the resolved base after the * default chain exhausts; the explicit range with its refs for `--diff`), * with `selectorForm` falling back to the REQUESTED selector where the * resolver supplies none — so empty runs under different scopes or selectors * are different observations (codex conformance note 1). `source: 'none'` * survives only for the scope-less legacy caller arm (a caller passing no * resolver result at all); no shipped CLI path produces it. */ export declare const AdmissionScopeSchema: z.ZodObject<{ source: z.ZodEnum<["explicit-range", "staged", "uncommitted", "branch-vs-base", "none"]>; base: z.ZodNullable; head: z.ZodNullable; selectorForm: z.ZodNullable; }, "strict", z.ZodTypeAny, { source: "explicit-range" | "staged" | "uncommitted" | "branch-vs-base" | "none"; head: string | null; base: string | null; selectorForm: string | null; }, { source: "explicit-range" | "staged" | "uncommitted" | "branch-vs-base" | "none"; head: string | null; base: string | null; selectorForm: string | null; }>; export type AdmissionScope = z.infer; /** * The effective selection policy whose projection produced the admission * outcome. `not-applicable` is a projection result; equal diff bytes classify * differently when any of these inputs change, so the record binds them * (codex: the disclosure/authorization distinction does not remove the * binding requirement). * * The caller (the CLI) assembles the EFFECTIVE values — config-resolved * extensions, seeded generated globs unioned with `.gitattributes` rules, * ignore/filter patterns, and an identifier for the classifier whose * non-code/code partition applies. Core owns only the canonicalization. */ export interface ProjectionPolicy { /** Effective review source extensions (config-resolved). */ sourceExtensions: readonly string[]; /** Effective generated-artifact globs (defaults ∪ .gitattributes generated). */ generatedGlobs: readonly string[]; /** Effective NOT-generated exclusions (.gitattributes -linguist-generated). */ notGeneratedGlobs: readonly string[]; /** Effective ignore/filter patterns applied during diff resolution. */ ignorePatterns: readonly string[]; /** * Identifier + version of the non-code classifier whose partition applies * (e.g. `'classifyChangedFiles@1'`). Bumped when the classification * BEHAVIOR changes without any config input changing — a projection-version * bump explains drift with no input change (ADR-113's distinction). */ classifierId: string; } /** * Canonical fingerprint over the effective selection policy. Deterministic by * construction: array fields are sorted copies (set semantics — ordering is * config-file incidental), and `calculateDeterministicHash` performs a * recursive key sort. Fixture-locked for cross-platform / key-order stability. */ export declare function computeProjectionPolicyHash(policy: ProjectionPolicy): string; export declare const AdmissionRecordSchema: z.ZodObject<{ /** Writer version; readers tolerate any 1.x (F1 policy). */ schemaVersion: z.ZodString; /** The only admission disposition persisted: an admitted run persists a verdict instead. */ disposition: z.ZodLiteral<"not-applicable">; reason: z.ZodEnum<["no-diff", "all-non-code", "filtered-empty", "all-generated"]>; /** * Observability ONLY — excluded from the content address; never a * resolution key. Constrained to an ISO-8601 UTC instant (CR on #2641): * the value is address-excluded yet interpolated into the stdout * `local-lane:` transport line, so a hand-edited record must not be able * to smuggle line breaks (or any non-timestamp bytes) into that payload — * validate-on-write and the verified load both reject it. */ createdAt: z.ZodString; scope: z.ZodObject<{ source: z.ZodEnum<["explicit-range", "staged", "uncommitted", "branch-vs-base", "none"]>; base: z.ZodNullable; head: z.ZodNullable; selectorForm: z.ZodNullable; }, "strict", z.ZodTypeAny, { source: "explicit-range" | "staged" | "uncommitted" | "branch-vs-base" | "none"; head: string | null; base: string | null; selectorForm: string | null; }, { source: "explicit-range" | "staged" | "uncommitted" | "branch-vs-base" | "none"; head: string | null; base: string | null; selectorForm: string | null; }>; /** sha256 over the pre-filter diff bytes (hash of the empty string for `no-diff`). */ inputHash: z.ZodString; /** {@link computeProjectionPolicyHash} over the effective selection policy. */ projectionPolicyHash: z.ZodString; /** * Count only, never names (names ride the CLI's dim stderr line; smallest * honest record). The counting basis is REASON-DEPENDENT (re-arm leg NIT 8): * `all-generated` counts the changed files; `all-non-code` and * `filtered-empty` count the files remaining in scope AFTER generated- * artifact exclusion; `no-diff` is 0. A consumer must not interpret the * field uniformly across reasons. */ skippedFileCount: z.ZodNumber; }, "strip", z.ZodTypeAny, { createdAt: string; scope: { source: "explicit-range" | "staged" | "uncommitted" | "branch-vs-base" | "none"; head: string | null; base: string | null; selectorForm: string | null; }; reason: "no-diff" | "all-non-code" | "filtered-empty" | "all-generated"; schemaVersion: string; inputHash: string; disposition: "not-applicable"; projectionPolicyHash: string; skippedFileCount: number; }, { createdAt: string; scope: { source: "explicit-range" | "staged" | "uncommitted" | "branch-vs-base" | "none"; head: string | null; base: string | null; selectorForm: string | null; }; reason: "no-diff" | "all-non-code" | "filtered-empty" | "all-generated"; schemaVersion: string; inputHash: string; disposition: "not-applicable"; projectionPolicyHash: string; skippedFileCount: number; }>; export type AdmissionRecord = z.infer; /** A loaded record paired with its VERIFIED content address (the filename stem). */ export interface AdmissionWithAddress { record: AdmissionRecord; /** The verified content address = filename stem (raw-payload hash, `createdAt`/`schemaVersion` excluded). */ contentHash: string; } /** Content address over the validated record — observation identity only. */ export declare function computeAdmissionContentHash(record: AdmissionRecord): string; export interface SaveAdmissionRecordResult { /** The content address (= filename stem). */ hash: string; /** Absolute path of the stored record. */ path: string; /** True when an identical logical record was already recorded (no write happened). */ existed: boolean; } /** * Persist an admission record at its content address, write-if-absent (`wx`). * Validates on the way OUT so a writer bug never poisons the store. EEXIST is * logical-identity dedup: with `scope` + `inputHash` + `projectionPolicyHash` * bound, an identical address IS the same observation repeated — first-write- * wins, and the verified load below surfaces any address collision loud. */ export declare function saveAdmissionRecord(totemDirAbs: string, record: AdmissionRecord): SaveAdmissionRecordResult; /** * Load + validate an admission record by content address. Raw-address * verification runs FIRST (identity is major-agnostic, over the on-disk bytes * minus `createdAt`/`schemaVersion`); only then is any migration applied and * the output validated against the current schema. Throws loud on a missing * file, corrupt JSON, schema violation, address mismatch, or a NEWER/unknown * major with no migration entry (a NAMED upgrade-the-CLI error — an older CLI * meeting a newer record must never report valid data as corruption; re-arm * leg MINOR 5). */ export declare function loadAdmissionRecord(totemDirAbs: string, hash: string): AdmissionWithAddress; /** * Exact-identity lookup: does a record for THIS observation exist? The caller * re-derives the current admission outcome, builds the record identity, and * asks for exactly its address — deterministic, no scanning, no wall-clock. * Returns `undefined` when absent; a PRESENT-but-corrupt record routes to * `onCorrupt` and returns `undefined` (the caller's loud no-current-record * sensor covers both — never a silent fallback to an older verdict). */ export declare function findAdmissionRecordByIdentity(totemDirAbs: string, identity: Omit, onCorrupt: (message: string) => void): AdmissionWithAddress | undefined; /** * The core-owned admission form of the `local-lane:` line. Same prefix as * {@link renderCovariateLine} so the round-disposition comment carries either * form. `at=` renders the RECORD's timestamp: under dedup a repeat identical * skip shows the first observation's stamp (the record's own truth, named in * the changeset so it never reads as staleness of the check). */ export declare function renderAdmissionLine(admission: AdmissionWithAddress): string; //# sourceMappingURL=admission.d.ts.map