#!/usr/bin/env node

import { createHash, randomBytes } from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';

export const EVIDENCE_PROVENANCE_SCHEMA_VERSION = 2;
export const EVIDENCE_PROVENANCE_CANONICALIZATION =
  'gitnexus-evidence-provenance-v2 NUL-framed UTF-8 records';

const ABSENT = 'absent';
const OBJECT_KINDS = new Set(['regular', 'symlink', 'gitlink', 'directory', ABSENT]);
const STATES = new Set([
  'clean',
  'staged',
  'unstaged',
  'untracked',
  'deleted',
  'renamed',
  'mixed',
  ABSENT,
]);
const RECORD_FIELDS = [
  'path',
  'state',
  'head_kind',
  'index_kind',
  'worktree_kind',
  'untracked_kind',
  'rename_from',
  'rename_to',
  'head_digest',
  'index_digest',
  'worktree_digest',
  'untracked_digest',
];
const UTF8_FATAL = new TextDecoder('utf-8', { fatal: true });
const MAX_GIT_OUTPUT = 1024 * 1024 * 1024;
const MAX_PLAN_BYTES = 16 * 1024 * 1024;
const GENERATED_PLAN_READ_PATTERN = /^docs\/plans\/[^/]*gitnexus-plan[^/]*\.md$/;
const GENERATED_PLAN_WRITE_PATTERN =
  /^docs\/plans\/(\d{4}-\d{2}-\d{2})-gitnexus-plan-[a-z0-9]+(?:-[a-z0-9]+){2,4}\.md$/;
export const DIRECTORY_LIMITS = Object.freeze({
  maxEntries: 10_000,
  maxDepth: 256,
  maxBytes: 256 * 1024 * 1024,
});

function sha256(bytes) {
  return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
}

function statIdentity(stat) {
  return [stat.dev, stat.ino, stat.mode, stat.nlink, stat.size, stat.mtimeNs, stat.ctimeNs]
    .map(String)
    .join(':');
}

function assertStableIdentity(before, after, label) {
  if (statIdentity(before) !== statIdentity(after)) {
    throw new Error(`${label} changed while evidence was being read`);
  }
}

function hashFile(file, mutationGuards, directoryTraversal) {
  const hash = createHash('sha256');
  const noFollow = fs.constants.O_NOFOLLOW ?? 0;
  const fd = fs.openSync(file, fs.constants.O_RDONLY | noFollow);
  const buffer = Buffer.allocUnsafe(1024 * 1024);
  try {
    const before = fs.fstatSync(fd, { bigint: true });
    if (!before.isFile()) throw new Error(`Expected a regular file at ${file}`);
    if (directoryTraversal) {
      directoryTraversal.bytes += before.size;
      if (directoryTraversal.bytes > BigInt(DIRECTORY_LIMITS.maxBytes)) {
        throw new Error(`Directory inventory exceeds ${DIRECTORY_LIMITS.maxBytes} content bytes`);
      }
    }
    for (;;) {
      const count = fs.readSync(fd, buffer, 0, buffer.length, null);
      if (count === 0) break;
      hash.update(buffer.subarray(0, count));
    }
    const after = fs.fstatSync(fd, { bigint: true });
    assertStableIdentity(before, after, file);
    mutationGuards.push({ type: 'stat', absolute: file, identity: statIdentity(after) });
  } finally {
    fs.closeSync(fd);
  }
  return `sha256:${hash.digest('hex')}`;
}

function git(repo, args, { allowFailure = false, input } = {}) {
  const result = spawnSync('git', ['-C', repo, ...args], {
    encoding: null,
    env: { ...process.env, LANG: 'C', LC_ALL: 'C', GIT_OPTIONAL_LOCKS: '0' },
    input,
    maxBuffer: MAX_GIT_OUTPUT,
    windowsHide: true,
  });
  if (result.error) throw result.error;
  if (result.status !== 0 && !allowFailure) {
    const stderr = Buffer.from(result.stderr ?? [])
      .toString('utf8')
      .trim();
    throw new Error(`git ${args.join(' ')} failed (${result.status}): ${stderr}`);
  }
  return {
    status: result.status,
    stdout: Buffer.from(result.stdout ?? []),
    stderr: Buffer.from(result.stderr ?? []),
  };
}

function decodeUtf8(bytes, label) {
  let decoded;
  try {
    decoded = UTF8_FATAL.decode(bytes);
  } catch {
    throw new Error(`${label} is not valid UTF-8`);
  }
  return decoded;
}

export function normalizeRepoPath(input, label = 'path') {
  if (typeof input !== 'string') throw new Error(`${label} must be a string`);
  if (input.length === 0) throw new Error(`${label} must not be empty`);
  if (input.includes('\0')) throw new Error(`${label} must not contain NUL`);
  if (input.includes('\\')) throw new Error(`${label} must use POSIX '/' separators`);
  if (input !== input.normalize('NFC')) throw new Error(`${label} must already be Unicode NFC`);
  if (Buffer.from(input, 'utf8').toString('utf8') !== input) {
    throw new Error(`${label} contains an invalid Unicode scalar value`);
  }
  if (input.startsWith('/') || /^[A-Za-z]:\//.test(input)) {
    throw new Error(`${label} must be repo-relative`);
  }
  const components = input.split('/');
  if (components.some((component) => component === '' || component === '.' || component === '..')) {
    throw new Error(`${label} must be a normalized repo-relative path without dot segments`);
  }
  return input;
}

function requireString(value, label) {
  if (typeof value !== 'string') throw new Error(`${label} must be a string`);
  return value;
}

function requireBoolean(value, label) {
  if (typeof value !== 'boolean') throw new Error(`${label} must be a literal boolean`);
  return value;
}

function normalizeSha256Digest(value, label = 'plan digest') {
  if (typeof value !== 'string' || !/^sha256:[0-9a-f]{64}$/.test(value)) {
    throw new Error(`${label} must be sha256:<64 lowercase hexadecimal characters>`);
  }
  return value;
}

function normalizeGeneratedPlanWritePath(input) {
  const normalized = normalizeRepoPath(input, 'generated plan path');
  const match = GENERATED_PLAN_WRITE_PATTERN.exec(normalized);
  if (!match) {
    throw new Error(
      'Generated-plan writes are restricted to docs/plans/YYYY-MM-DD-gitnexus-plan-<3-5-word-slug>.md',
    );
  }
  const parsedDate = new Date(`${match[1]}T00:00:00Z`);
  if (Number.isNaN(parsedDate.valueOf()) || parsedDate.toISOString().slice(0, 10) !== match[1]) {
    throw new Error(`Generated-plan path has an invalid calendar date: ${match[1]}`);
  }
  return normalized;
}

function normalizeGeneratedPlanReadPath(input) {
  const normalized = normalizeRepoPath(input, 'existing plan path');
  if (!GENERATED_PLAN_READ_PATTERN.test(normalized)) {
    throw new Error('Existing-plan reads are restricted to docs/plans/*gitnexus-plan*.md');
  }
  return normalized;
}

function decodeRepoPath(bytes, label) {
  return normalizeRepoPath(decodeUtf8(bytes, label), label);
}

function splitNul(bytes) {
  const parts = [];
  let start = 0;
  for (let index = 0; index < bytes.length; index += 1) {
    if (bytes[index] !== 0) continue;
    parts.push(bytes.subarray(start, index));
    start = index + 1;
  }
  if (start !== bytes.length) throw new Error('Git emitted a non-NUL-terminated record stream');
  return parts;
}

function splitFixedHeader(record, fieldCount, label) {
  const fields = [];
  let cursor = 0;
  for (let index = 0; index < fieldCount; index += 1) {
    const separator = record.indexOf(' ', cursor);
    if (separator < 0) throw new Error(`Malformed ${label} record`);
    fields.push(record.slice(cursor, separator));
    cursor = separator + 1;
  }
  return { fields, path: record.slice(cursor) };
}

function classifyXY(xy) {
  if (!/^[.MTADRCU?!]{2}$/.test(xy)) throw new Error(`Unsupported Git XY status: ${xy}`);
  const [indexState, worktreeState] = xy;
  if (indexState === 'U' || worktreeState === 'U') {
    throw new Error('Unmerged paths cannot be canonicalized; resolve the index first');
  }
  if (indexState !== '.' && worktreeState !== '.') return 'mixed';
  if (indexState === 'D' || worktreeState === 'D') return 'deleted';
  if (indexState !== '.') return 'staged';
  if (worktreeState !== '.') return 'unstaged';
  throw new Error(`Porcelain reported a non-dirty ordinary record (${xy})`);
}

function addDirtyRecord(records, record) {
  const incomingFacts = new Set(record.fact_states ?? [record.state]);
  const current = records.get(record.path);
  if (!current) {
    records.set(record.path, {
      ...record,
      fact_states: incomingFacts,
      has_untracked: record.has_untracked ?? record.state === 'untracked',
      directory_hint: record.directory_hint ?? false,
    });
    return;
  }
  const mergeEndpoint = (field) => {
    const left = current[field];
    const right = record[field];
    if (left && right && left !== right) {
      throw new Error(`Conflicting ${field} facts for ${JSON.stringify(record.path)}`);
    }
    return left ?? right ?? null;
  };
  const facts = new Set([...current.fact_states, ...incomingFacts]);
  current.fact_states = facts;
  current.state = facts.has('mixed') || facts.size > 1 ? 'mixed' : [...facts][0];
  current.rename_from = mergeEndpoint('rename_from');
  current.rename_to = mergeEndpoint('rename_to');
  current.has_untracked =
    current.has_untracked || record.has_untracked || record.state === 'untracked';
  current.directory_hint = current.directory_hint || record.directory_hint;
}

function readDirtySnapshot(repo) {
  const output = git(repo, [
    '-c',
    'diff.renameLimit=0',
    '-c',
    'status.renameLimit=0',
    'status',
    '--porcelain=v2',
    '-z',
    '--untracked-files=all',
    '--find-renames=50%',
    '--ignore-submodules=none',
  ]).stdout;
  const tokens = splitNul(output);
  const records = new Map();

  for (let index = 0; index < tokens.length; index += 1) {
    const token = tokens[index];
    if (token.length === 0) continue;
    const kind = String.fromCharCode(token[0]);
    const text = decodeUtf8(token, 'git status record');

    if (kind === '1') {
      const parsed = splitFixedHeader(text, 8, 'ordinary status');
      const xy = parsed.fields[1];
      const repoPath = normalizeRepoPath(parsed.path, 'git status path');
      addDirtyRecord(records, {
        path: repoPath,
        state: classifyXY(xy),
        rename_from: null,
        rename_to: null,
        has_untracked: false,
      });
      continue;
    }

    if (kind === '2') {
      const parsed = splitFixedHeader(text, 9, 'rename status');
      const newPath = normalizeRepoPath(parsed.path, 'rename destination');
      index += 1;
      if (index >= tokens.length) throw new Error('Rename status is missing its source endpoint');
      const oldPath = decodeRepoPath(tokens[index], 'rename source');
      addDirtyRecord(records, {
        path: oldPath,
        state: 'renamed',
        rename_from: null,
        rename_to: newPath,
        has_untracked: false,
      });
      addDirtyRecord(records, {
        path: newPath,
        state: parsed.fields[1][1] === '.' ? 'renamed' : 'mixed',
        rename_from: oldPath,
        rename_to: null,
        has_untracked: false,
      });
      continue;
    }

    if (kind === '?') {
      const rawPath = text.slice(2);
      const directoryHint = rawPath.endsWith('/');
      const repoPath = normalizeRepoPath(
        directoryHint ? rawPath.slice(0, -1) : rawPath,
        'untracked path',
      );
      addDirtyRecord(records, {
        path: repoPath,
        state: 'untracked',
        rename_from: null,
        rename_to: null,
        has_untracked: true,
        directory_hint: directoryHint,
      });
      continue;
    }

    if (kind === 'u') {
      throw new Error('Unmerged paths cannot be canonicalized; resolve the index first');
    }
    if (kind !== '!') throw new Error(`Unsupported porcelain-v2 record kind: ${kind}`);
  }
  return { output, records };
}

