import { execFile } from "node:child_process";
import { createHash, randomUUID } from "node:crypto";
import { chmod, lstat, mkdir, readFile, readdir, realpath, writeFile } from "node:fs/promises";
import path from "node:path";
import { promisify, TextDecoder } from "node:util";
import {
  createDefaultRunrailReviewContract,
  evaluateRunrailReviewContract,
  parseRunrailReviewContract,
  RUNRAIL_REVIEW_CONTRACT_PATH
} from "./runrail-review-contract-core.mjs";
import {
  buildRunrailReviewFocus,
  writeRunrailReviewFocusArtifacts
} from "./runrail-review-focus-core.mjs";
import { writeRunrailHunkContinuityArtifacts } from "./runrail-hunk-continuity-core.mjs";
import { buildRunrailReviewPassport, stableRunrailPassportJson } from "./runrail-review-passport-core.mjs";
import { LOCAL_AUDIT_RUNNER, LocalAuditRunnerError } from "./veyact-local-audit-runner-core.mjs";
import { deriveRunrailReviewDecision } from "./runrail-review-policy-core.mjs";
import { runRunrailStaticVerifier } from "./runrail-safe-static-verifier-core.mjs";

const execFileAsync = promisify(execFile);
const MAX_FILES = 2_000;
const MAX_TOTAL_BYTES = 25 * 1024 * 1024;
const MAX_FILE_BYTES = 2 * 1024 * 1024;
const MAX_CHANGED_FILES = 100;
const MAX_DIFF_LINES = 5_000;
const allowedBlobModes = new Set(["100644", "100755"]);
const fatalUtf8Decoder = new TextDecoder("utf-8", { fatal: true });
const unsafePortableSeparators = /[\u2044\u2215\u29f8\uff0f\uff3c]/u;
const windowsReservedBasename = /^(?:con|prn|aux|nul|clock\$|com[1-9]|lpt[1-9])(?:\..*)?$/i;
const forbiddenPathParts = new Set([
  ".git",
  ".ssh",
  ".gnupg",
  ".venv",
  "venv",
  ".tox",
  "node_modules",
  "__pycache__",
  ".pytest_cache"
]);
const forbiddenBasenames = new Set([".env", ".env.local", ".npmrc", ".pypirc", "id_rsa", "id_ed25519"]);
const secretPatterns = [
  /\b(?:sk|rk|ghp|github_pat|xox[baprs])[-_A-Za-z0-9]{12,}\b/,
  /\bAKIA[0-9A-Z]{16}\b/,
  /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
  /\b(?:password|passwd|api[_-]?key|secret|token)\s*[:=]\s*["'][^"']{8,}["']/i
];

function sha256(value) {
  return createHash("sha256").update(value).digest("hex");
}

function stableValue(value) {
  if (Array.isArray(value)) return value.map(stableValue);
  if (value && typeof value === "object") {
    return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
  }
  return value;
}

function stableJson(value) {
  return `${JSON.stringify(stableValue(value), null, 2)}\n`;
}

function escapeHtml(value) {
  return String(value)
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}

async function restrictOwnerOnlyTree(root) {
  if (process.platform === "win32") return;
  const entry = await lstat(root);
  if (entry.isSymbolicLink() || (!entry.isDirectory() && !entry.isFile())) {
    throw new LocalAuditRunnerError(
      "run-artifact-boundary",
      "Runrail review storage contains an unsupported filesystem entry."
    );
  }
  if (entry.isDirectory()) {
    await chmod(root, 0o700);
    for (const child of await readdir(root)) {
      await restrictOwnerOnlyTree(path.join(root, child));
    }
    return;
  }
  await chmod(root, 0o600);
}

