import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { existsSync, lstatSync, readFileSync, readlinkSync, readdirSync } from "node:fs";
import { homedir } from "node:os";
import { dirname, isAbsolute, join } from "node:path";
import { fileURLToPath } from "node:url";
import { buildAgentSeatSliceApplyScript, buildAgentSeatSliceVerifyLines } from "./agent-seat-slice.mjs";
import { buildSeatImplementerPrereqScript, buildSeatScopeEntryInstallScript } from "./seat-runtime.mjs";
import { buildIpForwardPrereqScript } from "./seat-proxy.mjs";
import { SEAT_SCOPE_ENTRY_BIN, SEAT_SCOPE_ENTRY_MODULE, SEAT_SCOPE_ENTRY_SUDOERS } from "./seat-constants.mjs";
import { homeRel, shellHomePath } from "./home-path.mjs";
import { probeMs, remoteTransport, shq, sshOpts, sshTarget } from "./remote-ssh.mjs";
import { canonicalJson, validateRuntimeReleaseV1 } from "./runtime-release.mjs";

export { homeRel, shellHomePath };

const HOME = homedir();
export const CLAUDE_MODULE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
export const CLAUDE_MANIFEST_PATH = join(CLAUDE_MODULE_ROOT, "..", "manifest", "claude.json");
export const CLAUDE_INSTALL_URL = "https://claude.ai/install.sh";
export const REMOTE_RUNTIME_BASE = ".local/share/overdeck/claude-runtime";
export const COPY_PROVENANCE_STATE_REL = ".local/state/overdeck/claude-runtime-copy";
export const SEATS_DIR = "seats";
export const MIN_DISK_GB = 5;
export const RUNTIME_COMPLETE_MARKER = ".runtime-complete";
export const CLAUDE_CODE_PROXY_VERSION = "0.1.22";
export const SEAT_PROXY_USER = "overdeck-seat-proxy";
export const SEAT_PROXY_LIB = "/var/lib/overdeck/seat-proxy";
export const SEAT_PROXY_UNIT = "overdeck-seat-proxy@.service";
export const SEAT_GUARD_BINARIES = ["claude", "claudex", "claude-code-proxy"];
export const SEAT_GUARD_ROOT = ".local/share/overdeck/seat-guard";
export const SEAT_PROXY_VALIDATE = "overdeck-seat-proxy-validate";

const VERSION_RE = /(\d+\.\d+\.\d+)/;
const S_IFMT = 0o170000;
const S_IFREG = 0o100000;
const S_IFDIR = 0o040000;
const S_IFLNK = 0o120000;
const SAFE_REV_RE = /^[0-9a-zA-Z][0-9a-zA-Z._-]*$/;
const SAFE_STAGING_SUFFIX_RE = /^[0-9a-zA-Z][0-9a-zA-Z._-]*$/;
const MANIFEST_PATH_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*(?:\/[A-Za-z0-9][A-Za-z0-9._-]*)*$/;
const MANIFEST_HOME_RE = /^\.claude$/;
const GITHUB_HOST = "github.com";
const GITHUB_OWNER_REPO_RE = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?\/[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;
const CONTROL_CHAR_RE = /[\x00-\x1f\x7f]/;

function assertSafeRev(rev) {
  const value = String(rev);
  if (!SAFE_REV_RE.test(value) || value.includes("..") || value.includes("/")) {
    throw new Error(`unsafe runtime revision: ${rev}`);
  }
  return value;
}

export function normalizeRemoteRoot(remoteRoot, home = HOME) {
  if (!remoteRoot) return "";
  const trimmed = String(remoteRoot).trim();
  if (trimmed === "~") return home;
  if (trimmed.startsWith("~/")) return join(home, trimmed.slice(2)).replace(/\/+$/, "");
  return trimmed.replace(/\/+$/, "");
}

export function seatsGcUnsafe(remoteRoot, home = HOME) {
  const seats = join(home, SEATS_DIR);
  const root = normalizeRemoteRoot(remoteRoot, home);
  if (!root) return true;
  return seats === root || seats.startsWith(`${root}/`);
}

export function tokenFingerprint(token) {
  return createHash("sha256").update(String(token)).digest("hex");
}

export function loadClaudeManifest(path = CLAUDE_MANIFEST_PATH) {
  const raw = JSON.parse(readFileSync(path, "utf8"));
  if (!raw?.home || !Array.isArray(raw.entries)) throw new Error(`invalid claude manifest: ${path}`);
  return validateManifest(raw);
}

export function validateManifestEntry(entry) {
  const path = String(entry?.path ?? "");
  const strategy = String(entry?.strategy ?? "");
  if (!path || path.startsWith("/") || path.includes("..") || CONTROL_CHAR_RE.test(path)) {
    throw new Error(`invalid manifest entry path: ${entry?.path}`);
  }
  if (!MANIFEST_PATH_RE.test(path)) throw new Error(`unsafe manifest entry path: ${path}`);
  if (strategy !== "symlink" && strategy !== "copy") throw new Error(`invalid manifest strategy: ${strategy}`);
  return { path, strategy };
}

export function validateManifest(manifest) {
  manifestHomeRel(manifest);
  return { ...manifest, entries: manifest.entries.map(validateManifestEntry) };
}

export function copyProvenanceRel(entryPath) {
  validateManifestEntry({ path: entryPath, strategy: "copy" });
  const digest = createHash("sha256").update(entryPath).digest("hex");
  return homeRel(COPY_PROVENANCE_STATE_REL, `${digest}.sha256`);
}

function normalizeInsideRoot(root, candidate) {
  const normRoot = join(root).replace(/\/+$/, "");
  const normCandidate = join(candidate);
  return normCandidate === normRoot || normCandidate.startsWith(`${normRoot}/`);
}

function walkRuntimeSource(absPath, relPath, root, deps, symlinkChain, errors) {
  const lstat = deps.lstatSync ?? lstatSync;
  const readlink = deps.readlinkSync ?? readlinkSync;
  const readdir = deps.readdirSync ?? readdirSync;
  let st;
  try {
    st = lstat(absPath);
  } catch {
    errors.push(`preflight:missing:${relPath || "."}`);
    return;
  }
  const mode = st.mode & S_IFMT;
  if (mode === S_IFLNK) {
    const linkAbs = join(absPath);
    if (symlinkChain.has(linkAbs)) {
      errors.push(`preflight:symlink-cycle:${relPath}`);
      return;
    }
    let link;
    try {
      link = readlink(absPath);
    } catch {
      errors.push(`preflight:symlink-unreadable:${relPath}`);
      return;
    }
    if (link.startsWith("/")) {
      errors.push(`preflight:absolute-symlink:${relPath}`);
      return;
    }
    const resolved = join(dirname(absPath), link);
    if (!normalizeInsideRoot(root, resolved)) {
      errors.push(`preflight:escape-symlink:${relPath}`);
      return;
    }
    const nextChain = new Set(symlinkChain);
    nextChain.add(linkAbs);
    walkRuntimeSource(resolved, relPath, root, deps, nextChain, errors);
    return;
  }
  if (mode === S_IFDIR) {
    for (const name of readdir(absPath).sort()) {
      if (name === "." || name === "..") continue;
      const childRel = relPath ? `${relPath}/${name}` : name;
      walkRuntimeSource(join(absPath, name), childRel, root, deps, symlinkChain, errors);
    }
    return;
  }
  if (mode === S_IFREG) return;
  errors.push(`preflight:special-file:${relPath}`);
}

export function preflightRuntimeManifest(repoRoot, manifest, deps = {}) {
  validateManifest(manifest);
  const errors = [];
  const entries = [...manifest.entries].sort((a, b) => a.path.localeCompare(b.path));
  for (const entry of entries) {
    const abs = join(repoRoot, entry.path);
    if (!normalizeInsideRoot(repoRoot, abs)) {
      errors.push(`preflight:outside-root:${entry.path}`);
      continue;
    }
    try {
      walkRuntimeSource(abs, entry.path, repoRoot, deps, new Set(), errors);
    } catch {
      errors.push(`preflight:missing:${entry.path}`);
    }
  }
  if (errors.length) return { ok: false, errors };
  return { ok: true };
}

export function normalizeGitHubOriginHttps(originUrl) {
  const raw = String(originUrl || "").trim();
  if (!raw) return null;
  let host = "";
  let pathname = "";
  let reject = false;
  const scpMatch = raw.match(/^(?:[^@/]+@)?([^:/]+):(.+)$/);
  if (scpMatch && !raw.includes("://")) {
    host = scpMatch[1].toLowerCase();
    pathname = scpMatch[2].replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
  } else {
    try {
      const parsed = new URL(raw.includes("://") ? raw : `https://${raw}`);
      if (parsed.username || parsed.password) reject = true;
      if (parsed.search || parsed.hash) reject = true;
      host = parsed.hostname.toLowerCase();
      pathname = parsed.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "");
    } catch {
      return null;
    }
  }
  if (reject || host !== GITHUB_HOST || !pathname) return null;
  const segments = pathname.split("/").filter(Boolean);
  if (segments.length !== 2) return null;
  const ownerRepo = `${segments[0].toLowerCase()}/${segments[1].toLowerCase()}`;
  if (!GITHUB_OWNER_REPO_RE.test(ownerRepo)) return null;
  return `https://${GITHUB_HOST}/${ownerRepo}`;
}