function kindFromMode(mode) {
  if (mode === '040000') return 'directory';
  if (mode === '100644' || mode === '100755') return 'regular';
  if (mode === '120000') return 'symlink';
  if (mode === '160000') return 'gitlink';
  throw new Error(`Unsupported Git object mode: ${mode}`);
}

function readBatchObjects(repo, descriptors) {
  const requested = new Map();
  for (const descriptor of descriptors) {
    if (descriptor.kind === 'gitlink') continue;
    const expectedType = descriptor.kind === 'directory' ? 'tree' : 'blob';
    const prior = requested.get(descriptor.oid);
    if (prior && prior !== expectedType) {
      throw new Error(
        `Git object ${descriptor.oid} is requested as both ${prior} and ${expectedType}`,
      );
    }
    requested.set(descriptor.oid, expectedType);
  }
  if (requested.size === 0) return new Map();
  const input = Buffer.from(`${[...requested.keys()].join('\n')}\n`, 'ascii');
  const output = git(repo, ['cat-file', '--batch'], { input }).stdout;
  const digests = new Map();
  let cursor = 0;
  for (const [requestedOid, expectedType] of requested) {
    const newline = output.indexOf(10, cursor);
    if (newline < 0) throw new Error(`Missing cat-file header for ${requestedOid}`);
    const header = decodeUtf8(output.subarray(cursor, newline), 'cat-file header').split(' ');
    if (header.length !== 3 || header[0] !== requestedOid) {
      throw new Error(`Malformed cat-file header for ${requestedOid}`);
    }
    const [, actualType, sizeText] = header;
    const size = Number(sizeText);
    if (actualType !== expectedType || !Number.isSafeInteger(size) || size < 0) {
      throw new Error(`Unexpected cat-file object metadata for ${requestedOid}`);
    }
    const start = newline + 1;
    const end = start + size;
    if (end >= output.length || output[end] !== 10) {
      throw new Error(`Truncated cat-file object ${requestedOid}`);
    }
    digests.set(requestedOid, sha256(output.subarray(start, end)));
    cursor = end + 1;
  }
  if (cursor !== output.length) throw new Error('cat-file emitted unexpected trailing bytes');
  return digests;
}

function loadGitLayers(repo, neededPaths, headOid, indexOutput) {
  const headDescriptors = new Map();
  const headOutput = git(repo, ['ls-tree', '-r', '-t', '-z', '--full-tree', headOid]).stdout;
  for (const record of splitNul(headOutput)) {
    if (record.length === 0) continue;
    const tab = record.indexOf(9);
    if (tab < 0) throw new Error('Malformed HEAD tree entry');
    const repoPath = decodeRepoPath(record.subarray(tab + 1), 'HEAD path');
    if (!neededPaths.has(repoPath)) continue;
    const header = decodeUtf8(record.subarray(0, tab), 'HEAD entry').split(' ');
    if (header.length !== 3) throw new Error(`Malformed HEAD entry for ${repoPath}`);
    const [mode, type, oid] = header;
    const objectKind = kindFromMode(mode);
    const expectedType =
      objectKind === 'directory' ? 'tree' : objectKind === 'gitlink' ? 'commit' : 'blob';
    if (type !== expectedType) throw new Error(`Unexpected HEAD object type for ${repoPath}`);
    headDescriptors.set(repoPath, { kind: objectKind, oid });
  }

  const indexDescriptors = new Map();
  for (const record of splitNul(indexOutput)) {
    if (record.length === 0) continue;
    const tab = record.indexOf(9);
    if (tab < 0) throw new Error('Malformed index entry');
    const repoPath = decodeRepoPath(record.subarray(tab + 1), 'index path');
    if (!neededPaths.has(repoPath)) continue;
    const header = decodeUtf8(record.subarray(0, tab), 'index entry').split(' ');
    if (header.length !== 3) throw new Error(`Malformed index entry for ${repoPath}`);
    const [mode, oid, stage] = header;
    if (stage !== '0' || indexDescriptors.has(repoPath)) {
      throw new Error(`Unmerged index stages cannot be canonicalized for ${repoPath}`);
    }
    const objectKind = kindFromMode(mode);
    if (objectKind === 'directory') throw new Error('The Git index cannot contain a tree entry');
    indexDescriptors.set(repoPath, { kind: objectKind, oid });
  }

  const allDescriptors = [...headDescriptors.values(), ...indexDescriptors.values()];
  const objectDigests = readBatchObjects(repo, allDescriptors);
  const materialize = (descriptor) => {
    if (!descriptor) return { kind: ABSENT, digest: ABSENT };
    return {
      kind: descriptor.kind,
      digest:
        descriptor.kind === 'gitlink'
          ? sha256(Buffer.from(descriptor.oid, 'ascii'))
          : objectDigests.get(descriptor.oid),
    };
  };
  return {
    head(repoPath) {
      return materialize(headDescriptors.get(repoPath));
    },
    index(repoPath) {
      return materialize(indexDescriptors.get(repoPath));
    },
  };
}

function compareUtf8(left, right) {
  return Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8'));
}

function serializeFields(prefixFields, records, fields) {
  const chunks = [];
  const append = (value) => {
    if (typeof value !== 'string' || value.includes('\0')) {
      throw new Error('Canonical provenance fields must be NUL-free strings');
    }
    chunks.push(Buffer.from(value, 'utf8'), Buffer.from([0]));
  };
  for (const field of prefixFields) append(field);
  chunks.push(Buffer.from([0]));
  for (const record of records) {
    append('record');
    for (const field of fields) {
      append(field);
      append(record[field]);
    }
    chunks.push(Buffer.from([0]));
  }
  return Buffer.concat(chunks);
}

function resolveOwnGitTopLevel(absolute) {
  const result = git(absolute, ['rev-parse', '--show-toplevel'], { allowFailure: true });
  if (result.status !== 0) return null;
  let topLevel;
  try {
    topLevel = fs.realpathSync.native(decodeUtf8(result.stdout, 'nested repository root').trim());
  } catch {
    return null;
  }
  return topLevel === fs.realpathSync.native(absolute) ? topLevel : null;
}

function readOwnGitlinkHead(absolute) {
  const topLevel = resolveOwnGitTopLevel(absolute);
  if (!topLevel) {
    throw new Error(`Gitlink worktree is not its own repository: ${absolute}`);
  }
  const result = git(absolute, ['rev-parse', '--verify', 'HEAD'], { allowFailure: true });
  if (result.status !== 0)
    throw new Error(`Cannot resolve checked-out gitlink HEAD at ${absolute}`);
  const oid = decodeUtf8(result.stdout, 'gitlink HEAD').trim();
  if (!/^[0-9a-f]{40,64}$/.test(oid)) throw new Error(`Invalid gitlink object ID at ${absolute}`);
  const status = git(absolute, [
    'status',
    '--porcelain=v2',
    '-z',
    '--untracked-files=all',
    '--ignored=matching',
    '--ignore-submodules=none',
  ]).stdout;
  if (status.length !== 0) {
    throw new Error(
      `Checked-out gitlink is dirty at ${absolute}; commit or clean staged, unstaged, untracked, and ignored changes before snapshotting`,
    );
  }
  return { oid, topLevel };
}

function readStableSymlink(absolute, mutationGuards) {
  const before = fs.lstatSync(absolute, { bigint: true });
  const target = fs.readlinkSync(absolute, { encoding: 'buffer' });
  const after = fs.lstatSync(absolute, { bigint: true });
  assertStableIdentity(before, after, absolute);
  mutationGuards.push({
    type: 'symlink',
    absolute,
    identity: statIdentity(after),
    target: Buffer.from(target),
  });
  return { kind: 'symlink', digest: sha256(target) };
}

function digestDirectory(root, mutationGuards, testHooks) {
  const traversal = { entries: 0, bytes: 0n };
  const walk = (directory, depth) => {
    if (depth > DIRECTORY_LIMITS.maxDepth) {
      throw new Error(`Directory inventory exceeds depth ${DIRECTORY_LIMITS.maxDepth}`);
    }
    const before = fs.lstatSync(directory, { bigint: true });
    if (!before.isDirectory()) throw new Error(`Expected a directory at ${directory}`);
    const children = fs
      .readdirSync(directory, { withFileTypes: true, encoding: 'buffer' })
      .map((child) => ({
        child,
        name: decodeUtf8(Buffer.from(child.name), 'directory entry name'),
      }))
      .sort((left, right) => compareUtf8(left.name, right.name));
    const ownRepository = children.some(({ name }) => name === '.git')
      ? resolveOwnGitTopLevel(directory)
      : null;
    const entries = [];
    for (const { name: childName } of children) {
      if (ownRepository && childName === '.git') continue;
      normalizeRepoPath(childName, 'directory entry name');
      const absolute = path.join(directory, childName);
      const childStat = fs.lstatSync(absolute, { bigint: true });
      traversal.entries += 1;
      if (traversal.entries > DIRECTORY_LIMITS.maxEntries) {
        throw new Error(`Directory inventory exceeds ${DIRECTORY_LIMITS.maxEntries} entries`);
      }
      testHooks?.onDirectoryEntry?.({ absolute, count: traversal.entries, depth: depth + 1 });

      let layer;
      let descendants = [];
      if (childStat.isFile()) {
        layer = {
          kind: 'regular',
          digest: hashFile(absolute, mutationGuards, traversal),
        };
      } else if (childStat.isSymbolicLink()) {
        layer = readStableSymlink(absolute, mutationGuards);
      } else if (childStat.isDirectory()) {
        const nested = walk(absolute, depth + 1);
        layer = { kind: 'directory', digest: nested.digest };
        descendants = nested.entries.map((entry) => ({
          ...entry,
          path: `${childName}/${entry.path}`,
        }));
      } else {
        throw new Error(`Unsupported filesystem object at ${absolute}`);
      }
      entries.push({ path: childName, kind: layer.kind, digest: layer.digest }, ...descendants);
    }
    const after = fs.lstatSync(directory, { bigint: true });
    assertStableIdentity(before, after, directory);
    mutationGuards.push({ type: 'stat', absolute: directory, identity: statIdentity(after) });
    entries.sort((left, right) => compareUtf8(left.path, right.path));
    const bytes = serializeFields(['gitnexus-evidence-directory', 'schema_version', '1'], entries, [
      'path',
      'kind',
      'digest',
    ]);
    return { digest: sha256(bytes), entries };
  };
  return walk(root, 0).digest;
}

function filesystemObject(absolute, expectedKind, mutationGuards, testHooks) {
  let stat;
  try {
    stat = fs.lstatSync(absolute);
  } catch (error) {
    if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') {
      return { kind: ABSENT, digest: ABSENT };
    }
    throw error;
  }

  if (expectedKind === 'gitlink') {
    if (!stat.isDirectory()) throw new Error(`Expected gitlink directory at ${absolute}`);
    const { oid, topLevel } = readOwnGitlinkHead(absolute);
    mutationGuards.push({ type: 'gitlink', absolute, oid, topLevel });
    return { kind: 'gitlink', digest: sha256(Buffer.from(oid, 'ascii')) };
  }
  if (stat.isFile()) return { kind: 'regular', digest: hashFile(absolute, mutationGuards) };
  if (stat.isSymbolicLink()) return readStableSymlink(absolute, mutationGuards);
  if (stat.isDirectory()) {
    return { kind: 'directory', digest: digestDirectory(absolute, mutationGuards, testHooks) };
  }
  throw new Error(`Unsupported filesystem object at ${absolute}`);
}