export function renderRunrailReviewHtml(report, focus = buildRunrailReviewFocus(report)) {
  const findings = report.decision.findings
    .map((entry) => `
      <li class="finding finding-${escapeHtml(entry.severity)}">
        <span>${escapeHtml(entry.severity)}</span>
        <div><strong>${escapeHtml(entry.title)}</strong><p>${escapeHtml(entry.detail)}</p></div>
        <small>${escapeHtml(entry.fileCount)} file${entry.fileCount === 1 ? "" : "s"}</small>
      </li>`)
    .join("");
  const changedFiles = report.policy.changedFiles
    .map((entry) => `<li><span>${escapeHtml(entry.status)}</span><code>${escapeHtml(entry.path)}</code></li>`)
    .join("");
  const candidates = report.decision.candidateIds.length > 0
    ? report.decision.candidateIds.map((candidateId) => `<code>${escapeHtml(candidateId)}</code>`).join(" ")
    : "<span>Manual review only</span>";
  const reviewLanes = report.reviewContract.requiredReviewLanes
    .map((lane) => `<code>${escapeHtml(lane)}</code>`)
    .join(" ");
  const requiredEvidence = report.reviewContract.requiredEvidence
    .map((evidence) => `<li><code>${escapeHtml(evidence)}</code></li>`)
    .join("");
  const verifierChecks = report.verifier.checks
    .map((check) => `
      <li>
        <span>${escapeHtml(check.language)}</span>
        <strong>${escapeHtml(check.status)}</strong>
        <small>${escapeHtml(check.fileCount)} file${check.fileCount === 1 ? "" : "s"}</small>
      </li>`)
    .join("");
  const focusItems = focus.items
    .map((item) => `
      <li>
        <span>${item.rank}</span>
        <div><strong>${escapeHtml(item.label)}</strong><p>${escapeHtml(item.reason)}</p></div>
      </li>`)
    .join("");
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'">
  <title>Runrail local review ${escapeHtml(report.runId)}</title>
  <style>
    :root{color-scheme:light;--ink:#121923;--muted:#5b6572;--line:#dce1e6;--paper:#fff;--wash:#f4f6f7;--green:#0d6848;--amber:#805500;--red:#9f2d23}*{box-sizing:border-box}body{margin:0;background:var(--wash);color:var(--ink);font:15px/1.55 ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}main{width:min(1080px,calc(100% - 32px));margin:0 auto;padding:40px 0 64px}header{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:24px;align-items:end;padding:0 0 28px;border-bottom:1px solid var(--line)}h1{margin:5px 0 8px;font-size:clamp(30px,5vw,52px);line-height:1.02;letter-spacing:0}h2{margin:0 0 16px;font-size:20px}p{margin:0;color:var(--muted)}.eyebrow{text-transform:uppercase;font-size:11px;font-weight:800;letter-spacing:.12em;color:var(--green)}.risk{min-width:150px;padding:16px;border:1px solid currentColor;text-align:center;text-transform:uppercase;font-weight:800}.risk-low{color:#365166}.risk-medium{color:var(--amber)}.risk-high{color:var(--red)}.summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));border-bottom:1px solid var(--line);background:var(--paper)}.summary div{min-height:104px;padding:18px;border-right:1px solid var(--line)}.summary div:last-child{border-right:0}.summary span{display:block;color:var(--muted);font-size:12px}.summary strong{display:block;margin-top:7px;font-size:17px}.layout{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(280px,.7fr);gap:32px;padding-top:34px}.panel{padding:24px 0;border-bottom:1px solid var(--line)}ul,ol{margin:0;padding:0;list-style:none}.focus-list{border-top:1px solid var(--line)}.focus-list li{display:grid;grid-template-columns:34px minmax(0,1fr);gap:13px;padding:14px 0;border-bottom:1px solid var(--line)}.focus-list li>span{display:grid;width:28px;height:28px;place-items:center;border:1px solid var(--line);font-weight:800}.focus-list strong,.focus-list p{display:block}.finding{display:grid;grid-template-columns:66px minmax(0,1fr) auto;gap:14px;padding:16px 0;border-top:1px solid var(--line)}.finding>span{font-size:10px;font-weight:800;text-transform:uppercase}.finding-high>span{color:var(--red)}.finding-medium>span{color:var(--amber)}.finding-info>span{color:var(--green)}.finding strong,.finding p{display:block}.finding small{color:var(--muted)}.files li,.checks li{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:10px;padding:8px 0;border-top:1px solid var(--line)}.files li{grid-template-columns:28px minmax(0,1fr)}.files span{font-weight:800;color:var(--green)}.checks span{text-transform:capitalize}.checks small{color:var(--muted)}code{overflow-wrap:anywhere}.boundary{padding:22px;border:1px solid var(--line);background:var(--paper)}.boundary dl{margin:18px 0}.boundary dl div{display:flex;justify-content:space-between;gap:18px;padding:10px 0;border-top:1px solid var(--line)}dt{color:var(--muted)}dd{margin:0;font-weight:700;text-align:right}.candidate-list{display:flex;flex-wrap:wrap;gap:6px}.candidate-list code{padding:4px 7px;background:var(--wash);font-size:11px}.obligations{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 16px}.obligations code{padding:4px 7px;background:var(--wash);font-size:11px}.evidence-list li{padding:7px 0;border-top:1px solid var(--line)}.artifacts a{display:block;padding:10px 0;border-top:1px solid var(--line);color:var(--ink);font-weight:700}.checksum{font:11px/1.5 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere;color:var(--muted)}.limit{margin-top:28px;padding:18px;border-left:3px solid var(--amber);background:#fffaf0}.limit strong{display:block;margin-bottom:5px}@media(max-width:760px){main{width:min(100% - 24px,1080px);padding-top:24px}header,.layout{grid-template-columns:1fr}.risk{text-align:left}.summary{grid-template-columns:1fr 1fr}.summary div:nth-child(2){border-right:0}.summary div:nth-child(-n+2){border-bottom:1px solid var(--line)}.finding{grid-template-columns:58px minmax(0,1fr)}.finding small{grid-column:2}.layout{gap:8px}}
  </style>
</head>
<body>
<main>
  <header>
    <div><p class="eyebrow">Runrail local review</p><h1>Human decision required</h1><p>${escapeHtml(report.runId)} · checksum-identified local evidence</p></div>
    <div class="risk risk-${escapeHtml(report.decision.riskLevel)}">${escapeHtml(report.decision.riskLevel)} review priority</div>
  </header>
  <section class="summary" aria-label="Review summary">
    <div><span>Contract and syntax preflight</span><strong>${report.preflightPassed ? "Passed" : "Failed"}</strong></div>
    <div><span>Source checkout</span><strong>${report.source.sourceCheckoutUnchanged ? "Unchanged" : "Changed"}</strong></div>
    <div><span>Project tests</span><strong>Not run</strong></div>
    <div><span>Automatic apply</span><strong>Disabled</strong></div>
  </section>
  <div class="layout">
    <div>
      <section class="panel"><p class="eyebrow">Start here</p><h2>Ordered Review Focus</h2><ol class="focus-list">${focusItems}</ol></section>
      <section class="panel"><h2>Deterministic findings</h2><ul>${findings}</ul></section>
      <section class="panel">
        <p class="eyebrow">Baseline-owned contract</p>
        <h2>${escapeHtml(report.reviewContract.source === "baseline-file" ? "Repository contract applied" : "Built-in contract applied")}</h2>
        <div class="obligations">${reviewLanes}</div>
        <ul class="evidence-list">${requiredEvidence}</ul>
      </section>
      <section class="panel"><h2>Static validation</h2><ul class="checks">${verifierChecks}</ul></section>
      <section class="panel"><h2>Changed files</h2><ul class="files">${changedFiles}</ul></section>
    </div>
    <aside>
      <section class="boundary">
        <p class="eyebrow">Decision boundary</p>
        <h2>${escapeHtml(report.decision.recommendation.replaceAll("-", " "))}</h2>
        <dl>
          <div><dt>Human approval</dt><dd>Required</dd></div>
          <div><dt>Merge ready</dt><dd>No</dd></div>
          <div><dt>Specialist review</dt><dd>${report.decision.specialistReviewRequired ? "Required" : "Not flagged"}</dd></div>
          <div><dt>Repository upload</dt><dd>Disabled</dd></div>
        </dl>
        <p class="eyebrow">Candidate next steps</p>
        <div class="candidate-list">${candidates}</div>
      </section>
      <section class="panel artifacts"><h2>Review artifacts</h2><a href="focus/focus.html">Review Focus</a><a href="focus/focus.json">focus.json</a><a href="candidate.patch">candidate.patch</a><a href="audit.json">audit.json</a><a href="REVIEW_PASSPORT.json">REVIEW_PASSPORT.json</a><a href="REVIEW.md">REVIEW.md</a><a href="ROLLBACK.md">ROLLBACK.md</a></section>
      <section class="panel"><h2>Audit checksum</h2><p class="checksum">${escapeHtml(report.checksumSha256)}</p></section>
    </aside>
  </div>
  <section class="limit"><strong>Preflight limits</strong><p>The contract is pinned to the base commit and cannot be weakened by the candidate. This report does not execute project tests, establish semantic correctness, perform a security review, authenticate artifact origin, or approve a merge.</p></section>
</main>
</body>
</html>
`;
}

function safeRef(value, label) {
  const ref = String(value || "").trim();
  if (!/^[A-Za-z0-9][A-Za-z0-9._/-]{0,199}$/.test(ref) || ref.includes("..") || ref.includes("@{")) {
    throw new LocalAuditRunnerError("unsafe-git-ref", `${label} must be a bounded Git ref name or commit id.`);
  }
  return ref;
}

function safeRelativePath(value) {
  const normalized = String(value || "");
  const parts = normalized.split("/");
  return (
    normalized.length > 0 &&
    normalized.length <= 240 &&
    !/[\0-\x1f\x7f\u202a-\u202e\u2066-\u2069]/u.test(normalized) &&
    !normalized.includes("\\") &&
    !unsafePortableSeparators.test(normalized) &&
    normalized === normalized.normalize("NFC") &&
    !path.posix.isAbsolute(normalized) &&
    !parts.includes("") &&
    !parts.includes("..") &&
    !parts.some(
      (part) =>
        forbiddenPathParts.has(part.toLowerCase()) ||
        part.endsWith(".") ||
        part.endsWith(" ") ||
        part.includes(":") ||
        windowsReservedBasename.test(part)
    ) &&
    !forbiddenBasenames.has(parts.at(-1).toLowerCase())
  );
}

function portablePathIdentity(value) {
  return String(value)
    .split("/")
    .map((part) => part.normalize("NFD").toLowerCase())
    .join("/");
}

export function validateCandidatePathSet(paths) {
  const identities = new Set();
  for (const candidatePath of paths) {
    if (!safeRelativePath(candidatePath)) {
      throw new LocalAuditRunnerError(
        "unsafe-candidate-tree",
        "Candidate tree contains a path that is not portable across supported filesystems."
      );
    }
    const identity = portablePathIdentity(candidatePath);
    if (identities.has(identity)) {
      throw new LocalAuditRunnerError(
        "candidate-path-collision",
        "Candidate tree contains paths that resolve to the same portable filesystem identity."
      );
    }
    identities.add(identity);
  }
  return identities.size;
}

export function decodeGitPathBytes(value) {
  const bytes = Buffer.from(value);
  let decoded;
  try {
    decoded = fatalUtf8Decoder.decode(bytes);
  } catch {
    throw new LocalAuditRunnerError("invalid-git-path-encoding", "Candidate Git paths must be valid UTF-8.");
  }
  if (!Buffer.from(decoded, "utf8").equals(bytes)) {
    throw new LocalAuditRunnerError("invalid-git-path-encoding", "Candidate Git paths must round-trip through UTF-8 exactly.");
  }
  return decoded;
}

function nulRecords(value) {
  const buffer = Buffer.from(value);
  const records = [];
  let start = 0;
  for (let index = 0; index < buffer.length; index += 1) {
    if (buffer[index] !== 0) continue;
    if (index > start) records.push(buffer.subarray(start, index));
    start = index + 1;
  }
  if (start !== buffer.length) {
    throw new LocalAuditRunnerError("invalid-git-output", "Git path output must be NUL terminated.");
  }
  return records;
}

function pathIsContained(root, candidate) {
  const relative = path.relative(root, candidate);
  return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
}

export function runrailGitNullDevicePath(platform = process.platform) {
  return platform === "win32" ? "NUL" : "/dev/null";
}

function isolatedGitEnvironment() {
  const allowed = [
    "ComSpec",
    "LANG",
    "LC_ALL",
    "PATH",
    "PATHEXT",
    "SystemDrive",
    "SystemRoot",
    "TEMP",
    "TMP",
    "TMPDIR",
    "WINDIR"
  ];
  return {
    ...Object.fromEntries(
      allowed
        .filter((key) => typeof process.env[key] === "string")
        .map((key) => [key, process.env[key]])
    ),
    GIT_ATTR_NOSYSTEM: "1",
    GIT_CONFIG_GLOBAL: runrailGitNullDevicePath(),
    GIT_CONFIG_NOSYSTEM: "1",
    GIT_NO_REPLACE_OBJECTS: "1",
    GIT_OPTIONAL_LOCKS: "0",
    GIT_TERMINAL_PROMPT: "0"
  };
}

async function git(repo, args, options = {}) {
  return execFileAsync("git", [
    "-c",
    `core.hooksPath=${runrailGitNullDevicePath()}`,
    "-c",
    "core.fsmonitor=false",
    "-c",
    "core.useBuiltinFSMonitor=false",
    "-C",
    repo,
    ...args
  ], {
    timeout: options.timeout || 30_000,
    maxBuffer: options.maxBuffer || 8 * 1024 * 1024,
    windowsHide: true,
    env: isolatedGitEnvironment(),
    encoding: options.encoding === "buffer" ? null : options.encoding || "utf8"
  });
}

function gitBlobObjectId(blob, objectFormat) {
  if (!new Set(["sha1", "sha256"]).has(objectFormat)) {
    throw new LocalAuditRunnerError("unsupported-git-object-format", "Runrail supports only SHA-1 or SHA-256 Git repositories.");
  }
  const header = Buffer.from(`blob ${blob.length}\0`, "utf8");
  return createHash(objectFormat).update(header).update(blob).digest("hex");
}

async function resolveCommit(repo, ref, label) {
  try {
    const { stdout } = await git(repo, ["rev-parse", "--verify", `${safeRef(ref, label)}^{commit}`]);
    const commit = String(stdout).trim();
    if (!/^[a-f0-9]{40,64}$/.test(commit)) throw new Error("invalid commit");
    return commit;
  } catch (error) {
    if (error instanceof LocalAuditRunnerError) throw error;
    throw new LocalAuditRunnerError("git-ref-not-found", `${label} does not resolve to a commit in the reviewed repository.`);
  }
}

async function assertCleanSourceRepo(repoInput) {
  let repo;
  try {
    repo = await realpath(path.resolve(String(repoInput || "")));
  } catch {
    throw new LocalAuditRunnerError("source-repo-not-found", "The reviewed source repository does not exist.");
  }
  const info = await lstat(repo);
  if (!info.isDirectory() || info.isSymbolicLink()) {
    throw new LocalAuditRunnerError("source-repo-type", "The reviewed source repository must be a real directory.");
  }
  let inside;
  let status;
  let head;
  try {
    const { stdout: replacementRefs } = await git(repo, ["for-each-ref", "--format=%(refname)", "refs/replace"]);
    if (String(replacementRefs).trim()) {
      throw new LocalAuditRunnerError(
        "git-replacement-objects-not-allowed",
        "The reviewed repository must not contain Git replacement-object refs."
      );
    }
    try {
      const { stdout: filterConfiguration } = await git(repo, [
        "config",
        "--local",
        "--get-regexp",
        "^filter\\."
      ]);
      if (String(filterConfiguration).trim()) {
        throw new LocalAuditRunnerError(
          "git-filter-configuration-not-allowed",
          "The reviewed repository must not define local Git clean, smudge, or process filters."
        );
      }
    } catch (error) {
      if (error instanceof LocalAuditRunnerError) throw error;
      if (Number(error?.code) !== 1) throw error;
    }
    ({ stdout: inside } = await git(repo, ["rev-parse", "--is-inside-work-tree"]));
    ({ stdout: status } = await git(repo, ["status", "--porcelain=v1", "--untracked-files=all"]));
    ({ stdout: head } = await git(repo, ["rev-parse", "HEAD"]));
  } catch (error) {
    if (error instanceof LocalAuditRunnerError) throw error;
    throw new LocalAuditRunnerError("source-repo-not-git", "The reviewed source repository must be a Git checkout.");
  }
  if (String(inside).trim() !== "true") throw new LocalAuditRunnerError("source-repo-not-git", "The source must be a Git worktree.");
  if (String(status).trim()) throw new LocalAuditRunnerError("source-repo-dirty", "The source checkout must be clean before review.");
  return { repo, head: String(head).trim() };
}

function parseTree(buffer) {
  return nulRecords(buffer).map((row) => {
    const tab = row.indexOf(9);
    if (tab < 1) {
      throw new LocalAuditRunnerError("invalid-git-tree", "Candidate Git tree output is malformed.");
    }
    const metadata = row.subarray(0, tab).toString("ascii");
    const match = metadata.match(/^([0-9]{6}) (blob|tree|commit) ([a-f0-9]{40,64})$/);
    if (!match) {
      throw new LocalAuditRunnerError("invalid-git-tree", "Candidate Git tree metadata is malformed.");
    }
    const [, mode, type, object] = match;
    return { mode, type, object, path: decodeGitPathBytes(row.subarray(tab + 1)) };
  });
}

async function materializeCommit(gitDir, commit, destination, objectFormat) {
  const { stdout } = await git(gitDir, ["ls-tree", "-rz", "--full-tree", commit], { encoding: "buffer" });
  const entries = parseTree(stdout);
  if (entries.length < 1 || entries.length > MAX_FILES) {
    throw new LocalAuditRunnerError("repository-size-boundary", `Candidate tree must contain between 1 and ${MAX_FILES} files.`);
  }
  const symlinkCount = entries.filter((entry) => entry.mode === "120000").length;
  const specialEntryCount = entries.filter((entry) => entry.type !== "blob" || !allowedBlobModes.has(entry.mode)).length;
  const unsafePathCount = entries.filter((entry) => !safeRelativePath(entry.path)).length;
  if (symlinkCount > 0 || specialEntryCount > 0) {
    throw new LocalAuditRunnerError("unsafe-candidate-tree", "Candidate tree contains a symlink, submodule, unsupported mode, special entry, or unsafe path.");
  }
  const portablePathIdentityCount = validateCandidatePathSet(entries.map((entry) => entry.path));

  const requestedDestinationRoot = path.resolve(destination);
  await mkdir(requestedDestinationRoot, { recursive: true, mode: 0o700 });
  if (process.platform !== "win32") await chmod(requestedDestinationRoot, 0o700);
  const destinationRoot = await realpath(requestedDestinationRoot);
  let totalBytes = 0;
  let pythonFileCount = 0;
  let javaScriptFileCount = 0;
  let jsonFileCount = 0;
  let postWriteContainmentCheckedCount = 0;
  let materializedHashCheckedCount = 0;
  for (const entry of entries) {
    if (entry.path.endsWith(".py")) pythonFileCount += 1;
    if (/\.(?:cjs|js|mjs)$/i.test(entry.path)) javaScriptFileCount += 1;
    if (/\.json$/i.test(entry.path)) jsonFileCount += 1;
    const { stdout: sizeOutput } = await git(gitDir, ["cat-file", "-s", entry.object]);
    const size = Number(String(sizeOutput).trim());
    if (!Number.isInteger(size) || size < 0 || size > MAX_FILE_BYTES) {
      throw new LocalAuditRunnerError("candidate-file-size-boundary", `Candidate files must be ${MAX_FILE_BYTES} bytes or smaller.`);
    }
    totalBytes += size;
    if (totalBytes > MAX_TOTAL_BYTES) {
      throw new LocalAuditRunnerError("repository-size-boundary", `Candidate tree must be ${MAX_TOTAL_BYTES} bytes or smaller.`);
    }
    const target = path.resolve(destinationRoot, ...entry.path.split("/"));
    if (!pathIsContained(destinationRoot, target)) {
      throw new LocalAuditRunnerError("candidate-path-escape", "Candidate path resolves outside the materialization root.");
    }
    await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
    const { stdout: blob } = await git(gitDir, ["cat-file", "blob", entry.object], {
      encoding: "buffer",
      maxBuffer: MAX_FILE_BYTES + 1024
    });
    if (gitBlobObjectId(blob, objectFormat) !== entry.object) {
      throw new LocalAuditRunnerError(
        "candidate-git-object-mismatch",
        "Candidate blob bytes do not match the Git tree object identity."
      );
    }
    await writeFile(target, blob, {
      flag: "wx",
      mode: 0o600
    });
    const writtenTarget = await realpath(target);
    if (!pathIsContained(destinationRoot, writtenTarget)) {
      throw new LocalAuditRunnerError("candidate-path-escape", "Materialized candidate file resolves outside the materialization root.");
    }
    if (sha256(await readFile(writtenTarget)) !== sha256(blob)) {
      throw new LocalAuditRunnerError("candidate-materialization-mismatch", "Materialized candidate bytes do not match the Git blob.");
    }
    postWriteContainmentCheckedCount += 1;
    materializedHashCheckedCount += 1;
  }
  return {
    fileCount: entries.length,
    totalBytes,
    pythonFileCount,
    javaScriptFileCount,
    jsonFileCount,
    regularBlobCount: entries.length,
    symlinkCount,
    specialEntryCount,
    unsafePathCount,
    portablePathIdentityCount,
    pathBoundaryCheckedFileCount: entries.length,
    postWriteContainmentCheckedCount,
    materializedHashCheckedCount,
    gitObjectFormat: objectFormat,
    gitCheckoutPerformed: false,
    gitHookExecutionPathUsed: false,
    checkoutFilterPathUsed: false
  };
}

function changedPathsFromNameStatus(value) {
  const fields = nulRecords(value);
  const changed = [];
  for (let index = 0; index < fields.length; index += 2) {
    const status = fields[index]?.toString("ascii") || "";
    const relative = fields[index + 1] ? decodeGitPathBytes(fields[index + 1]) : "";
    if (!/^[AMD]$/.test(status) || !safeRelativePath(relative)) {
      throw new LocalAuditRunnerError("unsafe-diff-entry", "Candidate diff contains a rename, copy, special status, or unsafe path.");
    }
    changed.push({ status, path: relative });
  }
  if (changed.length < 1 || changed.length > MAX_CHANGED_FILES) {
    throw new LocalAuditRunnerError("diff-size-boundary", `Candidate diff must change between 1 and ${MAX_CHANGED_FILES} files.`);
  }
  return changed;
}

function scanAddedDiffLines(diff) {
  const added = diff
    .split(/\r?\n/)
    .filter((line) => line.startsWith("+") && !line.startsWith("+++"))
    .map((line) => line.slice(1));
  return secretPatterns.filter((pattern) => added.some((line) => pattern.test(line))).length;
}

function binaryChangedPathsFromNumstat(value) {
  return nulRecords(value)
    .flatMap((record) => {
      const firstTab = record.indexOf(9);
      const secondTab = record.indexOf(9, firstTab + 1);
      if (firstTab < 1 || secondTab <= firstTab) {
        throw new LocalAuditRunnerError("invalid-numstat", "Candidate numstat output is malformed.");
      }
      const added = record.subarray(0, firstTab).toString("ascii");
      const deleted = record.subarray(firstTab + 1, secondTab).toString("ascii");
      const relative = decodeGitPathBytes(record.subarray(secondTab + 1));
      if (!safeRelativePath(relative)) {
        throw new LocalAuditRunnerError("unsafe-diff-entry", "Candidate numstat contains an unsafe path.");
      }
      return added === "-" || deleted === "-" ? [relative] : [];
    });
}

async function loadBaselineReviewContract(gitDir, baseCommit, changedFiles, diffLineCount) {
  const { stdout } = await git(
    gitDir,
    ["ls-tree", "-z", "--full-tree", baseCommit, "--", RUNRAIL_REVIEW_CONTRACT_PATH],
    { encoding: "buffer" }
  );
  const entries = parseTree(stdout);
  if (entries.length > 1) {
    throw new LocalAuditRunnerError("review-contract-invalid", "The base commit contains an ambiguous review contract.");
  }
  let contract = createDefaultRunrailReviewContract();
  let source = "built-in";
  let sourceBlobSha256 = null;
  if (entries.length === 1) {
    const [entry] = entries;
    if (
      entry.path !== RUNRAIL_REVIEW_CONTRACT_PATH ||
      entry.type !== "blob" ||
      !allowedBlobModes.has(entry.mode)
    ) {
      throw new LocalAuditRunnerError("review-contract-invalid", "The base review contract must be an ordinary file.");
    }
    const { stdout: blob } = await git(gitDir, ["cat-file", "blob", entry.object], {
      encoding: "buffer",
      maxBuffer: 65 * 1024
    });
    let text;
    try {
      text = fatalUtf8Decoder.decode(blob);
    } catch {
      throw new LocalAuditRunnerError("review-contract-invalid", "The base review contract must be valid UTF-8.");
    }
    try {
      contract = parseRunrailReviewContract(text);
    } catch (error) {
      throw new LocalAuditRunnerError(
        String(error?.code || "review-contract-invalid"),
        String(error?.message || "The base review contract is invalid.")
      );
    }
    source = "baseline-file";
    sourceBlobSha256 = sha256(blob);
  }
  const candidateContractChanged = changedFiles.some(
    (row) => row.path === RUNRAIL_REVIEW_CONTRACT_PATH
  );
  return evaluateRunrailReviewContract({
    contract,
    changedFiles,
    diffLineCount,
    source,
    contractSha256: sha256(stableJson(contract)),
    sourceBlobSha256,
    candidateContractChanged
  });
}

export async function reviewCandidateBranch({ repo, base, candidate, verifierProfile = "safe-static" }) {
  const source = await assertCleanSourceRepo(repo);
  const baseCommit = await resolveCommit(source.repo, base, "Base ref");
  const candidateCommit = await resolveCommit(source.repo, candidate, "Candidate ref");
  if (baseCommit === candidateCommit) throw new LocalAuditRunnerError("empty-candidate", "Base and candidate resolve to the same commit.");

  const runId = `RRV-${sha256(`${baseCommit}\0${candidateCommit}`).slice(0, 16).toUpperCase()}-${randomUUID().slice(0, 8)}`;
  const runRoot = path.join(LOCAL_AUDIT_RUNNER.runsRoot, runId);
  const bareRepo = path.join(runRoot, "objects.git");
  const candidateRoot = path.join(runRoot, "candidate-files");
  const outputRoot = path.join(runRoot, "review");
  await mkdir(runRoot, { recursive: true, mode: 0o700 });
  if (process.platform !== "win32") await chmod(runRoot, 0o700);
  await execFileAsync("git", [
    "-c",
    `core.hooksPath=${runrailGitNullDevicePath()}`,
    "-c",
    "core.fsmonitor=false",
    "-c",
    "core.useBuiltinFSMonitor=false",
    "clone",
    "--quiet",
    "--bare",
    "--local",
    "--no-hardlinks",
    "--no-recurse-submodules",
    source.repo,
    bareRepo
  ], {
    timeout: 120_000,
    maxBuffer: 4 * 1024 * 1024,
    windowsHide: true,
    env: isolatedGitEnvironment()
  });
  await restrictOwnerOnlyTree(runRoot);

  const objectFormat = String(
    (await git(bareRepo, ["rev-parse", "--show-object-format"])).stdout
  ).trim();
  const materialized = await materializeCommit(
    bareRepo,
    candidateCommit,
    candidateRoot,
    objectFormat
  );
  const { stdout: nameStatus } = await git(bareRepo, [
    "diff",
    "--no-ext-diff",
    "--no-textconv",
    "--name-status",
    "-z",
    "--no-renames",
    baseCommit,
    candidateCommit
  ], { encoding: "buffer" });
  const changed = changedPathsFromNameStatus(nameStatus);
  const { stdout: diff } = await git(bareRepo, ["diff", "--no-ext-diff", "--no-textconv", "--no-renames", "--unified=3", baseCommit, candidateCommit, "--"], {
    maxBuffer: 12 * 1024 * 1024
  });
  const { stdout: numstat } = await git(bareRepo, [
    "diff",
    "--no-ext-diff",
    "--no-textconv",
    "--numstat",
    "-z",
    "--no-renames",
    baseCommit,
    candidateCommit,
    "--"
  ], { encoding: "buffer" });
  const binaryChangedPaths = binaryChangedPathsFromNumstat(numstat);
  if (binaryChangedPaths.length > 0) {
    throw new LocalAuditRunnerError("binary-diff-not-allowed", "Candidate review does not accept binary file changes.");
  }
  const diffLineCount = String(diff).split(/\r?\n/).length;
  if (diffLineCount > MAX_DIFF_LINES) throw new LocalAuditRunnerError("diff-size-boundary", `Candidate diff exceeds ${MAX_DIFF_LINES} lines.`);
  const secretFindingCount = scanAddedDiffLines(String(diff));
  if (secretFindingCount > 0) throw new LocalAuditRunnerError("secret-like-diff", "Candidate diff contains secret-like added content.");

  const reviewContract = await loadBaselineReviewContract(
    bareRepo,
    baseCommit,
    changed,
    diffLineCount
  );
  const verifier = await runRunrailStaticVerifier({
    profile: verifierProfile,
    candidateRoot,
    runRoot,
    changedFiles: changed,
    materialized
  });
  const sourceStatusAfter = (await git(source.repo, ["status", "--porcelain=v1", "--untracked-files=all"])).stdout.trim();
  const sourceHeadAfter = (await git(source.repo, ["rev-parse", "HEAD"])).stdout.trim();
  const sourceCheckoutUnchanged = sourceStatusAfter === "" && sourceHeadAfter === source.head;
  const passed = verifier.passed && reviewContract.passed && sourceCheckoutUnchanged;
  await mkdir(outputRoot, { recursive: true, mode: 0o700 });
  const patchPath = path.join(outputRoot, "candidate.patch");
  const reportPath = path.join(outputRoot, "audit.json");
  const passportPath = path.join(outputRoot, "REVIEW_PASSPORT.json");
  const reviewPath = path.join(outputRoot, "REVIEW.md");
  const rollbackPath = path.join(outputRoot, "ROLLBACK.md");
  const htmlReportPath = path.join(outputRoot, "review.html");
  await writeFile(patchPath, String(diff), { encoding: "utf8", mode: 0o600 });
  const decision = deriveRunrailReviewDecision({
    changedFiles: changed,
    diffLineCount,
    verifierPassed: verifier.passed,
    reviewContract
  });
  const reportBody = {
    schemaVersion: 4,
    artifactType: "runrail-candidate-branch-review",
    product: "Runrail Local v1",
    runId,
    passed,
    preflightPassed: passed,
    scope: {
      policyAndSyntaxPreflight: true,
      reviewContractEvaluated: true,
      projectTestsExecuted: false,
      semanticCorrectnessEvaluated: false,
      securityReviewPerformed: false
    },
    source: {
      baseCommit,
      candidateCommit,
      sourceCheckoutUnchanged,
      sourceApplyPerformed: false
    },
    policy: {
      changedFileCount: changed.length,
      changedFiles: changed,
      diffLineCount,
      diffSha256: sha256(String(diff)),
      secretFindingCount,
      pathBoundaryPassed:
        materialized.pathBoundaryCheckedFileCount === materialized.fileCount &&
        materialized.postWriteContainmentCheckedCount === materialized.fileCount &&
        materialized.materializedHashCheckedCount === materialized.fileCount &&
        materialized.portablePathIdentityCount === materialized.fileCount,
      symlinkCount: materialized.symlinkCount,
      specialEntryCount: materialized.specialEntryCount,
      binaryDiffCount: binaryChangedPaths.length,
      arbitraryCommandAccepted: false
    },
    decision,
    reviewContract,
    verifier,
    materialized: {
      fileCount: materialized.fileCount,
      totalBytes: materialized.totalBytes,
      pythonFileCount: materialized.pythonFileCount,
      javaScriptFileCount: materialized.javaScriptFileCount,
      jsonFileCount: materialized.jsonFileCount,
      regularBlobCount: materialized.regularBlobCount,
      portablePathIdentityCount: materialized.portablePathIdentityCount,
      pathBoundaryCheckedFileCount: materialized.pathBoundaryCheckedFileCount,
      postWriteContainmentCheckedCount: materialized.postWriteContainmentCheckedCount,
      materializedHashCheckedCount: materialized.materializedHashCheckedCount,
      gitObjectFormat: materialized.gitObjectFormat,
      gitCheckoutPerformed: materialized.gitCheckoutPerformed,
      gitHookExecutionPathUsed: materialized.gitHookExecutionPathUsed,
      checkoutFilterPathUsed: materialized.checkoutFilterPathUsed
    },
    boundaries: {
      networkRequired: false,
      browserExecutionEnabled: false,
      repositoryUploadEnabled: false,
      automaticSourceApplyEnabled: false,
      osSandboxEnabled: false,
      localOperatorMachineTrustRequired: true,
      learnedRuntimeAuthorityAdded: false
    }
  };
  const report = { ...reportBody, checksumSha256: sha256(stableJson(reportBody)) };
  const passport = buildRunrailReviewPassport(report);
  const focus = buildRunrailReviewFocus(report);
  await writeFile(reportPath, stableJson(report), { encoding: "utf8", mode: 0o600 });
  await writeFile(passportPath, stableRunrailPassportJson(passport), { encoding: "utf8", mode: 0o600 });
  const focusArtifacts = await writeRunrailReviewFocusArtifacts({
    audit: report,
    reviewRoot: outputRoot
  });
  const coverageArtifacts = await writeRunrailHunkContinuityArtifacts({
    audit: report,
    patchText: String(diff),
    reviewRoot: outputRoot
  });
  await writeFile(htmlReportPath, renderRunrailReviewHtml(report, focus), { encoding: "utf8", mode: 0o600 });
  await writeFile(
    reviewPath,
    `# Runrail candidate preflight\n\nPreflight outcome: ${passed ? "passed" : "failed"}\n\nDeterministic risk level: ${decision.riskLevel}\n\nRecommended review path: ${decision.recommendation}\n\nBaseline review contract: ${reviewContract.source}\n\nReview contract passed: ${reviewContract.passed ? "yes" : "no"}\n\nRequired review lanes: ${reviewContract.requiredReviewLanes.join(", ")}\n\nRequired evidence: ${reviewContract.requiredEvidence.join(", ")}\n\nHuman approval required: yes\n\nMerge ready: no\n\n## Review Focus\n\n${focus.items.map((item) => `${item.rank}. **${item.label}:** ${item.reason}`).join("\n")}\n\n## Findings\n\n${decision.findings.map((entry) => `- **${entry.severity.toUpperCase()} — ${entry.title}:** ${entry.detail}`).join("\n")}\n\nRunrail reviewed ${changed.length} changed file(s) between the selected Git commits, enforced bounded paths and secret scanning, applied the base-commit-owned review contract, and ran the non-executing ${verifier.profile} verifier. Open review.html for a self-contained local decision view, focus/focus.html for the path-free ordered review plan, the current coverage/ snapshot for exact-hunk attention, or share REVIEW_PASSPORT.json as a path-free checksum-linked summary. Checksums do not authenticate origin. Review Coverage records attention rather than approval. This does not execute project tests, establish semantic correctness, or constitute a security review. Inspect candidate.patch and run your own project checks before deciding whether to merge the candidate branch.\n`,
    { encoding: "utf8", mode: 0o600 }
  );
  await writeFile(
    rollbackPath,
    "# Rollback note\n\nRunrail did not modify the source checkout, base branch, or candidate branch. No rollback is required. Reject or delete the candidate branch through your normal Git workflow if the review is not acceptable.\n",
    { encoding: "utf8", mode: 0o600 }
  );
  await restrictOwnerOnlyTree(runRoot);
  return {
    ok: passed,
    mode: "candidate-branch-review",
    runId,
    outputRoot,
    patchPath,
    reportPath,
    passportPath,
    reviewPath,
    rollbackPath,
    htmlReportPath,
    report,
    passport,
    focus,
    focusPath: focusArtifacts.focusPath,
    focusReviewPath: focusArtifacts.reviewPath,
    focusHtmlPath: focusArtifacts.htmlReportPath,
    coverage: coverageArtifacts.coverage,
    coveragePath: coverageArtifacts.coveragePath,
    coverageReviewPath: coverageArtifacts.reviewPath,
    coverageHtmlPath: coverageArtifacts.htmlReportPath
  };
}
