import { z } from 'zod'; export declare const CompileManifestSchema: z.ZodObject<{ compiled_at: z.ZodString; model: z.ZodString; input_hash: z.ZodString; output_hash: z.ZodString; rule_count: z.ZodNumber; compile_worker_fingerprint: z.ZodOptional; /** * Prop 310 § Design 1 — the aggregate attestation over the RECORD file class * (`/rules/**\/*.rule.yaml`), computed by `generateRecordsHash`. * * OPTIONAL, per the slice-3 OQ-3 ruling: absence is legal IFF zero record files * exist on disk (the state every pre-Prop-310 manifest is in), so no shipped * manifest needs a hand-edit and downstream manifests keep verifying. The first * record landing forces attestation — `verify-manifest` hard-FAILS on records * present with no `records_hash` ("unattested file class"), and hard-FAILS on a * mismatch. Neither ever takes the freeze WARN-downgrade, which is input-hash-only. */ records_hash: z.ZodOptional; }, "strip", z.ZodTypeAny, { model: string; compiled_at: string; input_hash: string; output_hash: string; rule_count: number; compile_worker_fingerprint?: string | undefined; records_hash?: string | undefined; }, { model: string; compiled_at: string; input_hash: string; output_hash: string; rule_count: number; compile_worker_fingerprint?: string | undefined; records_hash?: string | undefined; }>; export type CompileManifest = z.infer; /** * Generate a deterministic SHA-256 hash of `.md` lesson files in a directory. * * Files are discovered recursively, sorted alphabetically by relative path, * and line endings are normalized to `\n` before hashing. * * When `repoCwd` is supplied and resolves inside a git repository, only * **git-tracked** lessons are hashed — untracked working-tree lessons (e.g. an * MCP `add_lesson`/`extract` scratch file) are excluded so they cannot diverge * the hash and block an unrelated push (mmnto-ai/totem#2051 / #2055 * working-tree-scope class). The producer (`totem compile`) runs on a clean * tree, so the hash it records already equals the tracked-only hash; checkers * (`verify-manifest`, `lint`, `status`) pass `repoCwd` to stay symmetric * without forcing a recompile. Outside a git repo, or when git is unavailable, * the function falls back to hashing every `.md` (the legacy/pure behavior) — * so the default no-arg form is byte-for-byte unchanged. */ export declare function generateInputHash(lessonsDir: string, repoCwd?: string): string; /** `.totem`-relative directory the Prop 310 rule records live in (§ Design 1). */ export declare const RECORDS_DIR_REL = "rules"; /** The double extension § Design 1 rules: `*.rule.yaml`, never a bare `*.yaml`. */ export declare const RECORD_FILE_SUFFIX = ".rule.yaml"; /** * The `records_hash` of an EMPTY record set — sha256 over nothing, the value * `generateRecordsHash` returns when `/rules/` is absent or holds no * `*.rule.yaml`. COMPUTED, never a transcribed literal: a hand-copied digest is a * mirror that can be typo'd, and this constant is what the OQ-3 "records: none" * verdict and the cross-platform stability test both bind to. */ export declare const EMPTY_RECORDS_HASH: string; /** * Enumerate the record files under `rulesDir`, sorted, '/'-separated, restricted * to the git-tracked set when `repoCwd` resolves inside a repo (the same * producer/consumer symmetry `generateInputHash` documents). Returns `[]` when the * directory does not exist — an absent record class is the pre-Prop-310 state, not * an error, which is the one place this differs from the lesson class. * * GIT-TRACKED, not "on disk": inside a repo an UNTRACKED `*.rule.yaml` is neither * hashed nor counted, exactly as an untracked lesson is not. That is what keeps a * draft record from diverging the attestation and blocking an unrelated push — and * it is why every surface that reports on this class says "git-tracked" rather * than "on disk". Outside a repo (or when git is unavailable) the restriction * degrades to the plain fs walk, so the no-`repoCwd` form stays pure. * * Prefer `listRecordFilesUnder(totemDir, repoCwd)` at call sites that hold a * `.totem` dir: it single-homes the `rules/` join with `attestRecordsHash`. */ export declare function listRecordFiles(rulesDir: string, repoCwd?: string): string[]; /** * Generate the deterministic sha256 attestation over the Prop 310 record file * class — the same rolling `${relPath}\n${lfContent}\n` method `generateInputHash` * uses over lessons, so the two attestations are path-AND-content sensitive in * exactly the same way: one edited byte, one added, removed, or renamed record all * move it. Absent directory ⇒ `EMPTY_RECORDS_HASH`. */ export declare function generateRecordsHash(rulesDir: string, repoCwd?: string): string; /** * The ONE call every `writeCompileManifest` caller makes to attest the record * class, given the `.totem` directory. Single-homed so six writer sites cannot * disagree about where records live or how they are hashed (Tenet 20). */ export declare function attestRecordsHash(totemDir: string, repoCwd?: string): string; /** * The record files the attestation covers, given the `.totem` dir — the READ-side * twin of `attestRecordsHash`, over exactly the same directory and exactly the * same tracked-set restriction. * * `verify-manifest` and `compile --refresh-manifest` need the COUNT, not the hash, * to decide the OQ-3 verdict ("absent `records_hash` is OK iff zero records"), and * they must be asking about the same set the writer hashed. Deciding it by * comparing against `EMPTY_RECORDS_HASH` would infer a file count from a digest; * re-deriving the directory at each call site would give the two surfaces two * chances to disagree. */ export declare function listRecordFilesUnder(totemDir: string, repoCwd?: string): string[]; /** * The OQ-3 freshness predicate: is a manifest's `records_hash` still an honest * statement about the record class on disk? * * TRUE when the field is ABSENT and zero git-tracked records exist (the * pre-Prop-310 state the ruling keeps legal), or when it is PRESENT and equals * the current attestation. FALSE otherwise — which is exactly the pair of * conditions `verify-manifest` hard-FAILs on ("unattested file class" and * "records hash mismatch"). * * Single-homed because THREE surfaces have to agree about it: the verifier, the * `--refresh-manifest` no-LLM path, and the ordinary compile no-op path. Two of * them re-deriving the rule independently is how a writer comes to think a * manifest is fresh that the verifier then rejects — the exact divergence bot * round 1 found (B-1), where a record-only change left the no-op path convinced * nothing needed rewriting. */ export declare function isRecordsAttestationFresh(existingRecordsHash: string | undefined, totemDir: string, repoCwd?: string): boolean; /** * Recursively shape an object tree so every nested record's keys are sorted * lexicographically and `undefined` properties are dropped. Pure data * transform — caller chooses how to serialize the result (minified for hash * payloads, pretty-printed for committable files). */ export declare function canonicalizeKeys(value: unknown): unknown; /** * Stringify a value with deterministic key order. The 1-arg form returns * minified JSON (the manifest-hash payload form per mmnto/totem#1407). Pass * `indent` to produce pretty-printed canonical JSON (used for committable * artefacts like `.totem/verification-outcomes.json` per mmnto-ai/totem#1684). */ export declare function canonicalStringify(value: unknown, indent?: number): string; /** * Generate a deterministic SHA-256 hash of the compiled rules file. * * Line endings are normalized to `\n` before hashing. For files that * contain at least one compound `astGrepYamlRule`, the payload is * re-serialised through `canonicalStringify` so key-order variation * inside the yaml object cannot shift the hash (mmnto/totem#1407). * Files without any compound rule keep the byte-stream path for * backward compatibility with manifests written by pre-#1407 CLIs: * the old and new computation match byte-for-byte in that case, so * every user's existing compile-manifest.json stays valid after * upgrading without a forced recompile. * * A parse failure falls back to the raw content so verify-manifest * can still catch tampering on partial writes; it does not mask the * error. */ export declare function generateOutputHash(rulesPath: string): string; /** * Write a compile manifest to disk as pretty-printed JSON. */ export declare function writeCompileManifest(manifestPath: string, manifest: CompileManifest): void; /** * Read and validate a compile manifest from disk. * * @throws {TotemParseError} if the file is missing or contains invalid JSON/schema. */ export declare function readCompileManifest(manifestPath: string): CompileManifest; //# sourceMappingURL=compile-manifest.d.ts.map