// Every dirty path re-walks its own parents, and dirty paths overwhelmingly
// share them — the repository root is re-stat'ed once per path. `guarded` is
// per-snapshot and remembers which absolute directories already carry a guard,
// so each distinct directory is stat'ed and guarded exactly once.
//
// Keeping the first-seen identity is the conservative choice: verifyGuards
// re-checks every guard against the filesystem at the end, so a directory that
// changes after it was guarded still fails there. Skipping a re-stat cannot hide
// a change; it only avoids recording the same directory twice.
function guardPathParents(repo, repoPath, mutationGuards, guarded) {
  const components = repoPath.split('/');
  let current = repo;
  if (!guarded.has(repo)) {
    guarded.add(repo);
    mutationGuards.push({
      type: 'directory',
      absolute: repo,
      identity: stableDirectoryIdentity(fs.lstatSync(repo, { bigint: true })),
    });
  }
  for (const component of components.slice(0, -1)) {
    current = path.join(current, component);
    // Already proved a real directory and already guarded on an earlier path.
    if (guarded.has(current)) continue;
    let stat;
    try {
      stat = fs.lstatSync(current, { bigint: true });
    } catch (error) {
      if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') return;
      throw error;
    }
    if (stat.isSymbolicLink()) {
      throw new Error(`Refusing to traverse symlink parent for ${repoPath}`);
    }
    if (!stat.isDirectory()) return;
    guarded.add(current);
    mutationGuards.push({
      type: 'directory',
      absolute: current,
      identity: stableDirectoryIdentity(stat),
    });
  }
}

// A bound, not a bug: the absence cache deduplicates correctly and leaks nothing,
// but citedPaths is caller-supplied and unbounded, so a pathological snapshot
// could hold more descriptors than the process is allowed (macOS
// kern.maxfilesperproc is 24576). The peak precedes a `git` spawn, so exhaustion
// would surface as a git failure misreported as evidence instability.
//
// Refuse rather than evict: closing a cached descriptor would silently break the
// pinned chain of an absence guard that was already recorded against it, which is
// exactly the inode-recycling hole the pins exist to close.
const ABSENCE_ANCHOR_LIMITS = Object.freeze({ maxPinnedDirectories: 4096 });

// Every no-follow read and every exclusive create in this file uses one of these
// two, so a change lands in one place rather than in seven.
const VERIFIED_READ_FLAGS =
  fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | (fs.constants.O_CLOEXEC ?? 0);
const VERIFIED_CREATE_FLAGS =
  fs.constants.O_RDWR |
  fs.constants.O_CREAT |
  fs.constants.O_EXCL |
  fs.constants.O_NOFOLLOW |
  (fs.constants.O_CLOEXEC ?? 0);

function requireAbsenceAnchorCapacity(cache) {
  if (cache.size >= ABSENCE_ANCHOR_LIMITS.maxPinnedDirectories) {
    throw new Error(
      `Absence anchoring exceeds ${ABSENCE_ANCHOR_LIMITS.maxPinnedDirectories} pinned directories`,
    );
  }
}

const ANCHORED_DIRECTORY_FLAGS =
  fs.constants.O_RDONLY |
  fs.constants.O_DIRECTORY |
  fs.constants.O_NOFOLLOW |
  (fs.constants.O_CLOEXEC ?? 0);

// Every absence receipt is verified long after its walk returns, so the chain
// that produced it has to stay pinned until the snapshot ends — an unpinned inode
// number can be recycled by a replacement directory that then reproduces the
// recorded identity exactly. Absent cited paths overwhelmingly share prefixes, so
// the walked directories are cached per snapshot and keyed by repo-relative
// prefix: one open descriptor and one anchored walk per distinct directory rather
// than per path. snapshotEvidence owns every descriptor in this cache and closes
// each exactly once; guards only borrow them for verification.
function anchoredAbsenceRoot(repo, cache) {
  const cached = cache.get('');
  if (cached) return cached;
  requireAbsenceAnchorCapacity(cache);
  const fd = openVerifiedDirectory(repo, ANCHORED_DIRECTORY_FLAGS);
  const handle = {
    fd,
    expectedPath: repo,
    chain: [
      { expectedPath: repo, identity: stableDirectoryIdentity(fs.fstatSync(fd, { bigint: true })) },
    ],
    descriptors: [fd],
  };
  cache.set('', handle);
  return handle;
}

function recordAnchoredAbsence(repo, repoPath, mutationGuards, cache) {
  requireDescriptorAnchoring();
  const components = repoPath.split('/');
  let handle = anchoredAbsenceRoot(repo, cache);
  let prefix = '';
  for (let index = 0; index < components.length; index += 1) {
    const component = components[index];
    const isFinal = index === components.length - 1;
    prefix = prefix === '' ? component : `${prefix}/${component}`;
    // The final component is always re-checked against the filesystem: it is the
    // one whose absence is being recorded, and a cached answer would be a stale
    // one. Only the prefix directories are reused.
    const cached = isFinal ? undefined : cache.get(prefix);
    if (cached) {
      handle = cached;
      continue;
    }
    const child = anchoredChild(handle, component);
    let childStat;
    try {
      childStat = lstatChild(child);
    } catch (error) {
      if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR') throw error;
      const parentStat = fs.fstatSync(handle.fd, { bigint: true });
      if (!parentStat.isDirectory()) {
        throw new Error(`Absence parent is no longer a directory for ${repoPath}`);
      }
      mutationGuards.push({
        type: 'absence',
        // The handle is the holder the guard verifies against, and `ref` is the
        // child path already built through the anchoredChild chokepoint — the
        // guard must never re-derive that name itself.
        handle,
        ref: child,
        fd: handle.fd,
        repoPath,
        parentMutationIdentity: statIdentity(parentStat),
      });
      return;
    }
    if (isFinal) {
      throw new Error(`${repoPath} appeared while its absence was being anchored`);
    }
    if (childStat.isSymbolicLink() || !childStat.isDirectory()) {
      throw new Error(`Refusing a non-directory parent while anchoring absence for ${repoPath}`);
    }
    requireAbsenceAnchorCapacity(cache);
    const childFd = openVerifiedDirectory(child.path, ANCHORED_DIRECTORY_FLAGS);
    const expectedPath = path.join(handle.expectedPath, component);
    let next;
    try {
      if (!anchoringBackend().descriptorMatchesChild(childFd, expectedPath, childStat)) {
        throw new Error(
          `Absence parent descriptor does not match its verified inode for ${repoPath}`,
        );
      }
      next = {
        fd: childFd,
        expectedPath,
        chain: [...handle.chain, { expectedPath, identity: stableDirectoryIdentity(childStat) }],
        descriptors: [...handle.descriptors, childFd],
      };
    } catch (error) {
      fs.closeSync(childFd);
      throw error;
    }
    cache.set(prefix, next);
    handle = next;
  }
  throw new Error(`Could not anchor absence for ${repoPath}`);
}

function materializeRecord(repo, statusRecord, layers, mutationGuards, testHooks, walkState) {
  const head = layers.head(statusRecord.path);
  const index = layers.index(statusRecord.path);
  const expectedKind = index.kind === 'gitlink' || head.kind === 'gitlink' ? 'gitlink' : null;
  guardPathParents(repo, statusRecord.path, mutationGuards, walkState.guardedDirectories);
  const filesystem = filesystemObject(
    path.join(repo, ...statusRecord.path.split('/')),
    expectedKind,
    mutationGuards,
    testHooks,
  );
  if (filesystem.kind === ABSENT) {
    recordAnchoredAbsence(repo, statusRecord.path, mutationGuards, walkState.absenceCache);
  }
  if (statusRecord.directory_hint && filesystem.kind !== 'directory') {
    throw new Error(
      `Git reported an embedded directory but found ${filesystem.kind}: ${statusRecord.path}`,
    );
  }
  const isUntracked = statusRecord.has_untracked || (head.kind === ABSENT && index.kind === ABSENT);
  const worktree = isUntracked ? { kind: ABSENT, digest: ABSENT } : filesystem;
  const untracked = isUntracked ? filesystem : { kind: ABSENT, digest: ABSENT };

  return {
    path: statusRecord.path,
    object_kind: {
      head: head.kind,
      index: index.kind,
      worktree: worktree.kind,
      untracked: untracked.kind,
    },
    state: statusRecord.state,
    rename_from: statusRecord.rename_from,
    rename_to: statusRecord.rename_to,
    head_digest: head.digest,
    index_digest: index.digest,
    worktree_digest: worktree.digest,
    untracked_digest: untracked.digest,
  };
}

function canonicalRecord(manifestEntry) {
  const record = {
    path: manifestEntry.path,
    state: manifestEntry.state,
    head_kind: manifestEntry.object_kind.head,
    index_kind: manifestEntry.object_kind.index,
    worktree_kind: manifestEntry.object_kind.worktree,
    untracked_kind: manifestEntry.object_kind.untracked,
    rename_from: manifestEntry.rename_from ?? ABSENT,
    rename_to: manifestEntry.rename_to ?? ABSENT,
    head_digest: manifestEntry.head_digest,
    index_digest: manifestEntry.index_digest,
    worktree_digest: manifestEntry.worktree_digest,
    untracked_digest: manifestEntry.untracked_digest,
  };
  if (!STATES.has(record.state)) throw new Error(`Unsupported evidence state: ${record.state}`);
  for (const kindField of ['head_kind', 'index_kind', 'worktree_kind', 'untracked_kind']) {
    if (!OBJECT_KINDS.has(record[kindField])) {
      throw new Error(`Unsupported object kind: ${record[kindField]}`);
    }
  }
  return record;
}

export function serializeDirtyRecords(entries) {
  const records = entries
    .map(canonicalRecord)
    .sort((left, right) => compareUtf8(left.path, right.path));
  for (let index = 1; index < records.length; index += 1) {
    if (records[index - 1].path === records[index].path) {
      throw new Error(`Duplicate canonical dirty path: ${records[index].path}`);
    }
  }
  return serializeFields(
    ['gitnexus-evidence-provenance', 'schema_version', String(EVIDENCE_PROVENANCE_SCHEMA_VERSION)],
    records,
    RECORD_FIELDS,
  );
}

function assertRepository(repoInput) {
  // realpathSync.native, not realpathSync: the JS resolver preserves a Windows
  // 8.3 short component (C:\Users\RUNNER~1\...) while git always reports the long
  // form, so the two would never compare equal and every caller would be told the
  // worktree root is not the worktree root it just named.
  const repo = fs.realpathSync.native(requireString(repoInput, 'repo'));
  const topLevelResult = git(repo, ['rev-parse', '--show-toplevel']);
  const topLevel = fs.realpathSync.native(
    decodeUtf8(topLevelResult.stdout, 'repository root').trim(),
  );
  if (topLevel !== repo) throw new Error(`--repo must be the Git worktree root (${topLevel})`);
  return repo;
}

function resolveAdministrativePath(repo, gitPath) {
  const raw = decodeUtf8(
    git(repo, ['rev-parse', '--git-path', gitPath]).stdout,
    `Git administrative path ${gitPath}`,
  ).trim();
  return path.resolve(repo, raw);
}

