import io
import subprocess
import sys

P = r"C:\Users\decid\Documents\projects\spt-claude-code\tools\claude-spt\src\hook.rs"
MANIFEST = r"C:\Users\decid\Documents\projects\spt-claude-code\tools\claude-spt\Cargo.toml"

MUTATIONS = [
    (
        "M8 frame leg deleted from Stop",
        "    arm_from_commune_frame(env, &id, &sid);",
        "",
        ["the_frame_leg_arms_from_a_commune_core_already_ingested", "the_frame_poll_authenticates_with_the_sessions_own_sid"],
    ),
    (
        "M9 truncation check removed (clears on a guess)",
        """        if e.get("truncated").and_then(|t| t.as_bool()).unwrap_or(false) {""",
        """        if false {""",
        ["a_truncated_commune_frame_refuses_loudly_instead_of_guessing"],
    ),
    (
        "M10 pending-arm guard removed (double boundary)",
        """    if env.read_adapter_state(&clearing_latch_rel(id)).is_some() {
        return;
    }""",
        """    if false {
        return;
    }""",
        ["the_frame_leg_stands_down_when_an_arm_is_already_pending"],
    ),
    (
        "M11 SessionStart cursor seed removed (first turn is the blind poll)",
        """                env.spt(
                    &["api", "--adapter", ADAPTER, "io-events", eid, "--session-id", &sid, "--json"],
                    None,
                    &[],
                );""",
        "",
        ["session_start_seeds_the_io_cursor_so_the_first_stop_is_not_the_blind_poll"],
    ),
    (
        "M12 kind filter dropped (a peer's quoted marker would arm us)",
        """        if e.get("kind").and_then(|k| k.as_str()) != Some("COMMUNE") {
            continue;
        }""",
        "",
        ["a_plain_commune_frame_and_a_peer_message_never_arm"],
    ),
    (
        "M13 poll refusal swallowed",
        """            env.log(&format!(
                "claude-spt hook: commune-frame poll REFUSED for {id} — {e}; an across-commune ingested this turn will not fire its boundary from the frame leg (the drop leg still covers a drop that is still on disk)"
            ));""",
        "",
        ["a_refused_frame_poll_is_loud"],
    ),
]

orig = io.open(P, encoding="utf-8").read()
fails = []
for name, old, new, tests in MUTATIONS:
    n = orig.count(old)
    if n != 1:
        print(f"ANCHOR MISS ({n}) for {name} — mutation not applied, a green suite would mean nothing")
        fails.append(name)
        continue
    io.open(P, "w", encoding="utf-8", newline="\n").write(orig.replace(old, new, 1))
    ok = True
    for t in tests:
        r = subprocess.run(["cargo", "test", "--manifest-path", MANIFEST, t], capture_output=True, text=True)
        if r.returncode == 0:
            print(f"  UNCAUGHT: {t}")
            ok = False
    print(f"{'CAUGHT ' if ok else 'ESCAPED'} {name}")
    if not ok:
        fails.append(name)

io.open(P, "w", encoding="utf-8", newline="\n").write(orig)
print("\nreverted;", "ALL CAUGHT" if not fails else f"ESCAPES: {fails}")
sys.exit(1 if fails else 0)
