/** * Durable, concurrency-safe persistence primitives for the JSON stores. * (change: harden-memory-integrity-invariant) * * OpenLore's promise — *never serve an unverified or stale fact as * authoritative* — is only as strong as the durability of the files behind it. * Both stores (`.openlore/memory/notes.json`, `.openlore/decisions/pending.json`) * are single JSON files rewritten in full. Without these primitives: * * - a crash mid-write leaves a torn file that loads as a silent empty store * (memory vanishes, nothing says so), and * - two concurrent writers race: last-writer-wins silently drops the other. * * This module supplies three stdlib-only primitives (no new dependency, no LLM): * * 1. {@link atomicWriteFile} — write to a temp file, fsync, then POSIX-rename * into place. A crash before the rename leaves the prior store intact. * 2. {@link casUpdate} — optimistic compare-and-swap on a monotonic `sequence`: * load → mutate → commit only if the on-disk sequence is unchanged; on a * conflict, re-read and re-apply the (append/supersede) merge rather than * clobber. No concurrent write is lost. * 3. {@link quarantineCorrupt} — move a store that fails validation aside to * `*.corrupt-` and signal it, instead of silently substituting empty. */ /** Any persisted store that carries the monotonic CAS counter. */ export interface SequencedStore { sequence?: number; } /** * Write `data` to `path` atomically. The data goes to a sibling temp file, is * flushed to disk (`fsync`), and is moved into place with a single atomic * `rename`. A crash or interruption before the rename leaves the previously * committed file untouched — never a partially written (torn) file. */ export declare function atomicWriteFile(path: string, data: string): Promise; /** * Atomically read-modify-write a sequenced JSON store. The load → mutate → * write happens entirely inside the per-store advisory lock, so the lock — not an * optimistic sequence guard — is the real serialization point: `mutate` always * runs against the freshest on-disk store, and a competing write cannot interleave * between the read and the write. The monotonic `sequence` is still bumped (it * orders writes, names quarantine files, and lets external readers detect change), * but correctness no longer depends on every writer honoring it. * * `mutate` MUST be a pure merge over the loaded store (append / id-keyed * upsert / supersede) so that applying it to the latest store is always correct. * ALL writers of a given store MUST go through this function (or a wrapper of it) * — a raw, lock-free write to the same path defeats the serialization. */ export declare function casUpdate(opts: { storePath: string; load: () => Promise; mutate: (current: T) => T; serialize: (next: T) => string; }): Promise; /** * Move a store that failed validation aside to a deterministic quarantine path * `${path}.corrupt-` and emit a recoverable signal, instead of silently * substituting an empty store (which would present absence as current fact). * * The suffix is the first free non-negative integer — derived from what is already * on disk, never wall-clock time — so recovery is reproducible. The claim is atomic * (a hard link that fails if the destination exists), so two concurrent loaders can * never overwrite each other's quarantine file and lose preserved bytes. Returns the * quarantine path, or `null` when the move was unnecessary or impossible (caller * still degrades to empty, but loudly). */ export declare function quarantineCorrupt(path: string, reason: string): Promise; //# sourceMappingURL=atomic-store.d.ts.map