function captureControlFile(absolute, label) {
  let before;
  try {
    before = fs.lstatSync(absolute, { bigint: true });
  } catch (error) {
    if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') {
      return { absolute, label, kind: ABSENT };
    }
    throw error;
  }
  if (before.isSymbolicLink() || !before.isFile()) {
    throw new Error(`${label} must be a regular no-follow file`);
  }
  const fd = fs.openSync(
    absolute,
    fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_CLOEXEC ?? 0),
  );
  try {
    const opened = fs.fstatSync(fd, { bigint: true });
    if (!opened.isFile() || statIdentity(opened) !== statIdentity(before)) {
      throw new Error(`${label} changed while its descriptor opened`);
    }
    const hash = createHash('sha256');
    const buffer = Buffer.allocUnsafe(1024 * 1024);
    for (;;) {
      const count = fs.readSync(fd, buffer, 0, buffer.length, null);
      if (count === 0) break;
      hash.update(buffer.subarray(0, count));
    }
    const after = fs.fstatSync(fd, { bigint: true });
    assertStableIdentity(opened, after, label);
    return {
      absolute,
      label,
      kind: 'regular',
      identity: statIdentity(after),
      digest: `sha256:${hash.digest('hex')}`,
    };
  } finally {
    fs.closeSync(fd);
  }
}

function verifyControlFile(guard) {
  const current = captureControlFile(guard.absolute, guard.label);
  if (
    current.kind !== guard.kind ||
    current.identity !== guard.identity ||
    current.digest !== guard.digest
  ) {
    throw new Error(`${guard.label} changed while evidence was materialized`);
  }
}

function captureHeadGuards(repo) {
  const symbolic = git(repo, ['symbolic-ref', '-q', 'HEAD'], { allowFailure: true });
  const paths = new Set(['HEAD', 'logs/HEAD', 'packed-refs']);
  if (symbolic.status === 0) {
    const ref = decodeUtf8(symbolic.stdout, 'symbolic HEAD ref').trim();
    if (!/^refs\/[A-Za-z0-9._\/-]+$/.test(ref) || ref.includes('..')) {
      throw new Error(`Invalid symbolic HEAD ref: ${ref}`);
    }
    paths.add(ref);
    paths.add(`logs/${ref}`);
  }
  return [...paths].map((gitPath) =>
    captureControlFile(resolveAdministrativePath(repo, gitPath), `Git ${gitPath}`),
  );
}

function stableDirectoryIdentity(stat) {
  return [stat.dev, stat.ino, stat.mode].map(String).join(':');
}

function stableFileIdentity(stat) {
  return [stat.dev, stat.ino, stat.mode, stat.size].map(String).join(':');
}

// The two backends below differ in one decisive way, and it is worth stating
// plainly because the security properties are not the same.
//
// Linux ANCHORS. A name is resolved through /proc/self/fd/<fd>/<child>, which
// starts the walk at the inode the descriptor holds, so a parent that is renamed
// away cannot be traversed at all: the descriptor keeps pointing at the original
// directory and the impostor planted at the same name is simply never reached.
//
// macOS VERIFIES. Node cannot resolve a name relative to a descriptor there —
// /dev/fd/<fd> is not a magic link (it stats as the directory but every attempt
// to traverse a child through it returns ENOENT), and fcntl F_GETPATH is a
// name-cache snapshot rather than a live anchor. So the Darwin backend resolves
// lexically, holds an open descriptor on every element of the chain, and proves
// before and after each operation that the path chain still names exactly the
// inodes it is holding. That DETECTS a swapped parent and aborts the write; it
// does not make the swap impossible the way the Linux path does. A swap landing
// inside the window between a check and the call it guards is caught by the
// following check, after the fact, rather than being unreachable.
//
// Every other platform gets neither and is refused outright.
function requireDescriptorAnchoring() {
  const directoryFlagsAvailable =
    fs.constants.O_DIRECTORY !== undefined && fs.constants.O_NOFOLLOW !== undefined;
  if (process.platform === 'linux') {
    if (!directoryFlagsAvailable || !fs.existsSync('/proc/self/fd')) {
      throw new Error(
        'Safe generated-plan writes require Linux /proc/self/fd and O_DIRECTORY/O_NOFOLLOW; refusing an unanchored write',
      );
    }
    return;
  }
  if (process.platform === 'darwin') {
    if (!directoryFlagsAvailable) {
      throw new Error(
        'Safe generated-plan writes require macOS O_DIRECTORY/O_NOFOLLOW; refusing an unverified write',
      );
    }
    return;
  }
  throw new Error(
    `Safe generated-plan writes require Linux /proc/self/fd or macOS O_DIRECTORY/O_NOFOLLOW; ${process.platform} offers neither, so refusing an unanchored write`,
  );
}

function descriptorPath(fd, childName) {
  const base = `/proc/self/fd/${fd}`;
  return childName === undefined ? base : path.join(base, childName);
}

// Directory opens are plain O_RDONLY|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC on both
// platforms, and deliberately nothing else.
//
// O_NOFOLLOW_ANY (macOS 11+) used to be ORed in here on the theory that XNU
// ignores unrecognized open flag bits, so it would be inert where unsupported.
// That was wrong: combined with O_DIRECTORY macOS rejects it outright with
// EINVAL, and every directory open on Darwin failed. It is gone and is not
// coming back behind a probe or a degrade-on-EINVAL path — the per-component
// O_NOFOLLOW walk is what delivers the guarantee. Rust's cap-std, the closest
// reference implementation of this problem, has not adopted O_NOFOLLOW_ANY
// either (their issue #179 is still open).
function openVerifiedDirectory(absolute, flags) {
  return fs.openSync(absolute, flags);
}

// File opens additionally get O_NONBLOCK, which directory opens do not need:
// it stops a FIFO swapped in at the target name from wedging the process on
// open. The identity comparison that follows rejects the FIFO anyway, but only
// if we ever get as far as running it.
function openVerifiedFile(absolute, flags, mode) {
  const nonBlocking = flags | (fs.constants.O_NONBLOCK ?? 0);
  return mode === undefined
    ? fs.openSync(absolute, nonBlocking)
    : fs.openSync(absolute, nonBlocking, mode);
}

// The publish primitive, identical on both platforms.
//
// link() is the portable no-replace publish: it fails with EEXIST if the
// destination name is taken — by a regular file, by a directory, or by a symlink,
// live or dangling — and it never follows that symlink to clobber its target.
// It also works where renameat2(RENAME_NOREPLACE) does not, notably v9fs, which
// is why the WSL2 9p case that used to fail every time now works.
//
// The published file is the same inode as the temporary, so every identity
// comparison the callers already make still holds, and validateCommittedPlan
// becomes strictly stronger: it compares the destination against the exact inode
// whose bytes were fsynced.
//
// On Linux both paths are /proc/self/fd/<fd>/<name>, so the publish is anchored
// to the held parent descriptors exactly like every other operation.
// link(2) BUGS: "On NFS filesystems, the return code may be wrong in case the NFS
// server performs the link creation and dies before it can say so. Use stat(2) to
// find out if the link got created." open(2) NOTES gives the remedy this
// implements: on a reported failure, stat the source and see whether its link
// count reached 2. A false positive would need someone to have hardlinked a
// 16-random-byte name inside a directory we hold open — and validateCommittedPlan
// still proves the destination is the exact temporary inode afterwards.
function linkCreatedDespiteError(sourcePath) {
  try {
    return fs.statSync(sourcePath, { bigint: true }).nlink === 2n;
  } catch {
    return false;
  }
}

function linkNoReplace(sourcePath, destinationPath) {
  try {
    fs.linkSync(sourcePath, destinationPath);
  } catch (error) {
    // Callers treat "destination taken" as a distinct outcome, not a failure.
    if (error?.code === 'EEXIST') return false;
    if (!linkCreatedDespiteError(sourcePath)) {
      // FAT, Coda, and some SMB/FUSE/virtiofs mounts have no hardlinks at all.
      // Git falls back to rename here, but git can afford to lose collision
      // detection because its objects are content-addressed; a plan destination
      // is a plain name, so a replacing rename would silently clobber whatever
      // is already there. Refuse loudly instead.
      if (error?.code === 'EPERM' || error?.code === 'ENOTSUP' || error?.code === 'EMLINK') {
        throw new Error(
          `Generated-plan publication requires hard links, which this filesystem refused (${error.code}); refusing to fall back to a replacing rename`,
        );
      }
      throw error;
    }
  }
  try {
    fs.unlinkSync(sourcePath);
  } catch {
    // The link succeeded, so the plan IS published. A temporary name left behind
    // is a stray file, not an unpublished plan: reporting it as a failure would
    // be a lie, and rolling back would unpublish a plan that is already live.
  }
  return true;
}

// A directory holder is anything that owns a verified chain: a plan-parent
// handle, a ref's parent directory, or an absence guard. Two arrays describe it,
// both root-first and the same length — `chain` records each element's expected
// path and dev/ino/mode, and `descriptors` holds an open descriptor on each.
//
// Holding those descriptors is load-bearing rather than decorative. dev/ino/mode
// is unique only among *live* inodes: an inode number freed by an rmdir is handed
// straight back to the next mkdir, so a replacement directory can reproduce a
// recorded identity exactly. An open descriptor pins the inode, so the number
// cannot be recycled for as long as the holder exists.
function verifyPinnedDescriptors(holder) {
  const { chain, descriptors } = holder;
  if (!Array.isArray(descriptors) || descriptors.length !== chain.length) {
    throw new Error('Generated-plan parent chain is missing the descriptors that pin it');
  }
  chain.forEach((item, index) => {
    const pinned = fs.fstatSync(descriptors[index], { bigint: true });
    if (!pinned.isDirectory() || stableDirectoryIdentity(pinned) !== item.identity) {
      throw new Error('Generated-plan parent descriptor changed during the write');
    }
  });
}

function verifyLexicalChain(holder) {
  for (const item of holder.chain) {
    let lexical;
    try {
      lexical = fs.lstatSync(item.expectedPath, { bigint: true });
    } catch (error) {
      if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR') throw error;
      // A parent renamed out from under us is a mismatch, not a missing file:
      // reporting the raw ENOENT would leak an unrelated-looking error out of a
      // check whose whole job is to say the chain no longer holds.
      throw new Error('Generated-plan lexical parent no longer matches its directory descriptor');
    }
    if (
      lexical.isSymbolicLink() ||
      !lexical.isDirectory() ||
      stableDirectoryIdentity(lexical) !== item.identity
    ) {
      throw new Error('Generated-plan lexical parent no longer matches its directory descriptor');
    }
  }
}

// The whole platform seam, in five methods. Everything else an operation does is
// identical on both platforms and lives in the shared functions below.
//
// Only two things actually differ: how a name becomes a path, and what guard
// wraps the operation that uses it.
//
// Linux ANCHORS. /proc/self/fd/<fd>/<name> starts the walk at the inode the
// descriptor holds, so a parent renamed away cannot be traversed at all and the
// guard is a no-op — there is nothing left to verify.
//
// macOS VERIFIES. It resolves lexically, so before and after every operation it
// proves that each element of the path chain still names the exact inode being
// held for it. That DETECTS a swapped parent and aborts; it does not make the
// swap impossible. A swap landing inside the window is caught by the trailing
// check, after the fact, rather than being unreachable. The check runs after a
// failure too, because a verdict observed through a chain that has since changed
// is not a verdict.
const LINUX_ANCHORING = {
  childPath(dirHandle, childName) {
    return descriptorPath(dirHandle.fd, childName);
  },
  verified(holders, run) {
    return run();
  },
  descriptorMatchesChild(fd, expectedPath) {
    return fs.realpathSync.native(descriptorPath(fd)) === expectedPath;
  },
  parentStillResolves(parentHandle) {
    return fs.realpathSync.native(descriptorPath(parentHandle.fd)) === parentHandle.expectedPath;
  },
  verifyAbsentChild(guard) {
    if (absentChildIsPresent(guard.ref)) {
      throw new Error(`${guard.repoPath} appeared before evidence materialization completed`);
    }
  },
};

