UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

3,859 lines • 196 kB
import { existsSync, fstatSync, linkSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, openSync, fsyncSync, closeSync, utimesSync } from "node:fs";
import { createHash, randomUUID } from "node:crypto";
import { dirname, join, relative } from "node:path";
import { jaccardDistance, termSet } from "../memory-vector.js";
import { MAX_MEMORY_BYTES } from "../memory.js";
import { inlineUntrusted } from "../untrusted-text.js";
import { ambiguousOriginRepresentation, committedDistilledOf, committedOriginOf, computeEntryRev, distilledEquals, entryFromFile, isValidEntryId, originEquals, parseEntryFile, serializeEntryFile } from "./frontmatter.js";
import { convertV1SidecarKeys } from "./delegation-settlement.js";
import { layerObservationLookup, observedDivergenceNotice } from "./layer-observations.js";
import { DEFAULT_MAX_ENTRY_DEPTH, MEMORY_INDEX_FILENAME, scanEntryFiles } from "./entry-scan.js";
import { ControlPlaneCorruptError, CURSORS_FILE, QUARANTINE_DIR, CHALLENGES_FILE, IMPORT_RECEIPTS_DIR, LINEAGE_FILE, SESSION_POLLUTION_DIR, appendChallengeEvents, atomicWriteFileSync, quarantineAndTombstone, captureAndClearLineageForEntries, adoptRootScopeLayout, adoptCanonicalKeyedControlDir, deriveControlPlaneDir, ensureDirExists, enqueueMemoryAnnouncement, importLatchTxnId, importLineageCommitted, importSessionPollution, isContainedIn, permissionRemedyOf, withPermissionRemedy, lineageContributionsOfSession, listSessionPollution, readChallengeEvents, readLineageRecord, registerScope, registeredScopes, releaseImportLatch, layoutNeedsAdoption, resolveMemoryEngineRoot, scopeDirFor, scopeDirName, scopeRegistryShape, stageImportLatch, landingPathOf, storeTargetEscape, TARGET_OUTSIDE_STORE, writeAllSync, } from "./layout.js";
import { screenInboundEntries } from "./data-plane.js";
import { scanMemoryFileName, scanMemoryWrite } from "./scan.js";
import { thrownText } from "../thrown-value.js";
export { DEFAULT_MAX_ENTRY_DEPTH, MEMORY_INDEX_FILENAME, scanEntryFiles } from "./entry-scan.js";
export function canonicalJsonStringify(value) {
    if (Array.isArray(value))
        return `[${value.map((v) => (v === undefined || typeof v === "function" || typeof v === "symbol" ? "null" : canonicalJsonStringify(v))).join(",")}]`;
    if (value !== null && typeof value === "object") {
        const rec = value;
        const keys = Object.keys(rec)
            .filter((k) => rec[k] !== undefined)
            .sort();
        return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJsonStringify(rec[k])}`).join(",")}}`;
    }
    return JSON.stringify(value);
}
export function erasureSelectHash(select) {
    return createHash("sha256").update(`${canonicalJsonStringify(select)}\n`, "utf8").digest("hex");
}
export function erasureRequestInvalid(input) {
    if (!input || typeof input !== "object")
        return "input is not an object";
    if (typeof input.requestId !== "string" || input.requestId.length === 0)
        return "requestId is required (idempotency identity — a retry must reuse it; the engine never mints one)";
    const sel = input.select;
    if (!sel || typeof sel !== "object" || Array.isArray(sel))
        return "select is not an object";
    const rec = sel;
    const keys = Object.keys(rec);
    const kind = keys[0];
    if (keys.length !== 1 || (kind !== "ids" && kind !== "scope" && kind !== "sessionId"))
        return "select must carry exactly one of ids/scope/sessionId";
    if (kind === "ids") {
        const list = Array.isArray(rec.ids) ? rec.ids : undefined;
        if (list === undefined)
            return "select.ids is not an array";
        const seen = new Set();
        for (const id of list) {
            if (typeof id !== "string" || id.length === 0)
                return "select.ids carries a non-string/empty id";
            if (seen.has(id))
                return "select.ids carries a duplicate id (the pinned set is a set — deduplicate before calling)";
            seen.add(id);
        }
    }
    else {
        const v = rec[kind];
        if (typeof v !== "string" || v.length === 0)
            return `select.${kind} is not a non-empty string`;
    }
    if (input.allowUnevidenced !== undefined && typeof input.allowUnevidenced !== "boolean")
        return "allowUnevidenced must be a boolean when present";
    return undefined;
}
function erasureCodedError(message, code) {
    const e = new Error(message);
    e.code = code;
    return e;
}
function cloneErasureSelect(select) {
    if ("ids" in select)
        return { ids: [...select.ids] };
    if ("scope" in select)
        return { scope: select.scope };
    return { sessionId: select.sessionId };
}
export function captureErasureInput(input) {
    if (!input || typeof input !== "object")
        return input;
    const requestId = input.requestId;
    const allowUnevidenced = input.allowUnevidenced;
    const rawSel = input.select;
    let select;
    if (rawSel && typeof rawSel === "object" && !Array.isArray(rawSel)) {
        const s = rawSel;
        const captured = {};
        for (const k of Object.keys(s)) {
            const v = s[k];
            captured[k] = Array.isArray(v) ? [...v] : v;
        }
        select = captured;
    }
    else {
        select = rawSel;
    }
    return { requestId, select, ...(allowUnevidenced !== undefined ? { allowUnevidenced } : {}) };
}
class AuditSnapshotContendedError extends Error {
}
class AuditObservationUnstableError extends Error {
}
const LEDGER_FILE = "revs.json";
const LEDGER_SCHEMA_VERSION = 2;
const LEDGER_V1_BACKUP_FILE = "revs.v1.json.bak";
const TRANSFERS_FILE = "transfers.jsonl";
const CHAIN_DEGRADED_FILE = "transfers.chain-degraded.json";
const JOURNAL_FILE = "journal.json";
const TXN_LOCK_STALE_MS = 30_000;
const TXN_LOCK_WAIT_MS = 15_000;
const TXN_LOCK_STEAL_GRACE_MS = 250;
const SHADOW_DIR = "shadow";
const STORE_IDENTITY_FILE = "store-identity.json";
function rowsRev(rows, id) {
    return rows[id]?.rev;
}
function rowsBinding(rows, id) {
    const row = rows[id];
    if (row === undefined || typeof row.scope !== "string" || typeof row.slug !== "string")
        return undefined;
    return { scope: row.scope, slug: row.slug };
}
function rowsEntries(rows) {
    return Object.entries(rows);
}
function deleteRow(rows, id) {
    delete rows[id];
}
function hasUnboundRows(rows) {
    return rowsEntries(rows).some(([id]) => rowsBinding(rows, id) === undefined);
}
const UNBOUND_DISCLOSURE_PREFIX = "memory ledger has ";
function unboundRowIds(rows) {
    return rowsEntries(rows)
        .filter(([id]) => rowsBinding(rows, id) === undefined)
        .map(([id]) => id);
}
function bindingEquals(a, b) {
    if (a === undefined || b === undefined)
        return a === b;
    return a.scope === b.scope && a.slug === b.slug;
}
function bindRowTracked(rows, id, next, transfers, channel) {
    const old = rows[id];
    const oldBinding = rowsBinding(rows, id);
    const row = { ...(old ?? {}), rev: next.rev, scope: next.scope, slug: next.slug, at: next.at };
    if (oldBinding !== undefined && !bindingEquals(oldBinding, { scope: next.scope, slug: next.slug })) {
        row.prev = { scope: oldBinding.scope, slug: oldBinding.slug, at: next.at };
        transfers.push({ ev: randomUUID(), channel, id, from: oldBinding, to: { scope: next.scope, slug: next.slug }, at: next.at });
    }
    else if (old !== undefined && oldBinding === undefined) {
        transfers.push({ ev: randomUUID(), channel: "migration-bind", id, to: { scope: next.scope, slug: next.slug }, at: next.at });
    }
    rows[id] = row;
}
function cloneRows(rows) {
    const out = Object.create(null);
    for (const [id, row] of rowsEntries(rows))
        out[id] = { ...row, ...(row.prev !== undefined ? { prev: { ...row.prev } } : {}) };
    return out;
}
function ledgerEnvelope(rows, extras) {
    return { v: LEDGER_SCHEMA_VERSION, ...(extras ?? {}), rows };
}
function slugEscapes(slug) {
    if (slug.length === 0 || slug.startsWith("/") || slug.includes("\\"))
        return true;
    return slug.split("/").some((seg) => seg === "" || seg === "." || seg === "..");
}
function validateLedgerRowsV2(rowsRaw, path) {
    if (!rowsRaw || typeof rowsRaw !== "object" || Array.isArray(rowsRaw)) {
        throw new ControlPlaneCorruptError(`committed-rev ledger rows have the wrong shape: ${path}`);
    }
    const out = Object.create(null);
    for (const [id, rowRaw] of Object.entries(rowsRaw)) {
        if (!rowRaw || typeof rowRaw !== "object" || Array.isArray(rowRaw)) {
            throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} has the wrong shape: ${path}`);
        }
        const row = rowRaw;
        if (typeof row.rev !== "string") {
            throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} has no string rev: ${path}`);
        }
        const hasScope = row.scope !== undefined;
        const hasSlug = row.slug !== undefined;
        if (hasScope !== hasSlug) {
            throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} is half-bound (scope and slug must appear together): ${path}`);
        }
        if (hasScope && (typeof row.scope !== "string" || typeof row.slug !== "string")) {
            throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} has a non-string binding: ${path}`);
        }
        if (typeof row.slug === "string" && slugEscapes(row.slug)) {
            throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} has a path-escaping slug: ${path}`);
        }
        if (row.at !== undefined && (typeof row.at !== "number" || !Number.isInteger(row.at))) {
            throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} has a non-integer 'at': ${path}`);
        }
        if (row.prev !== undefined) {
            const prev = row.prev;
            if (!prev || typeof prev !== "object" || Array.isArray(prev) || typeof prev.scope !== "string" || typeof prev.slug !== "string" || typeof prev.at !== "number" || !Number.isInteger(prev.at)) {
                throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} has a malformed 'prev' trace: ${path}`);
            }
            if (slugEscapes(prev.slug)) {
                throw new ControlPlaneCorruptError(`committed-rev ledger row ${JSON.stringify(id)} has a path-escaping 'prev' slug: ${path}`);
            }
        }
        out[id] = { ...row };
    }
    return out;
}
function parseJournal(raw, jp) {
    try {
        const parsed = JSON.parse(raw);
        if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.ops))
            throw new Error("wrong shape");
        return parsed;
    }
    catch {
        throw new ControlPlaneCorruptError(`transaction journal is unparseable: ${jp}`);
    }
}
function journalLedgerSnapshot(journal, jp) {
    const hasV2 = journal.ledger2 !== undefined;
    const hasV1 = journal.ledger !== undefined;
    if (hasV2 === hasV1) {
        throw new ControlPlaneCorruptError(`transaction journal has ${hasV2 ? "both ledger snapshots" : "no ledger snapshot"} (wrong shape): ${jp}`);
    }
    if (hasV2) {
        const env = journal.ledger2;
        if (!env || typeof env !== "object" || Array.isArray(env) || env.v !== LEDGER_SCHEMA_VERSION) {
            throw new ControlPlaneCorruptError(`transaction journal ledger2 snapshot has the wrong shape: ${jp}`);
        }
        const extras = {};
        for (const [k, val] of Object.entries(env))
            if (k !== "v" && k !== "rows")
                extras[k] = val;
        return { form: "v2", rows: validateLedgerRowsV2(env.rows, jp), extras };
    }
    const map = journal.ledger;
    if (!map || typeof map !== "object" || Array.isArray(map))
        throw new ControlPlaneCorruptError(`transaction journal ledger snapshot has the wrong shape: ${jp}`);
    const rows = Object.create(null);
    for (const [id, rev] of Object.entries(map)) {
        if (typeof rev !== "string")
            throw new ControlPlaneCorruptError(`transaction journal v1 ledger snapshot entry ${JSON.stringify(id)} is not a string: ${jp}`);
        rows[id] = { rev };
    }
    return { form: "v1", rows };
}
function parseLedgerText(raw, path) {
    let parsed;
    try {
        parsed = JSON.parse(raw);
    }
    catch {
        throw new ControlPlaneCorruptError(`committed-rev ledger is unparseable: ${path}`);
    }
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
        throw new ControlPlaneCorruptError(`committed-rev ledger has the wrong shape: ${path}`);
    }
    const rec = parsed;
    if ("v" in rec) {
        const v = rec.v;
        if (typeof v !== "number" || !Number.isInteger(v)) {
            throw new ControlPlaneCorruptError(`committed-rev ledger schema version is not an integer: ${path}`);
        }
        if (v > LEDGER_SCHEMA_VERSION) {
            throw new ControlPlaneCorruptError(`committed-rev ledger schema v${v} is newer than this engine supports (v${LEDGER_SCHEMA_VERSION}) — refusing to reinterpret it: ${path}`);
        }
        if (v !== LEDGER_SCHEMA_VERSION) {
            throw new ControlPlaneCorruptError(`committed-rev ledger has an unknown enveloped schema v${v}: ${path}`);
        }
        const rows = validateLedgerRowsV2(rec.rows, path);
        const envelopeExtras = {};
        for (const [k, val] of Object.entries(rec)) {
            if (k !== "v" && k !== "rows")
                envelopeExtras[k] = val;
        }
        return { form: "v2", rows, envelopeExtras };
    }
    const rows = Object.create(null);
    for (const [k, v] of Object.entries(rec)) {
        if (typeof v !== "string") {
            throw new ControlPlaneCorruptError(`committed-rev ledger entry ${JSON.stringify(k)} is not a string: ${path}`);
        }
        rows[k] = { rev: v };
    }
    return { form: "v1", rows };
}
const KNOWN_TRANSFER_CHANNELS = new Set(["adopted-move", "applyPatches-move", "migration-bind", "migration", "delete", "erasure-anchor", "erasure-request"]);
function transferEventInvalid(raw) {
    if (!raw || typeof raw !== "object" || Array.isArray(raw))
        return "event is not an object";
    const e = raw;
    if (typeof e.ev !== "string" || e.ev.length === 0)
        return "missing event id 'ev'";
    if (typeof e.at !== "number" || !Number.isInteger(e.at))
        return "missing/non-integer 'at'";
    const bindingInvalid = (b) => {
        if (!b || typeof b !== "object" || Array.isArray(b))
            return true;
        const r = b;
        if (typeof r.scope !== "string" || r.scope.length === 0)
            return true;
        return typeof r.slug !== "string" || slugEscapes(r.slug);
    };
    const idListInvalid = (v, what) => {
        const list = Array.isArray(v) ? v : undefined;
        if (list === undefined)
            return `${what} is not an array`;
        const seen = new Set();
        for (const id of list) {
            if (typeof id !== "string" || id.length === 0)
                return `${what} carries a non-string/empty id`;
            if (seen.has(id))
                return `${what} carries a duplicate id`;
            seen.add(id);
        }
        return undefined;
    };
    if ((e.origin !== undefined) !== (e.srcEv !== undefined))
        return "'origin' and 'srcEv' must appear together (import rows carry both; local rows carry neither)";
    if (e.origin !== undefined && (typeof e.origin !== "string" || e.origin.length === 0))
        return "non-string/empty 'origin'";
    if (e.srcEv !== undefined && (typeof e.srcEv !== "string" || e.srcEv.length === 0))
        return "non-string/empty 'srcEv'";
    const redactedKeys = new Set();
    if (e.redacted !== undefined) {
        const list = Array.isArray(e.redacted) ? e.redacted : undefined;
        if (list === undefined || list.length === 0)
            return "'redacted', when present, must be a non-empty array naming the removed keys";
        for (const k of list) {
            if (k !== "from" && k !== "to")
                return "'redacted' may only name 'from'/'to'";
            if (redactedKeys.has(k))
                return "'redacted' names a key twice";
            redactedKeys.add(k);
        }
        if (e.origin === undefined)
            return "'redacted' requires the import pair (origin/srcEv) — a local row is never redacted";
    }
    if (e.channel === "migration") {
        if (typeof e.boundRows !== "number" || !Number.isInteger(e.boundRows))
            return "migration summary missing integer 'boundRows'";
        if (typeof e.unboundRows !== "number" || !Number.isInteger(e.unboundRows))
            return "migration summary missing integer 'unboundRows'";
        if (e.id !== undefined || e.from !== undefined || e.to !== undefined)
            return "migration summary must not carry id/from/to";
        return undefined;
    }
    if (e.channel === "adopted-move" || e.channel === "applyPatches-move" || e.channel === "migration-bind") {
        if (typeof e.id !== "string" || e.id.length === 0)
            return "row transfer missing 'id'";
        if (e.to !== undefined && redactedKeys.has("to"))
            return "'to' is present but named in 'redacted'";
        if (e.from !== undefined && redactedKeys.has("from"))
            return "'from' is present but named in 'redacted'";
        if (e.to === undefined) {
            if (!redactedKeys.has("to"))
                return "row transfer missing/malformed 'to'";
        }
        else if (bindingInvalid(e.to)) {
            return "row transfer missing/malformed 'to'";
        }
        if (e.channel === "migration-bind") {
            if (e.from !== undefined && bindingInvalid(e.from))
                return "malformed 'from'";
        }
        else if (e.from === undefined) {
            if (!redactedKeys.has("from"))
                return `'${e.channel}' requires a well-formed 'from'`;
        }
        else if (bindingInvalid(e.from)) {
            return `'${e.channel}' requires a well-formed 'from'`;
        }
        return undefined;
    }
    if (e.channel === "delete") {
        if (typeof e.id !== "string" || e.id.length === 0)
            return "delete event missing 'id'";
        if (typeof e.rev !== "string" || e.rev.length === 0)
            return "delete event missing 'rev'";
        if (e.boundRows !== undefined || e.unboundRows !== undefined || e.to !== undefined || e.select !== undefined)
            return "delete event must not carry boundRows/unboundRows/to/select";
        if (e.from !== undefined && bindingInvalid(e.from))
            return "delete event has a malformed 'from'";
        if (e.req !== undefined && (typeof e.req !== "string" || e.req.length === 0))
            return "delete event has a non-string/empty 'req'";
        if (e.sessions !== undefined) {
            const sessions = Array.isArray(e.sessions) ? e.sessions : undefined;
            if (sessions === undefined || sessions.some((s) => typeof s !== "string"))
                return "delete event has a malformed 'sessions'";
            if (e.req === undefined)
                return "delete event carries 'sessions' without 'req' (only the request leg captures lineage)";
        }
        if ((e.legacyId !== undefined) === isValidEntryId(e.id)) {
            return e.legacyId !== undefined ? "legacyId claimed for a well-formed entry id (downgrade laundering refused)" : "delete event for a non-entry-id ledger key must declare legacyId";
        }
        if (e.legacyId !== undefined && e.legacyId !== true)
            return "legacyId, when present, must be literal true";
        if (e.legacyId !== undefined && e.from !== undefined)
            return "legacyId and 'from' are mutually exclusive (a legacy row has no derivable projection)";
        return undefined;
    }
    if (e.channel === "erasure-anchor") {
        if (typeof e.req !== "string" || e.req.length === 0)
            return "erasure anchor missing 'req'";
        if (e.id !== undefined || e.from !== undefined || e.to !== undefined || e.scope !== undefined || e.boundRows !== undefined)
            return "erasure anchor must not carry id/from/to/scope/boundRows";
        const sel = e.select;
        if (!sel || typeof sel !== "object" || Array.isArray(sel))
            return "erasure anchor 'select' is not an object";
        const selRec = sel;
        const selKeys = Object.keys(selRec);
        if (selKeys.length !== 1 || (selKeys[0] !== "ids" && selKeys[0] !== "scope" && selKeys[0] !== "sessionId"))
            return "erasure anchor 'select' must carry exactly one of ids/scope/sessionId";
        if (selKeys[0] === "ids") {
            const bad = idListInvalid(selRec.ids, "'select.ids'");
            if (bad !== undefined)
                return bad;
        }
        else if (typeof selRec[selKeys[0]] !== "string" || selRec[selKeys[0]].length === 0) {
            return `erasure anchor 'select.${selKeys[0]}' is not a non-empty string`;
        }
        if (e.selectHash !== erasureSelectHash(sel))
            return "erasure anchor 'selectHash' does not equal the selector's canonical hash";
        const idsBad = idListInvalid(e.ids, "erasure anchor 'ids'");
        if (idsBad !== undefined)
            return idsBad;
        const nfBad = idListInvalid(e.notFound, "erasure anchor 'notFound'");
        if (nfBad !== undefined)
            return nfBad;
        const pinned = new Set(e.ids);
        for (const id of e.notFound) {
            if (!pinned.has(id))
                return "erasure anchor 'notFound' is not a subset of 'ids'";
        }
        if (selKeys[0] === "ids") {
            const selIds = selRec.ids;
            const anchorIds = e.ids;
            if (selIds.length !== anchorIds.length || selIds.some((id, i) => anchorIds[i] !== id)) {
                return "erasure anchor 'ids' does not equal the ids-selector's verbatim id list (a pinned set the selector never named)";
            }
        }
        return undefined;
    }
    if (e.channel === "erasure-request") {
        if (typeof e.req !== "string" || e.req.length === 0)
            return "erasure request row missing 'req'";
        if (e.id !== undefined || e.from !== undefined || e.to !== undefined || e.select !== undefined || e.notFound !== undefined)
            return "erasure request row must not carry id/from/to/select/notFound";
        if (typeof e.scope !== "string" || e.scope.length === 0)
            return "erasure request row missing 'scope'";
        if (typeof e.selectHash !== "string" || !/^[0-9a-f]{64}$/.test(e.selectHash))
            return "erasure request row 'selectHash' is not a sha256 hex";
        const idsBad = idListInvalid(e.ids, "erasure request row 'ids'");
        if (idsBad !== undefined)
            return idsBad;
        if (e.ids.length === 0)
            return "erasure request row carries an empty 'ids' (a projection row exists only for a scope with bound ids)";
        return undefined;
    }
    return `unknown channel ${JSON.stringify(e.channel)}`;
}
function readControlFileOrAbsent(path, what) {
    try {
        return readFileSync(path, "utf8");
    }
    catch (err) {
        const code = err.code;
        if (code === "ENOENT")
            return undefined;
        throw new ControlPlaneCorruptError(`${what} could not be read (${code ?? "io error"}) at ${path} — a read failure is not absence (fail-closed); fix the filesystem fault and retry`);
    }
}
function journalOpInvalid(raw, directoryRoot) {
    if (!raw || typeof raw !== "object" || Array.isArray(raw))
        return "op is not an object";
    const op = raw;
    if (typeof op.target !== "string" || op.target.length === 0)
        return "missing string target";
    if (op.staged !== undefined && typeof op.staged !== "string")
        return "non-string staged";
    switch (op.kind) {
        case "write":
            if (typeof op.content !== "string")
                return "write op without full content";
            if (!isContainedIn(directoryRoot, op.target))
                return "write target escapes the store root";
            return undefined;
        case "delete":
            if (op.content !== undefined)
                return "delete op must not carry content";
            if (!isContainedIn(directoryRoot, op.target))
                return "delete target escapes the store root";
            return undefined;
        case "shadow-write":
            if (typeof op.content !== "string")
                return "shadow-write op without full content";
            if (!isValidEntryId(op.target))
                return "shadow target is not an entry id";
            return undefined;
        case "shadow-delete":
            if (op.content !== undefined)
                return "shadow-delete op must not carry content";
            if (!isValidEntryId(op.target))
                return "shadow target is not an entry id";
            return undefined;
        default:
            return "unknown op kind";
    }
}
function probeProjection(absPath, id) {
    let text;
    try {
        text = readFileSync(absPath, "utf8");
    }
    catch (err) {
        return err.code === "ENOENT" ? "absent" : "undecidable";
    }
    return parseEntryFile(text).id === id ? "present" : "undecidable";
}
export class FileMemoryEngineBackend {
    directoryRoot;
    controlPlaneRoot;
    physicalRoot;
    now;
    onIncident;
    ledger;
    envelopeExtras;
    persistedSchemaVersion;
    announcedV1Compat = false;
    unboundRowsKnown = false;
    adoptionNoticeKeys = new Set();
    transfersAppendFault;
    recoveryCommitProbe;
    chainHealProbe;
    exportFaceProbe;
    deletedIdsDigest;
    inboundFindings = [];
    batchScan;
    constructor(dir, opts = {}) {
        this.now = opts.now ?? Date.now;
        this.onIncident = opts.onIncident;
        this.directoryRoot = dir;
        if (opts.controlDir === undefined)
            adoptCanonicalKeyedControlDir(resolveMemoryEngineRoot(), dir, opts.onIncident);
        this.controlPlaneRoot = opts.controlDir ?? deriveControlPlaneDir(resolveMemoryEngineRoot(), dir);
        ensureDirExists(dir);
        this.physicalRoot = landingPathOf(dir);
        ensureDirExists(this.controlPlaneRoot);
        this.recoverJournal({ unlocked: true });
        try {
            this.loadLedger();
        }
        catch {
        }
        if (this.persistedSchemaVersion === "v1" || (this.ledger !== undefined && hasUnboundRows(this.ledger))) {
            let layoutDerivesPaths = false;
            try {
                const shape = scopeRegistryShape(this.controlPlaneRoot);
                layoutDerivesPaths = shape !== "v1" && shape !== "migrating";
            }
            catch {
            }
            if (layoutDerivesPaths)
                this.tryOpportunisticMigrate();
        }
    }
    adoptLayoutLocked(lockToken, ownerCandidate, onIncident) {
        adoptRootScopeLayout(this.directoryRoot, this.controlPlaneRoot, {
            ownerCandidate,
            onIncident: onIncident ?? this.onIncident,
            convertSidecars: convertV1SidecarKeys,
            renew: () => {
                const now = new Date();
                utimesSync(this.txnLockDir(), now, now);
                this.assertTxnLockOwnership(lockToken);
            },
        });
    }
    ensureLayout(ownerCandidate) {
        this.assertRootIntact();
        if (!layoutNeedsAdoption(this.directoryRoot, this.controlPlaneRoot))
            return;
        const lock = this.tryAcquireTxnLockSync();
        if (lock === undefined)
            return;
        try {
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token, ownerCandidate);
        }
        finally {
            lock.release();
        }
    }
    async adoptRootScopeLayout(opts = {}) {
        this.assertRootIntact();
        if (!layoutNeedsAdoption(this.directoryRoot, this.controlPlaneRoot))
            return;
        const lock = await this.acquireTxnLock();
        try {
            this.assertTxnLockOwnership(lock.token);
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token, opts.ownerCandidate, opts.onIncident);
        }
        finally {
            lock.release();
        }
    }
    checkControlPlane() {
        this.recoverJournal({ unlocked: true });
        this.ensureLayout();
        this.ledger = undefined;
        this.loadLedger();
        registeredScopes(this.controlPlaneRoot);
        if (this.persistedSchemaVersion === "v1" || hasUnboundRows(this.loadLedger()))
            this.tryOpportunisticMigrate();
        if (this.persistedSchemaVersion === "v2")
            this.announceUnboundRows(this.loadLedger());
    }
    drainInboundFindings() {
        const out = this.inboundFindings;
        this.inboundFindings = [];
        return out;
    }
    readCommittedShadow(id) {
        return readSafe(this.shadowPath(id));
    }
    shadowPath(id) {
        return join(this.controlPlaneRoot, SHADOW_DIR, `${id}.md`);
    }
    loadLedger() {
        if (this.ledger)
            return this.ledger;
        const path = join(this.controlPlaneRoot, LEDGER_FILE);
        const raw = readControlFileOrAbsent(path, "committed-rev ledger");
        if (raw === undefined) {
            this.ledger = Object.create(null);
            this.persistedSchemaVersion = "v2";
            this.unboundRowsKnown = false;
            return this.ledger;
        }
        const parsed = parseLedgerText(raw, path);
        this.ledger = parsed.rows;
        this.envelopeExtras = parsed.envelopeExtras;
        this.persistedSchemaVersion = parsed.form;
        this.unboundRowsKnown = hasUnboundRows(parsed.rows);
        if (parsed.form === "v1" && !this.announcedV1Compat) {
            this.announcedV1Compat = true;
            this.enqueueExternalItems(["memory ledger v1-compat active — v2 binding guarantees not yet in force (the migration runs at the next uncontended entry)"]);
        }
        return this.ledger;
    }
    saveLedger() {
        if (!this.ledger)
            return;
        if (this.persistedSchemaVersion !== "v2") {
            throw new ControlPlaneCorruptError("committed-rev ledger write refused: the on-disk ledger is still schema v1 and the migration has not run (mutations must migrate first)");
        }
        atomicWriteFileSync(join(this.controlPlaneRoot, LEDGER_FILE), `${JSON.stringify(ledgerEnvelope(this.ledger, this.envelopeExtras), null, 2)}\n`);
        this.unboundRowsKnown = hasUnboundRows(this.ledger);
    }
    persistLedgerV1(rows) {
        const map = Object.create(null);
        for (const [id, row] of rowsEntries(rows))
            map[id] = row.rev;
        atomicWriteFileSync(join(this.controlPlaneRoot, LEDGER_FILE), `${JSON.stringify(map, null, 2)}\n`);
        this.unboundRowsKnown = Object.keys(map).length > 0;
    }
    enqueueExternalItems(items) {
        if (items.length === 0)
            return;
        try {
            enqueueMemoryAnnouncement(this.controlPlaneRoot, { kind: "external", at: this.now(), items });
        }
        catch {
        }
    }
    announceAdoptionNotice(key, item) {
        if (this.adoptionNoticeKeys.has(key))
            return;
        this.adoptionNoticeKeys.add(key);
        this.enqueueExternalItems([item]);
    }
    announceUnboundRows(rows) {
        const ids = unboundRowIds(rows);
        const item = ids.length === 0 ? undefined : `${UNBOUND_DISCLOSURE_PREFIX}${ids.length} unbound row(s) (no derivable committed projection): ${ids.slice(0, 8).join(", ")}${ids.length > 8 ? ", …" : ""} — v2 binding guarantees do not cover them until they converge (duplicate projections need an unrestricted harvest or manual removal)`;
        try {
            enqueueMemoryAnnouncement(this.controlPlaneRoot, { kind: "external", at: this.now(), items: item === undefined ? [] : [item] }, { supersedesPrefix: UNBOUND_DISCLOSURE_PREFIX });
        }
        catch {
        }
    }
    bindingAbsPath(binding) {
        return join(scopeDirFor(this.directoryRoot, this.controlPlaneRoot, binding.scope), `${binding.slug}.md`);
    }
    bindingRelPath(binding) {
        return relative(this.directoryRoot, this.bindingAbsPath(binding));
    }
    censusProjectionsLocked() {
        let complete = true;
        const byId = new Map();
        for (const scope of Object.keys(registeredScopes(this.controlPlaneRoot))) {
            const dir = this.scopeDir(scope);
            const walk = (d, depth) => {
                let names;
                try {
                    names = readdirSync(d);
                }
                catch (err) {
                    if (err.code === "ENOENT" && depth === 0)
                        return;
                    complete = false;
                    return;
                }
                for (const name of names.sort()) {
                    if (name.startsWith(".") || name === MEMORY_INDEX_FILENAME)
                        continue;
                    const p = join(d, name);
                    let st;
                    try {
                        st = lstatSync(p);
                    }
                    catch {
                        complete = false;
                        continue;
                    }
                    if (st.isSymbolicLink())
                        continue;
                    if (st.isDirectory()) {
                        if (depth + 1 <= DEFAULT_MAX_ENTRY_DEPTH)
                            walk(p, depth + 1);
                        continue;
                    }
                    if (!st.isFile() || !name.endsWith(".md"))
                        continue;
                    let text;
                    try {
                        text = readFileSync(p, "utf8");
                    }
                    catch {
                        complete = false;
                        continue;
                    }
                    const id = parseEntryFile(text).id;
                    if (id === undefined)
                        continue;
                    const list = byId.get(id) ?? [];
                    list.push({ scope, slug: relative(dir, p).replace(/\.md$/, "") });
                    byId.set(id, list);
                }
            };
            walk(dir, 0);
        }
        return { complete, byId };
    }
    writeV1BackupOnce() {
        const raw = readControlFileOrAbsent(join(this.controlPlaneRoot, LEDGER_FILE), "committed-rev ledger");
        if (raw === undefined)
            throw new ControlPlaneCorruptError(`committed-rev ledger vanished under the migration lock: ${join(this.controlPlaneRoot, LEDGER_FILE)}`);
        const bak = join(this.controlPlaneRoot, LEDGER_V1_BACKUP_FILE);
        let fd;
        try {
            fd = openSync(bak, "wx", 0o600);
        }
        catch (err) {
            if (err.code === "EEXIST")
                return;
            throw err;
        }
        try {
            writeAllSync(fd, raw);
            fsyncSync(fd);
        }
        finally {
            closeSync(fd);
        }
    }
    tryAcquireTxnLockSync() {
        const lockDir = this.txnLockDir();
        const ownerPath = join(lockDir, "owner");
        const tryMkdir = () => {
            try {
                mkdirSync(this.controlPlaneRoot, { recursive: true });
            }
            catch (err) {
                throw new ControlPlaneCorruptError(`memory control-plane root could not be created (${err.code ?? "io error"}) at ${this.controlPlaneRoot} — a lock-acquisition fault is not contention; deferring recovery/migration on it would be silent (fail-closed)`, { cause: err });
            }
            try {
                mkdirSync(lockDir);
                return true;
            }
            catch (err) {
                if (err.code === "EEXIST")
                    return false;
                throw new ControlPlaneCorruptError(`memory txn lock could not be created (${err.code ?? "io error"}) at ${lockDir} — a lock-acquisition fault is not contention; deferring recovery/migration on it would be silent (fail-closed)`, { cause: err });
            }
        };
        let acquired = tryMkdir();
        if (!acquired) {
            let mtimeMs;
            try {
                mtimeMs = statSync(lockDir).mtimeMs;
            }
            catch (err) {
                if (err.code !== "ENOENT") {
                    throw new ControlPlaneCorruptError(`memory txn lock state could not be probed (${err.code ?? "io error"}) at ${lockDir} — a probe failure is not contention (fail-closed)`, { cause: err });
                }
                let occupant;
                try {
                    occupant = lstatSync(lockDir);
                }
                catch (lerr) {
                    if (lerr.code !== "ENOENT") {
                        throw new ControlPlaneCorruptError(`memory txn lock re-probe failed (${lerr.code ?? "io error"}) at ${lockDir} — a probe failure is not contention (fail-closed)`, { cause: lerr });
                    }
                }
                if (occupant !== undefined && !occupant.isDirectory()) {
                    throw new ControlPlaneCorruptError(`memory txn lock path is occupied by an invalid node (a dangling symlink?) at ${lockDir} — not contention and not a release race; remove the occupant (fail-closed)`, { cause: err });
                }
                mtimeMs = undefined;
            }
            if (mtimeMs !== undefined && this.now() - mtimeMs > TXN_LOCK_STALE_MS) {
                let tokenPresent = false;
                try {
                    statSync(ownerPath);
                    tokenPresent = true;
                }
                catch (err) {
                    if (err.code !== "ENOENT") {
                        throw new ControlPlaneCorruptError(`memory txn lock owner token could not be probed (${err.code ?? "io error"}) at ${ownerPath} — a probe failure is not a mid-acquisition (fail-closed)`, { cause: err });
                    }
                }
                if (tokenPresent) {
                    const tomb = join(this.controlPlaneRoot, `txn.lock.stale-${randomUUID()}`);
                    let stolen = false;
                    try {
                        renameSync(lockDir, tomb);
                        stolen = true;
                    }
                    catch (err) {
                        if (err.code !== "ENOENT") {
                            throw new ControlPlaneCorruptError(`memory txn lock stale-steal rename failed (${err.code ?? "io error"}) at ${lockDir} — a rename fault is not a lost race (fail-closed)`, { cause: err });
                        }
                    }
                    if (stolen) {
                        rmSync(tomb, { recursive: true, force: true });
                        acquired = tryMkdir();
                    }
                }
            }
            if (!acquired)
                return undefined;
        }
        const token = randomUUID();
        try {
            const fd = openSync(ownerPath, "w", 0o600);
            try {
                writeAllSync(fd, token);
                fsyncSync(fd);
            }
            finally {
                closeSync(fd);
            }
        }
        catch (err) {
            try {
                rmSync(lockDir, { recursive: true, force: true });
            }
            catch {
            }
            throw new Error(`memory txn lock owner token could not be written at ${ownerPath}: ${thrownText(err)}`);
        }
        return {
            token,
            release: () => {
                try {
                    if (readFileSync(ownerPath, "utf8") !== token)
                        return;
                }
                catch {
                    return;
                }
                try {
                    rmSync(lockDir, { recursive: true, force: true });
                }
                catch {
                }
            },
        };
    }
    tryOpportunisticMigrate() {
        const lock = this.tryAcquireTxnLockSync();
        if (lock === undefined)
            return;
        try {
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token);
            this.migrateLockedIfNeeded(lock.token);
        }
        finally {
            lock.release();
        }
    }
    migrateLockedIfNeeded(lockToken) {
        this.ledger = undefined;
        const rows = this.loadLedger();
        if (this.persistedSchemaVersion === "v2" && !hasUnboundRows(rows))
            return;
        const fullMigration = this.persistedSchemaVersion === "v1";
        const census = this.censusProjectionsLocked();
        if (!census.complete) {
            this.announceAdoptionNotice("migration-incomplete", `memory ledger ${fullMigration ? "v1→v2 migration" : "unbound-row convergence"} abandoned this pass — the projection census could not read every scope (bindings derived from a partial view could be wrong); it retries at the next entry`);
            return;
        }
        const now = this.now();
        const next = Object.create(null);
        const bound = [];
        const unbound = [];
        const events = [];
        for (const [id, row] of rowsEntries(rows)) {
            if (!fullMigration && rowsBinding(rows, id) !== undefined) {
                next[id] = { ...row };
                continue;
            }
            const projections = census.byId.get(id) ?? [];
            const sole = projections[0];
            if (projections.length === 1 && sole !== undefined) {
                next[id] = { ...row, scope: sole.scope, slug: sole.slug };
                bound.push(id);
                if (!fullMigration)
                    events.push({ ev: randomUUID(), channel: "migration-bind", id, to: sole, at: now });
            }
            else {
                next[id] = { ...row };
                unbound.push(`${id} (${projections.length} projection(s))`);
            }
        }
        if (fullMigration) {
            this.writeV1BackupOnce();
            events.push({ ev: randomUUID(), channel: "migration", boundRows: bound.length, unboundRows: unbound.length, at: now });
        }
        else if (bound.length === 0) {
            return;
        }
        this.assertTxnLockOwnership(lockToken);
        this.precheckTransfersAppendable();
        const journal = { txn: `${Date.now()}-migrate`, ops: [], ledger2: ledgerEnvelope(next, this.envelopeExtras), transfers: events };
        atomicWriteFileSync(join(this.controlPlaneRoot, JOURNAL_FILE), JSON.stringify(journal));
        this.ledger = next;
        this.persistedSchemaVersion = "v2";
        this.saveLedger();
        this.appendTransfers(events);
        rmSync(join(this.controlPlaneRoot, JOURNAL_FILE), { force: true });
        this.enqueueExternalItems([
            fullMigration
                ? `memory ledger upgraded to v2 (${bound.length} row(s) bound, ${unbound.length} unbound${unbound.length > 0 ? `: ${unbound.slice(0, 8).join(", ")}${unbound.length > 8 ? ", …" : ""}` : ""})`
                : `memory ledger unbound row(s) converged to bound: ${bound.slice(0, 8).join(", ")}${bound.length > 8 ? ", …" : ""}`,
        ]);
    }
    chainDegradedMarkerPresent() {
        return readControlFileOrAbsent(join(this.controlPlaneRoot, CHAIN_DEGRADED_FILE), "evidence-chain degradation marker") !== undefined;
    }
    mintChainDegradedMarker(fragmentPath) {
        const path = join(this.controlPlaneRoot, CHAIN_DEGRADED_FILE);
        let fd;
        try {
            fd = openSync(path, "wx", 0o600);
        }
        catch (err) {
            if (err.code === "EEXIST") {
                this.fsyncControlPlaneDir();
                return;
            }
            throw new ControlPlaneCorruptError(`evidence-chain degradation marker could not be created at ${path} (${err.code ?? "io error"}) — heal aborted, the chain is unchanged (fail-closed)`);
        }
        try {
            writeAllSync(fd, `${JSON.stringify({ at: this.now(), ...(fragmentPath !== undefined ? { fragment: fragmentPath } : {}) }, null, 2)}\n`);
            fsyncSync(fd);
        }
        finally {
            closeSync(fd);
        }
        this.fsyncControlPlaneDir();
        if (readControlFileOrAbsent(path, "evidence-chain degradation marker") === undefined) {
            throw new ControlPlaneCorruptError(`evidence-chain degradation marker vanished after its write at ${path} — heal aborted, the chain is unchanged (fail-closed)`);
        }
    }
    fsyncControlPlaneDir() {
        if (process.platform === "win32")
            return;
        let dfd;
        try {
            dfd = openSync(this.controlPlaneRoot, "r");
        }
        catch (err) {
            throw new ControlPlaneCorruptError(`evidence-chain degradation marker's directory could not be opened for fsync (${err.code ?? "io error"}) at ${this.controlPlaneRoot} — heal aborted, the chain is unchanged (fail-closed: an unproven marker must not authorize a truncation)`, { cause: err });
        }
        try {
            fsyncSync(dfd);
        }
        catch (err) {
            throw new ControlPlaneCorruptError(`evidence-chain degradation marker's directory entry could not be fsynced (${err.code ?? "io error"}) at ${this.controlPlaneRoot} — heal aborted, the chain is unchanged (fail-closed: an unproven marker must not authorize a truncation)`, { cause: err });
        }
        finally {
            closeSync(dfd);
        }
    }
    readTransferChain(fenceToken) {
        const path = join(this.controlPlaneRoot, TRANSFERS_FILE);
        const raw = readControlFileOrAbsent(path, "transfer evidence log");
        const out = [];
        if (raw === undefined)
            return out;
        const byEv = new Map();
        const lines = raw.split("\n");
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (line === undefined || line === "")
                continue;
            let parsed;
            try {
                parsed = JSON.parse(line);
            }
            catch {
                const isTail = lines.slice(i + 1).every((l) => l === "");
                if (!isTail)
                    throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is unparseable (not a torn tail — fail-closed): ${path}`);
                let journalHasPendingEvidence = false;
                const jraw = readControlFileOrAbsent(join(this.controlPlaneRoot, JOURNAL_FILE), "transaction journal");
                if (jraw !== undefined) {
                    try {
                        const jparsed = JSON.parse(jraw);
                        const jt = typeof jparsed === "object" && jparsed !== null ? jparsed.transfers : undefined;
                        journalHasPendingEvidence = Array.isArray(jt) && jt.length > 0;
                    }
                    catch {
                        journalHasPendingEvidence = false;
                    }
                }
                const outOfBand = !journalHasPendingEvidence;
                if (fenceToken !== undefined)
                    this.assertTxnLockOwnership(fenceToken);
                let fragmentPath;
                try {
                    ensureDirExists(join(this.controlPlaneRoot, QUARANTINE_DIR));
                    fragmentPath = join(this.controlPlaneRoot, QUARANTINE_DIR, `transfers-torn-tail-${this.now()}.fragment`);
                    atomicWriteFileSync(fragmentPath, line);
                }
                catch {
                    throw new ControlPlaneCorruptError(`transfer evidence log has a torn tail that could not be quarantined: ${path}`);
                }
                if (outOfBand)
                    this.mintChainDegradedMarker(fragmentPath);
                this.chainHealProbe?.();
                if (fenceToken !== undefined)
                    this.assertTxnLockOwnership(fenceToken);
                try {
                    atomicWriteFileSync(path, lines.slice(0, i).join("\n") + (i > 0 ? "\n" : ""));
                }
                catch {
                    throw new ControlPlaneCorruptError(`transfer evidence log has a torn tail that could not be healed (truncation failed): ${path}`);
                }
                this.enqueueExternalItems([
                    outOfBand
                        ? `transfer evidence log had a torn tail with NO transaction journal beside it (out-of-band damage) — the fragment is quarantined, the sound prefix stands, and the chain is durably marked degraded: new id-bearing files are refused adoption until an explicit rebuild clears ${CHAIN_DEGRADED_FILE}`
                        : `transfer evidence log had a torn tail (crash mid-append) — the fragment is quarantined and the sound prefix stands: ${TRANSFERS_FILE}`,
                ]);
                break;
            }
            const invalid = transferEventInvalid(parsed);
            if (invalid !== undefined)
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is invalid (${invalid}): ${path}`);
            const rec = parsed;
            const ev = rec.ev;
            const canonical = canonicalJsonStringify(rec);
            const prior = byEv.get(ev);
            if (prior !== undefined) {
                if (prior !== canonical) {
                    throw new ControlPlaneCorruptError(`transfer evidence log carries event id ${JSON.stringify(ev)} twice with DIFFERENT payloads (line ${i + 1}) — one ev is one identity; refusing to pick a side (fail-closed): ${path}`);
                }
                continue;
            }
            byEv.set(ev, canonical);
            out.push({ ev, canonical, parsed: rec });
        }
        return out;
    }
    readTransferEvents() {
        const out = new Map();
        for (const row of this.readTransferChain())
            out.set(row.ev, row.canonical);
        return out;
    }
    deletedIdsDigestLocked() {
        const path = join(this.controlPlaneRoot, TRANSFERS_FILE);
        let fp;
        try {
            const st = statSync(path);
            fp = `${st.size}:${st.mtimeMs}`;
        }
        catch (err) {
            if (err.code === "ENOENT")
                return { state: "complete", ids: new Set() };
            throw new ControlPlaneCorruptError(`transfer evidence log could not be probed (${err.code ?? "io error"}) at ${path} — a probe failure is not an empty chain (fail-closed)`);
        }
        const cached = this.deletedIdsDigest;
        if (cached !== undefined && cached.fp === fp)
            return { state: cached.state, ids: cached.ids };
        const raw = readControlFileOrAbsent(path, "transfer evidence log");
        if (raw === undefined)
            return { state: "complete", ids: new Set() };
        let state = "complete";
        const ids = new Set();
        const lines = raw.split("\n");
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (line === undefined || line === "")
                continue;
            let parsed;
            try {
                parsed = JSON.parse(line);
            }
            catch {
                if (lines.slice(i + 1).every((l) => l === "")) {
                    state = "damaged";
                    break;
                }
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is unparseable (not a torn tail — fail-closed): ${path}`);
            }
            if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is not an object (fail-closed): ${path}`);
            }
            const e = parsed;
            if (KNOWN_TRANSFER_CHANNELS.has(e.channel)) {
                const invalid = transferEventInvalid(parsed);
                if (invalid !== undefined)
                    throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is invalid (${invalid}): ${path}`);
                if (e.channel === "delete")
                    ids.add(e.id);
            }
        }
        this.deletedIdsDigest = { fp, state, ids };
        return { state, ids };
    }
    resurrectionBackstopVerdict(id) {
        const digest = this.deletedIdsDigestLocked();
        if (digest.ids.has(id))
            return "erased";
        if (digest.state === "damaged" || this.chainDegradedMarkerPresent())
            return "degraded";
        return "clear";
    }
    retrievalDeletedIds() {
        return this.deletedIdsDigestLocked().ids;
    }
    freshCommittedRowsPure() {
        try {
            const raw = readControlFileOrAbsent(join(this.controlPlaneRoot, LEDGER_FILE), "committed-rev ledger");
            if (raw === undefined)
                return Object.create(null);
            const parsed = parseLedgerText(raw, join(this.controlPlaneRoot, LEDGER_FILE));
            return parsed.form === "v2" ? parsed.rows : Object.create(null);
        }
        catch (err) {
            const code = err.code ?? (err instanceof ControlPlaneCorruptError ? "corrupt" : "io error");
            this.announceAdoptionNotice(`retrieval-ledger-fault|${code}`, `memory retrieval guard: the committed-rev ledger could not be read on the lock-free retrieval face (${thrownText(err)}) — ` +
                `ids carrying delete evidence are WITHHELD from retrieval (a legitimate re-add cannot be verified over an unreadable account) until the fault clears; ` +
                `entries with no delete evidence serve normally`);
            return Object.create(null);
        }
    }
    precheckTransfersAppendable() {
        this.readTransferEvents();
        const fd = openSync(join(this.controlPlaneRoot, TRANSFERS_FILE), "a", 0o600);
        closeSync(fd);
    }
    appendTransfers(events) {
        if (events.length === 0)
            return;
        this.transfersAppendFault?.();
        const existing = this.readTransferEvents();
        const batch = new Map();
        const toWrite = [];
        for (const e of events) {
            const canonical = canonicalJsonStringify(e);
            const inBatch = batch.get(e.ev);
            if (inBatch !== undefined) {
                if (inBatch !== canonical) {
                    throw new ControlPlaneCorruptError(`transfer evidence batch mints event id ${JSON.stringify(e.ev)} twice with DIFFERENT payloads — one ev is one identity (minting bug, fail-closed)`);
                }
                continue;
            }
            batch.set(e.ev, canonical);
            const onChain = existing.get(e.ev);
            if (onChain !== undefined) {
                if (onChain !== canonical) {
                    throw new ControlPlaneCorruptError(`transfer evidence append collides on event id ${JSON.stringify(e.ev)}: the chain already carries a DIFFERENT payload under it — one ev is one identity (fail-closed)`);
                }
                continue;
            }
            toWrite.push(canonical);
        }
        if (toWrite.length === 0)
            return;
        const raw = readControlFileOrAbsent(join(this.controlPlaneRoot, TRANSFERS_FILE), "transfer evidence log");
        const needsSeparator = raw !== undefined && raw.length > 0 && !raw.endsWith("\n");
        const fd = openSync(join(this.controlPlaneRoot, TRANSFERS_FILE), "a", 0o600);
        try {
            writeAllSync(fd, (needsSeparator ? "\n" : "") + toWrite.map((c) => `${c}\n`).join(""));
            fsyncSync(fd);
        }
        finally {
            closeSync(fd);
        }
    }
    recoverJournal(fence) {
        this.assertRootIntact();
        if (fence.token === undefined) {
            if (this.txnInFlight())
                return;
            const lock = this.tryAcquireTxnLockSync();
            if (lock === undefined)
                return;
            try {
                this.recoverJournal({ token: lock.token });
            }
            finally {
                lock.release();
            }
            return;
        }
        const fenceToken = fence.token;
        const jp = join(this.controlPlaneRoot, JOURNAL_FILE);
        const raw = readControlFileOrAbsent(jp, "transaction journal");
        if (raw !== undefined) {
            const journal = parseJournal(raw, jp);
            const { rows: snapshotRows, extras: snapshotExtras, form: snapshotForm } = journalLedgerSnapshot(journal, jp);
            if (snapshotForm === "v1") {
                try {
                    const diskRaw = readControlFileOrAbsent(join(this.controlPlaneRoot, LEDGER_FILE), "committed-rev ledger");
                    const diskParsed = diskRaw !== undefined ? JSON.parse(diskRaw) : undefined;
                    if (diskParsed !== null && typeof diskParsed === "object" && "v" in diskParsed && diskParsed.v === LEDGER_SCHEMA_VERSION) {
                        this.enqueueExternalItems([
                            "memory ledger: recovering a v1-format journal over a v2 ledger — a pre-v2 process wrote AFTER this store migrated (mixed-version deployment window). Retire old readers/writers before further writes; see the v2 rollback runbook.",
                        ]);
                    }
                }
                catch {
                }
            }
            for (const op of journal.ops) {
                const invalid = journalOpInvalid(op, this.directoryRoot);
                if (invalid !== undefined) {
                    throw new ControlPlaneCorruptError(`transaction journal op is invalid (${invalid}; kind=${JSON.stringify(op.kind)} target=${JSON.stringify(op.target)}) — the whole journal is refused and kept at ${jp} (control data is malformed: repair via the explicit control-plane rebuild path, not a replay guess)`);
                }
            }
            if (journal.transfers !== undefined) {
                if (!Array.isArray(journal.transfers))
                    throw new ControlPlaneCorruptError(`transaction journal transfers is not an array: ${jp}`);
                const journalEvs = new Map();
                for (const e of journal.transfers) {
                    const invalid = transferEventInvalid(e);
                    if (invalid !== undefined)
                        throw new ControlPlaneCorruptError(`transaction journal transfer event is invalid (${invalid}): ${jp}`);
                    if (e.origin !== undefined) {
                        throw new ControlPlaneCorruptError(`transaction journal carries an origin-bearing (imported) transfer event — imports never ride a journal: ${jp}`);
                    }
                    const canonical = canonicalJsonStringify(e);
                    const prior = journalEvs.get(e.ev);
                    if (prior !== undefined && prior !== canonical) {
                        throw new ControlPlaneCorruptError(`transaction journal mints event id ${JSON.stringify(e.ev)} twice with DIFFERENT payloads (fail-closed before redo): ${jp}`);
                    }
                    journalEvs.set(e.ev, canonical);
                }
                if (snapshotForm === "v1")
                    throw new ControlPlaneCorruptError(`transaction journal carries transfers with a v1 snapshot (impossible form): ${jp}`);
                if (journalEvs.size > 0) {
                    for (const row of this.readTransferChain(fenceToken)) {
                        const j = journalEvs.get(row.ev);
                        if (j !== undefined && j !== row.canonical) {
                            throw new ControlPlaneCorruptError(`transaction journal event id ${JSON.stringify(row.ev)} collides with a DIFFERENT payload already on the evidence chain (fail-closed before redo): ${jp}`);
                        }
                    }
                }
                const transfers = journal.transfers;
                const rowPresent = (id) => Object.prototype.hasOwnProperty.call(snapshotRows, id);
                const deleteRows = transfers.filter((t) => t.channel === "delete");
                const anchorRows = transfers.filter((t) => t.channel === "erasure-anchor");
                const requestRows = transfers.filter((t) => t.channel === "erasure-request");
                let chainAnchors;
                for (const e of transfers) {
                    if (e.channel === "adopted-move" || e.channel === "applyPatches-move" || e.channel === "migration-bind") {
                        if (!bindingEquals(rowsBinding(snapshotRows, e.id), e.to)) {
                            throw new ControlPlaneCorruptError(`transaction journal transfer event for ${JSON.stringify(e.id)} disagrees with the snapshot binding: ${jp}`);
                        }
                        continue;
                    }
                    if (e.channel !== "delete")
                        continue;
                    if (rowPresent(e.id)) {
                        throw new ControlPlaneCorruptError(`transaction journal delete evidence for ${JSON.stringify(e.id)} disagrees with its snapshot (the row survives) — a delete claim over a surviving row is refused: ${jp}`);
                    }
                    if (e.legacyId !== true) {
                        if (!journal.ops.some((op) => op.kind === "shadow-delete" && op.target === e.id)) {
                            throw new ControlPlaneCorruptError(`transaction journal delete evidence for ${JSON.stringify(e.id)} has no same-journal shadow-delete op (uncorrelated delete claim, fail-closed): ${jp}`);
                        }
                        if (e.from !== undefined) {
                            const derived = this.bindingAbsPath(e.from);
                            if (!journal.ops.some((op) => op.kind === "delete" && op.target === derived)) {
                                throw new ControlPlaneCorruptError(`transaction journal delete evidence for ${JSON.stringify(e.id)} has no same-journal physical delete op at its bound projection path (an account-only delete would leave the disk copy to resurrect, fail-closed): ${jp}`);
                            }
                        }
                    }
                    if (e.req !== undefined && !anchorRows.some((a) => a.req === e.req)) {
                        chainAnchors ??= this.readTransferChain(fenceToken)
                            .map((r) => r.parsed)
                            .filter((p) => p.channel === "erasure-anchor" && p.origin === undefined);
                        const matching = chainAnchors.filter((p) => p.req === e.req);
                        const pinned = matching.length === 1 ? matching[0]?.ids : undefined;
                        if (pinned === undefined || !pinned.includes(e.id)) {
                            throw new ControlPlaneCorruptError(`transaction journal carries a request-driven delete for ${JSON.stringify(e.id)} (req ${JSON.stringify(e.req)}) with no same-journal anchor and no matching single chain anchor pinning the id — an unanchored erasure claim is refused: ${jp}`);
                        }
                    }
                }
                const chainAnchorEvsByReq = new Map();
                if (anchorRows.length > 0) {
                    for (const p of (chainAnchors ??= this.readTransferChain(fenceToken)
                        .map((r) => r.parsed)
                        .filter((p) => p.channel === "erasure-anchor" && p.origin === undefined))) {
                        const set = chainAnchorEvsByReq.get(p.req) ?? new Set();
                        set.add(p.ev);
                        chainAnchorEvsByReq.set(p.req, set);
                    }
                }
                const anchorReqs = new Set();
                for (const a of anchorRows) {
                    if (anchorReqs.has(a.req))
                        throw new ControlPlaneCorruptError(`transaction journal carries two erasure anchors for req ${JSON.stringify(a.req)} (one request, one anchor): ${jp}`);
                    const chainEvs = chainAnchorEvsByReq.get(a.req);
                    if (chainEvs !== undefined && [...chainEvs].some((ev) => ev !== a.ev)) {
                        throw new ControlPlaneCorruptError(`transaction journal mints an erasure anchor for req ${JSON.stringify(a.req)} under a DIFFERENT ev than the one already on the evidence chain (one request, one anchor — spliced journal refused): ${jp}`);
                    }
                    anchorReqs.add(a.req);
                    const pinned = new Set(a.ids);
                    const reqDeletes = deleteRows.filter((d) => d.req === a.req);
                    const seenDeleteIds = new Set();
                    for (const d of reqDeletes) {
                        if (!pinned.has(d.id))
                            throw new ControlPlaneCorruptError(`transaction journal delete evidence for ${JSON.stringify(d.id)} is outside its anchor's pinned set (req ${JSON.stringify(a.req)}): ${jp}`);
                        if (seenDeleteIds.has(d.id))
                            throw new ControlPlaneCorruptError(`transaction journal carries two delete rows for ${JSON.stringify(d.id)} under one request (req ${JSON.stringify(a.req)}): ${jp}`);
                        seenDeleteIds.add(d.id);
                    }
                    const reqProjections = requestRows.filter((r) => r.req === a.req);
                    const projScopesOf = new Map();
                    const seenProjScopes = new Set();
                    for (const r of reqProjections) {
                        if (seenProjScopes.has(r.scope))
                            throw new ControlPlaneCorruptError(`transaction journal carries two erasure projection rows for scope ${JSON.stringify(r.scope)} under one request (req ${JSON.stringify(a.req)}): ${jp}`);
                        seenProjScopes.add(r.scope);
                        if (r.selectHash !== a.selectHash)
                            throw new ControlPlaneCorruptError(`transaction journal erasure projection row (req ${JSON.stringify(a.req)}, scope ${JSON.stringify(r.scope)}) disagrees with its anchor's selectHash: ${jp}`);
                        for (const id of r.ids) {
                            if (!pinned.has(id))
                                throw new ControlPlaneCorruptError(`transaction journal erasure projection row carries ${JSON.stringify(id)} outside its anchor's pinned set (req ${JSON.stringify(a.req)}): ${jp}`);
                            const list = projScopesOf.get(id) ?? [];
                            list.push(r.scope);
                            projScopesOf.set(id, list);
                        }
                    }
                    const expectedNotFound = new Set();
                    for (const id of a.ids) {
                        const dRows = reqDeletes.filter((d) => d.id === id);
                        let expectScope;
                        if (dRows.length > 0) {
                            const withFrom = dRows.find((d) => d.from !== undefined);
                            expectScope = withFrom?.from?.scope;
                        }
                        else if (rowPresent(id)) {
                            expectScope = rowsBinding(snapshotRows, id)?.scope;
                        }
                        else {
                            expectedNotFound.add(id);
                        }
                        const cells = projScopesOf.get(id) ?? [];
                        if (expectScope === undefined) {
                            if (cells.length !== 0)
                                throw new ControlPlaneCorruptError(`transaction journal erasure partition violation: ${JSON.stringify(id)} appears in a projection row but belongs to no scope cell (req ${JSON.stringify(a.req)}): ${jp}`);
                        }
                        else if (cells.length !== 1 || cells[0] !== expectScope) {
                            throw new ControlPlaneCorruptError(`transaction journal erasure partition violation: ${JSON.stringify(id)} must appear exactly once in the ${JSON.stringify(expectScope)} projection row (req ${JSON.stringify(a.req)}): ${jp}`);
                        }
                    }
                    const claimedNotFound = new Set(a.notFound);
                    if (claimedNotFound.size !== expectedNotFound.size || [...expectedNotFound].some((id) => !claimedNotFound.has(id))) {
                        throw new ControlPlaneCorruptError(`transaction journal erasure anchor's notFound set disagrees with the journal's own terminal facts (req ${JSON.stringify(a.req)}): ${jp}`);
                    }
                }
                for (const r of requestRows) {
                    if (!anchorRows.some((a) => a.req === r.req)) {
                        throw new ControlPlaneCorruptError(`transaction journal erasure projection row (req ${JSON.stringify(r.req)}) has no same-journal anchor — projection rows ride the first-execution journal only: ${jp}`);
                    }
                }
            }
            this.assertTxnLockOwnership(fenceToken);
            const replayAt = `the journal replay (journal kept at ${jp})`;
            this.refuseEscapingTargets(journal.ops, replayAt);
            for (const op of journal.ops) {
                this.refuseEscapingTargets([op], replayAt);
                try {
                    if (op.kind === "write" && op.content !== undefined) {
                        atomicWriteFileSync(op.target, op.content);
                        if (op.staged)
                            rmSync(op.staged, { force: true });
                    }
                    else if (op.kind === "delete") {
                        rmSync(op.target, { force: true });
                    }
                    else if (op.kind === "shadow-write" && op.content !== undefined) {
                        atomicWriteFileSync(this.shadowPath(op.target), op.content);
                    }
                    else if (op.kind === "shadow-delete") {
                        rmSync(this.shadowPath(op.target), { force: true });
                    }
                }
                catch (err) {
                    const code = err.code;
                    throw new ControlPlaneCorruptError(`transaction journal replay failed on op kind=${op.kind} target=${JSON.stringify(op.target)} (${code ?? (thrownText(err))}) — the journal is kept at ${jp}; ${permissionRemedyOf(err, this.directoryRoot) ?? "an I/O failure retries at the next construction, a persistent fault needs the explicit control-plane rebuild path"}`);
                }
            }
            this.recoveryCommitProbe?.();
            this.assertTxnLockOwnership(fenceToken);
            if (snapshotForm === "v2") {
                this.ledger = snapshotRows;
                this.envelopeExtras = snapshotExtras;
                this.persistedSchemaVersion = "v2";
                this.saveLedger();
                this.appendTransfers(journal.transfers ?? []);
            }
            else {
                this.ledger = snapshotRows;
                this.persistedSchemaVersion = "v1";
                this.persistLedgerV1(snapshotRows);
            }
            if (typeof journal.txn === "string") {
                this.closeJournalIfOurs(journal.txn);
            }
            else {
                this.assertTxnLockOwnership(fenceToken);
                rmSync(jp, { force: true });
            }
        }
        this.assertTxnLockOwnership(fenceToken);
        this.sweepStagedFiles();
    }
    txnInFlight() {
        try {
            return this.now() - statSync(this.txnLockDir()).mtimeMs < TXN_LOCK_STALE_MS;
        }
        catch (err) {
            const code = err.code;
            if (code === "ENOENT")
                return false;
            throw new ControlPlaneCorruptError(`txn lock state could not be probed (${code ?? "io error"}) at ${this.txnLockDir()} — a probe failure is not "no writer" (fail-closed)`);
        }
    }
    refuseEscapingTargets(ops, at) {
        for (const op of ops) {
            if (op.kind !== "write" && op.kind !== "delete")
                continue;
            for (const path of op.staged === undefined ? [op.target] : [op.target, op.staged]) {
                const escape = storeTargetEscape(this.physicalRoot, path);
                if (escape === undefined)
                    continue;
                throw erasureCodedError(`${TARGET_OUTSIDE_STORE}: ${at} refused a ${op.kind} of ${op.target}${path === op.target ? "" : ` (its staged file ${path})`} — ${escape}; not performed`, TARGET_OUTSIDE_STORE);
            }
        }
    }
    assertRootIntact() {
        const now = landingPathOf(this.directoryRoot);
        if (now === this.physicalRoot)
            return;
        throw erasureCodedError(`${TARGET_OUTSIDE_STORE}: the store root ${this.directoryRoot} now resolves to ${now}, not ${this.physicalRoot} where this backend opened it — refused; open a new backend on the directory you mean`, TARGET_OUTSIDE_STORE);
    }
    sweepStagedFiles() {
        const walk = (d, depth) => {
            let names;
            try {
                names = readdirSync(d);
            }
            catch {
                return;
            }
            for (const name of names) {
                const p = join(d, name);
                if (name.startsWith(".stage-")) {
                    rmSync(p, { force: true });
                    continue;
                }
                if (depth < DEFAULT_MAX_ENTRY_DEPTH + 1) {
                    try {
                        if (lstatSync(p).isDirectory())
                            walk(p, depth + 1);
                    }
                    catch {
                    }
                }
            }
        };
        walk(this.directoryRoot, 0);
    }
    scopeDir(scope) {
        return registerScope(this.directoryRoot, this.controlPlaneRoot, scope);
    }
    containInboundReject(finding, absPath, text, entryId, committed) {
        this.inboundFindings.push(finding);
        const q = quarantineAndTombstone(absPath, text, join(this.controlPlaneRoot, QUARANTINE_DIR), this.now);
        const shadowText = committed !== undefined ? this.readCommittedShadow(entryId) : undefined;
        let restoredShadow = false;
        if (shadowText !== undefined) {
            try {
                atomicWriteFileSync(absPath, shadowText);
                restoredShadow = true;
            }
            catch {
            }
        }
        if (q.detail !== undefined || (!q.removed && !restoredShadow)) {
            const contained = q.removed || restoredShadow;
            this.inboundFindings.push({
                path: finding.path,
                code: "quarantine_failed",
                reason: `quarantine escalation: ${q.detail ?? "suspect copy not captured"}${contained ? "" : " — rejected content may still be on the model-visible plane"}`,
            });
        }
        return shadowText;
    }
    inboundGate(relPath, text) {
        const nameFinding = scanMemoryFileName(relPath);
        if (nameFinding !== undefined)
            return { path: relPath, code: "filename", reason: `inbound memory change blocked: ${nameFinding.reason} (quarantined)` };
        const findings = scanMemoryWrite(text, { maxBytes: MAX_MEMORY_BYTES });
        const first = findings[0];
        if (first !== undefined) {
            return { path: relPath, code: first.code, reason: `inbound memory change blocked: ${first.reason} (quarantined)` };
        }
        if (ambiguousOriginRepresentation(parseEntryFile(text).frontmatter)) {
            return { path: relPath, code: "invalid", reason: `inbound memory change blocked: conflicting or duplicated origin representations (ambiguous external-origin marker; quarantined)` };
        }
        return undefined;
    }
    readScope(scope, sync, probeOnly, lockToken) {
        if (!sync && this.batchScan !== undefined) {
            const memo = this.batchScan.get(scope);
            if (memo !== undefined)
                return memo;
        }
        if (this.persistedSchemaVersion === "v1" || this.unboundRowsKnown)
            this.tryOpportunisticMigrate();
        const dir = this.scopeDir(scope);
        const files = scanEntryFiles(dir);
        const entries = [];
        const rows = sync ? this.loadLedger() : undefined;
        const retrievalDeleted = !sync && files.length > 0 ? this.retrievalDeletedIds() : undefined;
        const retrievalGuard = retrievalDeleted !== undefined && retrievalDeleted.size > 0 ? { deleted: retrievalDeleted, account: this.freshCommittedRowsPure() } : undefined;
        let ledgerChanged = false;
        const adoptedExternal = [];
        const moveEvents = [];
        const moveAnnouncements = [];
        const skippedUnreadable = [];
        let table;
        const observed = () => (table ??= layerObservationLookup(this.controlPlaneRoot));
        let freshAccount;
        for (const f of files) {
            let text;
            try {
                text = readFileSync(f.path, "utf8");
            }
            catch {
                skippedUnreadable.push(relative(this.directoryRoot, f.path));
                continue;
            }
            const parsed = parseEntryFile(text);
            if (parsed.id === undefined)
                continue;
            let entry = entryFromFile(text, parsed.id, f.slug, scope);
            if (retrievalGuard !== undefined && retrievalGuard.deleted.has(entry.id) && rowsRev(retrievalGuard.account, entry.id) === undefined)
                continue;
            if (rows) {
                const committed = rowsRev(rows, entry.id);
                const binding = rowsBinding(rows, entry.id);
                const relPath = relative(this.directoryRoot, f.path);
                if (probeOnly === true && observed().covers(entry.id)) {
                    const fresh = (freshAccount ??= this.committedRowsOrFault() ?? null);
                    const freshBinding = fresh !== null ? rowsBinding(fresh, entry.id) : undefined;
                    if (fresh === null || rowsRev(fresh, entry.id) !== committed || freshBinding?.scope !== binding?.scope || freshBinding?.slug !== binding?.slug)
                        return undefined;
                }
                if (committed !== undefined && binding !== undefined && !(binding.scope === scope && binding.slug === f.slug)) {
                    const probe = probeProjection(this.bindingAbsPath(binding), entry.id);
                    if (probe === "present") {
                        this.announceAdoptionNotice(`copy|${relPath}|${entry.rev}`, `duplicate projection of committed entry ${entry.id} at ${JSON.stringify(inlineUntrusted(relPath))} — the committed projection lives at ${JSON.stringify(inlineUntrusted(this.bindingRelPath(binding)))}; the copy is not served (a copy is not a scope credential) and stays on disk for an unrestricted harvest or manual removal to adjudicate`);
                        continue;
                    }
                    if (probe === "undecidable") {
                        this.announceAdoptionNotice(`bindprobe|${relPath}`, `committed entry ${entry.id} appears at ${JSON.stringify(inlineUntrusted(relPath))} but is bound to ${JSON.stringify(inlineUntrusted(this.bindingRelPath(binding)))}, and the bound projection could not be probed conclusively (a read failure, or a foreign file at that address — not ENOENT) — nothing is served or adopted this pass`);
                        continue;
                    }
                    if (this.settledAsObserved(observed(), entries, entry, relPath, f.slug, scope, committed, binding, text, "quarantined-only"))
                        continue;
                    if (probeOnly)
                        return undefined;
                    if (lockToken !== undefined)
                        this.assertTxnLockOwnership(lockToken);
                    const adopted = this.adoptMoveCandidateLocked(rows, entry, f.path, f.slug, scope, relPath, text, committed, binding, moveEvents, moveAnnouncements, adoptedExternal);
                    if (adopted !== undefined) {
                        entries.push(adopted);
                        ledgerChanged = true;
                    }
                    continue;
                }
                if (committed !== entry.rev) {
                    if (committed !== undefined && this.settledAsObserved(observed(), entries, entry, relPath, f.slug, scope, committed, binding, text, "restored"))
                        continue;
                    if (probeOnly)
                        return undefined;
                    if (lockToken !== undefined)
                        this.assertTxnLockOwnership(lockToken);
                    if (committed === undefined) {
                        const verdict = this.resurrectionBackstopVerdict(entry.id);
                        if (verdict === "erased") {
                            this.containInboundReject({
                                path: relPath,
                                code: "invalid",
                                reason: `resurrected entry refused: id ${entry.id} carries a delete evidence row on the store's custody chain and has no committed account row — a deleted entry's bytes reappearing on disk are never re-adopted (quarantined; restoring the content is an explicit write minting a NEW id)`,
                            }, f.path, text, entry.id, undefined);
                            continue;
                        }
                        if (verdict === "degraded") {
                            this.announceAdoptionNotice(`chain-degraded|${relPath}|${entry.rev}`, `new memory entry file ${JSON.stringify(inlineUntrusted(relPath))} is NOT adopted: the transfer evidence chain is degraded (out-of-band tail damage), so "no delete row for this id" is no longer evidence it was never deleted — the file stays on disk unserved until the chain state is explicitly rebuilt (${CHAIN_DEGRADED_FILE})`);
                            continue;
                        }
                    }
                    let finding = this.inboundGate(relPath, text);
                    if (finding === undefined && committed !== undefined)
                        finding = this.whitewashInboundFinding(relPath, entry, "restored");
                    if (finding) {
                        const shadowText = this.containInboundReject(finding, f.path, text, entry.id, committed);
                        if (shadowText === undefined) {
                            if (committed !== undefined) {
                                deleteRow(rows, entry.id);
                                ledgerChanged = true;
                            }
                            continue;
                        }
                        entry = entryFromFile(shadowText, entry.id, f.slug, scope);
                    }
                    else {
                        if (committed === undefined && this.settledAsObserved(observed(), entries, entry, relPath, f.slug, scope, committed, binding, text, "restored"))
                            continue;
                        if (committed !== undefined)
                            adoptedExternal.push(relPath);
                        bindRowTracked(rows, entry.id, { rev: entry.rev, scope, slug: f.slug, at: this.now() }, moveEvents, "adopted-move");
                        try {
                            atomicWriteFileSync(this.shadowPath(entry.id), text);
                        }
                        catch {
                        }
                        ledgerChanged = true;
                    }
                }
                else if (this.readCommittedShadow(entry.id) === undefined) {
                    try {
                        atomicWriteFileSync(this.shadowPath(entry.id), text);
                    }
                    catch {
                    }
                }
            }
            entries.push(entry);
        }
        if (ledgerChanged) {
            if (lockToken !== undefined)
                this.assertTxnLockOwnership(lockToken);
            if (moveEvents.length > 0) {
                this.precheckTransfersAppendable();
                const journal = {
                    txn: `${Date.now()}-${randomUUID().slice(0, 8)}`,
                    ops: [],
                    ledger2: ledgerEnvelope(rows, this.envelopeExtras),
                    transfers: moveEvents,
                };
                atomicWriteFileSync(join(this.controlPlaneRoot, JOURNAL_FILE), JSON.stringify(journal));
                this.saveLedger();
                this.appendTransfers(moveEvents);
                rmSync(join(this.controlPlaneRoot, JOURNAL_FILE), { force: true });
            }
            else {
                this.saveLedger();
            }
        }
        if (moveAnnouncements.length > 0) {
            this.enqueueExternalItems(moveAnnouncements.slice(0, 12).concat(moveAnnouncements.length > 12 ? [`…and ${moveAnnouncements.length - 12} more adopted move(s)`] : []));
        }
        if (adoptedExternal.length > 0) {
            try {
                enqueueMemoryAnnouncement(this.controlPlaneRoot, {
                    kind: "external",
                    at: this.now(),
                    items: adoptedExternal
                        .slice(0, 12)
                        .map((rel) => `out-of-session memory change adopted: ${JSON.stringify(inlineUntrusted(rel))} — re-read it before relying on prior knowledge of it`)
                        .concat(adoptedExternal.length > 12 ? [`…and ${adoptedExternal.length - 12} more adopted change(s)`] : []),
                });
            }
            catch {
            }
        }
        if (skippedUnreadable.length > 0) {
            try {
                enqueueMemoryAnnouncement(this.controlPlaneRoot, {
                    kind: "external",
                    at: this.now(),
                    items: skippedUnreadable
                        .slice(0, 12)
                        .map((rel) => `committed memory entry could not be read and is MISSING from this session's memory: ${JSON.stringify(inlineUntrusted(rel))} — check the file (permissions/fs)`)
                        .concat(skippedUnreadable.length > 12 ? [`…and ${skippedUnreadable.length - 12} more unreadable entr(ies)`] : []),
                });
            }
            catch {
            }
        }
        if (!sync && this.batchScan !== undefined)
            this.batchScan.set(scope, entries);
        return entries;
    }
    settledAsObserved(table, out, entry, relPath, slug, scope, committed, binding, text, containment) {
        if (!table.covers(entry.id) || this.inboundGate(relPath, text) !== undefined)
            return false;
        if (committed !== undefined && committed !== entry.rev && this.whitewashInboundFinding(relPath, entry, containment) !== undefined)
            return false;
        const hit = table.find(entry.id, committed, entry.rev);
        if (hit === undefined)
            return false;
        const standIn = committed !== undefined && binding !== undefined && binding.scope === scope && binding.slug === slug ? this.readCommittedShadow(entry.id) : undefined;
        const notice = observedDivergenceNotice(relPath, standIn !== undefined, hit.undecided === true);
        try {
            enqueueMemoryAnnouncement(this.controlPlaneRoot, { kind: "external", at: this.now(), items: [notice] }, { supersedesPrefix: notice });
        }
        catch {
        }
        if (standIn !== undefined)
            out.push(entryFromFile(standIn, entry.id, slug, scope));
        return true;
    }
    whitewashInboundFinding(relPath, entry, containment) {
        const committedShadow = this.readCommittedShadow(entry.id);
        const committedFm = committedShadow !== undefined ? parseEntryFile(committedShadow).frontmatter : undefined;
        if (committedFm === undefined)
            return undefined;
        if (committedFm.provenance?.kind === "repo_file") {
            const stripsProvenance = entry.frontmatter.provenance === undefined;
            const stripsTrust = committedFm.trust !== undefined && entry.frontmatter.trust === undefined;
            if (stripsProvenance || stripsTrust) {
                return {
                    path: relPath,
                    code: "invalid",
                    reason: `inbound memory change blocked: repo_file provenance whitewash refused — the on-disk change drops ${stripsProvenance ? "provenance" : "trust"} from a repo-ingested entry ${containment === "restored" ? "(quarantined; committed content restored)" : "(quarantined)"}`,
                };
            }
        }
        const committedOrigin = committedOriginOf(committedFm);
        if (committedOrigin !== undefined && !originEquals(committedOrigin, committedOriginOf(entry.frontmatter))) {
            return {
                path: relPath,
                code: "invalid",
                reason: `inbound memory change blocked: external-origin marker whitewash refused — the on-disk change strips or rewrites the origin marker of a marked entry ${containment === "restored" ? "(quarantined; committed content restored)" : "(quarantined)"}`,
            };
        }
        return undefined;
    }
    adoptMoveCandidateLocked(rows, entry, absPath, slug, scope, relPath, text, committedRev, from, moveEvents, moveAnnouncements, adoptedExternal) {
        const census = this.censusProjectionsLocked();
        if (!census.complete) {
            this.inboundFindings.push({
                path: relPath,
                code: "unreadable",
                reason: `move adoption deferred: committed entry ${entry.id} lost its committed projection, but the store-wide projection census could not read every scope — nothing is served or adopted off a partial view; the next pass retries`,
            });
            return undefined;
        }
        const projections = census.byId.get(entry.id) ?? [];
        const others = projections.filter((p) => !(p.scope === scope && p.slug === slug));
        if (others.length > 0) {
            this.announceAdoptionNotice(`ambiguous|${entry.id}|${projections
                .map((p) => `${p.scope}/${p.slug}`)
                .sort()
                .join(",")}`, `committed entry ${entry.id} lost its committed projection and now appears at ${projections.length} path(s) — no copy is a scope credential, so none is served or adopted (read order never picks an owner); an unrestricted harvest or manual removal adjudicates which is real`);
            return undefined;
        }
        const gateFinding = this.inboundGate(relPath, text) ?? (committedRev !== entry.rev ? this.whitewashInboundFinding(relPath, entry, "quarantined-only") : undefined);
        if (gateFinding !== undefined) {
            this.containInboundReject(gateFinding, absPath, text, entry.id, undefined);
            return undefined;
        }
        const now = this.now();
        bindRowTracked(rows, entry.id, { rev: entry.rev, scope, slug, at: now }, moveEvents, "adopted-move");
        moveAnnouncements.push(`out-of-session memory move adopted: ${entry.id} ${JSON.stringify(inlineUntrusted(this.bindingRelPath(from)))} → ${JSON.stringify(inlineUntrusted(relPath))} — re-read it before relying on prior knowledge of it`);
        if (committedRev !== entry.rev) {
            adoptedExternal.push(relPath);
            try {
                atomicWriteFileSync(this.shadowPath(entry.id), text);
            }
            catch {
            }
        }
        return entry;
    }
    async readScopeAdopting(scope, timings) {
        const probed = this.readScope(scope, true, true);
        if (probed !== undefined)
            return probed;
        const lock = await this.acquireTxnLock(timings);
        try {
            this.assertTxnLockOwnership(lock.token);
            this.ledger = undefined;
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token);
            this.migrateLockedIfNeeded(lock.token);
            return this.readScope(scope, true, false, lock.token);
        }
        catch (err) {
            this.ledger = undefined;
            throw err;
        }
        finally {
            lock.release();
        }
    }
    listScopes() {
        this.ensureLayout();
        return { supported: true, scopes: Object.keys(registeredScopes(this.controlPlaneRoot)).sort() };
    }
    async listHeaders(scopes) {
        this.ensureLayout();
        return this.listHeadersFrom(scopes, (s) => this.readScopeAdopting(s));
    }
    async listHeadersFrom(scopes, read) {
        const out = [];
        for (const scope of scopes) {
            const dir = this.scopeDir(scope);
            for (const e of await read(scope)) {
                out.push(headerOf(e, join(dir, `${e.slug}.md`)));
            }
        }
        return out;
    }
    async getByIds(ids) {
        this.ensureLayout();
        return this.getByIdsFrom(ids, (s) => this.readScopeAdopting(s));
    }
    async getByIdsFrom(ids, read) {
        const want = new Set(ids);
        const out = [];
        for (const scope of Object.keys(registeredScopes(this.controlPlaneRoot))) {
            for (const e of await read(scope)) {
                if (want.has(e.id))
                    out.push(e);
            }
        }
        return out;
    }
    retrievalView() {
        const refuse = (op) => {
            throw new Error(`memory retrieval view is read-only — ${op} must go through the backend itself`);
        };
        return {
            listHeaders: async (scopes) => (this.ensureLayout(), this.listHeadersFrom(scopes, (s) => this.withoutObserved(this.readScope(s, false), s))),
            getByIds: async (ids) => (this.ensureLayout(), this.getByIdsFrom(ids, (s) => this.withoutObserved(this.readScope(s, false), s))),
            search: async (query, scopes, opts) => (this.ensureLayout(), this.searchFrom(query, scopes, opts, (s) => this.withoutObserved(this.readScope(s, false), s))),
            applyPatches: async () => refuse("applyPatches"),
            getConsolidationCursor: async (scope) => this.getConsolidationCursor(scope),
            setConsolidationCursor: async () => refuse("setConsolidationCursor"),
        };
    }
    withoutObserved(entries, scope) {
        const table = layerObservationLookup(this.controlPlaneRoot);
        if (!entries.some((e) => table.covers(e.id)))
            return entries;
        const account = this.committedRowsOrFault();
        if (account === undefined)
            return entries.filter((e) => !table.covers(e.id));
        const out = [];
        for (const e of entries) {
            const committed = rowsRev(account, e.id);
            const binding = rowsBinding(account, e.id);
            const boundHere = binding !== undefined && binding.scope === scope && binding.slug === e.slug;
            if ((committed === e.rev && boundHere) || table.find(e.id, committed, e.rev) === undefined) {
                out.push(e);
                continue;
            }
            const standIn = committed !== undefined && boundHere ? this.readCommittedShadow(e.id) : undefined;
            if (standIn !== undefined)
                out.push(entryFromFile(standIn, e.id, e.slug, scope));
        }
        return out;
    }
    committedRowsOrFault() {
        const path = join(this.controlPlaneRoot, LEDGER_FILE);
        try {
            const raw = readControlFileOrAbsent(path, "committed-rev ledger");
            return raw === undefined ? Object.create(null) : parseLedgerText(raw, path).rows;
        }
        catch {
            return undefined;
        }
    }
    restrictedFindingKeys = new Set();
    recordRestrictedFinding(key, finding) {
        if (this.restrictedFindingKeys.has(key))
            return;
        this.restrictedFindingKeys.add(key);
        this.inboundFindings.push(finding);
    }
    restrictedIdlessScope;
    restrictedIdlessBaseline;
    readScopeCommitted(scope, audit) {
        if (this.persistedSchemaVersion === "v1" || this.unboundRowsKnown)
            this.tryOpportunisticMigrate();
        const dir = this.scopeDir(scope);
        const files = scanEntryFiles(dir, {
            onSkip: audit
                ? (p, kind) => {
                    if (kind !== "unreadable")
                        return;
                    const rel = relative(this.directoryRoot, p);
                    this.recordRestrictedFinding(`unreadable|${rel}`, {
                        path: rel,
                        code: "unreadable",
                        reason: "the path could not be read (lstat/readdir failed) — the restricted-session divergence audit is incomplete under it",
                    });
                }
                : undefined,
        });
        this.ledger = undefined;
        const rows = this.loadLedger();
        const idlessSeen = audit && scope === this.restrictedIdlessScope ? new Set() : undefined;
        const entries = [];
        for (const f of files) {
            const rel = relative(this.directoryRoot, f.path);
            let text;
            try {
                text = readFileSync(f.path, "utf8");
            }
            catch {
                if (audit) {
                    this.recordRestrictedFinding(`unreadable|${rel}`, {
                        path: rel,
                        code: "unreadable",
                        reason: "memory entry file could not be read — it is missing from this restricted session's view",
                    });
                }
                continue;
            }
            const parsed = parseEntryFile(text);
            if (parsed.id === undefined) {
                idlessSeen?.add(rel);
                continue;
            }
            const entry = entryFromFile(text, parsed.id, f.slug, scope);
            const committed = rowsRev(rows, entry.id);
            const binding = rowsBinding(rows, entry.id);
            const boundElsewhere = binding !== undefined && !(binding.scope === scope && binding.slug === f.slug);
            if (committed === entry.rev) {
                if (boundElsewhere) {
                    if (audit) {
                        const boundRel = this.bindingRelPath(binding);
                        const probe = probeProjection(this.bindingAbsPath(binding), entry.id);
                        if (probe === "present") {
                            this.recordRestrictedFinding(`bindcopy|${rel}|${entry.rev}`, {
                                path: rel,
                                code: "restricted_divergence",
                                reason: `this file carries the bytes of committed entry ${entry.id}, whose committed projection is in place at ${JSON.stringify(inlineUntrusted(boundRel))} — a filesystem copy is not a scope credential, so this copy is withheld; an unrestricted session's harvest adjudicates it`,
                            });
                        }
                        else if (probe === "absent") {
                            this.recordRestrictedFinding(`bindmove|${rel}|${entry.rev}`, {
                                path: rel,
                                code: "restricted_divergence",
                                reason: `committed entry ${entry.id} is bound to ${JSON.stringify(inlineUntrusted(boundRel))} but its projection appears here instead — an unattributed move, or a delete-then-plant; this adoption-restricted session serves neither reading and leaves adjudication to an unrestricted session`,
                            });
                        }
                        else {
                            this.recordRestrictedFinding(`bindprobe|${rel}`, {
                                path: rel,
                                code: "unreadable",
                                reason: `committed entry ${entry.id} is bound elsewhere and the bound projection could not be probed conclusively (a read failure, not absence) — the binding audit for this file is incomplete; it is withheld this pass`,
                            });
                        }
                    }
                    continue;
                }
                if (audit && this.readCommittedShadow(entry.id) === undefined) {
                    try {
                        atomicWriteFileSync(this.shadowPath(entry.id), text);
                    }
                    catch {
                    }
                }
                entries.push(entry);
                continue;
            }
            const shadowText = committed !== undefined ? this.readCommittedShadow(entry.id) : undefined;
            if (shadowText !== undefined) {
                const shadowEntry = entryFromFile(shadowText, entry.id, f.slug, scope);
                if (shadowEntry.rev === entry.rev && !boundElsewhere) {
                    entries.push(entry);
                    continue;
                }
                const otherProjection = binding !== undefined ? (boundElsewhere ? this.bindingRelPath(binding) : undefined) : this.idProjectionElsewhere(entry.id, f.path);
                if (otherProjection !== undefined) {
                    if (audit) {
                        const finding = this.inboundGate(rel, text);
                        if (finding !== undefined) {
                            this.containInboundReject(finding, f.path, text, entry.id, undefined);
                        }
                        else {
                            this.recordRestrictedFinding(`dupserve|${rel}|${entry.rev}`, {
                                path: rel,
                                code: "restricted_divergence",
                                reason: binding !== undefined
                                    ? `this file diverges from committed entry ${entry.id}, whose committed projection is bound at ${JSON.stringify(inlineUntrusted(otherProjection))} — the committed copy may only stand in at the entry's committed projection, so this copy is withheld; an unrestricted session's harvest adjudicates it`
                                    : `this file carries the id of a committed entry that has another projection (${otherProjection}) — the committed copy may only stand in for an entry's unique projection, so this copy is withheld; an unrestricted session's harvest adjudicates it`,
                            });
                        }
                    }
                    continue;
                }
                if (audit) {
                    const finding = this.inboundGate(rel, text);
                    if (finding !== undefined) {
                        this.containInboundReject(finding, f.path, text, entry.id, committed);
                    }
                    else {
                        this.recordRestrictedFinding(`divergence|${rel}|${entry.rev}`, {
                            path: rel,
                            code: "restricted_divergence",
                            reason: "on-disk content diverges from the committed entry with no backend transaction backing it; this adoption-restricted session serves the committed content and leaves the disk state pending — an unrestricted session can adopt it normally",
                        });
                    }
                }
                entries.push(shadowEntry);
                continue;
            }
            if (committed !== undefined) {
                if (audit) {
                    this.recordRestrictedFinding(`legacy|${rel}|${entry.rev}`, {
                        path: rel,
                        code: "restricted_divergence",
                        reason: "on-disk content diverges from a committed entry that has no committed shadow copy; this adoption-restricted session withholds the entry (nothing trustworthy to serve) — an unrestricted session can adjudicate it",
                    });
                }
                continue;
            }
            if (audit) {
                const finding = this.inboundGate(rel, text);
                if (finding !== undefined) {
                    this.containInboundReject(finding, f.path, text, entry.id, undefined);
                }
                else {
                    this.recordRestrictedFinding(`uncommitted|${rel}|${entry.rev}`, {
                        path: rel,
                        code: "restricted_divergence",
                        reason: "an id-bearing memory file exists on disk that the committed account has never seen; this adoption-restricted session neither adopts nor serves it — it stays on disk for an unrestricted session to adopt normally",
                    });
                }
            }
        }
        if (idlessSeen !== undefined && this.restrictedIdlessBaseline === undefined)
            this.restrictedIdlessBaseline = idlessSeen;
        return entries;
    }
    idProjectionElsewhere(id, excludeAbsPath) {
        for (const scope of Object.keys(registeredScopes(this.controlPlaneRoot))) {
            const dir = this.scopeDir(scope);
            for (const f of scanEntryFiles(dir)) {
                if (f.path === excludeAbsPath)
                    continue;
                const text = readSafe(f.path);
                if (text === undefined)
                    continue;
                if (parseEntryFile(text).id === id)
                    return relative(this.directoryRoot, f.path);
            }
        }
        return undefined;
    }
    restrictedAdoptionView(opts) {
        const audit = opts?.audit === true;
        if (audit) {
            this.restrictedFindingKeys = new Set();
            this.restrictedIdlessScope = typeof opts?.writeScope === "string" ? opts.writeScope : undefined;
            this.restrictedIdlessBaseline = undefined;
        }
        const refuse = (op) => {
            throw new Error(`memory adoption-restricted view is read-only — ${op} must go through the backend itself`);
        };
        return {
            listHeaders: async (scopes) => (this.ensureLayout(), this.listHeadersFrom(scopes, (s) => this.readScopeCommitted(s, audit))),
            getByIds: async (ids) => (this.ensureLayout(), this.getByIdsFrom(ids, (s) => this.readScopeCommitted(s, audit))),
            search: async (query, scopes, o) => (this.ensureLayout(), this.searchFrom(query, scopes, o, (s) => this.readScopeCommitted(s, audit))),
            applyPatches: async () => refuse("applyPatches"),
            getConsolidationCursor: async (scope) => this.getConsolidationCursor(scope),
            setConsolidationCursor: async () => refuse("setConsolidationCursor"),
        };
    }
    auditRestrictedDivergence(scopes, opts) {
        this.ensureLayout();
        for (const scope of scopes)
            this.readScopeCommitted(scope, true);
        const idlessBaseline = opts?.idlessWriteScope !== undefined && opts.idlessWriteScope === this.restrictedIdlessScope ? this.restrictedIdlessBaseline : undefined;
        const idlessScope = idlessBaseline !== undefined ? opts?.idlessWriteScope : undefined;
        if (this.persistedSchemaVersion === "v1" || hasUnboundRows(this.loadLedger()))
            this.tryOpportunisticMigrate();
        const rows = { ...this.loadLedger() };
        if (this.persistedSchemaVersion === "v2")
            this.announceUnboundRows(rows);
        const present = new Map();
        let complete = true;
        for (const scope of Object.keys(registeredScopes(this.controlPlaneRoot))) {
            const dir = this.scopeDir(scope);
            const files = scanEntryFiles(dir, {
                onSkip: (_p, kind) => {
                    if (kind === "unreadable")
                        complete = false;
                },
            });
            for (const f of files) {
                const text = readSafe(f.path);
                if (text === undefined) {
                    complete = false;
                    continue;
                }
                const id = parseEntryFile(text).id;
                if (id !== undefined) {
                    present.set(id, [...(present.get(id) ?? []), relative(this.directoryRoot, f.path)]);
                    continue;
                }
                if (scope === idlessScope && idlessBaseline !== undefined) {
                    const rel = relative(this.directoryRoot, f.path);
                    if (!idlessBaseline.has(rel)) {
                        this.recordRestrictedFinding(`idless|${rel}|${createHash("sha256").update(text, "utf8").digest("hex").slice(0, 16)}`, {
                            path: rel,
                            code: "restricted_divergence",
                            reason: "an id-less file appeared under the session's write scope after materialize, through none of this session's channels: a concurrent session's in-flight material, or an out-of-band drop — left on disk for an unrestricted harvest to adjudicate",
                        });
                    }
                }
            }
        }
        for (const [id, paths] of present) {
            if (paths.length < 2 || rowsRev(rows, id) === undefined)
                continue;
            const dupBinding = rowsBinding(rows, id);
            this.recordRestrictedFinding(`dup|${id}|${paths.sort().join(",")}`, {
                path: paths[0] ?? id,
                code: "restricted_divergence",
                reason: `committed memory entry ${id} appears at ${paths.length} paths (${paths.join(", ")}) — a filesystem copy is not a rename; ${dupBinding !== undefined ? `the committed projection is ${JSON.stringify(inlineUntrusted(this.bindingRelPath(dupBinding)))}, the other path(s) carry no account backing` : "this adoption-restricted session flags it"} and an unrestricted session's harvest adjudicates`,
            });
        }
        if (!complete) {
            this.recordRestrictedFinding(`missing-audit-incomplete`, {
                path: ".",
                code: "restricted_divergence",
                reason: "the divergence audit could not scan every path (unreadable subtree) — missing-entry detection was skipped this pass rather than judged from an incomplete scan",
            });
            return;
        }
        for (const [id, row] of rowsEntries(rows)) {
            const binding = rowsBinding(rows, id);
            const paths = present.get(id) ?? [];
            if (binding === undefined) {
                if (paths.length > 0)
                    continue;
            }
            else {
                if (paths.includes(this.bindingRelPath(binding)))
                    continue;
            }
            this.ledger = undefined;
            const current = this.loadLedger();
            if (rowsRev(current, id) === undefined)
                continue;
            if (!bindingEquals(rowsBinding(current, id), binding))
                continue;
            const aliens = binding !== undefined ? paths.filter((p) => p !== this.bindingRelPath(binding)) : [];
            this.recordRestrictedFinding(`missing|${id}|${row.rev}`, {
                path: binding !== undefined ? this.bindingRelPath(binding) : `id:${id}`,
                code: "restricted_divergence",
                reason: binding !== undefined
                    ? `committed memory entry ${id} is missing from its committed projection ${JSON.stringify(inlineUntrusted(this.bindingRelPath(binding)))} with no transaction backing the removal${aliens.length > 0 ? ` while byte-carriers of its id appear at ${aliens.join(", ")} — an unattributed move, or a delete-then-plant` : ""}; this adoption-restricted session neither restores nor forgets it — an unrestricted session adjudicates`
                    : `committed memory entry ${id} is missing from disk with no transaction backing the removal; this adoption-restricted session neither restores nor forgets it — an unrestricted session can restore it from the committed copy`,
            });
        }
    }
    async search(query, scopes, opts) {
        this.ensureLayout();
        return this.searchFrom(query, scopes, opts, (s) => this.readScopeAdopting(s));
    }
    async searchFrom(query, scopes, opts, read) {
        const limit = opts?.limit ?? 20;
        const q = termSet(query);
        if (q.size === 0)
            return [];
        const scored = [];
        for (const scope of scopes) {
            const dir = this.scopeDir(scope);
            for (const e of await read(scope)) {
                const haystack = `${e.frontmatter.name ?? e.slug} ${e.frontmatter.description ?? ""} ${e.body}`;
                const d = jaccardDistance(q, haystack);
                if (d === null)
                    continue;
                scored.push({ ...headerOf(e, join(dir, `${e.slug}.md`)), score: d });
            }
        }
        const band = opts?.exposureBands === true ? (h) => (h.exposure === "external" ? 1 : 0) : () => 0;
        scored.sort((a, b) => band(a) - band(b) || a.score - b.score || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
        return scored.slice(0, limit);
    }
    async applyPatches(patches) {
        const lock = await this.acquireTxnLock();
        try {
            return await this.applyPatchesLocked(patches, lock.token);
        }
        catch (err) {
            throw withPermissionRemedy(err, this.directoryRoot);
        }
        finally {
            lock.release();
        }
    }
    txnLockDir() {
        return join(this.controlPlaneRoot, "txn.lock");
    }
    async acquireTxnLock(timings = {}) {
        const staleMs = timings.staleMs ?? TXN_LOCK_STALE_MS;
        const waitMs = timings.waitMs ?? TXN_LOCK_WAIT_MS;
        const stealGraceMs = timings.stealGraceMs ?? TXN_LOCK_STEAL_GRACE_MS;
        const lockDir = this.txnLockDir();
        const ownerPath = join(lockDir, "owner");
        mkdirSync(this.controlPlaneRoot, { recursive: true });
        const start = Date.now();
        let deadline = start + waitMs;
        for (;;) {
            let acquired = false;
            try {
                mkdirSync(lockDir);
                acquired = true;
            }
            catch (err) {
                if (err.code !== "EEXIST")
                    throw err;
            }
            if (acquired) {
                const token = randomUUID();
                try {
                    const fd = openSync(ownerPath, "w", 0o600);
                    try {
                        writeAllSync(fd, token);
                        fsyncSync(fd);
                    }
                    finally {
                        closeSync(fd);
                    }
                }
                catch (err) {
                    try {
                        rmSync(lockDir, { recursive: true, force: true });
                    }
                    catch {
                    }
                    throw new Error(`memory txn lock owner token could not be written at ${ownerPath}: ${thrownText(err)}`);
                }
                return {
                    token,
                    release: () => {
                        try {
                            if (readFileSync(ownerPath, "utf8") !== token)
                                return;
                        }
                        catch {
                            return;
                        }
                        try {
                            rmSync(lockDir, { recursive: true, force: true });
                        }
                        catch {
                        }
                    },
                };
            }
            let mtimeMs;
            try {
                mtimeMs = statSync(lockDir).mtimeMs;
            }
            catch {
                mtimeMs = undefined;
            }
            if (mtimeMs !== undefined) {
                const staleAt = mtimeMs + staleMs;
                deadline = Math.max(deadline, staleAt + stealGraceMs);
                if (Date.now() > staleAt) {
                    let tokenPresent = false;
                    try {
                        statSync(ownerPath);
                        tokenPresent = true;
                    }
                    catch {
                    }
                    if (tokenPresent) {
                        const tomb = join(this.controlPlaneRoot, `txn.lock.stale-${randomUUID()}`);
                        let stolen = false;
                        try {
                            renameSync(lockDir, tomb);
                            stolen = true;
                        }
                        catch {
                        }
                        if (stolen)
                            rmSync(tomb, { recursive: true, force: true });
                    }
                }
            }
            if (Date.now() > deadline) {
                throw new Error(`memory txn lock timeout (${waitMs}ms, extended to the lock's stale line) at ${lockDir} — another writer is wedged?`);
            }
            await new Promise((r) => setTimeout(r, 20 + Math.floor(Math.random() * 40)));
        }
    }
    assertTxnLockOwnership(token) {
        let held;
        try {
            held = readFileSync(join(this.txnLockDir(), "owner"), "utf8");
        }
        catch {
            held = undefined;
        }
        if (held !== token) {
            throw new ControlPlaneCorruptError("txn lock ownership lost before commit (holder stalled past the stale deadline and was stolen from?) — transaction aborted, nothing was committed", { facet: "lock-ownership" });
        }
    }
    async applyPatchesLocked(patches, lockToken) {
        this.assertTxnLockOwnership(lockToken);
        this.ledger = undefined;
        this.recoverJournal({ token: lockToken });
        this.adoptLayoutLocked(lockToken, patches.find((p) => p.op === "add" && p.entry !== undefined)?.entry?.scope);
        this.migrateLockedIfNeeded(lockToken);
        const rows = cloneRows(this.loadLedger());
        const report = { applied: [], conflicts: [] };
        const ops = [];
        const transfers = [];
        const plannedTargets = new Set();
        const nonDeleteIds = new Set();
        const plannedDeletes = new Map();
        this.batchScan = new Map();
        try {
            for (const patch of patches) {
                try {
                    if (patch.op !== "delete") {
                        if (patch.entry !== undefined && patch.entry.id !== patch.id) {
                            report.conflicts.push({ op: patch.op, id: patch.id, reason: `patch id ${JSON.stringify(patch.id)} != entry id ${JSON.stringify(patch.entry.id)} (malformed patch refused)` });
                            continue;
                        }
                        if (nonDeleteIds.has(patch.id)) {
                            report.conflicts.push({ op: patch.op, id: patch.id, reason: "duplicate id in batch: another add/update for this id is already planned (copied file keeping its frontmatter id?)" });
                            continue;
                        }
                        nonDeleteIds.add(patch.id);
                    }
                    this.planOne(patch, report, ops, rows, plannedTargets, plannedDeletes, transfers);
                }
                catch (err) {
                    report.conflicts.push({ op: patch.op, id: patch.id, reason: `io error: ${thrownText(err)}` });
                }
            }
        }
        finally {
            this.batchScan = undefined;
        }
        if (ops.length === 0) {
            return report;
        }
        const txn = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
        const stagedFiles = [];
        const refuseUnstaging = (batch, at) => {
            try {
                this.refuseEscapingTargets(batch, at);
            }
            catch (err) {
                for (const f of stagedFiles) {
                    try {
                        const st = lstatSync(f.path);
                        if (st.dev === f.dev && st.ino === f.ino)
                            rmSync(f.path, { force: true });
                    }
                    catch {
                    }
                }
                throw err;
            }
        };
        let n = 0;
        for (const op of ops) {
            if (op.kind !== "write" || op.content === undefined)
                continue;
            refuseUnstaging([op], "applyPatches (staging)");
            const staged = join(dirname(op.target), `.stage-${txn}-${n++}`);
            mkdirSync(dirname(op.target), { recursive: true });
            const fd = openSync(staged, "w", 0o600);
            try {
                writeAllSync(fd, op.content);
                fsyncSync(fd);
                const st = fstatSync(fd);
                stagedFiles.push({ path: staged, dev: st.dev, ino: st.ino });
            }
            finally {
                closeSync(fd);
            }
            op.staged = staged;
        }
        const coherentTransfers = transfers.filter((e) => {
            if (e.channel === "migration" || e.channel === "erasure-anchor" || e.channel === "erasure-request")
                return true;
            if (e.channel === "delete")
                return !Object.prototype.hasOwnProperty.call(rows, e.id);
            return bindingEquals(rowsBinding(rows, e.id), e.to);
        });
        this.assertTxnLockOwnership(lockToken);
        if (coherentTransfers.length > 0)
            this.precheckTransfersAppendable();
        const journal = { txn, ops, ledger2: ledgerEnvelope(rows, this.envelopeExtras), ...(coherentTransfers.length > 0 ? { transfers: coherentTransfers } : {}) };
        refuseUnstaging(ops, "applyPatches (commit point)");
        atomicWriteFileSync(join(this.controlPlaneRoot, JOURNAL_FILE), JSON.stringify(journal));
        for (const op of ops) {
            this.refuseEscapingTargets([op], "applyPatches (after the commit point; the journal is kept)");
            if (op.kind === "write" && op.staged) {
                renameSync(op.staged, op.target);
            }
            else if (op.kind === "delete") {
                rmSync(op.target, { force: true });
            }
            else if (op.kind === "shadow-write" && op.content !== undefined) {
                atomicWriteFileSync(this.shadowPath(op.target), op.content);
            }
            else if (op.kind === "shadow-delete") {
                rmSync(this.shadowPath(op.target), { force: true });
            }
        }
        this.ledger = rows;
        this.saveLedger();
        this.appendTransfers(coherentTransfers);
        this.closeJournalIfOurs(txn);
        return report;
    }
    closeJournalIfOurs(txn) {
        const jp = join(this.controlPlaneRoot, JOURNAL_FILE);
        let raw;
        try {
            raw = readFileSync(jp, "utf8");
        }
        catch {
            return;
        }
        try {
            const jp = JSON.parse(raw);
            if (typeof jp !== "object" || jp === null || jp.txn !== txn)
                return;
        }
        catch {
            return;
        }
        rmSync(jp, { force: true });
    }
    planOne(patch, report, ops, rows, plannedTargets, plannedDeletes, transfers) {
        if (!isValidEntryId(patch.id)) {
            report.conflicts.push({ op: patch.op, id: patch.id, reason: `invalid_id_shape (id ${JSON.stringify(patch.id)} fails the frontmatter id contract — the read side would drop it, leaving an applied-but-invisible entry)` });
            return;
        }
        if (patch.op !== "delete" && patch.entry !== undefined && slugEscapes(patch.entry.slug)) {
            report.conflicts.push({ op: patch.op, id: patch.id, reason: `invalid_slug_shape (slug ${JSON.stringify(patch.entry.slug)} is empty, absolute, or path-escaping — a projection must stay inside its scope dir)` });
            return;
        }
        if (patch.op !== "delete" && patch.entry !== undefined && ambiguousOriginRepresentation(patch.entry.frontmatter)) {
            report.conflicts.push({
                op: patch.op,
                id: patch.id,
                reason: `external-origin marker representation conflict: the entry carries conflicting or duplicated origin representations (malformed patch refused)`,
            });
            return;
        }
        if (patch.op === "add") {
            const entry = patch.entry;
            if (!entry) {
                report.conflicts.push({ op: "add", id: patch.id, reason: "add patch without an entry" });
                return;
            }
            const locatedAnywhere = this.locateById(patch.id);
            const existingAnywhere = locatedAnywhere !== undefined && plannedDeletes.has(patch.id) ? undefined : locatedAnywhere;
            if (locatedAnywhere !== undefined) {
                const shadowText = this.readCommittedShadow(patch.id);
                const committedFm = shadowText !== undefined ? parseEntryFile(shadowText).frontmatter : locatedAnywhere.entry.frontmatter;
                const committedOrigin = committedOriginOf(committedFm);
                if (committedOrigin !== undefined && !originEquals(committedOrigin, committedOriginOf(entry.frontmatter))) {
                    report.conflicts.push({
                        op: "add",
                        id: patch.id,
                        reason: `external-origin marker whitewash refused: the add strips or rewrites the origin marker of a marked entry (malformed patch refused)`,
                    });
                    return;
                }
                const committedDistilled = committedDistilledOf(committedFm);
                if (committedDistilled !== undefined && !distilledEquals(committedDistilled, committedDistilledOf(entry.frontmatter))) {
                    report.conflicts.push({
                        op: "add",
                        id: patch.id,
                        reason: `distilled lineage whitewash refused: the add strips or rewrites the distilled block of a consolidation product (malformed patch refused)`,
                    });
                    return;
                }
            }
            if (patch.guard === "absent") {
                if (existingAnywhere) {
                    const currentRev = rowsRev(rows, patch.id) ?? existingAnywhere.entry.rev;
                    if (currentRev !== computeEntryRev(entry)) {
                        report.conflicts.push({ op: "add", id: patch.id, reason: "add_guard_absent_conflict (id already exists at a different rev; add-if-absent refuses to overwrite)", currentRev });
                        return;
                    }
                    if (existingAnywhere.entry.scope !== entry.scope || existingAnywhere.entry.slug !== entry.slug) {
                        report.conflicts.push({
                            op: "add",
                            id: patch.id,
                            reason: `add_guard_absent_conflict (id already exists at the same rev but a different projection ${JSON.stringify(`${existingAnywhere.entry.scope}/${existingAnywhere.entry.slug}`)}; add-if-absent refuses to duplicate)`,
                            currentRev,
                        });
                        return;
                    }
                }
            }
            if (existingAnywhere !== undefined && existingAnywhere.entry.scope !== entry.scope) {
                report.conflicts.push({ op: "add", id: patch.id, reason: `id already exists in scope ${JSON.stringify(existingAnywhere.entry.scope)} (cross-scope add refused)` });
                return;
            }
            if (existingAnywhere !== undefined && existingAnywhere.entry.slug !== entry.slug) {
                ops.push({ kind: "delete", target: existingAnywhere.path });
            }
            const dir = this.scopeDir(entry.scope);
            let slug = entry.slug;
            let target = join(dir, `${slug}.md`);
            for (let n = 2;; n++) {
                const existing = plannedTargets.has(target) ? "" : readSafe(target);
                if (existing === undefined)
                    break;
                if (existing !== "") {
                    const parsed = parseEntryFile(existing);
                    if (parsed.id === undefined || parsed.id === entry.id)
                        break;
                }
                slug = `${entry.slug}-${n}`;
                target = join(dir, `${slug}.md`);
            }
            const content = serializeEntryFile(entry);
            plannedTargets.add(target);
            ops.push({ kind: "write", target, content });
            ops.push({ kind: "shadow-write", target: entry.id, content });
            const now = this.now();
            bindRowTracked(rows, entry.id, { rev: computeEntryRev(entry), scope: entry.scope, slug, at: now }, transfers, "applyPatches-move");
            const priorBinding = plannedDeletes.get(entry.id);
            const justBound = rows[entry.id];
            if (priorBinding !== undefined && justBound !== undefined && !bindingEquals(priorBinding, { scope: entry.scope, slug })) {
                rows[entry.id] = { ...justBound, prev: { scope: priorBinding.scope, slug: priorBinding.slug, at: now } };
                transfers.push({ ev: randomUUID(), channel: "applyPatches-move", id: entry.id, from: priorBinding, to: { scope: entry.scope, slug }, at: now });
            }
            report.applied.push({ op: "add", id: entry.id, slug });
            return;
        }
        const found = this.locateById(patch.id, patch.op === "update" ? patch.entry?.scope : undefined);
        if (!found) {
            report.conflicts.push({ op: patch.op, id: patch.id, reason: "unknown id (entry not found in backend)" });
            return;
        }
        if (patch.op === "update" && patch.entry !== undefined) {
            const shadowText = this.readCommittedShadow(patch.id);
            const committedFm = shadowText !== undefined ? parseEntryFile(shadowText).frontmatter : found.entry.frontmatter;
            if (committedFm.provenance?.kind === "repo_file") {
                const next = patch.entry.frontmatter;
                const stripsProvenance = next.provenance === undefined;
                const stripsTrust = committedFm.trust !== undefined && next.trust === undefined;
                if (stripsProvenance || stripsTrust) {
                    report.conflicts.push({
                        op: "update",
                        id: patch.id,
                        reason: `repo_file provenance whitewash refused: update drops ${stripsProvenance ? "provenance" : "trust"} from a repo-ingested entry (malformed patch refused)`,
                    });
                    return;
                }
            }
            const committedOrigin = committedOriginOf(committedFm);
            if (committedOrigin !== undefined && !originEquals(committedOrigin, committedOriginOf(patch.entry.frontmatter))) {
                report.conflicts.push({
                    op: "update",
                    id: patch.id,
                    reason: `external-origin marker whitewash refused: the update strips or rewrites the origin marker of a marked entry (malformed patch refused)`,
                });
                return;
            }
            const committedDistilled = committedDistilledOf(committedFm);
            if (committedDistilled !== undefined && !distilledEquals(committedDistilled, committedDistilledOf(patch.entry.frontmatter))) {
                report.conflicts.push({
                    op: "update",
                    id: patch.id,
                    reason: `distilled lineage whitewash refused: the update strips or rewrites the distilled block of a consolidation product (malformed patch refused)`,
                });
                return;
            }
        }
        const currentRev = rowsRev(rows, patch.id) ?? found.entry.rev;
        if (patch.baseRev !== undefined && patch.baseRev !== currentRev) {
            report.conflicts.push({ op: patch.op, id: patch.id, reason: "rev mismatch (concurrent change)", baseRev: patch.baseRev, currentRev });
            const shadowText = this.readCommittedShadow(patch.id);
            if (shadowText !== undefined && shadowText !== serializeEntryFile(found.entry)) {
                ops.push({ kind: "write", target: found.path, content: shadowText });
            }
            return;
        }
        if (patch.op === "delete") {
            ops.push({ kind: "delete", target: found.path });
            ops.push({ kind: "shadow-delete", target: patch.id });
            const deletedBinding = rowsBinding(rows, patch.id);
            if (deletedBinding !== undefined) {
                const derived = this.bindingAbsPath(deletedBinding);
                if (derived !== found.path)
                    ops.push({ kind: "delete", target: derived });
            }
            transfers.push({
                ev: randomUUID(),
                channel: "delete",
                id: patch.id,
                ...(deletedBinding !== undefined ? { from: deletedBinding } : {}),
                rev: currentRev,
                at: this.now(),
            });
            deleteRow(rows, patch.id);
            plannedDeletes.set(patch.id, deletedBinding);
            report.applied.push({ op: "delete", id: patch.id, slug: found.entry.slug });
            return;
        }
        const entry = patch.entry;
        if (!entry) {
            report.conflicts.push({ op: "update", id: patch.id, reason: "update patch without an entry" });
            return;
        }
        const dir = this.scopeDir(entry.scope);
        const target = join(dir, `${entry.slug}.md`);
        const content = serializeEntryFile(entry);
        if (target !== found.path)
            ops.push({ kind: "delete", target: found.path });
        plannedTargets.add(target);
        ops.push({ kind: "write", target, content });
        ops.push({ kind: "shadow-write", target: entry.id, content });
        bindRowTracked(rows, entry.id, { rev: computeEntryRev(entry), scope: entry.scope, slug: entry.slug, at: this.now() }, transfers, "applyPatches-move");
        report.applied.push({ op: "update", id: entry.id, slug: entry.slug });
    }
    locateById(id, preferScope) {
        const scopes = Object.keys(registeredScopes(this.controlPlaneRoot));
        const ordered = preferScope !== undefined ? [preferScope, ...scopes.filter((s) => s !== preferScope)] : scopes;
        for (const scope of ordered) {
            const dir = this.scopeDir(scope);
            for (const e of this.readScope(scope, false)) {
                if (e.id === id)
                    return { entry: e, path: join(dir, `${e.slug}.md`) };
            }
        }
        return undefined;
    }
    async eraseWithEvidence(input) {
        const captured = captureErasureInput(input);
        const bad = erasureRequestInvalid(captured);
        if (bad !== undefined)
            throw erasureCodedError(`eraseWithEvidence: ${bad}`, "config.memory_erasure_request");
        const requestId = captured.requestId;
        const select = captured.select;
        const lock = await this.acquireTxnLock();
        try {
            this.assertTxnLockOwnership(lock.token);
            this.ledger = undefined;
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token);
            this.migrateLockedIfNeeded(lock.token);
            const rows = cloneRows(this.loadLedger());
            const chain = this.readTransferChain(lock.token);
            const degraded = this.chainDegradedMarkerPresent();
            const custodyState = degraded ? "damaged" : "complete";
            const selectHash = erasureSelectHash(select);
            const now = this.now();
            const localAnchors = chain.filter((r) => r.parsed.channel === "erasure-anchor" && r.parsed.origin === undefined && r.parsed.req === requestId);
            if (localAnchors.length > 1) {
                throw new ControlPlaneCorruptError(`erasure request ${JSON.stringify(requestId)} has ${localAnchors.length} anchors on the evidence chain — a second anchor cannot be minted by this store (the lock-held chain check runs first), so the chain was spliced out of band; refusing to pick one by file order (a human adjudicates)`);
            }
            const priorAnchor = localAnchors[0]?.parsed;
            if (priorAnchor !== undefined && priorAnchor.selectHash !== selectHash) {
                throw erasureCodedError(`eraseWithEvidence: requestId ${JSON.stringify(requestId)} was first executed with a DIFFERENT selector — a requestId names ONE request (pinned set ${JSON.stringify(priorAnchor.selectHash)}, this call ${JSON.stringify(selectHash)}); use a new requestId for a new selector`, "memory.erasure_selector_mismatch");
            }
            const replay = priorAnchor !== undefined;
            let census;
            const takeCensus = () => {
                if (census === undefined) {
                    census = this.censusProjectionsLocked();
                    if (!census.complete) {
                        throw erasureCodedError("eraseWithEvidence: the store-wide projection census could not read every scope — an erasure attestation cannot claim a clean delete over a store that cannot be fully read; fix the filesystem fault and retry (nothing was deleted, nothing was recorded)", "memory.erasure_census_incomplete");
                    }
                }
                return census;
            };
            const rowPresent = (id) => Object.prototype.hasOwnProperty.call(rows, id);
            let resolvedIds;
            if (replay) {
                resolvedIds = [...priorAnchor.ids];
            }
            else if ("ids" in select) {
                resolvedIds = [...select.ids];
            }
            else if ("scope" in select) {
                const scope = select.scope;
                const ledgerIds = rowsEntries(rows)
                    .filter(([id]) => rowsBinding(rows, id)?.scope === scope)
                    .map(([id]) => id);
                const ghostIds = [...takeCensus().byId.entries()]
                    .filter(([id, projections]) => !rowPresent(id) && projections.some((p) => p.scope === scope))
                    .map(([id]) => id);
                resolvedIds = [...new Set([...ledgerIds, ...ghostIds])].sort();
            }
            else {
                resolvedIds = lineageContributionsOfSession(this.controlPlaneRoot, select.sessionId)
                    .map((c) => c.entryId)
                    .sort();
            }
            const priorDeletes = new Map();
            for (const r of chain) {
                if (r.parsed.channel === "delete" && r.parsed.origin === undefined && r.parsed.req === requestId && typeof r.parsed.id === "string") {
                    const rawFrom = r.parsed.from;
                    const from = rawFrom !== undefined && typeof rawFrom.scope === "string" && typeof rawFrom.slug === "string" ? { scope: rawFrom.scope, slug: rawFrom.slug } : undefined;
                    const prev = priorDeletes.get(r.parsed.id);
                    priorDeletes.set(r.parsed.id, { ev: r.ev, at: r.parsed.at, ...(from !== undefined ? { from } : prev?.from !== undefined ? { from: prev.from } : {}) });
                }
            }
            const transfers = [];
            const ops = [];
            const erased = [];
            const erasedPreviously = [];
            const notFoundRows = [];
            const conflicts = [];
            const rowIds = resolvedIds.filter((id) => rowPresent(id));
            const accountAbsentIds = resolvedIds.filter((id) => !rowPresent(id));
            if (rowIds.some((id) => isValidEntryId(id)) || accountAbsentIds.length > 0)
                takeCensus();
            const censusOf = (id) => census?.byId.get(id) ?? [];
            const unledgeredLiveIds = accountAbsentIds.filter((id) => censusOf(id).length > 0);
            const trulyAbsentIds = accountAbsentIds.filter((id) => censusOf(id).length === 0);
            if (!replay) {
                transfers.push({ ev: randomUUID(), channel: "erasure-anchor", req: requestId, select: cloneErasureSelect(select), selectHash, ids: [...resolvedIds], notFound: trulyAbsentIds, at: now });
                const byScope = new Map();
                for (const id of rowIds) {
                    if (!isValidEntryId(id))
                        continue;
                    const binding = rowsBinding(rows, id);
                    if (binding === undefined)
                        continue;
                    const list = byScope.get(binding.scope) ?? [];
                    list.push(id);
                    byScope.set(binding.scope, list);
                }
                for (const scope of [...byScope.keys()].sort()) {
                    transfers.push({ ev: randomUUID(), channel: "erasure-request", req: requestId, scope, selectHash, ids: byScope.get(scope) ?? [], at: now });
                }
            }
            const lineageCommitted = resolvedIds.length > 0 ? readLineageRecord(this.controlPlaneRoot).committed : {};
            const preSessionsOf = (id) => Object.prototype.hasOwnProperty.call(lineageCommitted, id) ? Object.keys(lineageCommitted[id] ?? {}).sort() : [];
            for (const id of rowIds) {
                const row = rows[id];
                if (row === undefined)
                    continue;
                const preSessions = preSessionsOf(id);
                if (!isValidEntryId(id)) {
                    const ev = randomUUID();
                    transfers.push({ ev, channel: "delete", id, rev: row.rev, req: requestId, sessions: preSessions, legacyId: true, at: now });
                    deleteRow(rows, id);
                    erased.push({ id, rev: row.rev, binding: { state: "unbound" }, evidenceEv: ev, projectionsRemoved: 0, sessions: preSessions });
                    continue;
                }
                try {
                    const binding = rowsBinding(rows, id);
                    const idOps = [{ kind: "shadow-delete", target: id }];
                    const targets = new Set();
                    if (binding !== undefined)
                        targets.add(this.bindingAbsPath(binding));
                    const censusProjections = censusOf(id);
                    for (const p of censusProjections)
                        targets.add(this.bindingAbsPath(p));
                    for (const t of targets)
                        idOps.push({ kind: "delete", target: t });
                    const ev = randomUUID();
                    transfers.push({ ev, channel: "delete", id, ...(binding !== undefined ? { from: binding } : {}), rev: row.rev, req: requestId, sessions: preSessions, at: now });
                    ops.push(...idOps);
                    deleteRow(rows, id);
                    erased.push({
                        id,
                        rev: row.rev,
                        binding: binding !== undefined ? { state: "bound", scope: binding.scope, slug: binding.slug } : { state: "unbound" },
                        evidenceEv: ev,
                        projectionsRemoved: censusProjections.length,
                        sessions: preSessions,
                    });
                }
                catch (err) {
                    conflicts.push({ id, reason: `io error: ${thrownText(err)}` });
                }
            }
            for (const id of unledgeredLiveIds) {
                const preSessions = preSessionsOf(id);
                try {
                    const projections = censusOf(id);
                    let rev = "unledgered";
                    const first = projections[0];
                    if (first !== undefined) {
                        const txt = readSafe(this.bindingAbsPath(first));
                        if (txt !== undefined)
                            rev = computeEntryRev(entryFromFile(txt, id, first.slug, first.scope));
                    }
                    const idOps = [{ kind: "shadow-delete", target: id }];
                    for (const p of projections)
                        idOps.push({ kind: "delete", target: this.bindingAbsPath(p) });
                    const ev = randomUUID();
                    transfers.push({ ev, channel: "delete", id, rev, req: requestId, sessions: preSessions, at: now });
                    ops.push(...idOps);
                    erased.push({ id, rev, binding: { state: "unbound" }, evidenceEv: ev, projectionsRemoved: projections.length, sessions: preSessions });
                }
                catch (err) {
                    conflicts.push({ id, reason: `io error: ${thrownText(err)}` });
                }
            }
            for (const id of trulyAbsentIds) {
                if (erased.some((e) => e.id === id))
                    continue;
                const prior = priorDeletes.get(id);
                if (prior !== undefined && custodyState === "complete") {
                    const fromFree = prior.from !== undefined &&
                        !Object.values(rows).some((row) => row.scope === prior.from.scope && row.slug === prior.from.slug) &&
                        !existsSync(this.bindingAbsPath({ scope: prior.from.scope, slug: prior.from.slug }));
                    erasedPreviously.push({ id, ev: prior.ev, at: prior.at, ...(fromFree ? { from: prior.from } : {}) });
                }
                else {
                    notFoundRows.push({ id, ...(custodyState !== "complete" ? { historyUnknown: true } : {}) });
                }
            }
            if (ops.length > 0 || transfers.length > 0) {
                const txn = `${Date.now()}-erase-${randomUUID().slice(0, 8)}`;
                this.assertTxnLockOwnership(lock.token);
                this.precheckTransfersAppendable();
                const journal = { txn, ops, ledger2: ledgerEnvelope(rows, this.envelopeExtras), transfers };
                this.refuseEscapingTargets(ops, "eraseWithEvidence (commit point)");
                atomicWriteFileSync(join(this.controlPlaneRoot, JOURNAL_FILE), JSON.stringify(journal));
                for (const op of ops) {
                    this.refuseEscapingTargets([op], "eraseWithEvidence (after the commit point; the journal is kept)");
                    if (op.kind === "delete") {
                        rmSync(op.target, { force: true });
                    }
                    else if (op.kind === "shadow-delete") {
                        rmSync(this.shadowPath(op.target), { force: true });
                    }
                }
                this.ledger = rows;
                this.saveLedger();
                this.appendTransfers(transfers);
                this.closeJournalIfOurs(txn);
            }
            const late = captureAndClearLineageForEntries(this.controlPlaneRoot, resolvedIds);
            for (const e of erased) {
                const lateSessions = late.get(e.id);
                if (lateSessions !== undefined && lateSessions.length > 0)
                    e.sessions = [...new Set([...e.sessions, ...lateSessions])].sort();
            }
            const resolvedSet = new Set(resolvedIds);
            const quarantineHits = [];
            let quarantineOpaque = 0;
            const qDir = join(this.controlPlaneRoot, QUARANTINE_DIR);
            let qNames = [];
            try {
                qNames = readdirSync(qDir);
            }
            catch (err) {
                if (err.code !== "ENOENT") {
                    throw new ControlPlaneCorruptError(`erasure residual enumeration failed: the quarantine dir could not be read (${err.code ?? "io error"}) at ${qDir} — the attestation must not silently claim an empty residual set; retry after the filesystem fault clears (the deletions themselves are committed and a replay converges)`);
                }
            }
            for (const name of qNames.sort()) {
                const text = readSafe(join(qDir, name));
                if (text === undefined) {
                    quarantineOpaque++;
                    continue;
                }
                const qid = parseEntryFile(text).id;
                if (qid === undefined) {
                    quarantineOpaque++;
                    continue;
                }
                if (resolvedSet.has(qid))
                    quarantineHits.push(name);
            }
            const claimed = new Set([...erased.map((e) => e.id), ...erasedPreviously.map((e) => e.id)]);
            const notFoundSet = new Set(notFoundRows.map((n) => n.id));
            const complete = conflicts.length === 0 && resolvedIds.every((id) => claimed.has(id) || (custodyState === "complete" && notFoundSet.has(id)));
            return {
                v: 1,
                requestId,
                at: now,
                status: complete ? "complete" : "partial",
                evidenceCapability: "journal",
                custodyState,
                select: cloneErasureSelect(select),
                selectHash,
                resolvedIds,
                erased,
                notFound: notFoundRows,
                ...(erasedPreviously.length > 0 ? { erasedPreviously } : {}),
                conflicts,
                residuals: { quarantineHits, quarantineOpaque, propagation: "local-store-only" },
            };
        }
        catch (err) {
            this.ledger = undefined;
            throw withPermissionRemedy(err, this.directoryRoot);
        }
        finally {
            lock.release();
        }
    }
    static AUDIT_FENCE_ATTEMPTS = 6;
    static AUDIT_FENCE_RETRY_MS = 25;
    auditJournalPending() {
        return readControlFileOrAbsent(join(this.controlPlaneRoot, JOURNAL_FILE), "transaction journal") !== undefined;
    }
    auditReadLedger() {
        const path = join(this.controlPlaneRoot, LEDGER_FILE);
        const raw = readControlFileOrAbsent(path, "committed-rev ledger");
        if (raw === undefined)
            return { raw, rows: Object.create(null) };
        const parsed = parseLedgerText(raw, path);
        if (parsed.form === "v1") {
            throw new Error(`memory audit read refused: the committed-rev ledger at ${path} is still schema v1 (pre-binding) — ` +
                `migrate first (the store migrates itself at its next uncontended write/read entry or checkControlPlane); ` +
                `an audit read never triggers the migration`);
        }
        return { raw, rows: parsed.rows };
    }
    async auditStable(read) {
        for (let attempt = 0; attempt < FileMemoryEngineBackend.AUDIT_FENCE_ATTEMPTS; attempt++) {
            if (attempt > 0)
                await new Promise((r) => setTimeout(r, FileMemoryEngineBackend.AUDIT_FENCE_RETRY_MS));
            if (this.auditJournalPending())
                continue;
            const before = this.auditReadLedger();
            let out;
            try {
                out = read(before.rows, attempt === FileMemoryEngineBackend.AUDIT_FENCE_ATTEMPTS - 1);
            }
            catch (err) {
                if (err instanceof AuditObservationUnstableError)
                    continue;
                throw err;
            }
            if (this.auditJournalPending())
                continue;
            const after = readControlFileOrAbsent(join(this.controlPlaneRoot, LEDGER_FILE), "committed-rev ledger");
            if (after !== before.raw)
                continue;
            return out;
        }
        throw new AuditSnapshotContendedError(`memory audit read refused after ${FileMemoryEngineBackend.AUDIT_FENCE_ATTEMPTS} attempts: a transaction journal stayed pending ` +
            `(or the ledger kept advancing) across every try — the committed account cannot be snapshotted without tearing. ` +
            `A journal that never clears is a crashed transaction: the next backend construction or locked write recovers it.`);
    }
    resolveCommittedContent(id, rev, binding, final, lockHeld = false) {
        if (binding === undefined)
            return { state: "unavailable", reason: "carrier-missing" };
        if (!isValidEntryId(id))
            return { state: "unavailable", reason: "carrier-missing" };
        const shadowRaw = readControlFileOrAbsent(this.shadowPath(id), "committed shadow copy");
        const absPath = this.bindingAbsPath(binding);
        if (shadowRaw !== undefined) {
            const shadowEntry = entryFromFile(shadowRaw, id, binding.slug, binding.scope);
            if (shadowEntry.rev !== rev) {
                let projText;
                try {
                    projText = readFileSync(absPath, "utf8");
                }
                catch {
                    projText = undefined;
                }
                if (projText !== undefined && parseEntryFile(projText).id === id) {
                    const projEntry = entryFromFile(projText, id, binding.slug, binding.scope);
                    if (projEntry.rev === rev)
                        return { state: "present", entry: projEntry };
                }
                if (lockHeld)
                    return { state: "unavailable", reason: "carrier-missing" };
                if (!final)
                    throw new AuditObservationUnstableError(`shadow bytes for ${id} do not carry the committed rev — possible adoption in flight`);
                if (this.txnInFlight()) {
                    throw new AuditSnapshotContendedError(`memory audit read refused: shadow bytes for ${id} do not carry the committed rev while a live writer holds the txn lock — an adoption is in flight; retry when the store settles.`);
                }
                return { state: "unavailable", reason: "carrier-missing" };
            }
            return probeProjection(absPath, id) === "present" ? { state: "present", entry: shadowEntry } : { state: "unavailable", reason: "carrier-missing" };
        }
        let text;
        try {
            text = readFileSync(absPath, "utf8");
        }
        catch {
            text = undefined;
        }
        if (text !== undefined && parseEntryFile(text).id === id) {
            const diskEntry = entryFromFile(text, id, binding.slug, binding.scope);
            if (diskEntry.rev === rev)
                return { state: "present", entry: diskEntry };
        }
        return { state: "unavailable", reason: "shadowless-legacy" };
    }
    async committedSnapshotOf(id) {
        this.ensureLayout();
        return this.auditStable((rows, final) => {
            const row = Object.prototype.hasOwnProperty.call(rows, id) ? rows[id] : undefined;
            if (row === undefined)
                return { state: "absent", id };
            const binding = rowsBinding(rows, id);
            return {
                state: "row",
                id,
                rev: row.rev,
                binding: this.publicBinding(row, binding),
                content: this.resolveCommittedContent(id, row.rev, binding, final),
            };
        });
    }
    publicBinding(row, binding) {
        if (binding === undefined)
            return { state: "unbound" };
        return {
            state: "bound",
            scope: binding.scope,
            slug: binding.slug,
            ...(typeof row.at === "number" ? { at: row.at } : {}),
            ...(row.prev !== undefined ? { prev: { ...row.prev } } : {}),
        };
    }
    async committedSnapshotsOfScopes(scopes) {
        const want = new Set(scopes);
        this.ensureLayout();
        try {
            return await this.auditStable((rows, final) => {
                const out = { complete: true, rows: [], unbound: [] };
                for (const [id, row] of rowsEntries(rows)) {
                    const binding = rowsBinding(rows, id);
                    if (binding === undefined) {
                        out.unbound.push(id);
                        continue;
                    }
                    if (!want.has(binding.scope))
                        continue;
                    out.rows.push({
                        state: "row",
                        id,
                        rev: row.rev,
                        binding: this.publicBinding(row, binding),
                        content: this.resolveCommittedContent(id, row.rev, binding, final),
                    });
                }
                return out;
            });
        }
        catch (err) {
            if (err instanceof AuditSnapshotContendedError)
                return { complete: false };
            throw err;
        }
    }
    async custodyOf(id) {
        const transfersPath = join(this.controlPlaneRoot, TRANSFERS_FILE);
        for (let attempt = 0; attempt < FileMemoryEngineBackend.AUDIT_FENCE_ATTEMPTS; attempt++) {
            if (attempt > 0)
                await new Promise((r) => setTimeout(r, FileMemoryEngineBackend.AUDIT_FENCE_RETRY_MS));
            if (this.auditJournalPending())
                continue;
            const rawBefore = readControlFileOrAbsent(transfersPath, "transfer evidence log");
            const out = this.custodyReadPure(id, rawBefore);
            if (out.state === "complete" && out.events.length === 0) {
                const ledgerRaw = readControlFileOrAbsent(join(this.controlPlaneRoot, LEDGER_FILE), "committed-rev ledger");
                if (ledgerRaw !== undefined) {
                    const parsed = parseLedgerText(ledgerRaw, join(this.controlPlaneRoot, LEDGER_FILE));
                    if (parsed.form === "v2" && Object.prototype.hasOwnProperty.call(parsed.rows, id) && parsed.rows[id]?.prev !== undefined) {
                        out.state = "damaged";
                    }
                }
            }
            if (this.auditJournalPending())
                continue;
            if (readControlFileOrAbsent(transfersPath, "transfer evidence log") !== rawBefore)
                continue;
            return out;
        }
        throw new AuditSnapshotContendedError(`memory custody read refused after ${FileMemoryEngineBackend.AUDIT_FENCE_ATTEMPTS} attempts: a transaction journal stayed pending across ` +
            `every try — the evidence chain may hold a committed transaction's rows not yet appended. A journal that never ` +
            `clears is a crashed transaction: the next backend construction or locked write recovers it.`);
    }
    custodyReadPure(id, raw) {
        const path = join(this.controlPlaneRoot, TRANSFERS_FILE);
        const out = { state: "complete", events: [] };
        if (raw === undefined)
            return out;
        const byEv = new Map();
        const lines = raw.split("\n");
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (line === undefined || line === "")
                continue;
            let parsed;
            try {
                parsed = JSON.parse(line);
            }
            catch {
                if (lines.slice(i + 1).every((l) => l === "")) {
                    out.state = "damaged";
                    break;
                }
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is unparseable (not a torn tail — fail-closed): ${path}`);
            }
            if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is not an object (fail-closed): ${path}`);
            }
            const e = parsed;
            if (typeof e.ev !== "string" || e.ev.length === 0 || typeof e.channel !== "string" || e.channel.length === 0 || typeof e.at !== "number" || !Number.isInteger(e.at)) {
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is missing its base keys (ev/channel/at) — fail-closed: ${path}`);
            }
            if (KNOWN_TRANSFER_CHANNELS.has(e.channel)) {
                const invalid = transferEventInvalid(parsed);
                if (invalid !== undefined)
                    throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is invalid (${invalid}): ${path}`);
            }
            const canonical = canonicalJsonStringify(e);
            const seen = byEv.get(e.ev);
            if (seen !== undefined) {
                if (seen.has(canonical))
                    continue;
                out.state = "damaged";
                out.reason ??= `evidence chain carries event id ${JSON.stringify(e.ev)} twice with DIFFERENT payloads (line ${i + 1}) — one ev is one identity; the chain was spliced or corrupted out of band and this store's own write path refuses to operate on it`;
                seen.add(canonical);
            }
            else {
                byEv.set(e.ev, new Set([canonical]));
            }
            if (e.id === id)
                out.events.push(e);
        }
        return out;
    }
    static EXPORT_INCOMPLETE = "memory.export_incomplete";
    storeIdentityLocked() {
        const path = join(this.controlPlaneRoot, STORE_IDENTITY_FILE);
        const parse = (raw) => {
            let parsed;
            try {
                parsed = JSON.parse(raw);
            }
            catch {
                throw new ControlPlaneCorruptError(`store identity is unparseable: ${path}`);
            }
            const rec = parsed;
            if (!rec || typeof rec !== "object" || rec.v !== 1 || typeof rec.storeId !== "string" || rec.storeId.length === 0) {
                throw new ControlPlaneCorruptError(`store identity has the wrong shape: ${path}`);
            }
            return rec.storeId;
        };
        const raw = readControlFileOrAbsent(path, "store identity");
        if (raw !== undefined)
            return parse(raw);
        const storeId = randomUUID();
        let fd;
        try {
            fd = openSync(path, "wx", 0o600);
        }
        catch (err) {
            if (err.code === "EEXIST") {
                const again = readControlFileOrAbsent(path, "store identity");
                if (again === undefined)
                    throw new ControlPlaneCorruptError(`store identity vanished between its EEXIST and the re-read: ${path}`);
                return parse(again);
            }
            throw err;
        }
        try {
            writeAllSync(fd, `${JSON.stringify({ v: 1, storeId }, null, 2)}\n`);
            fsyncSync(fd);
        }
        finally {
            closeSync(fd);
        }
        return storeId;
    }
    exportGovernanceFingerprint() {
        const h = createHash("sha256");
        const feed = (label, text) => {
            h.update(JSON.stringify([label, text]));
        };
        for (const file of [LINEAGE_FILE, CHALLENGES_FILE]) {
            feed(file, readControlFileOrAbsent(join(this.controlPlaneRoot, file), `${file} (export fence)`) ?? "<absent>");
            let journalRaw;
            try {
                journalRaw = readControlFileOrAbsent(join(this.controlPlaneRoot, `${file}.journal`), `${file} journal (export fence)`);
            }
            catch (err) {
                throw erasureCodedError(`memory export refused: the ${file} journal could not be read (${thrownText(err)}) — a journal read failure is not absence, and exporting over it could seal stale governance under an honest hash`, FileMemoryEngineBackend.EXPORT_INCOMPLETE);
            }
            feed(`${file}.journal`, journalRaw ?? "<absent>");
        }
        const dir = join(this.controlPlaneRoot, SESSION_POLLUTION_DIR);
        let names = [];
        try {
            names = readdirSync(dir).sort();
        }
        catch (err) {
            if (err.code !== "ENOENT") {
                throw new ControlPlaneCorruptError(`session pollution markers could not be enumerated for export (${err.code ?? "io error"}) at ${dir} — fail-closed`, { cause: err });
            }
        }
        for (const name of names) {
            let text;
            try {
                text = readFileSync(join(dir, name), "utf8");
            }
            catch (err) {
                text = `<unreadable:${err.code ?? "io error"}>`;
            }
            feed(`pollution/${name}`, text);
        }
        return h.digest("hex");
    }
    readExportChainRowsPure() {
        const path = join(this.controlPlaneRoot, TRANSFERS_FILE);
        const raw = readControlFileOrAbsent(path, "transfer evidence log");
        const out = [];
        if (raw === undefined)
            return out;
        const byEv = new Map();
        const lines = raw.split("\n");
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (line === undefined || line === "")
                continue;
            let parsed;
            try {
                parsed = JSON.parse(line);
            }
            catch {
                if (lines.slice(i + 1).every((l) => l === "")) {
                    throw erasureCodedError(`memory export refused: the transfer evidence log has a torn tail (line ${i + 1}) — the export reads it purely (the file is untouched); run any locked mutation entry (a write, an adopting read) to heal the chain, then export again`, FileMemoryEngineBackend.EXPORT_INCOMPLETE);
                }
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is unparseable (not a torn tail — fail-closed): ${path}`);
            }
            if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is not an object (fail-closed): ${path}`);
            }
            const e = parsed;
            if (typeof e.ev !== "string" || e.ev.length === 0 || typeof e.channel !== "string" || e.channel.length === 0 || typeof e.at !== "number" || !Number.isInteger(e.at)) {
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is missing its base keys (ev/channel/at) — fail-closed: ${path}`);
            }
            if (!KNOWN_TRANSFER_CHANNELS.has(e.channel)) {
                throw erasureCodedError(`memory export refused: the evidence chain carries channel ${JSON.stringify(e.channel)} this exporter does not know (line ${i + 1}) — an unknown row can neither be tenant-sliced nor silently dropped; export with the binary that wrote it (or newer)`, FileMemoryEngineBackend.EXPORT_INCOMPLETE);
            }
            const invalid = transferEventInvalid(parsed);
            if (invalid !== undefined)
                throw new ControlPlaneCorruptError(`transfer evidence log line ${i + 1} is invalid (${invalid}): ${path}`);
            const canonical = canonicalJsonStringify(e);
            const prior = byEv.get(e.ev);
            if (prior !== undefined) {
                if (prior !== canonical) {
                    throw erasureCodedError(`memory export refused: the evidence chain carries event id ${JSON.stringify(e.ev)} twice with DIFFERENT payloads (line ${i + 1}) — a spliced chain cannot be exported`, FileMemoryEngineBackend.EXPORT_INCOMPLETE);
                }
                continue;
            }
            byEv.set(e.ev, canonical);
            out.push(e);
        }
        return out;
    }
    sliceCustodyForScopes(rows, scopeSet, fullStore, storeId) {
        const custody = [];
        let unsliceable = 0;
        for (const e of rows) {
            const channel = e.channel;
            if (channel === "migration" || channel === "erasure-anchor")
                continue;
            if (channel === "erasure-request") {
                if (scopeSet.has(e.scope))
                    custody.push(e);
                continue;
            }
            if (channel === "delete") {
                const from = e.from;
                if (from !== undefined && typeof from.scope === "string") {
                    if (scopeSet.has(from.scope))
                        custody.push(e);
                }
                else if (fullStore) {
                    custody.push(e);
                }
                else {
                    unsliceable++;
                }
                continue;
            }
            const from = e.from;
            const to = e.to;
            const fromIn = from !== undefined && typeof from.scope === "string" && scopeSet.has(from.scope);
            const toIn = to !== undefined && typeof to.scope === "string" && scopeSet.has(to.scope);
            if (!fromIn && !toIn)
                continue;
            if ((from === undefined || fromIn) && (to === undefined || toIn)) {
                custody.push(e);
                continue;
            }
            const redacted = { ...e };
            const named = new Set(Array.isArray(e.redacted) ? e.redacted : []);
            if (from !== undefined && !fromIn) {
                delete redacted.from;
                named.add("from");
            }
            if (to !== undefined && !toIn) {
                delete redacted.to;
                named.add("to");
            }
            redacted.redacted = [...named].sort();
            if (redacted.origin === undefined) {
                redacted.origin = storeId();
                redacted.srcEv = e.ev;
            }
            custody.push(redacted);
        }
        return { custody, unsliceable };
    }
    enumerateQuarantineFor(ids) {
        const qDir = join(this.controlPlaneRoot, QUARANTINE_DIR);
        let names = [];
        try {
            names = readdirSync(qDir);
        }
        catch (err) {
            if (err.code !== "ENOENT") {
                throw new ControlPlaneCorruptError(`export residual enumeration failed: the quarantine dir could not be read (${err.code ?? "io error"}) at ${qDir} — a bundle must not silently claim an empty residual set`);
            }
        }
        const quarantined = [];
        let quarantineOpaque = 0;
        for (const name of names.sort()) {
            const text = readSafe(join(qDir, name));
            if (text === undefined) {
                quarantineOpaque++;
                continue;
            }
            const qid = parseEntryFile(text).id;
            if (qid === undefined) {
                quarantineOpaque++;
                continue;
            }
            if (ids.has(qid))
                quarantined.push(name);
        }
        return { quarantined, quarantineOpaque };
    }
    async exportSnapshotOf(scopes, timings) {
        const scopeSet = new Set(scopes);
        const lock = await this.acquireTxnLock(timings);
        try {
            this.assertTxnLockOwnership(lock.token);
            this.ledger = undefined;
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token);
            const rows = this.loadLedger();
            if (this.persistedSchemaVersion !== "v2") {
                throw erasureCodedError("memory export refused: the committed-rev ledger is still schema v1 (pre-binding) — the store migrates itself at its next write/read entry or checkControlPlane; an export never triggers the migration", FileMemoryEngineBackend.EXPORT_INCOMPLETE);
            }
            if (this.chainDegradedMarkerPresent()) {
                throw erasureCodedError("memory export refused: the custody chain carries the durable degradation marker (transfers.chain-degraded.json) — evidence rows were lost to an out-of-band tear and a bundle cannot testify to a complete deletion history; resolve the marker deliberately before exporting", FileMemoryEngineBackend.EXPORT_INCOMPLETE);
            }
            const storeId = this.storeIdentityLocked();
            const registered = registeredScopes(this.controlPlaneRoot);
            const fullStore = Object.keys(registered).every((s) => scopeSet.has(s));
            for (let round = 0; round < 3; round++) {
                const before = this.exportGovernanceFingerprint();
                const lineageRec = readLineageRecord(this.controlPlaneRoot);
                const challenges = readChallengeEvents(this.controlPlaneRoot);
                const polluted = listSessionPollution(this.controlPlaneRoot);
                const chainRows = this.readExportChainRowsPure();
                this.exportFaceProbe?.(round);
                const after = this.exportGovernanceFingerprint();
                if (before !== after)
                    continue;
                const latched = new Set();
                for (const txn of Object.values(lineageRec.pending))
                    for (const row of txn.rows)
                        latched.add(row.entryId);
                const entries = [];
                const contentUnavailable = [];
                const boundIdsInScopes = new Set();
                for (const [id, row] of rowsEntries(rows)) {
                    const binding = rowsBinding(rows, id);
                    if (binding === undefined || !scopeSet.has(binding.scope))
                        continue;
                    boundIdsInScopes.add(id);
                    if (latched.has(id))
                        continue;
                    const content = this.resolveCommittedContent(id, row.rev, binding, true, true);
                    if (content.state === "present")
                        entries.push(content.entry);
                    else
                        contentUnavailable.push({ id, scope: binding.scope, slug: binding.slug, rev: row.rev, reason: content.reason });
                }
                const sliced = this.sliceCustodyForScopes(chainRows, scopeSet, fullStore, () => storeId);
                const residual = this.enumerateQuarantineFor(boundIdsInScopes);
                const lineage = [];
                for (const [entryId, sessions] of Object.entries(lineageRec.committed)) {
                    for (const [sessionId, c] of Object.entries(sessions))
                        lineage.push({ entryId, sessionId, lastRev: c.lastRev, lastAt: c.lastAt, ...(c.marked === true ? { marked: true } : {}) });
                }
                const pollutedSessions = Object.entries(polluted).map(([sessionId, r]) => ({ sessionId, at: r.at, reason: r.reason, ...(r.cause !== undefined ? { cause: r.cause } : {}) }));
                try {
                    this.assertTxnLockOwnership(lock.token);
                }
                catch (err) {
                    throw erasureCodedError(`memory export refused: the transaction lock was lost during assembly (a stalled hold past the stale line was stolen from) — the read set can no longer be proven consistent; retry the export (${thrownText(err)})`, FileMemoryEngineBackend.EXPORT_INCOMPLETE);
                }
                return {
                    storeId,
                    fullStore,
                    entries,
                    contentUnavailable,
                    unbound: unboundRowIds(rows).sort(),
                    pendingLatch: [...latched].sort(),
                    custody: sliced.custody,
                    unsliceableCustody: sliced.unsliceable,
                    challenges,
                    lineage,
                    pollutedSessions,
                    quarantined: residual.quarantined,
                    quarantineOpaque: residual.quarantineOpaque,
                };
            }
            throw erasureCodedError("memory export refused: the governance faces (challenge/lineage/pollution) kept changing across three consistency rounds — the store is too hot to snapshot without tearing; retry when its writers settle", FileMemoryEngineBackend.EXPORT_INCOMPLETE);
        }
        finally {
            lock.release();
        }
    }
    async governanceExport(scopes, timings) {
        const scopeSet = new Set(scopes);
        const lock = await this.acquireTxnLock(timings);
        try {
            this.assertTxnLockOwnership(lock.token);
            this.ledger = undefined;
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token);
            const rows = this.loadLedger();
            if (this.persistedSchemaVersion !== "v2")
                return { custody: [], unbound: [], complete: false, unsliceableCustody: 0, reason: "the committed-rev ledger is still schema v1 (migrate first)" };
            if (this.chainDegradedMarkerPresent()) {
                return { custody: [], unbound: [], complete: false, unsliceableCustody: 0, reason: "the custody chain carries the durable degradation marker (transfers.chain-degraded.json) — evidence rows were lost to an out-of-band tear" };
            }
            const registered = registeredScopes(this.controlPlaneRoot);
            const fullStore = Object.keys(registered).every((s) => scopeSet.has(s));
            try {
                const chainRows = this.readExportChainRowsPure();
                const sliced = this.sliceCustodyForScopes(chainRows, scopeSet, fullStore, () => this.storeIdentityLocked());
                this.assertTxnLockOwnership(lock.token);
                return { custody: sliced.custody, unbound: unboundRowIds(rows).sort(), complete: true, unsliceableCustody: sliced.unsliceable };
            }
            catch (err) {
                if (err.code === FileMemoryEngineBackend.EXPORT_INCOMPLETE || err instanceof ControlPlaneCorruptError) {
                    if (err instanceof ControlPlaneCorruptError && err.facet !== "lock-ownership")
                        throw err;
                    return { custody: [], unbound: [], complete: false, unsliceableCustody: 0, reason: thrownText(err) };
                }
                throw err;
            }
        }
        finally {
            lock.release();
        }
    }
    async custodyImport(rows, opts, timings) {
        if (typeof opts?.bundleHash !== "string" || opts.bundleHash.length === 0 || typeof opts.sourceStoreId !== "string" || opts.sourceStoreId.length === 0) {
            throw erasureCodedError("custodyImport: bundleHash and sourceStoreId are required (the remint namespace and the origin stamp)", "config.memory_import_request");
        }
        const lock = await this.acquireTxnLock(timings);
        try {
            this.assertTxnLockOwnership(lock.token);
            this.recoverJournal({ token: lock.token });
            return this.custodyImportLocked(rows, opts);
        }
        finally {
            lock.release();
        }
    }
    custodyImportLocked(rows, opts) {
        const withheld = [];
        const toAppend = [];
        for (const row of rows) {
            const channel = typeof row.channel === "string" ? row.channel : "<malformed>";
            const srcEv = typeof row.srcEv === "string" ? row.srcEv : typeof row.ev === "string" ? row.ev : "<malformed>";
            if (typeof row.ev !== "string" || row.ev.length === 0 || typeof row.channel !== "string" || row.channel.length === 0 || typeof row.at !== "number" || !Number.isInteger(row.at)) {
                withheld.push({ srcEv, channel });
                continue;
            }
            if (!KNOWN_TRANSFER_CHANNELS.has(row.channel)) {
                withheld.push({ srcEv, channel });
                continue;
            }
            if (transferEventInvalid(row) !== undefined) {
                withheld.push({ srcEv, channel });
                continue;
            }
            const reminted = {
                ...row,
                ev: `imp:${opts.bundleHash}:${row.ev}`,
                origin: row.origin ?? opts.sourceStoreId,
                srcEv: row.srcEv ?? row.ev,
            };
            if (transferEventInvalid(reminted) !== undefined) {
                withheld.push({ srcEv, channel });
                continue;
            }
            toAppend.push(reminted);
        }
        this.appendTransfers(toAppend);
        return { appended: toAppend.length, withheld };
    }
    appendImportChallengesVerified(events) {
        if (events.length === 0)
            return;
        const { assignments } = appendChallengeEvents(this.controlPlaneRoot, events, this.now);
        const replayed = assignments.filter((a) => a.replayed);
        if (replayed.length === 0)
            return;
        const byEventId = new Map();
        for (const e of readChallengeEvents(this.controlPlaneRoot))
            if (!byEventId.has(e.eventId))
                byEventId.set(e.eventId, e);
        for (const a of replayed) {
            const want = events.find((e) => e.eventId === a.eventId);
            const got = byEventId.get(a.eventId);
            if (want === undefined || got === undefined || got.kind !== "challenge" || got.entryId !== want.entryId || got.reason !== want.reason || got.challengedRev !== want.challengedRev) {
                throw new ControlPlaneCorruptError(`bundle import aborted: challenge event ${JSON.stringify(a.eventId)} replayed onto an existing ledger event with a DIFFERENT payload (kind/entryId/reason/challengedRev) — key idempotency would silently swallow the imported challenge; the import latch stays until a clean re-import converges`);
            }
        }
    }
    async importBundleCommit(plan, timings) {
        const bundleHash = typeof plan?.bundleHash === "string" ? plan.bundleHash : "";
        const sourceStoreId = typeof plan?.sourceStoreId === "string" ? plan.sourceStoreId : "";
        if (!/^[0-9a-f]{64}$/.test(bundleHash) || sourceStoreId.length === 0) {
            throw erasureCodedError("importBundleCommit: plan.bundleHash must be the 64-hex bundle digest and sourceStoreId a non-empty string", "config.memory_import_request");
        }
        const lock = await this.acquireTxnLock(timings);
        try {
            this.assertTxnLockOwnership(lock.token);
            this.ledger = undefined;
            this.recoverJournal({ token: lock.token });
            this.adoptLayoutLocked(lock.token);
            this.migrateLockedIfNeeded(lock.token);
            const ctl = this.controlPlaneRoot;
            const txnId = importLatchTxnId(bundleHash);
            const receiptPath = join(ctl, IMPORT_RECEIPTS_DIR, `${bundleHash}.json`);
            const receiptRaw = readControlFileOrAbsent(receiptPath, "bundle import completion receipt");
            if (receiptRaw !== undefined) {
                let report;
                try {
                    const parsed = JSON.parse(receiptRaw);
                    if (parsed === null ||
                        typeof parsed !== "object" ||
                        !("v" in parsed) ||
                        parsed.v !== 1 ||
                        !("bundleHash" in parsed) ||
                        parsed.bundleHash !== bundleHash ||
                        !("report" in parsed)) {
                        throw new Error("wrong envelope");
                    }
                    const rep = parsed.report;
                    if (!rep ||
                        typeof rep !== "object" ||
                        rep.v !== 1 ||
                        rep.bundleHash !== bundleHash ||
                        typeof rep.sourceStoreId !== "string" ||
                        !Array.isArray(rep.landed) ||
                        !Array.isArray(rep.alreadyPresent) ||
                        !Array.isArray(rep.refusedErased) ||
                        !Array.isArray(rep.refusedUntrusted) ||
                        !Array.isArray(rep.conflicts) ||
                        !Array.isArray(rep.governanceWithheld) ||
                        !Array.isArray(rep.erasedAtSource) ||
                        !Array.isArray(rep.pollutionDivergence) ||
                        !Array.isArray(rep.referentialOrphans) ||
                        !Array.isArray(rep.lineageDivergence) ||
                        !Array.isArray(rep.custodyWithheld) ||
                        typeof rep.custodyAppended !== "number") {
                        throw new Error("wrong report shape");
                    }
                    report = rep;
                }
                catch {
                    throw new ControlPlaneCorruptError(`bundle import completion receipt is unreadable/malformed at ${receiptPath} — it cannot prove completion and cannot be re-minted; repair or remove it deliberately (fail-closed)`);
                }
                releaseImportLatch(ctl, txnId);
                return report;
            }
            const digest = this.deletedIdsDigestLocked();
            if (digest.state === "damaged" || this.chainDegradedMarkerPresent()) {
                throw erasureCodedError("memory bundle import refused: the destination evidence chain is damaged or durably marked degraded — deletion history cannot be proven, so the anti-resurrection judgment cannot run; heal/rebuild the chain (locked mutation entry / explicit rebuild), then import again (nothing was landed, no latch was set)", "memory.import_rejected");
            }
            stageImportLatch(ctl, bundleHash, plan.entries.map((e) => ({ entryId: e.id, rev: e.rev })), this.now);
            const rows = this.loadLedger();
            const rowPresent = (id) => Object.prototype.hasOwnProperty.call(rows, id);
            const report = {
                v: 1,
                bundleHash,
                sourceStoreId,
                landed: [],
                alreadyPresent: [],
                refusedErased: [],
                refusedUntrusted: [],
                conflicts: [],
                governanceWithheld: [],
                erasedAtSource: [],
                pollutionDivergence: [],
                referentialOrphans: [],
                lineageDivergence: [],
                custodyWithheld: [],
                custodyAppended: 0,
            };
            const entryById = new Map(plan.entries.map((e) => [e.id, e]));
            const toLand = [];
            for (const e of plan.entries) {
                const present = rowPresent(e.id);
                if (!present && digest.ids.has(e.id)) {
                    report.refusedErased.push(e.id);
                    continue;
                }
                if (present && rowsRev(rows, e.id) === e.rev && rowsBinding(rows, e.id)?.scope === e.scope) {
                    report.alreadyPresent.push(e.id);
                    continue;
                }
                if (!present && e.frontmatter.provenance?.kind === "repo_file" && e.frontmatter.trust === undefined) {
                    report.refusedUntrusted.push(e.id);
                    continue;
                }
                toLand.push(e);
            }
            const erasedAtSource = new Set();
            for (const c of plan.custody) {
                if (c.channel === "delete" && typeof c.id === "string" && rowPresent(c.id))
                    erasedAtSource.add(c.id);
            }
            report.erasedAtSource = [...erasedAtSource].sort();
            const screened = new Set();
            for (const finding of screenInboundEntries(toLand, { perFileBytes: MAX_MEMORY_BYTES })) {
                screened.add(finding.id);
                report.conflicts.push({ id: finding.id, reason: `inbound screen: ${finding.findings.map((f) => `${f.code} (${f.reason})`).join("; ")}` });
            }
            const patches = toLand.filter((e) => !screened.has(e.id)).map((e) => ({ op: "add", id: e.id, entry: e, guard: "absent" }));
            const patchReport = patches.length > 0 ? await this.applyPatchesLocked(patches, lock.token) : { applied: [], conflicts: [] };
            for (const c of patchReport.conflicts)
                report.conflicts.push({ id: c.id, reason: c.reason });
            const landedIds = new Set();
            for (const a of patchReport.applied) {
                if (a.op !== "add")
                    continue;
                landedIds.add(a.id);
                const src = entryById.get(a.id);
                report.landed.push({ id: a.id, scope: src?.scope ?? "", slug: a.slug ?? src?.slug ?? "" });
            }
            const custodyOutcome = this.custodyImportLocked(plan.custody, { bundleHash, sourceStoreId });
            report.custodyAppended = custodyOutcome.appended;
            report.custodyWithheld = custodyOutcome.withheld;
            const eligible = new Set([...landedIds, ...report.alreadyPresent]);
            const destRows = this.loadLedger();
            const destKnown = (id) => Object.prototype.hasOwnProperty.call(destRows, id);
            const withheldIds = new Set();
            const orphanIds = new Set();
            const lineageToApply = [];
            for (const row of plan.lineage) {
                if (eligible.has(row.entryId)) {
                    if (row.marked === true) {
                        const shadow = this.readCommittedShadow(row.entryId);
                        let committedFm = shadow !== undefined ? parseEntryFile(shadow).frontmatter : undefined;
                        if (committedFm === undefined)
                            committedFm = this.locateById(row.entryId)?.entry.frontmatter;
                        if (committedFm !== undefined && committedOriginOf(committedFm) === undefined) {
                            const { marked: _dropped, ...unmarked } = row;
                            lineageToApply.push(unmarked);
                            report.conflicts.push({ id: row.entryId, reason: `lineage row claims marked:true but the committed content carries no origin marker — the bit was dropped (the pollution sweep judges the contribution)` });
                            continue;
                        }
                    }
                    lineageToApply.push(row);
                }
                else if (destKnown(row.entryId) || entryById.has(row.entryId))
                    withheldIds.add(row.entryId);
                else
                    orphanIds.add(row.entryId);
            }
            const challengesToApply = [];
            for (const c of plan.challenges) {
                if (eligible.has(c.entryId))
                    challengesToApply.push(c);
                else if (destKnown(c.entryId) || entryById.has(c.entryId))
                    withheldIds.add(c.entryId);
                else
                    orphanIds.add(c.entryId);
            }
            report.governanceWithheld = [...withheldIds].sort();
            report.referentialOrphans = [...orphanIds].sort();
            report.lineageDivergence = importLineageCommitted(ctl, lineageToApply);
            this.appendImportChallengesVerified(challengesToApply.map((c) => ({
                eventId: `imp:${bundleHash}:${c.eventId}`,
                entryId: c.entryId,
                reason: c.reason,
                ...(c.challengedRev !== undefined ? { challengedRev: c.challengedRev } : {}),
            })));
            for (const p of [...plan.pollutedSessions].sort((a, b) => (a.sessionId < b.sessionId ? -1 : 1))) {
                const contributions = lineageContributionsOfSession(ctl, p.sessionId).filter((c) => c.marked !== true);
                this.appendImportChallengesVerified(contributions.map((c) => ({
                    eventId: `imp-sweep:${bundleHash}:pollution:${p.sessionId}:${p.at}:${c.entryId}`,
                    entryId: c.entryId,
                    reason: `challenged: the contributing session was marked polluted`,
                    challengedRev: c.lastRev,
                })));
                const outcome = importSessionPollution(ctl, p.sessionId, { at: p.at, reason: p.reason, ...(p.cause !== undefined ? { cause: p.cause } : {}) });
                if (outcome === "divergent")
                    report.pollutionDivergence.push({ sessionId: p.sessionId, kept: "destination" });
            }
            ensureDirExists(join(ctl, IMPORT_RECEIPTS_DIR));
            if (readControlFileOrAbsent(receiptPath, "bundle import completion receipt") !== undefined) {
                throw new ControlPlaneCorruptError(`bundle import completion receipt appeared mid-import at ${receiptPath} — refusing to overwrite it (fail-closed)`);
            }
            const staged = join(ctl, IMPORT_RECEIPTS_DIR, `.stage-${bundleHash}-${randomUUID().slice(0, 8)}`);
            const fd = openSync(staged, "wx", 0o600);
            try {
                writeAllSync(fd, `${JSON.stringify({ v: 1, bundleHash, at: this.now(), report }, null, 2)}\n`);
                fsyncSync(fd);
            }
            finally {
                closeSync(fd);
            }
            try {
                linkSync(staged, receiptPath);
                rmSync(staged, { force: true });
            }
            catch (err) {
                try {
                    rmSync(staged, { force: true });
                }
                catch {
                }
                if (err.code === "EEXIST") {
                    throw new ControlPlaneCorruptError(`bundle import completion receipt appeared mid-import at ${receiptPath} — refusing to overwrite it (fail-closed)`);
                }
                throw err;
            }
            releaseImportLatch(ctl, txnId);
            return report;
        }
        catch (err) {
            this.ledger = undefined;
            throw err;
        }
        finally {
            lock.release();
        }
    }
    async getConsolidationCursor(scope) {
        const cursors = this.readCursors();
        return cursors[scope];
    }
    async setConsolidationCursor(scope, cursor) {
        const cursors = this.readCursors();
        cursors[scope] = cursor;
        atomicWriteFileSync(join(this.controlPlaneRoot, CURSORS_FILE), `${JSON.stringify(cursors, null, 2)}\n`);
    }
    readCursors() {
        try {
            const parsed = JSON.parse(readFileSync(join(this.controlPlaneRoot, CURSORS_FILE), "utf8"));
            if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
                const out = Object.create(null);
                for (const [k, v] of Object.entries(parsed))
                    if (typeof v === "string")
                        out[k] = v;
                return out;
            }
        }
        catch {
        }
        return Object.create(null);
    }
}
function headerOf(e, path) {
    let mtimeMs = 0;
    try {
        mtimeMs = statSync(path).mtimeMs;
    }
    catch {
    }
    return {
        id: e.id,
        slug: e.slug,
        scope: e.scope,
        ...(e.frontmatter.name !== undefined ? { name: e.frontmatter.name } : {}),
        ...(e.frontmatter.description !== undefined ? { description: e.frontmatter.description } : {}),
        ...(e.frontmatter.type !== undefined ? { type: e.frontmatter.type } : {}),
        mtimeMs,
        rev: e.rev,
        sizeBytes: Buffer.byteLength(serializeEntryFile(e), "utf8"),
        ...(committedOriginOf(e.frontmatter) !== undefined ? { exposure: "external" } : {}),
        ...(e.frontmatter.distilled !== undefined
            ? {
                distilled: {
                    carrierRev: e.frontmatter.distilled.carrierRev,
                    supersedes: e.frontmatter.distilled.inputs.filter((i) => i.superseded === true).map((i) => ({ id: i.id, rev: i.rev })),
                },
            }
            : {}),
    };
}
function readSafe(path) {
    try {
        return readFileSync(path, "utf8");
    }
    catch {
        return undefined;
    }
}
export { scopeDirName };