import hashlib
import json
import os
import pathlib
import shutil
import stat
import subprocess

import pytest

ROOT = pathlib.Path(__file__).resolve().parents[3]
MODULE = ROOT / "modules/subrouter"
VERIFY = MODULE / "bin/verify-candidate"


def _input_digest() -> str:
    owned = [
        ROOT / "modules/subrouter",
        ROOT / "modules/harness/seat",
        ROOT / "modules/systray",
        ROOT / "modules/workstation/claude/buildbox-hosts.json",
        ROOT / "modules/workstation/claude/lib/buildbox-registry.mjs",
        ROOT / "modules/workstation/claude/tests/buildbox-registry.test.mjs",
        ROOT / "modules/workstation/claude/lib/remote-runner.sh",
        ROOT / "modules/workstation/claude/tests/remote-runner-path.test.sh",
        ROOT / "docs/plans/2026-08-17-subrouter-authority.md",
        ROOT / "docs/specs/2026-08-17-subrouter-authority-design.md",
        ROOT / "docs/plans/2026-08-22-subrouter-authority-s5.md",
        ROOT / "docs/plans/2026-08-22-subrouter-authority-s6.md",
        ROOT / "docs/plans/INDEX.md",
    ]
    digest = hashlib.sha256()
    head = subprocess.check_output(
        ["git", "-C", str(ROOT), "rev-parse", "HEAD"], text=True
    ).strip()
    digest.update(b"base\0" + head.encode() + b"\0")
    files: list[pathlib.Path] = []
    for entry in owned:
        if entry.is_dir():
            files.extend(
                path
                for path in entry.rglob("*")
                if path.is_file()
                and not ({".local", ".pytest_cache", "__pycache__"} & set(path.parts))
                and path.suffix != ".pyc"
            )
        elif entry.is_file():
            files.append(entry)
    for path in sorted(files, key=lambda value: value.relative_to(ROOT).as_posix()):
        assert not path.is_symlink()
        relative = path.relative_to(ROOT).as_posix().encode()
        body = path.read_bytes()
        digest.update(
            b"file\0"
            + relative
            + b"\0"
            + str(len(body)).encode()
            + b"\0"
            + body
        )
    return digest.hexdigest()


def _tree_diff_digest() -> str:
    paths = [
        "modules/subrouter",
        "modules/harness/seat",
        "modules/systray",
        "modules/workstation/claude/buildbox-hosts.json",
        "modules/workstation/claude/lib/buildbox-registry.mjs",
        "modules/workstation/claude/tests/buildbox-registry.test.mjs",
        "modules/workstation/claude/lib/remote-runner.sh",
        "modules/workstation/claude/tests/remote-runner-path.test.sh",
        "docs/plans/2026-08-17-subrouter-authority.md",
        "docs/specs/2026-08-17-subrouter-authority-design.md",
        "docs/plans/2026-08-22-subrouter-authority-s5.md",
        "docs/plans/2026-08-22-subrouter-authority-s6.md",
        "docs/plans/INDEX.md",
    ]
    digest = hashlib.sha256()
    tracked = subprocess.check_output(
        ["git", "-C", str(ROOT), "diff", "--binary", "--no-ext-diff", "HEAD", "--", *paths]
    )
    digest.update(b"tracked-diff\0" + tracked)
    untracked = subprocess.check_output(
        ["git", "-C", str(ROOT), "ls-files", "--others", "--exclude-standard", "-z", "--", *paths]
    ).split(b"\0")
    for raw in sorted(value for value in untracked if value):
        path = ROOT / raw.decode()
        if {".local", ".pytest_cache", "__pycache__"} & set(path.parts) or path.suffix == ".pyc":
            continue
        assert not path.is_symlink()
        body = path.read_bytes()
        digest.update(
            b"untracked\0" + raw + b"\0" + str(len(body)).encode() + b"\0" + body
        )
    return digest.hexdigest()