const DARWIN_ANCHORING = {
  childPath(dirHandle, childName) {
    return path.join(dirHandle.expectedPath, childName);
  },
  verified(holders, run) {
    const list = Array.isArray(holders) ? holders : [holders];
    const proveChain = () => {
      for (const holder of list) {
        verifyPinnedDescriptors(holder);
        verifyLexicalChain(holder);
      }
    };
    proveChain();
    let value;
    try {
      value = run();
    } catch (error) {
      proveChain();
      throw error;
    }
    proveChain();
    return value;
  },
  descriptorMatchesChild(fd, _expectedPath, childStat) {
    // There is no live fd-to-path oracle on macOS (F_GETPATH is a name-cache
    // snapshot, not an anchor), so escape is decided the other way round: the
    // name was just resolved under a verified chain, and the descriptor opened
    // from it counts only if it is that same inode.
    const opened = fs.fstatSync(fd, { bigint: true });
    return (
      opened.isDirectory() && stableDirectoryIdentity(opened) === stableDirectoryIdentity(childStat)
    );
  },
  parentStillResolves(parentHandle) {
    // Both halves are needed: a directory renamed away keeps its inode, so the
    // descriptors alone still match and only the lexical half notices it moved.
    try {
      verifyPinnedDescriptors(parentHandle);
      verifyLexicalChain(parentHandle);
    } catch {
      return false;
    }
    return true;
  },
  verifyAbsentChild(guard) {
    let present;
    try {
      present = DARWIN_ANCHORING.verified(guard.handle, () => absentChildIsPresent(guard.ref));
    } catch (error) {
      // A chain that no longer holds makes the absence verdict meaningless, and
      // the caller reports that as the anchor changing rather than as a stray
      // parent-descriptor error. Linux cannot reach this: its guard is a no-op.
      throw new Error(
        `Absence anchor changed for ${guard.repoPath}: ${
          error instanceof Error ? error.message : String(error)
        }`,
      );
    }
    if (present) {
      throw new Error(`${guard.repoPath} appeared before evidence materialization completed`);
    }
  },
};

const ANCHORING_BACKENDS = new Map([
  ['linux', LINUX_ANCHORING],
  ['darwin', DARWIN_ANCHORING],
]);

function anchoringBackend() {
  const backend = ANCHORING_BACKENDS.get(process.platform);
  if (!backend) {
    // requireDescriptorAnchoring normally refuses first; this is the same answer
    // from the other side, so an unsupported platform can never fall through to
    // whichever backend happened to be the ternary's default.
    throw new Error(
      `No generated-plan anchoring backend for ${process.platform}; refusing an unanchored write`,
    );
  }
  return backend;
}

// Open, fstat, compare, close on mismatch. The descriptor never escapes this
// function unless it refers to the inode the caller already verified by name, so
// a lexical open that landed anywhere else cannot be used by accident. On Linux
// the comparison passes trivially — the /proc walk already resolved from the
// held parent — and costs one fstat to keep the guarantee structural rather than
// dependent on which backend is in play.
function adoptVerifiedFile(ref, expectedStat, flags) {
  const fd = openVerifiedFile(ref.path, flags);
  let opened;
  try {
    opened = fs.fstatSync(fd, { bigint: true });
  } catch (error) {
    fs.closeSync(fd);
    throw error;
  }
  if (stableFileIdentity(opened) !== stableFileIdentity(expectedStat)) {
    fs.closeSync(fd);
    return null;
  }
  return fd;
}

function absentChildIsPresent(ref) {
  try {
    fs.lstatSync(ref.path, { bigint: true });
  } catch (error) {
    if (error?.code === 'ENOENT') return false;
    throw error;
  }
  return true;
}

// The operations. Each is the same on both platforms; only the guard differs.
function lstatChild(ref) {
  return anchoringBackend().verified(ref.dir, () => fs.lstatSync(ref.path, { bigint: true }));
}

function openChildRead(ref, flags, expectedStat) {
  return anchoringBackend().verified(ref.dir, () => {
    const fd = adoptVerifiedFile(ref, expectedStat, flags);
    if (fd === null) {
      throw new Error(`${ref.name} was replaced between its verified stat and its no-follow open`);
    }
    return fd;
  });
}

function createChild(ref, flags, mode) {
  // O_CREAT|O_EXCL|O_NOFOLLOW is atomic at the leaf, so the only thing the guard
  // has to cover is which directory the leaf landed in.
  return anchoringBackend().verified(ref.dir, () => openVerifiedFile(ref.path, flags, mode));
}

function mkdirChild(ref, mode) {
  anchoringBackend().verified(ref.dir, () => fs.mkdirSync(ref.path, { mode }));
}

function publishNoReplace(sourceRef, destinationRef) {
  return anchoringBackend().verified([sourceRef.dir, destinationRef.dir], () =>
    linkNoReplace(sourceRef.path, destinationRef.path),
  );
}

// The single place a name becomes a path, and therefore the right place to
// enforce that a name is one ordinary component.
//
// A trailing separator is the sharp edge here, not a tidiness concern:
// open(path, O_NOFOLLOW) FOLLOWS a symlink when path ends in "/" — the trap
// behind CVE-2026-39822 / golang/go#79005, which let os.Root escape its own
// root. path.join preserves that trailing slash, so a component carrying one
// would turn every no-follow open in this file into a following one.
// normalizeRepoPath already rejects such components upstream; this is the
// chokepoint that makes it true for every caller, including the generated
// temporary and vault names that never pass through it.
function anchoredChild(dirHandle, childName) {
  if (
    typeof childName !== 'string' ||
    childName === '' ||
    childName === '.' ||
    childName === '..' ||
    childName.includes('/') ||
    childName.includes('\\') ||
    childName.includes('\0')
  ) {
    throw new Error(`Refusing to resolve ${JSON.stringify(childName)} as a single path component`);
  }
  return {
    dir: dirHandle,
    name: childName,
    path: anchoringBackend().childPath(dirHandle, childName),
  };
}

function lstatAnchoredOptional(ref) {
  try {
    return lstatChild(ref);
  } catch (error) {
    if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') return null;
    throw error;
  }
}

function openPlanParent(
  repo,
  parentComponents,
  { createMissing = true, purpose = 'Generated-plan' } = {},
) {
  requireDescriptorAnchoring();
  // Root-first and index-aligned with `chain`: verifyPinnedDescriptors relies on
  // that, and the descriptors are what pin each recorded inode against reuse.
  const descriptors = [];
  try {
    let currentFd = openVerifiedDirectory(repo, ANCHORED_DIRECTORY_FLAGS);
    descriptors.push(currentFd);
    const rootStat = fs.fstatSync(currentFd, { bigint: true });
    const chain = [{ expectedPath: repo, identity: stableDirectoryIdentity(rootStat) }];
    let currentHandle = { fd: currentFd, expectedPath: repo, chain, descriptors };
    const traversed = [];
    for (const component of parentComponents) {
      traversed.push(component);
      const child = anchoredChild(currentHandle, component);
      let childStat;
      let created = false;
      try {
        childStat = lstatChild(child);
      } catch (error) {
        if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR') throw error;
        if (!createMissing) {
          throw new Error(`${purpose} parent does not exist: ${traversed.join('/')}`);
        }
        mkdirChild(child, 0o755);
        childStat = lstatChild(child);
        created = true;
      }
      if (childStat.isSymbolicLink() || !childStat.isDirectory()) {
        throw new Error(`${purpose} parent is not a real directory: ${traversed.join('/')}`);
      }
      const parentFd = currentFd;
      const childFd = openVerifiedDirectory(child.path, ANCHORED_DIRECTORY_FLAGS);
      descriptors.push(childFd);
      currentFd = childFd;
      if (created) {
        fs.fsyncSync(childFd);
        fs.fsyncSync(parentFd);
      }
      const expected = path.join(repo, ...traversed);
      if (!anchoringBackend().descriptorMatchesChild(currentFd, expected, childStat)) {
        throw new Error(`${purpose} parent escaped the repository: ${traversed.join('/')}`);
      }
      const openedStat = fs.fstatSync(currentFd, { bigint: true });
      chain.push({ expectedPath: expected, identity: stableDirectoryIdentity(openedStat) });
      currentHandle = { fd: currentFd, expectedPath: expected, chain, descriptors };
    }
    return {
      descriptors,
      fd: currentFd,
      expectedPath: path.join(repo, ...parentComponents),
      chain,
    };
  } catch (error) {
    closeDescriptors(descriptors);
    throw error;
  }
}

function closeDescriptors(descriptors) {
  for (const fd of [...descriptors].reverse()) {
    try {
      fs.closeSync(fd);
    } catch {
      // Preserve the primary write result/error.
    }
  }
}

// A handle's identity IS its chain leaf's identity. Storing it twice meant two
// fstats a line apart and a re-stamp helper to keep them agreeing; deriving it
// removes both.
function handleIdentity(handle) {
  return handle.chain[handle.chain.length - 1].identity;
}

function resolveGitDirectory(repo) {
  const result = git(repo, ['rev-parse', '--absolute-git-dir']);
  return fs.realpathSync.native(decodeUtf8(result.stdout, 'Git administrative directory').trim());
}

function openBackupVault(repo, { createMissing = true } = {}) {
  const gitDirectory = resolveGitDirectory(repo);
  const handle = openPlanParent(gitDirectory, ['gitnexus-plan-backups'], {
    createMissing,
    purpose: 'Git-admin backup vault',
  });
  fs.fchmodSync(handle.fd, 0o700);
  fs.fsyncSync(handle.fd);
  // mode is part of every directory identity, so hardening the vault changes the
  // identity the chain recorded for it; without this the next verification would
  // reject the directory it just hardened.
  handle.chain[handle.chain.length - 1].identity = stableDirectoryIdentity(
    fs.fstatSync(handle.fd, { bigint: true }),
  );
  return { ...handle, gitDirectory };
}

function validatePlanParent(parentHandle) {
  const descriptorStat = fs.fstatSync(parentHandle.fd, { bigint: true });
  if (
    !descriptorStat.isDirectory() ||
    stableDirectoryIdentity(descriptorStat) !== handleIdentity(parentHandle)
  ) {
    throw new Error('Generated-plan parent descriptor changed during the write');
  }
  if (!anchoringBackend().parentStillResolves(parentHandle)) {
    throw new Error('Generated-plan parent moved or was replaced during the write');
  }
  // Both halves come from the shared helpers rather than being restated here: an
  // earlier hand-copy of the lexical loop lost verifyLexicalChain's ENOENT/ENOTDIR
  // translation, so a renamed parent could surface a raw errno from a function
  // with a dozen call sites.
  verifyPinnedDescriptors(parentHandle);
  verifyLexicalChain(parentHandle);
}

