import io, sys

P = "crates/spt-daemon/src/lifecycle.rs"
src = io.open(P, encoding="utf-8", newline="").read()
NL = "\r\n" if "\r\n" in src else "\n"


def sub(old, new, label):
    global src
    n = src.count(old)
    if n != 1:
        sys.exit("ANCHOR %s matched %d times (need exactly 1)" % (label, n))
    src = src.replace(old, new, 1)


def L(*lines):
    return NL.join(lines)


# ---------------------------------------------------------------- test 1 head
sub(
    L(
        "    // [unit->REQ-ECHO-DROP-DIR-RESOLVE] the ECHO fire resolves a RELATIVE manifest",
        "    // `commune_dir` (the PRODUCTION claude-spt \".claude\") against the ENDPOINT's",
        "    // recorded cwd through the SAME resolver the ingest leg uses — the drop lands in",
        "    // <endpoint cwd>/.claude, and the summarizer child itself RUNS in the endpoint",
        "    // cwd (the spawn-cwd rider). RED pre-fix: the raw relative dir went straight to",
        "    // `run_echo_commune`, which create_dir_all'd it against the DAEMON's process cwd",
        "    // (System32 under WMI = os error 5; a writable daemon cwd = communes silently",
        "    // lost to the wrong dir).",
        "    #[test]",
        "    fn echo_fire_resolves_relative_commune_dir_against_endpoint_cwd() {",
    ),
    L(
        "    // [unit->REQ-ECHO-DROP-DIR-RESOLVE] THE SPAWN-CWD RIDER, which survives the",
        "    // retirement of the write: the summarizer child RUNS in the ENDPOINT's recorded",
        "    // cwd, never the daemon's process cwd (System32 under WMI = os error 5).",
        "    // [unit->REQ-ECHO-BRIEF-DIRECT-ROUTE] and its brief reaches the LIVE TIER.",
        "    // [unit->REQ-HAZARD-ECHO-NEVER-WRITES-AGENT-DROP] while the agent's drop path,",
        "    // declared in this very manifest, stays EMPTY.",
        "    //",
        "    // This cell used to read the child's printed cwd out of the drop file at",
        "    // <endpoint cwd>/.claude/doyle-commune.md. There is no drop now (6.12), so the",
        "    // same fact is read from the tier the brief routes into. The rider it pins is",
        "    // unchanged; only where the evidence is read from moved.",
        "    #[test]",
        "    fn echo_fire_runs_child_in_endpoint_cwd_and_routes_brief() {",
    ),
    "test1-head",
)

# --------------------------------------------------------------- test 1 body
sub(
    L(
        "            host.fire_echo(None, \"test\").expect(\"echo fires clean\");",
        "",
        "            // The drop landed under the ENDPOINT cwd, not the daemon's.",
        "            let drop_path = project.path().join(\".claude\").join(\"doyle-commune.md\");",
        "            let body = std::fs::read_to_string(&drop_path)",
        "                .expect(\"commune drop written under <endpoint cwd>/.claude\");",
        "            // The rider: the summarizer child RAN in the endpoint cwd (its printed",
        "            // cwd is the project dir). Normalize separators/case for the compare.",
        "            let norm = |s: &str| s.replace('\\\\', \"/\").to_lowercase();",
        "            assert!(",
        "                norm(&body).contains(&norm(&project.path().to_string_lossy())),",
        "                \"echo child must run in the endpoint cwd, printed: {body:?}\"",
        "            );",
    ),
    L(
        "            host.fire_echo(None, \"test\").expect(\"echo fires clean\");",
        "",
        "            // NOTHING was filed. The manifest declares `commune_dir = \".claude\"`, so",
        "            // this is the exact path the echo used to write — and the exact path the",
        "            // agent's own commune lands on.",
        "            assert!(",
        "                !project.path().join(\".claude\").join(\"doyle-commune.md\").exists(),",
        "                \"the echo must never write the agent's drop path (KNOWN-HAZARDS 6.12)\"",
        "            );",
        "",
        "            // The brief routed straight into the live tier; the child's printed cwd",
        "            // rides in it. Normalize separators/case for the compare.",
        "            let cs = spt_store::contextstore::ContextStore::open_or_init().unwrap();",
        "            let live = cs.live_context_path(\"doyle\").unwrap();",
        "            let body = std::fs::read_to_string(&live)",
        "                .expect(\"the brief routed into the live tier\");",
        "            let norm = |s: &str| s.replace('\\\\', \"/\").to_lowercase();",
        "            assert!(",
        "                norm(&body).contains(&norm(&project.path().to_string_lossy())),",
        "                \"echo child must run in the endpoint cwd, live tier carries: {body:?}\"",
        "            );",
    ),
    "test1-body",
)

# ---------------------------------------------------------------- test 2 head
sub(
    L(
        "    // [unit->REQ-ECHO-DROP-DIR-RESOLVE] a RELATIVE commune_dir with NO recorded",
        "    // endpoint cwd = LOUD SKIP: fire_echo returns Ok (no strike), the summarizer is",
        "    // NEVER spawned, and nothing is written into whatever cwd the daemon happens to",
        "    // hold. RED pre-fix: the echo ran and wrote (or os-error-5'd) against the",
        "    // daemon's own process cwd.",
        "    #[test]",
        "    fn echo_fire_skips_loud_on_relative_dir_without_cwd() {",
    ),
    L(
        "    // [unit->REQ-ECHO-BRIEF-DIRECT-ROUTE] THE BEHAVIOUR CHANGE, pinned (releases#276):",
        "    // an endpoint with NO recorded cwd now ECHOES. It used to be a loud skip, because",
        "    // the echo had to resolve a directory before it could write one; it writes",
        "    // nothing now, so there is nothing left to resolve and no reason to skip.",
        "    //",
        "    // THIS CELL IS THE OLD ONE INVERTED, deliberately rather than deleted: it",
        "    // asserted `!marker.exists()` — the summarizer must never spawn. The marker is",
        "    // the same, the polarity is the change, and restoring the skip turns it RED.",
        "    // With no cwd there is also no project anchor, so this doubles as the",
        "    // no-project-tier arm: the live slice still commits.",
        "    #[test]",
        "    fn echo_fires_without_a_recorded_endpoint_cwd() {",
    ),
    "test2-head",
)

# --------------------------------------------------------------- test 2 body
sub(
    L(
        "            let out = host.fire_echo(None, \"test\");",
        "            assert!(out.is_ok(), \"a no-cwd skip is Ok (no strike), got {out:?}\");",
        "            assert!(",
        "                !marker.exists(),",
        "                \"the summarizer must never spawn on a no-cwd skip (skip precedes spawn)\"",
        "            );",
    ),
    L(
        "            let out = host.fire_echo(None, \"test\");",
        "            assert!(out.is_ok(), \"the echo fires clean without a cwd, got {out:?}\");",
        "            assert!(",
        "                marker.exists(),",
        "                \"the summarizer MUST spawn without a recorded cwd (releases#276): the \\",
        "                 echo no longer needs a directory, so the old loud skip is gone\"",
        "            );",
    ),
    "test2-body",
)

io.open(P, "w", encoding="utf-8", newline="").write(src)
print("lifecycle tests: rider repinned to the tier; no-cwd cell inverted")