@pytest.fixture
def candidate(tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> pathlib.Path:
    tree_key = _input_digest()
    tree_diff = _tree_diff_digest()
    go_sum_sha = "c" * 64
    manifest = json.loads((MODULE / "upstream.json").read_text())
    patch_sha = hashlib.sha256(
        (MODULE / "patches/0001-overdeck-authority-safety.patch").read_bytes()
    ).hexdigest()
    binary_body = b"""#!/usr/bin/env bash
if [[ ${1:-} == authority-status ]]; then
  printf '%s\\n' \\
    'Upstream revision: 29c7ebb306ac54739206f4752449e437047dd150' \\
    'Source digest: 74ab8da37d467a7d36bb73b11d82d90fd5ea14f3b75c535026adb0e45d9529f6' \\
    'Patch digest: fixture' \\
    'Service identity: overdeck-subrouter' \\
    'Store lease: held' \\
    "Provider credentials: ${FIXTURE_PROVIDER_CREDENTIALS:-0}" \\
    "Unresolved attempts: ${FIXTURE_UNRESOLVED_ATTEMPTS:-0}" \\
    'Gateway: ready'
fi
exit 0
"""

    binary_sha = hashlib.sha256(binary_body).hexdigest()
    candidate_hash = hashlib.sha256()
    for value in (
        tree_key,
        manifest["revision"],
        manifest["archive_sha256"],
        patch_sha,
        manifest["go"],
    ):
        candidate_hash.update(value.encode() + b"\0")
    candidate_hash.update(bytes.fromhex(binary_sha))
    candidate_key = candidate_hash.hexdigest()
    artifact_root = tmp_path / "subrouter-artifacts"
    monkeypatch.setenv("OVERDECK_SUBROUTER_TEST_MODE", "1")
    monkeypatch.setenv("OVERDECK_SUBROUTER_TEST_ARTIFACT_ROOT", str(artifact_root))
    artifact = artifact_root / candidate_key
    if artifact.exists():
        pytest.fail(f"test refuses to overwrite existing candidate {artifact}")
    (artifact / "bin").mkdir(parents=True)
    (artifact / "logs").mkdir()
    binary = artifact / "bin/subrouter"
    binary.write_bytes(binary_body)
    binary.chmod(0o555)
    logs = {}
    records = {}
    registry_path = ROOT / "modules/workstation/claude/buildbox-hosts.json"
    registry = json.loads(registry_path.read_text())
    fixture_worker = registry["orders"]["build"][0]
    fixture_machine_id = next(
        host["machine_id"] for host in registry["hosts"] if host["name"] == fixture_worker
    )
    registry_sha = hashlib.sha256(registry_path.read_bytes()).hexdigest()
    started_epoch = 1786924800
    for phase in ("focused", "static", "full"):
        log = artifact / "logs" / f"{phase}.log"
        log.write_text(f"synthetic {phase} fixture\n")
        log_hash = hashlib.sha256(log.read_bytes()).hexdigest()
        logs[phase] = {"path": f"logs/{phase}.log", "sha256": log_hash}
        remote_meta = {
            "key": f"subrouter-{tree_key[:16]}-{phase}",
            "mirror": "/var/lib/buildbox/repos/overdeck.git",
            "epoch": "7",
            "argv": [
                "/var/lib/buildbox/repos/overdeck.git/modules/subrouter/bin/verify-candidate",
                "__worker",
                phase,
                tree_key,
                tree_diff,
            ],
            "env": {},
            "supervisor_mode": "opaque",
            "started_at": "2026-08-17T00:00:00Z",
        }
        remote_meta_raw = json.dumps(remote_meta, separators=(",", ":"))
        dispatch = {
            "schema": 1,
            "dispatch_key": f"subrouter-{tree_key[:16]}-{phase}",
            "job_id": f"fixture-{phase}",
            "epoch": "7",
            "started_at": "2026-08-17T00:00:00Z",
            "remote_meta": remote_meta,
            "remote_meta_raw": remote_meta_raw,
            "argv_sha256": hashlib.sha256(
                json.dumps(remote_meta["argv"], separators=(",", ":")).encode()
            ).hexdigest(),
            "meta_sha256": hashlib.sha256(remote_meta_raw.encode()).hexdigest(),
            "registry_sha256": registry_sha,
            "worker": fixture_worker,
            "machine_id_sha256": hashlib.sha256(fixture_machine_id.encode()).hexdigest(),
            "identity_source": "machine-id",
        }
        dispatch_path = artifact / f"dispatch-{phase}.json"
        dispatch_path.write_text(json.dumps(dispatch, sort_keys=True, indent=2) + "\n")
        record = {
            "phase": phase,
            "input_digest": tree_key,
            "tree_diff_sha256": tree_diff,
            "go_sum_sha256": go_sum_sha,
            "started_epoch": started_epoch,
            "ended_at": "2026-08-17T00:00:01Z",
            "elapsed_seconds": 1,
            "exit": 0,
            "worker": fixture_worker,
            "dispatch": dispatch,
            "dispatch_sha256": hashlib.sha256(dispatch_path.read_bytes()).hexdigest(),
            "go_version": "go version go1.24.0 linux/amd64",
            "log": f"{phase}.log",
            "log_sha256": log_hash,
        }
        (artifact / f"worker-{phase}.json").write_text(
            json.dumps(record, sort_keys=True) + "\n"
        )
        records[phase] = record
    module_log = artifact / "logs/module.log"
    module_log.write_text("synthetic module fixture\n")
    module_log_hash = hashlib.sha256(module_log.read_bytes()).hexdigest()
    logs["module"] = {"path": "logs/module.log", "sha256": module_log_hash}
    module_remote_meta = {
        "key": f"subrouter-{tree_key[:16]}-module",
        "mirror": "/var/lib/buildbox/repos/overdeck.git",
        "epoch": "7",
        "argv": [
            "/var/lib/buildbox/repos/overdeck.git/modules/subrouter/bin/verify-candidate",
            "__worker",
            "module",
            tree_key,
            tree_diff,
        ],
        "env": {},
        "supervisor_mode": "opaque",
        "started_at": "2026-08-17T00:00:00Z",
    }
    module_remote_meta_raw = json.dumps(module_remote_meta, separators=(",", ":"))
    module_dispatch = {
        "schema": 1,
        "dispatch_key": f"subrouter-{tree_key[:16]}-module",
        "job_id": "fixture-module",
        "epoch": "7",
        "started_at": "2026-08-17T00:00:00Z",
        "remote_meta": module_remote_meta,
        "remote_meta_raw": module_remote_meta_raw,
        "argv_sha256": hashlib.sha256(
            json.dumps(module_remote_meta["argv"], separators=(",", ":")).encode()
        ).hexdigest(),
        "meta_sha256": hashlib.sha256(module_remote_meta_raw.encode()).hexdigest(),
        "registry_sha256": registry_sha,
        "worker": fixture_worker,
        "machine_id_sha256": hashlib.sha256(fixture_machine_id.encode()).hexdigest(),
        "identity_source": "machine-id",
    }
    module_dispatch_path = artifact / "dispatch-module.json"
    module_dispatch_path.write_text(json.dumps(module_dispatch, sort_keys=True, indent=2) + "\n")
    module_record = {
        "phase": "module",
        "input_digest": tree_key,
        "tree_diff_sha256": tree_diff,
        "collected_tests": 7,
        "nodeids_sha256": "d" * 64,
        "started_epoch": started_epoch,
        "ended_at": "2026-08-17T00:00:01Z",
        "elapsed_seconds": 1,
        "exit": 0,
        "worker": fixture_worker,
        "tool_version": "Python 3.13.0",
        "go_sum_sha256": go_sum_sha,
        "dispatch": module_dispatch,
        "dispatch_sha256": hashlib.sha256(module_dispatch_path.read_bytes()).hexdigest(),
        "log": "module.log",
        "log_sha256": module_log_hash,
        "basetemp": str(tmp_path / "pytest-temp"),
    }
    (artifact / "worker-module.json").write_text(
        json.dumps(module_record, sort_keys=True) + "\n"
    )
    records["module"] = module_record
    (artifact / "manifest.json").write_text(json.dumps(manifest, sort_keys=True) + "\n")
    receipt = {
        "schema": 1,
        "complete": True,
        "candidate_digest": candidate_key,
        "base_commit": subprocess.check_output(
            ["git", "-C", str(ROOT), "rev-parse", "HEAD"], text=True
        ).strip(),
        "input_digest": tree_key,
        "tree_diff_sha256": tree_diff,
        "revision": manifest["revision"],
        "archive_sha256": manifest["archive_sha256"],
        "patch_sha256": patch_sha,
        "go_version_required": manifest["go"],
        "binary_sha256": binary_sha,
        "commands": {
            "focused": "modules/subrouter/bin/verify-candidate focused",
            "static": "modules/subrouter/bin/verify-candidate static",
            "module": "modules/subrouter/bin/verify-candidate module",
            "full": "modules/subrouter/bin/verify-candidate full",
        },
        "exits": {phase: 0 for phase in records},
        "workers": {phase: records[phase]["worker"] for phase in records},
        "dispatch": {phase: records[phase]["dispatch"] for phase in records},
        "toolchains": {
            phase: records[phase].get("go_version", records[phase].get("tool_version"))
            for phase in records
        },
        "timing": {
            phase: {
                "started_epoch": records[phase]["started_epoch"],
                "ended_at": records[phase]["ended_at"],
                "elapsed_seconds": records[phase]["elapsed_seconds"],
            }
            for phase in records
        },
        "logs": logs,
        "dependency_state": {
            "go_sum_sha256": go_sum_sha,
            "source_archive_plus_patch_sha256": hashlib.sha256(
                (manifest["archive_sha256"] + "\0" + patch_sha).encode()
            ).hexdigest(),
        },
        "temp_root_policy": "${XDG_CACHE_HOME:-$HOME/.cache}/overdeck/tests/subrouter/<unique-run>",
        "module_basetemp": module_record["basetemp"],
        "created_at": "2026-08-17T00:00:01Z",
    }
    (artifact / "receipt.json").write_text(json.dumps(receipt, sort_keys=True) + "\n")
    for path in artifact.rglob("*"):
        if path.is_file():
            path.chmod(0o555 if path == binary else 0o444)
    for path in sorted((p for p in artifact.rglob("*") if p.is_dir()), reverse=True):
        path.chmod(0o555)
    artifact.chmod(0o555)
    try:
        yield artifact
    finally:
        if artifact.exists():
            for path in artifact.rglob("*"):
                if path.is_file():
                    path.chmod(0o600)
                elif path.is_dir():
                    path.chmod(0o700)
            artifact.chmod(0o700)
            shutil.rmtree(artifact)


def test_pinned_manifest_is_complete_and_exact():
    manifest = json.loads((MODULE / "upstream.json").read_text())
    assert manifest == {
        "repository": "https://github.com/manaflow-ai/subrouter.git",
        "release": "v0.1.81",
        "revision": "29c7ebb306ac54739206f4752449e437047dd150",
        "archive_command": "git archive --format=tar 29c7ebb306ac54739206f4752449e437047dd150",
        "archive_sha256": "74ab8da37d467a7d36bb73b11d82d90fd5ea14f3b75c535026adb0e45d9529f6",
        "go": "1.24.0",
        "patch": "modules/subrouter/patches/0001-overdeck-authority-safety.patch",
    }


def test_worker_phases_strip_inherited_secrets_and_confine_egress():
    script = VERIFY.read_text()
    worker = script[script.index("worker_phase() {") : script.index("run_remote_phase() {")]
    allowlist = script[script.index("run_allowlisted() {") : script.index("write_worker_record() {")]
    source = worker.index('prepare_source "$work"')
    dependency_fetch = worker.index('run_allowlisted "$work/source" go mod download')
    full_suite = worker.index('run_confined "$work/source" go test ./...')
    assert source < dependency_fetch < full_suite
    assert "/usr/bin/env -i" in allowlist
    assert 'HOME="$worker_home"' in allowlist
    assert 'PATH="$credential_bin:$go_bin:/usr/local/bin:/usr/bin:/bin"' in allowlist
    assert "unshare --user --map-root-user --net" in allowlist
    assert "/usr/sbin/ip link set lo up" in allowlist
    assert "GOTOOLCHAIN=local" in allowlist
    assert "SSH_AUTH_SOCK" not in allowlist
    assert "ANTHROPIC_API_KEY" not in allowlist
    assert "OPENAI_API_KEY" not in allowlist
    assert "for command in codex claude" in worker
    assert "native account login tools are refused" in worker
    assert "go test ./internal/accounts" in worker
    assert "run_confined" in worker
    assert "go vet ./internal/accounts" in worker
    assert "go build -trimpath" in worker


def test_all_candidate_go_test_and_build_commands_use_network_namespace():
    script = VERIFY.read_text()
    worker = script[script.index("worker_phase() {") : script.index("run_remote_phase() {")]
    for command in (
        "go test ./internal/accounts",
        "go vet ./internal/accounts",
        "go test ./...",
        "go build -trimpath",
    ):
        line = next(line for line in worker.splitlines() if command in line)
        assert "run_confined" in line


def test_worker_tmpdir_is_unique_short_and_exactly_cleaned():
    script = VERIFY.read_text()
    worker = script[script.index("worker_phase() {") : script.index("run_remote_phase() {")]
    assert 'worker_tmp=$(mktemp -d "/tmp/XXX")' in worker
    assert 'worker_tmp=$(realpath -e -- "$worker_tmp")' in worker
    assert "worker TMPDIR is inside a checkout ancestry" in worker
    assert 'worker_tmp="$work/tmp"' not in worker
    trap_line = next(line for line in worker.splitlines() if line.strip().startswith("trap "))
    assert "$(printf '%q' \"$work\")" in trap_line
    assert "$(printf '%q' \"$worker_tmp\")" in trap_line
    # Linux sockaddr_un has a 108-byte sun_path, leaving 107 bytes for a
    # NUL-terminated pathname. Use the longest upstream test suffix observed.
    longest_suffix = (
        "/TestListenerTransferAcceptsLegacyDualStackWildcardForIPv4Configuration"
        "9999999999/001/listener.sock"
    )
    assert len("/tmp/XXX") == 8
    assert len("/tmp/XXX" + longest_suffix) <= 107


def test_candidate_phases_require_remote_only_installed_local_gate():
    script = VERIFY.read_text()
    dispatch = script[script.index("run_remote_phase() {") : script.index("validate_worker_record() {")]
    cases = script[script.index('case "$PHASE" in') :]
    assert 'local gate="$HOME/.claude/bin/local-gate"' in dispatch
    assert '"$gate" --remote-only --key "$key" --mode full --' in dispatch
    assert "real_go_available" not in script
    assert "worker_phase focused" not in cases
    assert "worker_phase static" not in cases
    assert "worker_phase module" not in cases
    assert "worker_phase full" not in cases
    assert 'run_remote_phase module "$TREE_KEY"' in cases
    assert 'local dispatch_owner="$ROOT/modules/botmaster/tgbot"' in dispatch


def test_patch_is_exactly_limited_to_authorized_paths():
    allowed = {
        "internal/accounts/refresh_attempt.go",
        "internal/accounts/refresh_attempt_test.go",
        "internal/accounts/codex_auth.go",
        "internal/accounts/codex_auth_test.go",
        "internal/agents/claude/store.go",
        "internal/agents/claude/store_test.go",
        "internal/accounts/store_lease.go",
        "internal/accounts/store_lease_test.go",
        "cmd/subrouter/authority_status.go",
        "cmd/subrouter/authority_status_test.go",
        "cmd/subrouter/authority_routes.go",
        "cmd/subrouter/authority_routes_test.go",
        "internal/authority/routes.go",
        "internal/authority/routes_test.go",
        "internal/proxy/authority_routes.go",
        "internal/proxy/authority_routes_test.go",
        "internal/proxy/proxy.go",
        "internal/proxy/proxy_test.go",
        "cmd/subrouter/main.go",
    }
    patch_lines = (
        MODULE / "patches/0001-overdeck-authority-safety.patch"
    ).read_text().splitlines()
    old_paths = {
        line.removeprefix("--- a/") for line in patch_lines if line.startswith("--- a/")
    }
    new_paths = {
        line.removeprefix("+++ b/") for line in patch_lines if line.startswith("+++ b/")
    }
    assert old_paths | new_paths
    assert old_paths | new_paths <= allowed
    assert {
        "internal/accounts/refresh_attempt.go",
        "internal/accounts/refresh_attempt_test.go",
        "internal/accounts/store_lease.go",
        "internal/accounts/store_lease_test.go",
        "cmd/subrouter/authority_status.go",
        "cmd/subrouter/authority_status_test.go",
        "cmd/subrouter/authority_routes.go",
        "cmd/subrouter/authority_routes_test.go",
        "internal/authority/routes.go",
        "internal/authority/routes_test.go",
        "internal/proxy/authority_routes.go",
        "internal/proxy/authority_routes_test.go",
    } <= new_paths


def test_verifier_enforces_s1_scope_and_remote_focused_packages():
    script = VERIFY.read_text()
    assert "validate_patch_scope" in script
    assert 'validate_patch_scope\nTREE_KEY=$(input_digest)' in script
    assert 'go test ./internal/accounts ./internal/agents/claude ./internal/authority ./internal/proxy ./cmd/subrouter' in script
    assert 'go vet ./internal/accounts ./internal/agents/claude ./internal/authority ./internal/proxy ./cmd/subrouter' in script
    scope = script[script.index("validate_patch_scope() {") : script.index("input_digest() {")]
    for path in (
        "internal/authority/routes.go",
        "internal/authority/routes_test.go",
        "internal/proxy/authority_routes.go",
        "internal/proxy/authority_routes_test.go",
        "cmd/subrouter/authority_routes.go",
        "cmd/subrouter/authority_routes_test.go",
    ):
        assert path in scope


def test_path_accepts_exact_artifact_and_rejects_binary_tamper(candidate):
    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode == 0, result.stderr
    assert result.stdout.strip() == str(candidate)
    binary = candidate / "bin/subrouter"
    binary.chmod(0o755)
    binary.write_text("#!/usr/bin/env bash\nexit 9\n")
    refused = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert refused.returncode != 0
    assert "artifact path is writable" in refused.stderr or "binary digest mismatch" in refused.stderr


def test_path_rejects_unreceipted_artifact_file(candidate):
    candidate.chmod(0o755)
    extra = candidate / ".go-sum.sha256"
    extra.write_text("c" * 64 + "\n")
    extra.chmod(0o444)
    candidate.chmod(0o555)

    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode != 0
    assert "artifact tree does not exactly match receipt" in result.stderr


def test_path_rejects_receipt_for_another_tree(candidate):
    receipt_path = candidate / "receipt.json"
    receipt = json.loads(receipt_path.read_text())
    receipt["input_digest"] = "0" * 64
    receipt_path.chmod(0o644)
    receipt_path.write_text(json.dumps(receipt) + "\n")
    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode != 0


def _rewrite_receipt(candidate: pathlib.Path, mutate) -> None:
    receipt_path = candidate / "receipt.json"
    receipt = json.loads(receipt_path.read_text())
    mutate(receipt)
    receipt_path.chmod(0o644)
    receipt_path.write_text(json.dumps(receipt, sort_keys=True) + "\n")
    receipt_path.chmod(0o444)


def _rewrite_worker_record(candidate: pathlib.Path, phase: str, mutate) -> dict:
    record_path = candidate / f"worker-{phase}.json"
    record = json.loads(record_path.read_text())
    mutate(record)
    record_path.chmod(0o644)
    record_path.write_text(json.dumps(record, sort_keys=True) + "\n")
    record_path.chmod(0o444)
    return record


def _rewrite_dispatch(candidate: pathlib.Path, phase: str, mutate) -> dict:
    dispatch_path = candidate / f"dispatch-{phase}.json"
    dispatch = json.loads(dispatch_path.read_text())
    mutate(dispatch)
    dispatch_path.chmod(0o644)
    dispatch_path.write_text(json.dumps(dispatch, sort_keys=True, indent=2) + "\n")
    dispatch_path.chmod(0o444)
    return dispatch


def test_path_rejects_forged_worker_record(candidate):
    _rewrite_worker_record(candidate, "focused", lambda value: value.__setitem__("worker", "forged-worker"))
    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode != 0
    assert "worker identity" in result.stderr or "dispatch record" in result.stderr


def test_path_rejects_unregistered_worker_identity_even_when_records_agree(candidate):
    dispatch = _rewrite_dispatch(
        candidate,
        "focused",
        lambda value: value.update({"worker": "forged-worker", "machine_id_sha256": "f" * 64}),
    )
    dispatch_path = candidate / "dispatch-focused.json"
    record = _rewrite_worker_record(
        candidate,
        "focused",
        lambda value: value.update(
            {
                "worker": "forged-worker",
                "dispatch": dispatch,
                "dispatch_sha256": hashlib.sha256(dispatch_path.read_bytes()).hexdigest(),
            }
        ),
    )
    _rewrite_receipt(
        candidate,
        lambda value: (
            value["workers"].__setitem__("focused", "forged-worker"),
            value["dispatch"].__setitem__("focused", dispatch),
        ),
    )
    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode != 0
    assert "unregistered focused worker identity" in result.stderr


def test_path_rejects_worker_timing_tamper_even_when_final_receipt_agrees(candidate):
    record = _rewrite_worker_record(
        candidate,
        "focused",
        lambda value: value.__setitem__("elapsed_seconds", value["elapsed_seconds"] + 60),
    )
    _rewrite_receipt(
        candidate,
        lambda value: value["timing"].__setitem__(
            "focused",
            {
                "started_epoch": record["started_epoch"],
                "ended_at": record["ended_at"],
                "elapsed_seconds": record["elapsed_seconds"],
            },
        ),
    )
    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode != 0
    assert "timing record" in result.stderr


def test_path_rejects_dispatch_record_tamper(candidate):
    _rewrite_dispatch(candidate, "focused", lambda value: value.__setitem__("meta_sha256", "0" * 64))
    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode != 0
    assert "dispatch receipt" in result.stderr


@pytest.mark.parametrize(
    ("mutate", "expected"),
    [
        (lambda value: value.__setitem__("tree_diff_sha256", "0" * 64), "tree_diff_sha256"),
        (lambda value: value["commands"].__setitem__("focused", "wrong"), "command mismatch"),
        (lambda value: value["workers"].__setitem__("focused", ""), "worker mismatch"),
        (lambda value: value["timing"]["focused"].__setitem__("elapsed_seconds", -1), "timing mismatch"),
        (lambda value: value["dependency_state"].__setitem__("go_sum_sha256", "bad"), "go.sum digest"),
        (
            lambda value: value["dependency_state"].__setitem__(
                "source_archive_plus_patch_sha256", "0" * 64
            ),
            "source dependency digest",
        ),
        (lambda value: value["logs"]["focused"].__setitem__("path", "../outside.log"), "log escapes"),
    ],
)
def test_path_rejects_receipt_provenance_tamper(candidate, mutate, expected):
    _rewrite_receipt(candidate, mutate)
    result = subprocess.run([str(VERIFY), "path"], cwd=ROOT, text=True, capture_output=True)
    assert result.returncode != 0
    assert expected in result.stderr