function inspectPlanDestination(
  finalRef,
  { replace, expectedIdentity, mustBeAbsent = false } = {},
) {
  let stat;
  try {
    stat = lstatChild(finalRef);
  } catch (error) {
    if (error?.code === 'ENOENT') {
      if (expectedIdentity) throw new Error('Generated plan disappeared during the write');
      return null;
    }
    throw error;
  }
  if (stat.isSymbolicLink() || !stat.isFile()) {
    throw new Error('Generated-plan destination must be a regular file, never a symlink');
  }
  if (mustBeAbsent) throw new Error('Generated plan appeared during the write');
  const identity = statIdentity(stat);
  if (!replace)
    throw new Error('Generated plan already exists; use --replace only for Deepen mode');
  if (expectedIdentity && identity !== expectedIdentity) {
    throw new Error('Generated plan changed during the write');
  }
  return stat;
}

function openExistingPlanDestination(finalRef, replace) {
  const stat = inspectPlanDestination(finalRef, { replace });
  if (stat === null) {
    if (replace) throw new Error('Deepen mode requires an existing generated plan to replace');
    return { fd: undefined, identity: null, stableIdentity: null };
  }
  const identity = statIdentity(stat);
  const fd = openChildRead(finalRef, VERIFIED_READ_FLAGS, stat);
  try {
    const opened = fs.fstatSync(fd, { bigint: true });
    if (!opened.isFile() || statIdentity(opened) !== identity) {
      throw new Error('Generated plan changed while its no-follow descriptor was opened');
    }
    return { fd, identity, stableIdentity: stableFileIdentity(opened) };
  } catch (error) {
    fs.closeSync(fd);
    throw error;
  }
}

function validateOpenPlanDestination(destination) {
  if (destination.fd === undefined) return;
  const opened = fs.fstatSync(destination.fd, { bigint: true });
  if (!opened.isFile() || statIdentity(opened) !== destination.identity) {
    throw new Error('Generated plan changed through its open descriptor');
  }
}

function writeAll(fd, contents) {
  let offset = 0;
  while (offset < contents.length) {
    const written = fs.writeSync(fd, contents, offset, contents.length - offset);
    if (written <= 0) throw new Error('Generated-plan write made no progress');
    offset += written;
  }
}

function hashOpenFile(fd, label) {
  const before = fs.fstatSync(fd, { bigint: true });
  if (!before.isFile()) throw new Error(`${label} is no longer a regular file`);
  const hash = createHash('sha256');
  const buffer = Buffer.allocUnsafe(1024 * 1024);
  let position = 0;
  for (;;) {
    const count = fs.readSync(fd, buffer, 0, buffer.length, position);
    if (count === 0) break;
    hash.update(buffer.subarray(0, count));
    position += count;
  }
  const after = fs.fstatSync(fd, { bigint: true });
  assertStableIdentity(before, after, label);
  return {
    digest: `sha256:${hash.digest('hex')}`,
    identity: stableFileIdentity(after),
    size: after.size,
  };
}

function validateCommittedPlan(finalRef, tempFd, expectedTemp, testHooks) {
  const before = lstatChild(finalRef);
  if (
    before.isSymbolicLink() ||
    !before.isFile() ||
    stableFileIdentity(before) !== expectedTemp.identity
  ) {
    throw new Error('Generated-plan destination failed its first post-write identity check');
  }
  const finalFd = openChildRead(finalRef, VERIFIED_READ_FLAGS, before);
  try {
    const opened = fs.fstatSync(finalFd, { bigint: true });
    if (!opened.isFile() || stableFileIdentity(opened) !== expectedTemp.identity) {
      throw new Error('Generated-plan destination changed while its no-follow descriptor opened');
    }
    testHooks?.afterFinalOpen?.({ fd: finalFd, finalPath: finalRef.path });
    const committedViaTemp = hashOpenFile(tempFd, 'generated-plan committed file');
    const committedViaPath = hashOpenFile(finalFd, 'generated-plan destination descriptor');
    const after = lstatChild(finalRef);
    const openedAfter = fs.fstatSync(finalFd, { bigint: true });
    if (
      after.isSymbolicLink() ||
      !after.isFile() ||
      stableFileIdentity(after) !== expectedTemp.identity ||
      stableFileIdentity(openedAfter) !== expectedTemp.identity ||
      committedViaTemp.identity !== expectedTemp.identity ||
      committedViaPath.identity !== expectedTemp.identity ||
      committedViaTemp.digest !== expectedTemp.digest ||
      committedViaPath.digest !== expectedTemp.digest
    ) {
      throw new Error('Generated-plan destination failed post-write verification');
    }
  } finally {
    fs.closeSync(finalFd);
  }
}

function copyOpenFile(sourceFd, destinationFd, label) {
  const before = fs.fstatSync(sourceFd, { bigint: true });
  if (!before.isFile()) throw new Error(`${label} source is no longer a regular file`);
  const buffer = Buffer.allocUnsafe(1024 * 1024);
  let position = 0;
  for (;;) {
    const count = fs.readSync(sourceFd, buffer, 0, buffer.length, position);
    if (count === 0) break;
    writeAll(destinationFd, buffer.subarray(0, count));
    position += count;
  }
  const after = fs.fstatSync(sourceFd, { bigint: true });
  assertStableIdentity(before, after, `${label} source`);
  return after;
}

function openVerifiedAnchoredFile(ref, label, knownStat) {
  const before = knownStat ?? lstatChild(ref);
  if (before.isSymbolicLink() || !before.isFile()) {
    throw new Error(`${label} is not a regular no-follow file`);
  }
  const fd = openChildRead(ref, VERIFIED_READ_FLAGS, before);
  try {
    const opened = fs.fstatSync(fd, { bigint: true });
    if (!opened.isFile() || stableFileIdentity(opened) !== stableFileIdentity(before)) {
      throw new Error(`${label} changed while its descriptor opened`);
    }
    const layer = hashOpenFile(fd, label);
    const after = lstatChild(ref);
    if (after.isSymbolicLink() || !after.isFile() || stableFileIdentity(after) !== layer.identity) {
      throw new Error(`${label} changed after verification`);
    }
    return { fd, layer };
  } catch (error) {
    fs.closeSync(fd);
    throw error;
  }
}

export function readPlanSafely({ repo: repoInput, generatedPlanPath, testHooks } = {}) {
  const repo = assertRepository(repoInput);
  const generatedPlan = normalizeGeneratedPlanReadPath(generatedPlanPath);
  const components = generatedPlan.split('/');
  const finalName = components.pop();
  const parentHandle = openPlanParent(repo, components, {
    createMissing: false,
    purpose: 'Loaded-plan',
  });
  let fd;
  try {
    validatePlanParent(parentHandle);
    const finalRef = anchoredChild(parentHandle, finalName);
    let before;
    try {
      before = lstatChild(finalRef);
    } catch (error) {
      if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') {
        throw new Error(`Loaded plan does not exist: ${generatedPlan}`);
      }
      throw error;
    }
    if (before.isSymbolicLink() || !before.isFile()) {
      throw new Error('Loaded plan must be a regular file, never a symlink');
    }
    fd = openChildRead(finalRef, VERIFIED_READ_FLAGS, before);
    const opened = fs.fstatSync(fd, { bigint: true });
    if (!opened.isFile() || statIdentity(opened) !== statIdentity(before)) {
      throw new Error('Loaded plan changed while its no-follow descriptor opened');
    }
    testHooks?.afterPlanOpen?.({ fd, finalPath: finalRef.path });
    const chunks = [];
    let total = 0;
    const buffer = Buffer.allocUnsafe(64 * 1024);
    for (;;) {
      const count = fs.readSync(fd, buffer, 0, buffer.length, null);
      if (count === 0) break;
      total += count;
      if (total > MAX_PLAN_BYTES) throw new Error(`Loaded plan exceeds ${MAX_PLAN_BYTES} bytes`);
      chunks.push(Buffer.from(buffer.subarray(0, count)));
    }
    const contents = Buffer.concat(chunks, total);
    decodeUtf8(contents, 'loaded plan');
    const after = fs.fstatSync(fd, { bigint: true });
    assertStableIdentity(opened, after, 'loaded plan');
    const pathAfter = lstatChild(finalRef);
    if (
      pathAfter.isSymbolicLink() ||
      !pathAfter.isFile() ||
      statIdentity(pathAfter) !== statIdentity(after)
    ) {
      throw new Error('Loaded plan changed before its receipt was produced');
    }
    validatePlanParent(parentHandle);
    return {
      generated_plan_path: generatedPlan,
      bytes_read: contents.length,
      plan_digest: sha256(contents),
      plan_bytes_base64: contents.toString('base64'),
    };
  } finally {
    if (fd !== undefined) fs.closeSync(fd);
    closeDescriptors(parentHandle.descriptors);
  }
}

function artifactGitPath(name) {
  return `gitnexus-plan-backups/${name}`;
}

function verifyVaultArtifactFromFreshRoot(repo, name, expectedLayer) {
  const freshVault = openBackupVault(repo, { createMissing: false });
  try {
    validatePlanParent(freshVault);
    const opened = openVerifiedAnchoredFile(
      anchoredChild(freshVault, name),
      `Git-admin artifact ${artifactGitPath(name)}`,
    );
    try {
      if (
        opened.layer.identity !== expectedLayer.identity ||
        opened.layer.digest !== expectedLayer.digest
      ) {
        throw new Error(
          `Git-admin artifact changed before fresh-root verification: ${artifactGitPath(name)}`,
        );
      }
    } finally {
      fs.closeSync(opened.fd);
    }
  } finally {
    closeDescriptors(freshVault.descriptors);
  }
}

function createVaultCopyFromFd(repo, vault, sourceFd, role) {
  validatePlanParent(vault);
  const name = `.gitnexus-plan-${role}-${process.pid}-${randomBytes(16).toString('hex')}.bak`;
  const artifact = anchoredChild(vault, name);
  const destinationFd = createChild(artifact, VERIFIED_CREATE_FLAGS, 0o600);
  let destination;
  try {
    const sourceStat = copyOpenFile(sourceFd, destinationFd, role);
    fs.fchmodSync(destinationFd, Number(sourceStat.mode & 0o777n));
    fs.fsyncSync(destinationFd);
    const source = hashOpenFile(sourceFd, role);
    destination = hashOpenFile(destinationFd, `${role} vault copy`);
    if (source.size !== destination.size || source.digest !== destination.digest) {
      throw new Error(`${role} vault copy does not match its held source descriptor`);
    }
    const pathStat = lstatChild(artifact);
    if (
      pathStat.isSymbolicLink() ||
      !pathStat.isFile() ||
      stableFileIdentity(pathStat) !== destination.identity
    ) {
      throw new Error(`${role} vault path changed during preservation`);
    }
    fs.fsyncSync(vault.fd);
  } finally {
    fs.closeSync(destinationFd);
  }
  verifyVaultArtifactFromFreshRoot(repo, name, destination);
  return { role, gitPath: artifactGitPath(name), layer: destination };
}

function createVaultCopyFromBytes(repo, vault, contents, role) {
  validatePlanParent(vault);
  const name = `.gitnexus-plan-${role}-${process.pid}-${randomBytes(16).toString('hex')}.bak`;
  const artifact = anchoredChild(vault, name);
  const fd = createChild(artifact, VERIFIED_CREATE_FLAGS, 0o600);
  let layer;
  try {
    writeAll(fd, contents);
    fs.fchmodSync(fd, 0o644);
    fs.fsyncSync(fd);
    layer = hashOpenFile(fd, `${role} vault copy`);
    if (layer.size !== BigInt(contents.length) || layer.digest !== sha256(contents)) {
      throw new Error(`${role} vault copy does not match the intended plan bytes`);
    }
    const pathStat = lstatChild(artifact);
    if (
      pathStat.isSymbolicLink() ||
      !pathStat.isFile() ||
      stableFileIdentity(pathStat) !== layer.identity
    ) {
      throw new Error(`${role} vault path changed during preservation`);
    }
    fs.fsyncSync(vault.fd);
  } finally {
    fs.closeSync(fd);
  }
  verifyVaultArtifactFromFreshRoot(repo, name, layer);
  return { role, gitPath: artifactGitPath(name), layer };
}