export function resolveGitLsRemoteUrl(repoRoot, deps = {}) {
  const spawn = deps.spawnSync ?? spawnSync;
  const result = spawn("git", ["-C", repoRoot, "remote", "get-url", "origin"], { encoding: "utf8" });
  if (result.status !== 0) return null;
  return normalizeGitHubOriginHttps(String(result.stdout).trim());
}

export function manifestHomeRel(manifest) {
  const raw = String(manifest?.home ?? "");
  if (!raw || CONTROL_CHAR_RE.test(raw) || /['"`\\]/.test(raw)) {
    throw new Error(`invalid manifest home: ${manifest?.home}`);
  }
  if (raw.startsWith("/") || (raw.startsWith("~") && !raw.startsWith("~/"))) {
    throw new Error(`invalid manifest home: ${raw}`);
  }
  const home = raw.replace(/^~\/+/, "").replace(/\/+$/, "");
  if (!home || home.includes("..") || !MANIFEST_HOME_RE.test(home)) {
    throw new Error(`invalid manifest home: ${manifest?.home}`);
  }
  return home;
}

export function runtimeDestRel(rev) {
  assertSafeRev(rev);
  return homeRel(REMOTE_RUNTIME_BASE, rev);
}

export function runtimeStagingRel(rev, suffix = "") {
  assertSafeRev(rev);
  if (suffix) {
    if (!SAFE_STAGING_SUFFIX_RE.test(suffix)) throw new Error(`unsafe staging suffix: ${suffix}`);
    return homeRel(REMOTE_RUNTIME_BASE, `.staging-${rev}-${suffix}`);
  }
  return homeRel(REMOTE_RUNTIME_BASE, `.staging-${rev}`);
}

export function makeRuntimeStagingSuffix(deps = {}) {
  const now = deps.now ?? Date.now;
  const pid = deps.pid ?? process.pid;
  return `${pid}-${now().toString(36)}`;
}

export function resolveAuthoritativeClaudeVersion(deps = {}) {
  const home = deps.home ?? HOME;
  const spawn = deps.spawnSync ?? spawnSync;
  const shim = join(home, ".claude", "bin", "claude");
  const candidates = [join(home, ".local", "bin", "claude")];
  const versionsDir = join(home, ".local", "share", "claude", "versions");
  if (existsSync(versionsDir)) {
    const sorted = readdirSync(versionsDir)
      .filter((name) => VERSION_RE.test(name))
      .sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
    if (sorted[0]) candidates.push(join(versionsDir, sorted[0]));
  }
  for (const bin of candidates) {
    if (!existsSync(bin)) continue;
    let resolved = bin;
    try { resolved = deps.realpathSync?.(bin) ?? bin; } catch {}
    if (resolved === shim) continue;
    const result = spawn(resolved, ["--version"], { encoding: "utf8" });
    if (result.status !== 0) continue;
    const match = String(result.stdout).match(VERSION_RE);
    if (match) return match[1];
  }
  return null;
}

export function readGhToken(deps = {}) {
  const spawn = deps.spawnSync ?? spawnSync;
  const result = spawn("gh", ["auth", "token", "-h", "github.com"], { encoding: "utf8" });
  if (result.status !== 0) return null;
  const token = String(result.stdout).trim();
  return token || null;
}

export function seatGuardBinRel(name) {
  return homeRel(SEAT_GUARD_ROOT, "current", "bin", name);
}

export function seatGuardCurrentRel() {
  return homeRel(SEAT_GUARD_ROOT, "current");
}

export function resolveAuthoritativeCcpVersion(deps = {}) {
  const home = deps.home ?? HOME;
  const spawn = deps.spawnSync ?? spawnSync;
  const candidates = [
    join(home, ".local", "bin", "claude-code-proxy"),
    "/usr/local/bin/claude-code-proxy",
  ];
  for (const bin of candidates) {
    if (!existsSync(bin)) continue;
    const result = spawn(bin, ["--version"], { encoding: "utf8" });
    if (result.status !== 0) continue;
    const match = String(result.stdout).match(VERSION_RE);
    if (match) return match[1];
  }
  return null;
}

export function verifyLocalCcpBinary(version = CLAUDE_CODE_PROXY_VERSION, deps = {}) {
  const home = deps.home ?? HOME;
  const spawn = deps.spawnSync ?? spawnSync;
  const exists = deps.existsSync ?? existsSync;
  const candidates = deps.ccpCandidates ?? [
    join(home, ".local", "bin", "claude-code-proxy"),
    "/usr/local/bin/claude-code-proxy",
  ];
  let mismatch = null;
  for (const bin of candidates) {
    if (!exists(bin)) continue;
    const result = spawn(bin, ["--version"], { encoding: "utf8" });
    if (result.status !== 0) continue;
    const match = String(result.stdout).match(VERSION_RE);
    if (!match) continue;
    if (match[1] === version) return { ok: true, path: bin, version: match[1] };
    if (!mismatch) mismatch = { found: match[1], path: bin };
  }
  if (mismatch) return { ok: false, reason: "local-ccp-version-mismatch", found: mismatch.found, path: mismatch.path };
  return { ok: false, reason: "local-ccp-missing" };
}

export function buildCcpInstallScript(version) {
  return [
    "set -euo pipefail",
    `want=${shq(version)}`,
    "export PATH=\"$HOME/.local/bin:/usr/local/bin:$PATH\"",
    "stage=\"$HOME/.local/bin/claude-code-proxy\"",
    "[ -x \"$stage\" ] || { echo ccp-missing; exit 1; }",
    "got=$(\"$stage\" --version 2>/dev/null | awk '{print $2}')",
    "[ \"$got\" = \"$want\" ] || { echo ccp-version:$got; exit 1; }",
    "sudo -n install -d -m 0755 /usr/local/bin",
    "sudo -n install -m 0755 \"$stage\" /usr/local/bin/claude-code-proxy",
    "installed=$(/usr/local/bin/claude-code-proxy --version 2>/dev/null | awk '{print $2}')",
    "[ \"$installed\" = \"$want\" ] || { echo ccp-version:$installed; exit 1; }",
    "[ -x /usr/local/bin/claude-code-proxy ]",
  ].join("\n");
}

export function buildCcpVerifyScript(version) {
  return [
    "set -euo pipefail",
    `want=${shq(version)}`,
    "[ -x /usr/local/bin/claude-code-proxy ] || { echo ccp-system-missing; exit 1; }",
    "got=$(/usr/local/bin/claude-code-proxy --version 2>/dev/null | awk '{print $2}')",
    "[ \"$got\" = \"$want\" ] || { echo ccp-version:$got; exit 1; }",
  ].join("\n");
}

export function buildSeatGuardInstallScript() {
  return [
    "set -euo pipefail",
    "guard=\"$HOME/.claude/bin/install-seat-guard\"",
    "[ -x \"$guard\" ] || { echo seat-guard-missing; exit 1; }",
    "\"$guard\"",
    "\"$guard\" --status",
  ].join("\n");
}

export function buildSeatProxyPrereqScript(unitBody) {
  const body = String(unitBody).trimEnd();
  return [
    "set -euo pipefail",
    `proxy_user=${shq(SEAT_PROXY_USER)}`,
    `proxy_lib=${shq(SEAT_PROXY_LIB)}`,
    "unit_dst=/etc/systemd/system/overdeck-seat-proxy@.service",
    `validate_dst=/usr/local/bin/${SEAT_PROXY_VALIDATE}`,
    "ssh_user=$(id -un)",
    "sudo -n true",
    "if ! id \"$proxy_user\" >/dev/null 2>&1; then",
    "  sudo -n useradd --system --user-group --home-dir \"$proxy_lib\" --shell /usr/sbin/nologin \"$proxy_user\"",
    "fi",
    "sudo -n install -d -o root -g root -m 0755 /var/lib/overdeck",
    `sudo -n install -d -o root -g ${shq(SEAT_PROXY_USER)} -m 0710 "$proxy_lib"`,
    "validate_src=\"$HOME/.claude/bin/overdeck-seat-proxy-validate\"",
    "[ -x \"$validate_src\" ] || { echo proxy-validate-missing; exit 1; }",
    "sudo -n install -m 0755 -o root -g root \"$validate_src\" \"$validate_dst\"",
    "unit_tmp=$(mktemp)",
    "cat <<'OD_SEAT_PROXY_UNIT' >\"$unit_tmp\"",
    body,
    "OD_SEAT_PROXY_UNIT",
    "sudo -n install -m 0644 -o root -g root \"$unit_tmp\" \"$unit_dst\"",
    "rm -f \"$unit_tmp\"",
    "sudo -n systemctl daemon-reload",
    "owner=$(stat -c '%U:%G' \"$proxy_lib\")",
    `[ \"$owner\" = \"root:${SEAT_PROXY_USER}\" ] || { echo proxy-lib-owner; exit 1; }`,
    "mode=$(stat -c '%a' \"$proxy_lib\")",
    "[ \"$mode\" = \"710\" ] || { echo proxy-lib-mode; exit 1; }",
    "systemctl cat overdeck-seat-proxy@.service >/dev/null 2>&1 || { echo proxy-unit-missing; exit 1; }",
    "grep -q 'EnvironmentFile=/var/lib/overdeck/seat-proxy/%i/seat-proxy.env' \"$unit_dst\" || { echo proxy-unit-env-file; exit 1; }",
    "grep -q 'ExecStartPre=+/usr/local/bin/overdeck-seat-proxy-validate' \"$unit_dst\" || { echo proxy-unit-validate; exit 1; }",
    "grep -q 'Environment=CCP_CODEX_MODEL=' \"$unit_dst\" && { echo proxy-unit-ccp-model; exit 1; }",
    "[ -x \"$validate_dst\" ] || { echo proxy-validate-missing; exit 1; }",
    "sudo -n -u \"$proxy_user\" test -x \"$proxy_lib\" || { echo proxy-not-traversable-proxy; exit 1; }",
    "sudo -n -u \"$proxy_user\" test ! -r \"$proxy_lib\" || { echo proxy-readable-proxy; exit 1; }",
    "sudo -n -u \"$ssh_user\" test ! -x \"$proxy_lib\" || { echo proxy-traversable-ssh; exit 1; }",
    "sudo -n -u \"$ssh_user\" test ! -r \"$proxy_lib\" || { echo proxy-readable-ssh; exit 1; }",
    buildIpForwardPrereqScript(),
    buildSeatImplementerPrereqScript(),
    buildSeatScopeEntryInstallScript(),
  ].join("\n");
}

export function validateManifestSourcesForRsync(repoRoot, manifest, deps = {}) {
  validateManifest(manifest);
  const errors = [];
  const lstat = deps.lstatSync ?? lstatSync;
  for (const entry of manifest.entries) {
    const src = join(repoRoot, entry.path);
    if (!normalizeInsideRoot(repoRoot, src)) {
      errors.push(`rsync:outside-root:${entry.path}`);
      continue;
    }
    let st;
    try {
      st = lstat(src);
    } catch {
      errors.push(`rsync:missing:${entry.path}`);
      continue;
    }
    const mode = st.mode & S_IFMT;
    if (mode === S_IFLNK || mode === S_IFREG) continue;
    if (mode === S_IFDIR) continue;
    errors.push(`rsync:special-file:${entry.path}`);
  }
  if (errors.length) return { ok: false, errors };
  return { ok: true };
}

export const RUNTIME_SUBSET_VALIDATOR_PY = `import os
import stat
import sys

def fail(msg):
    print(msg, file=sys.stderr)
    sys.exit(1)

def symlink_resolves_inside(root, link_path, link_text):
    parent = os.path.dirname(os.path.abspath(link_path))
    resolved = os.path.normpath(os.path.join(parent, link_text))
    root = os.path.abspath(root)
    return resolved == root or resolved.startswith(root + os.sep)

def validate(quarantine_root, target_root):
    qroot = os.path.abspath(quarantine_root)
    troot = os.path.abspath(target_root)
    if not os.path.isdir(qroot):
        fail("subset:not-a-directory")
    if not os.path.exists(troot):
        fail("subset:target-missing")
    for dirpath, dirnames, filenames in os.walk(qroot, topdown=True, followlinks=False):
        rel_dir = os.path.relpath(dirpath, qroot)
        if rel_dir == ".":
            rel_dir = ""
        descend = []
        for name in sorted(set(dirnames) | set(filenames)):
            rel = name if not rel_dir else f"{rel_dir}/{name}"
            qpath = os.path.join(dirpath, name)
            try:
                qst = os.lstat(qpath)
            except OSError:
                fail(f"subset:unreadable:{rel}")
            tpath = os.path.join(troot, rel)
            try:
                tst = os.lstat(tpath)
            except FileNotFoundError:
                fail(f"subset:unknown:{rel}")
            except OSError:
                fail(f"subset:target-unreadable:{rel}")
            qmode = stat.S_IFMT(qst.st_mode)
            tmode = stat.S_IFMT(tst.st_mode)
            if qmode != tmode:
                fail(f"subset:type-mismatch:{rel}")
            if qmode == stat.S_IFREG:
                if qst.st_size != tst.st_size:
                    fail(f"subset:content-mismatch:{rel}")
                with open(qpath, "rb") as qf, open(tpath, "rb") as tf:
                    while True:
                        qb = qf.read(1024 * 1024)
                        tb = tf.read(1024 * 1024)
                        if qb != tb:
                            fail(f"subset:content-mismatch:{rel}")
                        if not qb:
                            break
            elif qmode == stat.S_IFLNK:
                try:
                    qlink = os.readlink(qpath)
                    tlink = os.readlink(tpath)
                except OSError:
                    fail(f"subset:symlink-unreadable:{rel}")
                if qlink != tlink:
                    fail(f"subset:symlink-mismatch:{rel}")
                if not symlink_resolves_inside(qroot, qpath, qlink):
                    fail(f"subset:unsafe-symlink:{rel}")
            elif qmode == stat.S_IFDIR:
                descend.append(name)
            else:
                fail(f"subset:special-file:{rel}")
        dirnames[:] = descend

if __name__ == "__main__":
    if len(sys.argv) != 3:
        fail("subset:usage")
    validate(sys.argv[1], sys.argv[2])
`;

function gitTreeMode(st) {
  if (st.isSymbolicLink()) return "120000";
  if (st.isFile()) return (st.mode & 0o111) ? "100755" : "100644";
  return null;
}

function hashManifestEntryTree(hash, absPath, relPath, deps = {}) {
  const read = deps.readFileSync ?? readFileSync;
  const readdir = deps.readdirSync ?? readdirSync;
  const lstat = deps.lstatSync ?? lstatSync;
  const readlink = deps.readlinkSync ?? readlinkSync;
  let st;
  try { st = lstat(absPath); } catch { return; }
  if (st.isSymbolicLink()) {
    hash.update(`${relPath}\0blob\0${gitTreeMode(st)}\0`);
    try { hash.update(readlink(absPath)); } catch {}
    return;
  }
  if (st.isDirectory()) {
    for (const name of readdir(absPath).sort()) {
      hashManifestEntryTree(hash, join(absPath, name), `${relPath}/${name}`, deps);
    }
    return;
  }
  if (st.isFile()) {
    hash.update(`${relPath}\0blob\0${gitTreeMode(st)}\0`);
    hash.update(read(absPath));
  }
}

export function manifestRuntimeFingerprint(repoRoot, manifest, deps = {}) {
  const hash = createHash("sha256");
  const entries = [...manifest.entries].sort((a, b) => a.path.localeCompare(b.path));
  for (const entry of entries) {
    const abs = join(repoRoot, entry.path);
    hash.update(`${entry.path}\0${entry.strategy}\0`);
    if (!existsSync(abs)) continue;
    hashManifestEntryTree(hash, abs, entry.path, deps);
  }
  return hash.digest("hex").slice(0, 12);
}

function manifestRuntimeFingerprintFromGit(repoRoot, manifest, deps = {}) {
  const spawn = deps.spawnSync ?? spawnSync;
  const hash = createHash("sha256");
  const entries = [...manifest.entries].sort((a, b) => a.path.localeCompare(b.path));
  for (const entry of entries) {
    hash.update(`${entry.path}\0${entry.strategy}\0`);
    const ls = spawn(
      "git",
      ["-C", repoRoot, "ls-tree", "-r", "-z", "HEAD", "--", entry.path],
      { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 },
    );
    if (ls.status !== 0) continue;
    for (const line of String(ls.stdout).split("\0").filter(Boolean)) {
      const tab = line.indexOf("\t");
      if (tab < 0) continue;
      const [mode, type, objHash] = line.slice(0, tab).split(/\s+/);
      const relPath = line.slice(tab + 1);
      hash.update(`${relPath}\0${type}\0${mode}\0`);
      if (type === "blob") {
        const blob = spawn("git", ["-C", repoRoot, "cat-file", "blob", objHash], {
          encoding: "buffer",
          maxBuffer: 64 * 1024 * 1024,
        });
        if (blob.status === 0) hash.update(blob.stdout);
      }
    }
  }
  return hash.digest("hex").slice(0, 12);
}

export function runtimeRevision(repoRoot = CLAUDE_MODULE_ROOT, deps = {}) {
  const spawn = deps.spawnSync ?? spawnSync;
  const manifest = deps.manifest ?? loadClaudeManifest(deps.manifestPath);
  validateManifest(manifest);
  const git = spawn("git", ["-C", repoRoot, "rev-parse", "--short", "HEAD"], { encoding: "utf8" });
  if (git.status !== 0) {
    const fp = manifestRuntimeFingerprint(repoRoot, manifest, deps);
    const tree = spawn("git", ["-C", repoRoot, "write-tree"], { encoding: "utf8" });
    if (tree.status === 0) {
      return `tree-${String(tree.stdout).trim().slice(0, 12)}-d-${fp}`;
    }
    return `content-${fp}`;
  }
  const head = String(git.stdout).trim();
  const working = manifestRuntimeFingerprint(repoRoot, manifest, deps);
  const committed = manifestRuntimeFingerprintFromGit(repoRoot, manifest, deps);
  return working === committed ? head : `${head}-d-${working}`;
}

export function buildPackageInstallScript() {
  return [
    "set -euo pipefail",
    "need=0",
    "command -v tmux >/dev/null 2>&1 || need=1",
    "command -v gh >/dev/null 2>&1 || need=1",
    "command -v curl >/dev/null 2>&1 || need=1",
    "command -v bwrap >/dev/null 2>&1 || need=1",
    "command -v socat >/dev/null 2>&1 || need=1",
    "if [ \"$need\" -eq 1 ]; then",
    "  sudo -n true",
    "  sudo -n apt-get update -qq",
    "  debian_pkgs=\"\"",
    "  command -v tmux >/dev/null 2>&1 || debian_pkgs=\"$debian_pkgs tmux\"",
    "  command -v curl >/dev/null 2>&1 || debian_pkgs=\"$debian_pkgs curl\"",
    "  command -v bwrap >/dev/null 2>&1 || debian_pkgs=\"$debian_pkgs bubblewrap\"",
    "  command -v socat >/dev/null 2>&1 || debian_pkgs=\"$debian_pkgs socat\"",
    "  debian_pkgs=${debian_pkgs# }",
    "  if [ -n \"$debian_pkgs\" ]; then",
    "    sudo -n DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $debian_pkgs",
    "  fi",
    "  if ! command -v gh >/dev/null 2>&1; then",
    "    gh_cand=$(apt-cache policy gh 2>/dev/null | awk '/Candidate:/ {print $2}')",
    "    if [ -n \"$gh_cand\" ] && [ \"$gh_cand\" != \"(none)\" ]; then",
    "      sudo -n DEBIAN_FRONTEND=noninteractive apt-get install -y -qq gh",
    "    else",
    "      sudo -n install -d -m 0755 /etc/apt/keyrings",
    "      curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo -n tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null",
    "      sudo -n chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg",
    "      arch=$(dpkg --print-architecture)",
    "      printf 'deb [arch=%s signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\\n' \"$arch\" | sudo -n tee /etc/apt/sources.list.d/github-cli.list >/dev/null",
    "      sudo -n apt-get update -qq",
    "      sudo -n DEBIAN_FRONTEND=noninteractive apt-get install -y -qq gh",
    "    fi",
    "  fi",
    "fi",
    "command -v tmux >/dev/null",
    "command -v gh >/dev/null",
    "command -v curl >/dev/null",
    "command -v bwrap >/dev/null || { echo bubblewrap-missing; exit 1; }",
    "command -v socat >/dev/null || { echo socat-missing; exit 1; }",
  ].join("\n");
}

export function buildClaudeInstallScript(version) {
  return [
    "set -euo pipefail",
    `want=${shq(version)}`,
    "export PATH=\"$HOME/.local/bin:$PATH\"",
    "have=\"\"",
    "if command -v claude >/dev/null 2>&1; then",
    "  bin=$(command -v claude)",
    "  case \"$bin\" in \"$HOME/.claude/bin/\"*) bin=\"\" ;; esac",
    "  if [ -n \"$bin\" ]; then have=$(\"$bin\" --version 2>/dev/null | awk '{print $1}'); fi",
    "fi",
    "if [ \"$have\" != \"$want\" ]; then",
    `  curl -fsSL ${shq(CLAUDE_INSTALL_URL)} | bash -s -- \"$want\"`,
    "fi",
    "bin=$(command -v claude)",
    "case \"$bin\" in \"$HOME/.claude/bin/\"*) echo claude-shim >&2; exit 1 ;; esac",
    "[ -n \"$bin\" ]",
    "got=$(\"$bin\" --version 2>/dev/null | awk '{print $1}')",
    "[ \"$got\" = \"$want\" ]",
  ].join("\n");
}

export function buildGhAuthScript(expectedFingerprint) {
  const lines = [
    "set +x",
    "set -euo pipefail",
    "IFS= read -r token",
    "[ -n \"$token\" ]",
  ];
  if (expectedFingerprint) {
    lines.push(
      "if gh auth status -h github.com >/dev/null 2>&1; then",
      "  fp=$(printf '%s' \"$(gh auth token -h github.com)\" | sha256sum | awk '{print $1}')",
      `  [ \"$fp\" = ${shq(expectedFingerprint)} ] && exit 0`,
      "fi",
    );
  } else {
    lines.push("gh auth status -h github.com >/dev/null 2>&1 && exit 0");
  }
  lines.push(
    "printf '%s\\n' \"$token\" | gh auth login --with-token -h github.com",
    "gh auth status -h github.com >/dev/null",
  );
  return lines.join("\n");
}

function buildGhGitHelperVerifyLines() {
  return [
    "gh_git_helper_ok=0",
    "if git config --global --get-all credential.https://github.com.helper 2>/dev/null | grep -q 'gh auth git-credential'; then",
    "  gh_git_helper_ok=1",
    "elif git config --global credential.helper 2>/dev/null | grep -q gh; then",
    "  gh_git_helper_ok=1",
    "fi",
    "[ \"$gh_git_helper_ok\" -eq 1 ] || { echo gh-git-helper-missing; exit 1; }",
  ];
}

export function buildGhGitAuthScript(expectedFingerprint, gitLsRemoteUrl) {
  const lines = [
    "set -euo pipefail",
    "gh auth status -h github.com >/dev/null",
    "gh api --hostname github.com user >/dev/null",
    "gh auth setup-git -h github.com",
    ...buildGhGitHelperVerifyLines(),
  ];
  if (gitLsRemoteUrl) {
    lines.push(`git ls-remote ${shq(gitLsRemoteUrl)} HEAD >/dev/null`);
  }
  if (expectedFingerprint) {
    lines.push(
      "fp=$(printf '%s' \"$(gh auth token -h github.com)\" | sha256sum | awk '{print $1}')",
      `[ \"$fp\" = ${shq(expectedFingerprint)} ] || { echo gh-token-mismatch; exit 1; }`,
    );
  }
  return lines.join("\n");
}

function buildRuntimeSubsetValidatorShell(entryPath) {
  return [
    "if ! runtime_subset_validate \"$quarantine\" \"$target\"; then",
    "  if [ ! -e \"$link\" ]; then",
    "    mv -T \"$quarantine\" \"$link\"",
    "  fi",
    `  echo link-collision:${entryPath} >&2`,
    "  exit 1",
    "fi",
  ];
}

function buildRuntimeSubsetValidatorDefinition() {
  return [
    "runtime_subset_validate() {",
    "  python3 - \"$1\" \"$2\" <<'OD_CLAUDE_RUNTIME_SUBSET'",
    RUNTIME_SUBSET_VALIDATOR_PY.trimEnd(),
    "OD_CLAUDE_RUNTIME_SUBSET",
    "}",
  ];
}

export function buildRuntimeDescriptorVerifyScript(descriptor, releaseRoot) {
  const release = validateRuntimeReleaseV1(descriptor);
  if (typeof releaseRoot !== "string" || !isAbsolute(releaseRoot)) {
    throw new Error("runtime release root must be an absolute path");
  }
  const digest = release.artifactSha256;
  const descriptorBase64 = Buffer.from(`${canonicalJson(release)}\n`).toString("base64");
  return [
    "set -euo pipefail",
    `root=${shq(releaseRoot)}`,
    `want=${shq(digest)}`,
    `descriptor_b64=${shq(descriptorBase64)}`,
    "[ -d \"$root\" ] || { echo runtime-root-missing >&2; exit 1; }",
    "target=$(readlink \"$root/current\")",
    "[ \"$target\" = \"releases/$want/runtime\" ] || { echo runtime-release-mismatch >&2; exit 1; }",
    "release=\"$root/releases/$want\"",
    "[ -d \"$release/runtime\" ] || { echo runtime-release-missing >&2; exit 1; }",
    "[ -f \"$release/descriptor.json\" ] || { echo runtime-descriptor-missing >&2; exit 1; }",
    "node -e 'const fs=require(\"node:fs\");const [p,b]=process.argv.slice(1);if(!fs.readFileSync(p).equals(Buffer.from(b,\"base64\")))process.exit(1)' \"$release/descriptor.json\" \"$descriptor_b64\" || { echo runtime-descriptor-mismatch >&2; exit 1; }",
  ].join("\n");
}

export function buildRuntimeVerifyScript(manifest, rev) {
  validateManifest(manifest);
  const homeRelPath = manifestHomeRel(manifest);
  const destRel = runtimeDestRel(rev);
  const lines = [
    "set -euo pipefail",
    `dest=${shellHomePath(destRel)}`,
    `marker=${shellHomePath(homeRel(destRel, RUNTIME_COMPLETE_MARKER))}`,
    "[ -d \"$dest\" ]",
    "[ -f \"$marker\" ]",
  ];
  for (const entry of manifest.entries) {
    const targetRel = homeRel(destRel, entry.path);
    const linkRel = homeRel(homeRelPath, entry.path);
    lines.push(`target=${shellHomePath(targetRel)}`);
    lines.push(`link=${shellHomePath(linkRel)}`);
    if (entry.strategy === "symlink") {
      lines.push(`[ -e "$target" ] || { echo missing-runtime:${entry.path} >&2; exit 1; }`);
      lines.push(`[ -L "$link" ] || { echo bad-link:${entry.path} >&2; exit 1; }`);
      lines.push(`[ "$(readlink -f "$link")" = "$(readlink -f "$target")" ] || { echo bad-target:${entry.path} >&2; exit 1; }`);
    } else if (entry.strategy === "copy") {
      lines.push(`[ -f "$target" ] || { echo missing-runtime:${entry.path} >&2; exit 1; }`);
      lines.push(`[ -f "$link" ] || { echo bad-copy:${entry.path} >&2; exit 1; }`);
      lines.push(`cmp -s "$target" "$link" || { echo bad-copy:${entry.path} >&2; exit 1; }`);
      lines.push(`prov=${shellHomePath(copyProvenanceRel(entry.path))}`);
      lines.push(`[ -f "$prov" ] || { echo bad-provenance:${entry.path} >&2; exit 1; }`);
      lines.push(`link_fp=$(sha256sum "$link" | awk '{print $1}')`);
      lines.push(`prov_fp=$(tr -d '\\n' <"$prov")`);
      lines.push(`[ "$link_fp" = "$prov_fp" ] || { echo bad-provenance:${entry.path} >&2; exit 1; }`);
    } else {
      lines.push(`echo unknown-strategy:${entry.strategy} >&2; exit 1`);
    }
  }
  return lines.join("\n");
}

export function buildRuntimeLinkScript(manifest, rev) {
  validateManifest(manifest);
  const homeRelPath = manifestHomeRel(manifest);
  const destRel = runtimeDestRel(rev);
  const managedRootRel = REMOTE_RUNTIME_BASE;
  const lines = [
    "set -euo pipefail",
    ...buildRuntimeSubsetValidatorDefinition(),
    `dest=${shellHomePath(destRel)}`,
    `home=${shellHomePath(homeRelPath)}`,
    `managed_root=${shellHomePath(managedRootRel)}/`,
    "mkdir -p \"$home\" \"$dest\"",
  ];
  for (const entry of manifest.entries) {
    const targetRel = homeRel(destRel, entry.path);
    const linkRel = homeRel(homeRelPath, entry.path);
    lines.push(`target=${shellHomePath(targetRel)}`);
    lines.push(`link=${shellHomePath(linkRel)}`);
    lines.push("mkdir -p \"$(dirname \"$link\")\"");
    if (entry.strategy === "symlink") {
      lines.push(`[ -e "$target" ] || { echo missing-runtime:${entry.path} >&2; exit 1; }`);
      lines.push("quarantine=\"\"");
      lines.push(`if [ -L "$link" ]; then`);
      lines.push(`  case "$(readlink "$link")" in "$managed_root"*) ;; *) echo link-collision:${entry.path} >&2; exit 1 ;; esac`);
      lines.push(`elif [ -e "$link" ]; then`);
      lines.push(`  if [ -d "$link" ]; then`);
      lines.push(`    if rmdir -- "$link" 2>/dev/null; then`);
      lines.push(`      :`);
      lines.push(`    else`);
      lines.push(`      quarantine="\${link}.quarantine.\$\$"`);
      lines.push(`      if ! mv -T "$link" "$quarantine"; then`);
      lines.push(`        echo link-collision:${entry.path} >&2; exit 1`);
      lines.push(`      fi`);
      for (const line of buildRuntimeSubsetValidatorShell(entry.path)) {
        lines.push(`      ${line}`);
      }
      lines.push(`    fi`);
      lines.push(`  elif [ -f "$link" ] && [ -f "$target" ]; then`);
      lines.push(`    quarantine="\${link}.quarantine.\$\$"`);
      lines.push(`    if ! mv -T "$link" "$quarantine"; then`);
      lines.push(`      echo link-collision:${entry.path} >&2; exit 1`);
      lines.push(`    fi`);
      lines.push(`    if ! cmp -s "$quarantine" "$target"; then`);
      lines.push(`      mv -T "$quarantine" "$link" || true`);
      lines.push(`      echo link-collision:${entry.path} >&2; exit 1`);
      lines.push(`    fi`);
      lines.push(`  else`);
      lines.push(`    echo link-collision:${entry.path} >&2; exit 1`);
      lines.push(`  fi`);
      lines.push("fi");
      lines.push(`if [ -n "$quarantine" ] && [ -e "$link" ]; then`);
      lines.push(`  echo link-collision:${entry.path} >&2; exit 1`);
      lines.push("fi");
      lines.push(`if ! ln -sfnT "$target" "$link"; then`);
      lines.push(`  if [ -n "$quarantine" ] && [ -e "$quarantine" ] && [ ! -e "$link" ]; then`);
      lines.push(`    mv -T "$quarantine" "$link" || true`);
      lines.push(`  fi`);
      lines.push(`  echo link-collision:${entry.path} >&2; exit 1`);
      lines.push("fi");
      lines.push(`if [ -n "$quarantine" ] && [ -e "$quarantine" ]; then`);
      lines.push(`  rm -rf "$quarantine"`);
      lines.push("fi");
    } else if (entry.strategy === "copy") {
      lines.push(`prov=${shellHomePath(copyProvenanceRel(entry.path))}`);
      lines.push(`[ -f "$target" ] || { echo missing-runtime:${entry.path} >&2; exit 1; }`);
      lines.push(`if [ -d "$link" ] || [ -L "$link" ]; then echo link-collision:${entry.path} >&2; exit 1; fi`);
      lines.push(`if [ -f "$link" ] && ! cmp -s "$target" "$link"; then`);
      lines.push(`  if [ ! -f "$prov" ]; then echo link-collision:${entry.path} >&2; exit 1; fi`);
      lines.push(`  link_fp=$(sha256sum "$link" | awk '{print $1}')`);
      lines.push(`  prov_fp=$(tr -d '\\n' <"$prov")`);
      lines.push(`  [ "$link_fp" = "$prov_fp" ] || { echo link-collision:${entry.path} >&2; exit 1; }`);
      lines.push("fi");
      lines.push("publish_tmp=\"${link}.publish.$$\"");
      lines.push("cp -a \"$target\" \"$publish_tmp\"");
      lines.push("mv -T \"$publish_tmp\" \"$link\"");
      lines.push("mkdir -p \"$(dirname \"$prov\")\"");
      lines.push("link_fp=$(sha256sum \"$link\" | awk '{print $1}')");
      lines.push("prov_tmp=\"${prov}.tmp.$$\"");
      lines.push("printf '%s\\n' \"$link_fp\" >\"$prov_tmp\"");
      lines.push("mv -T \"$prov_tmp\" \"$prov\"");
    } else {
      lines.push(`echo unknown-strategy:${entry.strategy} >&2; exit 1`);
    }
  }
  return lines.join("\n");
}

export function buildRuntimeFinalizeScript(rev, stagingSuffix) {
  if (!stagingSuffix) throw new Error("staging suffix required");
  const stagingRel = runtimeStagingRel(rev, stagingSuffix);
  const destRel = runtimeDestRel(rev);
  const markerName = RUNTIME_COMPLETE_MARKER;
  return [
    "set -euo pipefail",
    `staging=${shellHomePath(stagingRel)}`,
    `dest=${shellHomePath(destRel)}`,
    `marker=${shq(markerName)}`,
    "[ -d \"$staging\" ]",
    "if [ -f \"$dest/$marker\" ]; then rm -rf \"$staging\"; exit 0; fi",
    "if [ -e \"$dest\" ]; then echo dest-incomplete >&2; exit 1; fi",
    "touch \"$staging/$marker\"",
    "[ -f \"$staging/$marker\" ]",
    "if mv -T \"$staging\" \"$dest\"; then exit 0; fi",
    "if [ -f \"$dest/$marker\" ]; then rm -rf \"$staging\"; exit 0; fi",
    "echo dest-incomplete >&2",
    "exit 1",
  ].join("\n");
}

export function buildHeadlessGuardScript() {
  return [
    "set -euo pipefail",
    "guard=\"$HOME/.claude/bin/install-headless-guard\"",
    "[ -x \"$guard\" ] || { echo guard-missing >&2; exit 1; }",
    "\"$guard\"",
    "\"$guard\" --status",
  ].join("\n");
}

export function buildSeatDoctorScript({
  version,
  rev,
  remoteRoot,
  minDiskGb = MIN_DISK_GB,
  ghTokenFingerprint,
  manifest,
  gitLsRemoteUrl,
  ccpVersion = CLAUDE_CODE_PROXY_VERSION,
}) {
  if (manifest) validateManifest(manifest);
  const normalizedRoot = normalizeRemoteRoot(remoteRoot);
  const lines = [
    "set -euo pipefail",
    `want=${shq(version)}`,
    `ccp_want=${shq(ccpVersion)}`,
    `rev=${shq(rev)}`,
    `remote_root=${shq(normalizedRoot)}`,
    `min_gb=${minDiskGb}`,
    "seats=\"$HOME/seats\"",
    "case \"$seats\" in \"$remote_root\"|\"$remote_root/\"*) echo seats-in-gc-scope; exit 1 ;; esac",
    "avail=$(df -BG --output=avail \"$HOME\" | tail -1 | tr -dc 0-9)",
    "[ -n \"$avail\" ] && [ \"$avail\" -ge \"$min_gb\" ] || { echo disk-low:$avail; exit 1; }",
    "command -v tmux >/dev/null || { echo tmux-missing; exit 1; }",
    "command -v bwrap >/dev/null || { echo bubblewrap-missing; exit 1; }",
    "command -v socat >/dev/null || { echo socat-missing; exit 1; }",
    "command -v gh >/dev/null || { echo gh-missing; exit 1; }",
    "gh auth status -h github.com >/dev/null 2>&1 || { echo gh-auth; exit 1; }",
    "gh api --hostname github.com user >/dev/null || { echo gh-api-user; exit 1; }",
    ...buildGhGitHelperVerifyLines(),
  ];
  if (gitLsRemoteUrl) {
    lines.push(`git ls-remote ${shq(gitLsRemoteUrl)} HEAD >/dev/null || { echo git-ls-remote; exit 1; }`);
  }
  if (ghTokenFingerprint) {
    lines.push(
      "fp=$(printf '%s' \"$(gh auth token -h github.com)\" | sha256sum | awk '{print $1}')",
      `[ \"$fp\" = ${shq(ghTokenFingerprint)} ] || { echo gh-token-mismatch; exit 1; }`,
    );
  }
  lines.push(
    "export PATH=\"$HOME/.local/bin:$PATH\"",
    "bin=$(command -v claude || true)",
    "case \"$bin\" in \"$HOME/.claude/bin/\"*) echo claude-shim; exit 1 ;; esac",
    "[ -n \"$bin\" ] || { echo claude-missing; exit 1; }",
    "got=$(\"$bin\" --version 2>/dev/null | awk '{print $1}')",
    "[ \"$got\" = \"$want\" ] || { echo claude-version:$got; exit 1; }",
    "[ -x /usr/local/bin/claude-code-proxy ] || { echo ccp-system-missing; exit 1; }",
    "ccp_got=$(/usr/local/bin/claude-code-proxy --version 2>/dev/null | awk '{print $2}')",
    "[ \"$ccp_got\" = \"$ccp_want\" ] || { echo ccp-version:$ccp_got; exit 1; }",
    `dest=${shellHomePath(runtimeDestRel(rev))}`,
    `marker=${shellHomePath(homeRel(runtimeDestRel(rev), RUNTIME_COMPLETE_MARKER))}`,
    "[ -d \"$dest\" ] || { echo runtime-missing; exit 1; }",
    "[ -f \"$marker\" ] || { echo runtime-incomplete; exit 1; }",
  );
  if (manifest) lines.push(buildRuntimeVerifyScript(manifest, rev).split("\n").slice(1).join("\n"));
  lines.push(
    "guard=\"$HOME/.claude/bin/install-headless-guard\"",
    "[ -x \"$guard\" ] || { echo guard-missing; exit 1; }",
    "\"$guard\" --status >/dev/null || { echo guard-unwrapped; exit 1; }",
    "seat_guard=\"$HOME/.claude/bin/install-seat-guard\"",
    "[ -x \"$seat_guard\" ] || { echo seat-guard-missing; exit 1; }",
    `guard_current=${shellHomePath(seatGuardCurrentRel())}`,
    `guard_bin=${shellHomePath(homeRel(SEAT_GUARD_ROOT, "current", "bin"))}`,
    "[ -L \"$guard_current\" ] || { echo seat-guard-current-missing; exit 1; }",
    "for _sg_name in claude claudex claude-code-proxy; do",
    "  [ -x \"$guard_bin/$_sg_name\" ] || { echo seat-guard-wrapper-missing; exit 1; }",
    "  grep -qF 'overdeck-seat-guard v' \"$guard_bin/$_sg_name\" || { echo seat-guard-wrapper-missing; exit 1; }",
    "  grep -qE '(^|[[:space:]])exec[[:space:]]' \"$guard_bin/$_sg_name\" && { echo seat-guard-not-block-only; exit 1; }",
    "  grep -qF 'targets.json' \"$guard_bin/$_sg_name\" && { echo seat-guard-not-block-only; exit 1; }",
    "done",
    "\"$seat_guard\" --status >/dev/null || { echo seat-guard-unwrapped; exit 1; }",
    `proxy_user=${shq(SEAT_PROXY_USER)}`,
    `proxy_lib=${shq(SEAT_PROXY_LIB)}`,
    `validate_dst=/usr/local/bin/${SEAT_PROXY_VALIDATE}`,
    "ssh_user=$(id -un)",
    "id \"$proxy_user\" >/dev/null 2>&1 || { echo proxy-user-missing; exit 1; }",
    "[ -d \"$proxy_lib\" ] || { echo proxy-lib-missing; exit 1; }",
    "owner=$(stat -c '%U:%G' \"$proxy_lib\")",
    `[ \"$owner\" = \"root:${SEAT_PROXY_USER}\" ] || { echo proxy-lib-owner; exit 1; }`,
    "mode=$(stat -c '%a' \"$proxy_lib\")",
    "[ \"$mode\" = \"710\" ] || { echo proxy-lib-mode; exit 1; }",
    "sudo -n -u \"$proxy_user\" test -x \"$proxy_lib\" || { echo proxy-not-traversable-proxy; exit 1; }",
    "sudo -n -u \"$proxy_user\" test ! -r \"$proxy_lib\" || { echo proxy-readable-proxy; exit 1; }",
    "sudo -n -u \"$ssh_user\" test ! -x \"$proxy_lib\" || { echo proxy-traversable-ssh; exit 1; }",
    "sudo -n -u \"$ssh_user\" test ! -r \"$proxy_lib\" || { echo proxy-readable-ssh; exit 1; }",
    "[ -x \"$validate_dst\" ] || { echo proxy-validate-missing; exit 1; }",
    "[ -x /usr/local/bin/claude-code-proxy ] || { echo ccp-system-missing; exit 1; }",
    "systemctl cat overdeck-seat-proxy@.service >/dev/null 2>&1 || { echo proxy-unit-missing; exit 1; }",
    "unit=$(systemctl cat overdeck-seat-proxy@.service 2>/dev/null || true)",
    "echo \"$unit\" | grep -q 'EnvironmentFile=/var/lib/overdeck/seat-proxy/%i/seat-proxy.env' || { echo proxy-unit-env-file; exit 1; }",
    "echo \"$unit\" | grep -q 'ExecStartPre=+/usr/local/bin/overdeck-seat-proxy-validate' || { echo proxy-unit-validate; exit 1; }",
    "echo \"$unit\" | grep -q 'Environment=CCP_CODEX_MODEL=' && { echo proxy-unit-ccp-model; exit 1; }",
    ...buildAgentSeatSliceVerifyLines(),
    `[ -x ${shq(SEAT_SCOPE_ENTRY_BIN)} ] || { echo seat-scope-entry-missing; exit 1; }`,
    `scope_mode=$(stat -c '%a' ${shq(SEAT_SCOPE_ENTRY_BIN)})`,
    "[ \"$scope_mode\" = \"755\" ] || { echo seat-scope-entry-mode; exit 1; }",
    `scope_owner=$(stat -c '%u:%g' ${shq(SEAT_SCOPE_ENTRY_BIN)})`,
    "[ \"$scope_owner\" = \"0:0\" ] || { echo seat-scope-entry-owner; exit 1; }",
    `[ -f ${shq(SEAT_SCOPE_ENTRY_MODULE)} ] || { echo seat-scope-module-missing; exit 1; }`,
    `module_mode=$(stat -c '%a' ${shq(SEAT_SCOPE_ENTRY_MODULE)})`,
    "[ \"$module_mode\" = \"644\" ] || { echo seat-scope-module-mode; exit 1; }",
    `module_owner=$(stat -c '%u:%g' ${shq(SEAT_SCOPE_ENTRY_MODULE)})`,
    "[ \"$module_owner\" = \"0:0\" ] || { echo seat-scope-module-owner; exit 1; }",
    `rule_file=${shq(SEAT_SCOPE_ENTRY_SUDOERS)}`,
    "[ -f \"$rule_file\" ] || { echo seat-scope-sudoers-missing; exit 1; }",
    `sudo -n ${shq(SEAT_SCOPE_ENTRY_BIN)} --install-check || { echo seat-scope-install-check; exit 1; }`,
    `sudo -n -l 2>/dev/null | grep -Fq ${shq(SEAT_SCOPE_ENTRY_BIN)} || { echo seat-scope-sudoers-list; exit 1; }`,
  );
  lines.push("echo seat-ok");
  return lines.join("\n");
}

function rsyncEntries(cfg, repoRoot, rev, manifest, deps) {
  const spawn = deps.spawnSync ?? spawnSync;
  const lstat = deps.lstatSync ?? lstatSync;
  validateManifest(manifest);
  const sourceCheck = validateManifestSourcesForRsync(repoRoot, manifest, deps);
  if (!sourceCheck.ok) {
    return { ok: false, step: sourceCheck.errors[0], errors: sourceCheck.errors };
  }
  const stagingSuffix = deps.stagingSuffix ?? makeRuntimeStagingSuffix(deps);
  const stagingRel = runtimeStagingRel(rev, stagingSuffix);
  const verify = remoteTransport(cfg, buildRuntimeVerifyScript(manifest, rev), { timeoutSec: 120 }, deps);
  if (verify.status === 0) return { ok: true, reused: true };

  const mkdir = remoteTransport(cfg, `mkdir -p ${shellHomePath(stagingRel)}`, {}, deps);
  if (mkdir.status !== 0) return { ok: false, step: "runtime-mkdir", result: mkdir };
  for (const entry of manifest.entries) {
    const src = join(repoRoot, entry.path);
    let st;
    try {
      st = lstat(src);
    } catch {
      return { ok: false, step: `rsync:missing:${entry.path}` };
    }
    const mode = st.mode & S_IFMT;
    const isDir = mode === S_IFDIR;
    const remoteParentRel = homeRel(stagingRel, dirname(entry.path)).replace(/\/\.$/, "");
    const parentMk = remoteTransport(cfg, `mkdir -p ${shellHomePath(remoteParentRel)}`, {}, deps);
    if (parentMk.status !== 0) return { ok: false, step: `runtime-mkdir:${entry.path}`, result: parentMk };
    const remotePath = `${sshTarget(cfg)}:${homeRel(stagingRel, entry.path)}${isDir ? "/" : ""}`;
    const localPath = `${src}${isDir ? "/" : ""}`;
    const args = ["-az", "-e", `ssh ${sshOpts(cfg).join(" ")}`, localPath, remotePath];
    const result = spawn("rsync", args, { encoding: "utf8", timeout: probeMs(cfg) * 4 });
    if (result.status !== 0) return { ok: false, step: `runtime-rsync:${entry.path}`, result };
  }
  const finalize = remoteTransport(cfg, buildRuntimeFinalizeScript(rev, stagingSuffix), { timeoutSec: 120 }, deps);
  if (finalize.status !== 0) return { ok: false, step: "runtime-finalize", result: finalize };
  const link = remoteTransport(cfg, buildRuntimeLinkScript(manifest, rev), { timeoutSec: 120 }, deps);
  if (link.status !== 0) return { ok: false, step: "runtime-link", result: link };
  return { ok: true, reused: false, stagingSuffix };
}

function rsyncCcp(cfg, version, deps) {
  const spawn = deps.spawnSync ?? spawnSync;
  const verify = remoteTransport(cfg, buildCcpVerifyScript(version), { timeoutSec: 120 }, deps);
  if (verify.status === 0) return { ok: true, reused: true };
  const local = verifyLocalCcpBinary(version, deps);
  if (!local.ok) return { ok: false, reason: local.reason, found: local.found };
  const localBin = deps.ccpPath ?? local.path;
  const mkdir = remoteTransport(cfg, `mkdir -p ${shellHomePath(".local/bin")}`, {}, deps);
  if (mkdir.status !== 0) return { ok: false, step: "ccp-mkdir", result: mkdir };
  const remotePath = `${sshTarget(cfg)}:${homeRel(".local/bin", "claude-code-proxy")}`;
  const rsync = spawn("rsync", ["-az", "-e", `ssh ${sshOpts(cfg).join(" ")}`, localBin, remotePath], {
    encoding: "utf8",
    timeout: probeMs(cfg) * 8,
  });
  if (rsync.status !== 0) return { ok: false, step: "ccp-rsync", result: rsync };
  const install = remoteTransport(cfg, buildCcpInstallScript(version), { timeoutSec: 300 }, deps);
  if (install.status !== 0) return { ok: false, step: "ccp-install", result: install };
  return { ok: true, reused: false };
}

export function provisionHost(cfg, opts = {}, deps = {}) {
  const dryRun = Boolean(opts.dryRun);
  if (seatsGcUnsafe(cfg.remote_root, opts.home ?? HOME)) return { ok: false, reason: "seats-in-gc-scope" };
  const version = opts.version ?? resolveAuthoritativeClaudeVersion(deps);
  if (!version) return { ok: false, reason: "local-claude-version-missing" };
  const ccpVersion = CLAUDE_CODE_PROXY_VERSION;
  const localCcp = verifyLocalCcpBinary(ccpVersion, deps);
  if (!localCcp.ok && !opts.dryRun && !opts.skipCcpVerify) {
    return { ok: false, reason: localCcp.reason, found: localCcp.found, host: cfg.host };
  }
  let runtimeDescriptor;
  try {
    runtimeDescriptor = validateRuntimeReleaseV1(opts.runtimeDescriptor);
  } catch (error) {
    return { ok: false, reason: "runtime-descriptor-invalid", host: cfg.host, detail: error.message };
  }
  if (typeof opts.runtimeReleaseRoot !== "string" || !isAbsolute(opts.runtimeReleaseRoot)) {
    return { ok: false, reason: "runtime-release-root-invalid", host: cfg.host };
  }
  const runtimeDigest = runtimeDescriptor.artifactSha256;
  const gitLsRemoteUrl = opts.gitLsRemoteUrl ?? resolveGitLsRemoteUrl(opts.repoRoot ?? CLAUDE_MODULE_ROOT, deps);
  const token = opts.ghToken ?? readGhToken(deps);
  const ghTokenFingerprint = token ? tokenFingerprint(token) : null;
  if (!token && !opts.skipGhAuth) return { ok: false, reason: "local-gh-token-missing" };
  if (!opts.skipGhAuth && !gitLsRemoteUrl) return { ok: false, reason: "git-origin-unresolved" };

  const plan = [
    "packages",
    `claude:${version}`,
    `ccp:${ccpVersion}`,
    `runtime:${runtimeDigest}`,
    "seat-proxy-prereqs",
    "gh-auth",
    "gh-git-auth",
    "headless-guard",
    "seat-guard",
    "agent-seat-slice",
  ];
  if (dryRun) return { ok: true, dryRun: true, host: cfg.host, plan, version, runtimeDigest, ccpVersion };

  const runtime = remoteTransport(
    cfg,
    buildRuntimeDescriptorVerifyScript(runtimeDescriptor, opts.runtimeReleaseRoot),
    { timeoutSec: 120 },
    deps,
  );
  if (runtime.status !== 0) {
    return { ok: false, reason: "runtime-not-ready", host: cfg.host, result: runtime };
  }

  const preRuntime = [
    ["packages", buildPackageInstallScript()],
    ["claude", buildClaudeInstallScript(version)],
  ];
  for (const [name, script] of preRuntime) {
    const result = remoteTransport(cfg, script, { timeoutSec: 600 }, deps);
    if (result.status !== 0) return { ok: false, reason: `${name}-failed`, host: cfg.host, result };
  }

  const ccp = rsyncCcp(cfg, ccpVersion, deps);
  if (!ccp.ok) return { ok: false, reason: ccp.step ?? ccp.reason, host: cfg.host, result: ccp.result };

  const unitBody = readFileSync(join(CLAUDE_MODULE_ROOT, "systemd", "system", SEAT_PROXY_UNIT), "utf8");
  const proxy = remoteTransport(cfg, buildSeatProxyPrereqScript(unitBody), { timeoutSec: 300 }, deps);
  if (proxy.status !== 0) return { ok: false, reason: "seat-proxy-prereqs-failed", host: cfg.host, result: proxy };

  const postRuntime = [
    ["gh-auth", buildGhAuthScript(ghTokenFingerprint), { input: `${token}\n`, skip: opts.skipGhAuth }],
    ["gh-git-auth", buildGhGitAuthScript(ghTokenFingerprint, gitLsRemoteUrl), { skip: opts.skipGhAuth }],
    ["headless-guard", buildHeadlessGuardScript()],
    ["seat-guard", buildSeatGuardInstallScript()],
  ];
  for (const [name, script, extra] of postRuntime) {
    if (extra?.skip) continue;
    const result = remoteTransport(cfg, script, { timeoutSec: 600, ...(extra ?? {}) }, deps);
    if (result.status !== 0) return { ok: false, reason: `${name}-failed`, host: cfg.host, result };
  }

  const slice = remoteTransport(cfg, buildAgentSeatSliceApplyScript(), { timeoutSec: 120 }, deps);
  if (slice.status !== 0) {
    return { ok: false, reason: "agent-seat-slice-failed", host: cfg.host, result: slice };
  }

  const finalRuntime = remoteTransport(
    cfg,
    buildRuntimeDescriptorVerifyScript(runtimeDescriptor, opts.runtimeReleaseRoot),
    { timeoutSec: 120 },
    deps,
  );
  if (finalRuntime.status !== 0) {
    return { ok: false, reason: "runtime-changed-during-provision", host: cfg.host, result: finalRuntime };
  }

  return {
    ok: true,
    host: cfg.host,
    version,
    runtimeDigest,
    ccpVersion,
    plan,
    reusedCcp: ccp.reused,
  };
}

export function provisionAllHosts(cfg, opts = {}, deps = {}) {
  const hosts = opts.hosts ?? cfg.hosts ?? [];
  const results = [];
  for (const host of hosts) {
    const hc = { ...cfg, host };
    const result = provisionHost(hc, opts, deps);
    results.push(result);
    if (!result.ok) return { ok: false, results };
  }
  return { ok: true, results };
}

export function auditHostSeat(cfg, opts = {}, deps = {}) {
  const version = opts.version ?? resolveAuthoritativeClaudeVersion(deps);
  const manifest = loadClaudeManifest(opts.manifestPath);
  const repoRoot = opts.repoRoot ?? CLAUDE_MODULE_ROOT;
  const manifestDeps = { ...deps, manifest, manifestPath: opts.manifestPath };
  const rev = opts.rev ?? runtimeRevision(repoRoot, manifestDeps);
  const gitLsRemoteUrl = opts.gitLsRemoteUrl ?? resolveGitLsRemoteUrl(repoRoot, deps);
  const token = opts.ghToken ?? readGhToken(deps);
  const ghTokenFingerprint = token ? tokenFingerprint(token) : opts.ghTokenFingerprint ?? null;
  const lines = [];
  const pass = (msg) => lines.push({ level: "PASS", msg });
  const fail = (msg) => lines.push({ level: "FAIL", msg });
  const info = (msg) => lines.push({ level: "INFO", msg });

  if (!version) { fail("local claude version missing"); return lines; }
  if (!opts.skipGhAuth && !gitLsRemoteUrl) {
    fail("local git origin unresolved for github.com probe");
    return lines;
  }
  if (seatsGcUnsafe(cfg.remote_root, opts.home ?? HOME)) fail(`${cfg.host} seats path inside remote_root ${cfg.remote_root}`);
  else pass(`${cfg.host} seats outside GC scope`);

  const probe = remoteTransport(cfg, "echo ok", {}, deps);
  if (probe.status !== 0) { fail(`ssh ${sshTarget(cfg)} unreachable`); return lines; }
  pass(`ssh ${sshTarget(cfg)}`);

  const script = buildSeatDoctorScript({
    version,
    rev,
    remoteRoot: cfg.remote_root,
    minDiskGb: opts.minDiskGb ?? MIN_DISK_GB,
    ghTokenFingerprint,
    manifest,
    gitLsRemoteUrl,
    ccpVersion: opts.ccpVersion ?? CLAUDE_CODE_PROXY_VERSION,
  });
  const audit = remoteTransport(cfg, script, { timeoutSec: 120 }, deps);
  const code = String(audit.stdout || "").trim().split("\n").pop();
  if (audit.status !== 0) {
    fail(`${cfg.host} seat audit failed (${code || audit.stderr?.trim() || "unknown"})`);
    return lines;
  }
  pass(`${cfg.host} claude parity ${version}`);
  pass(`${cfg.host} ccp parity ${opts.ccpVersion ?? CLAUDE_CODE_PROXY_VERSION}`);
  pass(`${cfg.host} tmux present`);
  pass(`${cfg.host} gh auth`);
  pass(`${cfg.host} gh git auth`);
  pass(`${cfg.host} runtime ${rev}`);
  for (const entry of manifest.entries) pass(`${cfg.host} manifest ${entry.path} ${entry.strategy}`);
  pass(`${cfg.host} headless guard`);
  pass(`${cfg.host} seat guard`);
  pass(`${cfg.host} proxy prerequisites`);
  pass(`${cfg.host} seat contract dry-run`);
  info(`${cfg.host} disk ok`);
  return lines;
}

export function auditSeatHosts(cfg, opts = {}, deps = {}) {
  const hosts = opts.hosts ?? cfg.hosts ?? [];
  const lines = [];
  for (const host of hosts) lines.push(...auditHostSeat({ ...cfg, host }, opts, deps));
  return lines;
}