function movePathToVault(repo, sourceHandle, sourceName, vault, role) {
  const source = anchoredChild(sourceHandle, sourceName);
  if (!lstatAnchoredOptional(source)) return null;
  const name = `.gitnexus-plan-${role}-${process.pid}-${randomBytes(16).toString('hex')}.bak`;
  const destination = anchoredChild(vault, name);
  const moved = publishNoReplace(source, destination);
  if (!moved) throw new Error(`${role} preservation destination unexpectedly exists`);
  fs.fsyncSync(sourceHandle.fd);
  if (vault.fd !== sourceHandle.fd) fs.fsyncSync(vault.fd);
  const sourceAfter = lstatAnchoredOptional(source);
  const destinationAfter = lstatAnchoredOptional(destination);
  if (sourceAfter || !destinationAfter) {
    throw new Error(`${role} could not be atomically moved into the Git-admin vault`);
  }
  const opened = openVerifiedAnchoredFile(
    destination,
    `${role} Git-admin artifact`,
    destinationAfter,
  );
  verifyVaultArtifactFromFreshRoot(repo, name, opened.layer);
  return { role, gitPath: artifactGitPath(name), layer: opened.layer, fd: opened.fd };
}

function formatPreservedArtifacts(artifacts) {
  if (artifacts.length === 0) return '';
  return `; preserved Git-admin artifacts: ${artifacts
    .map((artifact) => `${artifact.role}=git-path:${artifact.gitPath}`)
    .join(', ')}`;
}

export function writePlanSafely({
  repo: repoInput,
  generatedPlanPath,
  contents: inputContents,
  replace = false,
  expectedPlanPath,
  expectedPlanDigest,
  testHooks,
} = {}) {
  const shouldReplace = requireBoolean(replace, 'replace');
  if (!Buffer.isBuffer(inputContents) && typeof inputContents !== 'string') {
    throw new Error('contents must be a string or Buffer');
  }
  let expectedDigest;
  if (shouldReplace) {
    expectedDigest = normalizeSha256Digest(
      expectedPlanDigest,
      'expectedPlanDigest from the read-plan receipt',
    );
  } else if (expectedPlanPath !== undefined || expectedPlanDigest !== undefined) {
    throw new Error('expectedPlanPath and expectedPlanDigest are valid only when replace is true');
  }
  const repo = assertRepository(repoInput);
  const generatedPlan = normalizeGeneratedPlanWritePath(generatedPlanPath);
  if (shouldReplace) {
    const receiptPath = normalizeGeneratedPlanWritePath(
      requireString(expectedPlanPath, 'expectedPlanPath from the read-plan receipt'),
    );
    if (receiptPath !== generatedPlan) {
      throw new Error(
        'expectedPlanPath from the read-plan receipt must exactly match generatedPlanPath',
      );
    }
  }
  const contents = Buffer.isBuffer(inputContents)
    ? Buffer.from(inputContents)
    : Buffer.from(inputContents, 'utf8');
  decodeUtf8(contents, 'generated plan');
  if (contents.length > MAX_PLAN_BYTES) {
    throw new Error(`Generated plan exceeds ${MAX_PLAN_BYTES} bytes`);
  }
  const components = generatedPlan.split('/');
  const finalName = components.pop();
  let parentHandle;
  let vaultHandle;
  let tempRef;
  let tempName;
  let tempFd;
  let finalRef;
  let expectedTemp;
  let originalDestination;
  let priorBackup;
  const preservedArtifacts = [];
  try {
    parentHandle = openPlanParent(repo, components);
    vaultHandle = openBackupVault(repo);
    const parentDevice = fs.fstatSync(parentHandle.fd, { bigint: true }).dev;
    const vaultDevice = fs.fstatSync(vaultHandle.fd, { bigint: true }).dev;
    if (parentDevice !== vaultDevice) {
      throw new Error(
        'Generated-plan parent and Git-admin backup vault must share a filesystem for atomic publication',
      );
    }
    testHooks?.afterParentOpen?.({ fd: parentHandle.fd, path: parentHandle.expectedPath });
    validatePlanParent(parentHandle);
    validatePlanParent(vaultHandle);
    finalRef = anchoredChild(parentHandle, finalName);
    originalDestination = openExistingPlanDestination(finalRef, shouldReplace);
    tempName = `.gitnexus-plan-${process.pid}-${randomBytes(16).toString('hex')}.tmp`;
    tempRef = anchoredChild(parentHandle, tempName);
    tempFd = createChild(tempRef, VERIFIED_CREATE_FLAGS, 0o600);
    writeAll(tempFd, contents);
    fs.fchmodSync(tempFd, 0o644);
    fs.fsyncSync(tempFd);
    expectedTemp = hashOpenFile(tempFd, 'generated-plan temporary file');
    if (expectedTemp.size !== BigInt(contents.length) || expectedTemp.digest !== sha256(contents)) {
      throw new Error('Generated-plan temporary file failed verification');
    }

    testHooks?.beforeRename?.({
      fd: parentHandle.fd,
      path: parentHandle.expectedPath,
      tempPath: tempRef.path,
    });
    validatePlanParent(parentHandle);
    validatePlanParent(vaultHandle);
    validateOpenPlanDestination(originalDestination);
    const tempPathStat = lstatChild(tempRef);
    const currentTemp = hashOpenFile(tempFd, 'generated-plan temporary file');
    if (
      tempPathStat.isSymbolicLink() ||
      !tempPathStat.isFile() ||
      stableFileIdentity(tempPathStat) !== expectedTemp.identity ||
      currentTemp.identity !== expectedTemp.identity ||
      currentTemp.digest !== expectedTemp.digest
    ) {
      throw new Error('Generated-plan temporary path or content changed before rename');
    }

    if (shouldReplace) {
      testHooks?.beforeBackupMove?.({ fd: parentHandle.fd, finalPath: finalRef.path });
      const originalLayer = hashOpenFile(originalDestination.fd, 'prior generated plan');
      if (originalLayer.digest !== expectedDigest) {
        throw new Error(
          'Generated plan no longer matches the exact digest from the read-plan receipt',
        );
      }
      validatePlanParent(parentHandle);
      validateOpenPlanDestination(originalDestination);
      inspectPlanDestination(finalRef, {
        replace: true,
        expectedIdentity: originalDestination.identity,
      });
      priorBackup = movePathToVault(repo, parentHandle, finalName, vaultHandle, 'prior-plan');
      if (!priorBackup) {
        throw new Error('Existing generated plan disappeared before preservation');
      }
      preservedArtifacts.push(priorBackup);
      if (
        priorBackup.layer.identity !== originalDestination.stableIdentity ||
        priorBackup.layer.digest !== originalLayer.digest
      ) {
        preservedArtifacts.push(
          createVaultCopyFromFd(repo, vaultHandle, originalDestination.fd, 'expected-prior-plan'),
        );
        throw new Error('Destination raced while the prior plan was moved into preservation');
      }
      if (lstatAnchoredOptional(finalRef)) {
        throw new Error('Destination reappeared after the prior plan was preserved');
      }
    }

    testHooks?.beforePublication?.({
      fd: parentHandle.fd,
      finalPath: finalRef.path,
      tempPath: tempRef.path,
      replace: shouldReplace,
    });
    validatePlanParent(parentHandle);
    validatePlanParent(vaultHandle);
    const finalTempPathStat = lstatChild(tempRef);
    const finalTemp = hashOpenFile(tempFd, 'generated-plan temporary file');
    if (
      finalTempPathStat.isSymbolicLink() ||
      !finalTempPathStat.isFile() ||
      stableFileIdentity(finalTempPathStat) !== expectedTemp.identity ||
      finalTemp.identity !== expectedTemp.identity ||
      finalTemp.digest !== expectedTemp.digest
    ) {
      throw new Error('Generated-plan temporary path or content changed at publication');
    }
    // link() reports the race itself; re-deriving that verdict from a later pair
    // of stats would be both slower and weaker.
    if (!publishNoReplace(tempRef, finalRef)) {
      throw new Error('Generated-plan publication was refused because the destination raced');
    }
    // link() creates a directory entry, so it needs the parent fsync that rename
    // needed: the file's own bytes were fsynced through tempFd before this point,
    // and this makes the name that now reaches them durable too. Skipping it is
    // the step write-file-atomic omits and maildir, git and atomicwrites all
    // mandate.
    //
    // Honest limitation: on macOS fsync is not a write barrier — the durable
    // primitive there is fcntl(F_FULLFSYNC), which Node does not expose. A
    // macOS plan write is therefore as durable as fsync makes it and no more.
    fs.fsyncSync(parentHandle.fd);
    testHooks?.afterPublication?.({ fd: parentHandle.fd, finalPath: finalRef.path });
    validatePlanParent(parentHandle);
    validatePlanParent(vaultHandle);
    validateCommittedPlan(finalRef, tempFd, expectedTemp, testHooks);
    const receipt = { generated_plan_path: generatedPlan, bytes_written: contents.length };
    if (priorBackup) receipt.prior_plan_backup_git_path = priorBackup.gitPath;
    return receipt;
  } catch (error) {
    const preservationErrors = [];
    let intendedPreserved = preservedArtifacts.some(
      (artifact) =>
        expectedTemp &&
        artifact.layer.identity === expectedTemp.identity &&
        artifact.layer.digest === expectedTemp.digest,
    );
    if (parentHandle && vaultHandle && tempName) {
      try {
        const movedTemp = movePathToVault(
          repo,
          parentHandle,
          tempName,
          vaultHandle,
          'unpublished-plan',
        );
        if (movedTemp) {
          preservedArtifacts.push(movedTemp);
          intendedPreserved =
            Boolean(expectedTemp) &&
            movedTemp.layer.identity === expectedTemp.identity &&
            movedTemp.layer.digest === expectedTemp.digest;
          fs.closeSync(movedTemp.fd);
        }
      } catch (preservationError) {
        preservationErrors.push(preservationError);
      }
    }
    if (vaultHandle && expectedTemp && !intendedPreserved) {
      try {
        preservedArtifacts.push(
          createVaultCopyFromBytes(repo, vaultHandle, contents, 'intended-plan'),
        );
      } catch (preservationError) {
        preservationErrors.push(preservationError);
      }
    }
    if (vaultHandle && originalDestination?.fd !== undefined) {
      try {
        const originalLayer = hashOpenFile(originalDestination.fd, 'prior generated plan');
        const priorPreserved = preservedArtifacts.some(
          (artifact) => artifact.layer.digest === originalLayer.digest,
        );
        if (!priorPreserved) {
          preservedArtifacts.push(
            createVaultCopyFromFd(repo, vaultHandle, originalDestination.fd, 'expected-prior-plan'),
          );
        }
      } catch (preservationError) {
        preservationErrors.push(preservationError);
      }
    }
    const message = error instanceof Error ? error.message : String(error);
    const artifactSummary = formatPreservedArtifacts(preservedArtifacts);
    const preservationSummary =
      preservationErrors.length === 0
        ? ''
        : `; preservation failures: ${preservationErrors
            .map((failure) => (failure instanceof Error ? failure.message : String(failure)))
            .join(' | ')}`;
    if (error?.code === 'EACCES' || error?.code === 'EPERM' || error?.code === 'EROFS') {
      throw new Error(
        `Cannot safely write generated plan: checkout is read-only or its parent is not writable (${error.code})${artifactSummary}${preservationSummary}`,
      );
    }
    throw new Error(`${message}${artifactSummary}${preservationSummary}`);
  } finally {
    if (priorBackup?.fd !== undefined) {
      try {
        fs.closeSync(priorBackup.fd);
      } catch {
        // Preserve the primary write result/error.
      }
    }
    if (originalDestination?.fd !== undefined) {
      try {
        fs.closeSync(originalDestination.fd);
      } catch {
        // Preserve the primary write result/error.
      }
    }
    if (tempFd !== undefined) {
      try {
        fs.closeSync(tempFd);
      } catch {
        // Preserve the primary write result/error.
      }
    }
    if (vaultHandle) closeDescriptors(vaultHandle.descriptors);
    if (parentHandle) closeDescriptors(parentHandle.descriptors);
  }
}

export function snapshotEvidence({
  repo: repoInput,
  generatedPlanPath,
  citedPaths = [],
  testHooks,
} = {}) {
  if (!Array.isArray(citedPaths) || citedPaths.some((entry) => typeof entry !== 'string')) {
    throw new Error('citedPaths must be an array of strings');
  }
  const repo = assertRepository(repoInput);
  const generatedPlan = normalizeGeneratedPlanWritePath(generatedPlanPath);
  const normalizedCitations = new Set(
    citedPaths.map((citedPath) => normalizeRepoPath(citedPath, 'cited path')),
  );
  const initialHead = git(repo, ['rev-parse', '--verify', 'HEAD']).stdout;
  const head = decodeUtf8(initialHead, 'HEAD commit').trim();
  if (!/^[0-9a-f]{40,64}$/.test(head)) throw new Error('HEAD did not resolve to a full object ID');
  const initialDirty = readDirtySnapshot(repo);
  const initialIndex = git(repo, ['ls-files', '--stage', '-z']).stdout;
  const indexGuard = captureControlFile(resolveAdministrativePath(repo, 'index'), 'Git index');
  const headGuards = captureHeadGuards(repo);
  const dirty = initialDirty.records;
  const mutationGuards = [];
  // Per-snapshot walk state: `absenceCache` owns every descriptor an absence
  // anchor holds, deduplicated by repo-relative prefix and closed exactly once
  // below; `guardedDirectories` keeps parent guarding to one stat per directory.
  const absenceCache = new Map();
  const walkState = { absenceCache, guardedDirectories: new Set() };

  try {
    testHooks?.afterAnchorCapture?.({ headCommit: head });
    for (const citedPath of [...normalizedCitations]) {
      const status = dirty.get(citedPath);
      if (status?.rename_from) normalizedCitations.add(status.rename_from);
      if (status?.rename_to) normalizedCitations.add(status.rename_to);
    }

    const neededPaths = new Set([...dirty.keys(), ...normalizedCitations]);
    const layers = loadGitLayers(repo, neededPaths, head, initialIndex);
    testHooks?.afterGitLayerLoad?.({ headCommit: head });
    const globalEntries = [...dirty.values()]
      .filter((record) => record.path !== generatedPlan)
      .map((record) =>
        materializeRecord(repo, record, layers, mutationGuards, testHooks, walkState),
      );
    const citedEntries = [...normalizedCitations].sort(compareUtf8).map((repoPath) => {
      const status = dirty.get(repoPath) ?? {
        path: repoPath,
        state: 'clean',
        rename_from: null,
        rename_to: null,
        has_untracked: false,
      };
      const entry = materializeRecord(repo, status, layers, mutationGuards, testHooks, walkState);
      const present = Object.values(entry.object_kind).some((kind) => kind !== ABSENT);
      if (!present) entry.state = ABSENT;
      else if (entry.state === 'clean' && entry.object_kind.untracked !== ABSENT) {
        entry.state = 'untracked';
      }
      return entry;
    });
    const dirtyBytes = serializeDirtyRecords(globalEntries);
    const verifyGuards = () => {
      for (const guard of mutationGuards) {
        if (guard.type === 'stat') {
          const current = fs.lstatSync(guard.absolute, { bigint: true });
          if (statIdentity(current) !== guard.identity) {
            throw new Error(`${guard.absolute} changed before evidence materialization completed`);
          }
        } else if (guard.type === 'directory') {
          const current = fs.lstatSync(guard.absolute, { bigint: true });
          if (!current.isDirectory() || stableDirectoryIdentity(current) !== guard.identity) {
            throw new Error(`${guard.absolute} changed before evidence materialization completed`);
          }
        } else if (guard.type === 'symlink') {
          const before = fs.lstatSync(guard.absolute, { bigint: true });
          const target = fs.readlinkSync(guard.absolute, { encoding: 'buffer' });
          const after = fs.lstatSync(guard.absolute, { bigint: true });
          assertStableIdentity(before, after, guard.absolute);
          if (statIdentity(after) !== guard.identity || !Buffer.from(target).equals(guard.target)) {
            throw new Error(`${guard.absolute} changed before evidence materialization completed`);
          }
        } else if (guard.type === 'gitlink') {
          const current = readOwnGitlinkHead(guard.absolute);
          if (current.oid !== guard.oid || current.topLevel !== guard.topLevel) {
            throw new Error(`${guard.absolute} changed before evidence materialization completed`);
          }
        } else if (guard.type === 'absence') {
          // statIdentity is a strict superset of stableDirectoryIdentity on the
          // same stat, so comparing both could only ever fire together.
          const parent = fs.fstatSync(guard.fd, { bigint: true });
          if (!parent.isDirectory() || statIdentity(parent) !== guard.parentMutationIdentity) {
            throw new Error(`Absence anchor changed for ${guard.repoPath}`);
          }
          anchoringBackend().verifyAbsentChild(guard);
        }
      }
      for (const guard of headGuards) verifyControlFile(guard);
      verifyControlFile(indexGuard);
    };
    testHooks?.afterMaterialize?.();
    verifyGuards();
    testHooks?.afterFirstGuardPass?.();
    const finalDirty = readDirtySnapshot(repo);
    const finalHead = git(repo, ['rev-parse', '--verify', 'HEAD']).stdout;
    const finalIndex = git(repo, ['ls-files', '--stage', '-z']).stdout;
    if (
      !initialDirty.output.equals(finalDirty.output) ||
      !initialHead.equals(finalHead) ||
      !initialIndex.equals(finalIndex)
    ) {
      throw new Error(
        'HEAD, index, or working-tree status changed while evidence was materialized',
      );
    }
    verifyGuards();

    return {
      schema_version: EVIDENCE_PROVENANCE_SCHEMA_VERSION,
      head_commit: head,
      generated_plan_path: generatedPlan,
      global_dirty_digest: {
        algorithm: 'sha256',
        canonicalization: EVIDENCE_PROVENANCE_CANONICALIZATION,
        value: sha256(dirtyBytes).slice('sha256:'.length),
      },
      cited_path_manifest: citedEntries,
    };
  } finally {
    // One entry per distinct anchored directory, so one close per descriptor.
    for (const handle of absenceCache.values()) {
      try {
        fs.closeSync(handle.fd);
      } catch {
        // Preserve the primary snapshot result/error.
      }
    }
  }
}

function parseCli(argv) {
  const args = [...argv];
  const command = args[0] && !args[0].startsWith('--') ? args.shift() : 'snapshot';
  if (!['snapshot', 'read-plan', 'write-plan'].includes(command)) {
    throw new Error(`Unsupported command: ${command}`);
  }
  const allowed = {
    snapshot: new Set(['--repo', '--generated-plan', '--cited', '--schema-version']),
    'read-plan': new Set(['--repo', '--generated-plan']),
    'write-plan': new Set([
      '--repo',
      '--generated-plan',
      '--replace',
      '--expected-plan-path',
      '--expected-plan-digest',
    ]),
  }[command];
  let repo;
  let generatedPlanPath;
  let schemaVersion = EVIDENCE_PROVENANCE_SCHEMA_VERSION;
  let replace = false;
  let expectedPlanPath;
  let expectedPlanDigest;
  const citedPaths = [];
  const seen = new Set();
  while (args.length > 0) {
    const flag = args.shift();
    if (typeof flag !== 'string' || !flag.startsWith('--')) {
      throw new Error(`Unexpected positional argument: ${flag}`);
    }
    if (!allowed.has(flag)) throw new Error(`${flag} is not valid for ${command}`);
    if (flag === '--replace') {
      if (seen.has(flag)) throw new Error(`Duplicate option: ${flag}`);
      seen.add(flag);
      replace = true;
      continue;
    }
    if (flag !== '--cited' && seen.has(flag)) throw new Error(`Duplicate option: ${flag}`);
    seen.add(flag);
    const value = args.shift();
    if (value === undefined || value.startsWith('--')) throw new Error(`Missing value for ${flag}`);
    if (flag === '--repo') repo = value;
    else if (flag === '--generated-plan') generatedPlanPath = value;
    else if (flag === '--cited') citedPaths.push(value);
    else if (flag === '--schema-version') {
      if (!/^\d+$/.test(value)) throw new Error('--schema-version must be an integer');
      schemaVersion = Number(value);
    } else if (flag === '--expected-plan-path') expectedPlanPath = value;
    else if (flag === '--expected-plan-digest') expectedPlanDigest = value;
  }
  if (!repo) throw new Error('--repo is required');
  if (!generatedPlanPath) throw new Error('--generated-plan is required');
  if (command === 'snapshot' && schemaVersion !== EVIDENCE_PROVENANCE_SCHEMA_VERSION) {
    throw new Error(
      `Unsupported evidence provenance schema ${schemaVersion}; schema 1 is legacy and must be conservatively re-anchored`,
    );
  }
  if (command === 'write-plan') {
    if (replace && (expectedPlanPath === undefined || expectedPlanDigest === undefined)) {
      throw new Error(
        '--replace requires --expected-plan-path and --expected-plan-digest from read-plan',
      );
    }
    if (!replace && (expectedPlanPath !== undefined || expectedPlanDigest !== undefined)) {
      throw new Error('--expected-plan-path and --expected-plan-digest require --replace');
    }
  }
  return {
    command,
    repo,
    generatedPlanPath,
    citedPaths,
    replace,
    expectedPlanPath,
    expectedPlanDigest,
  };
}

function readStdinBounded() {
  const chunks = [];
  let total = 0;
  const buffer = Buffer.allocUnsafe(64 * 1024);
  for (;;) {
    const count = fs.readSync(0, buffer, 0, buffer.length, null);
    if (count === 0) break;
    total += count;
    if (total > MAX_PLAN_BYTES) throw new Error(`Generated plan exceeds ${MAX_PLAN_BYTES} bytes`);
    chunks.push(Buffer.from(buffer.subarray(0, count)));
  }
  return Buffer.concat(chunks, total);
}

function main() {
  try {
    const options = parseCli(process.argv.slice(2));
    let result;
    if (options.command === 'write-plan') {
      result = writePlanSafely({ ...options, contents: readStdinBounded() });
    } else if (options.command === 'read-plan') {
      result = readPlanSafely(options);
    } else {
      result = snapshotEvidence(options);
    }
    process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
  } catch (error) {
    process.stderr.write(
      `evidence-provenance: ${error instanceof Error ? error.message : String(error)}\n`,
    );
    process.exitCode = 1;
  }
}

const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : null;
if (invokedPath && invokedPath === fileURLToPath(import.meta.url)) main();
