import io, sys


def edit(path, pairs, label):
    src = io.open(path, encoding="utf-8", newline="").read()
    for old, new, tag in pairs:
        n = src.count(old)
        if n != 1:
            sys.exit("ANCHOR %s/%s matched %d times" % (label, tag, n))
        src = src.replace(old, new, 1)
    io.open(path, "w", encoding="utf-8", newline="").write(src)
    print("%s: %d edit(s)" % (label, len(pairs)))


# ------------------------------------------------------------------ CONTEXT.md
edit(
    "CONTEXT.md",
    [
        (
            "The agent/adapter writes `<id>-commune.md` / `<id>-signoff.md`; the daemon watches the manifest-declared commune/signoff dirs (the spawn-session seam fields), ingests, and deletes (drop files are daemon-owned single-writer — KNOWN-HAZARDS 6.4). These stay off the `api` surface and the agent surface alike.",
            "The agent/adapter writes `<id>-commune.md` / `<id>-signoff.md`; the daemon watches the manifest-declared commune/signoff dirs (the spawn-session seam fields), ingests, and deletes (drop files are daemon-owned single-writer — KNOWN-HAZARDS 6.4). These stay off the `api` surface and the agent surface alike. **This path is the Self's, and spt-core's echo-commune never writes it** (operator-ruled 2026-09-06, releases#276): the echo's brief routes straight into the two-tier store on the fire. Until then core wrote its echo here too, and a second writer on a single-writer path overwrote unread Self communes — KNOWN-HAZARDS 6.12.",
            "208",
        ),
        (
            "**AN ECHO COMMUNE IS NOT SURFACED INTO THE LIVE AGENT'S RUNNING CONTEXT** (ratified 2026-08-03, LOCKSMITH grill — releases#113). Its purpose is to keep the Psyche's durable *live-context* and *project-context* current;",
            "**AN ECHO COMMUNE IS NOT SURFACED INTO THE LIVE AGENT'S RUNNING CONTEXT** (ratified 2026-08-03, LOCKSMITH grill — releases#113). Its purpose is to keep the Psyche's durable *live-context* and *project-context* current — **which it now does directly**, routing the brief from the summarizer into those tiers on the fire rather than transiting a drop file (operator-ruled 2026-09-06, releases#276);",
            "259",
        ),
    ],
    "CONTEXT.md",
)

# ------------------------------------------------------------------ inject.rs
edit(
    "crates/spt-live/src/inject.rs",
    [
        (
            """/// `echo_commune` was `echo_mirror` until releases#113. **Nothing ever mirrored**
/// — [`crate::echo::run_echo_commune`] writes the brief as a commune DROP FILE,
/// whose ingest feeds the durable context tiers read at resume, never the running
/// session's window; this function only appends a digest line. The old name""".replace(
                "\n", "\r\n"
            ),
            """/// `echo_commune` was `echo_mirror` until releases#113. **Nothing ever mirrored**
/// — [`crate::echo::run_echo_commune`] returns the brief, which its caller routes
/// straight into the durable context tiers read at resume, never the running
/// session's window; this function only appends a digest line. (Until
/// releases#276 that routing went through a commune DROP FILE — the agent's own
/// path, which is why it no longer does; KNOWN-HAZARDS 6.12.) The old name""".replace(
                "\n", "\r\n"
            ),
            "22",
        )
    ],
    "inject.rs",
)

# ------------------------------------------------------------ KNOWN-HAZARDS.md
ENTRY = """### 6.12 The echo-commune brief never transits the agent's drop path (one writer on `<id>-commune.md`, and it is the agent) `[REQ-HAZARD-ECHO-NEVER-WRITES-AGENT-DROP]`
- **Failure:** spt-core wrote its echo-commune brief to `<id>-commune.md` — the SAME path an agent writes its own boundary commune to — with no arbitration between the two writers. A Self commune authored at a `/clear` and not yet ingested was therefore overwritten **unread** by the boundary echo, which fires 25-45s later and lands exactly in that window. Measured on one endpoint 2026-09-06: **two of three Self communes lost inside one hour**, with the reborn session reporting its own wake text as stale. 6.4 named the invariant as "the LLM is read-only; only the supervisor deletes" and never contemplated core itself becoming a second WRITER on the Self's path; `echo.rs` read "the mind" as the Psyche and forgot the Self, while `docs/CONTEXT-MEMORY.md` says the Self *does* write that path at a boundary and that the Self signal is the STRONG live-context signal. So the echo — weak, project-primary, live-conservative — overwrote the strong Self capture: the exact inversion of the tiering the provenance asymmetry exists to protect.
- **Invariant:** exactly **one** writer on the Self's drop path — the agent — and the echo's brief routes **direct** into the two-tier store on the fire. The file was never required by the design; it was an implementation convenience that re-used the ingest, and re-using it cost the Self's own words.
- **spt-core mapping:** `fire_echo` (`spt-daemon/src/lifecycle.rs`) calls `spt_live::ingest_body` — the routing core shared with `ingest_drops`, so a later tiering change cannot fork — then publishes the COMMUNE io-event and consumes the write-ahead intent. `run_echo_commune` takes no directory and writes nothing on any arm. A deferred project slice (no project anchor) is a **loud discard**, never a file.
- **Testing note:** the pin is a **byte-identity** assertion over a pre-placed authored drop, not an existence check — the failure is an OVERWRITE, so the file still exists afterwards and `exists()` stays green straight through the defect. Restoring the write turns that arm red.
- **Origin:** operator ruling 2026-09-06 on doyle's own funnel measurement (releases#276); built by todlando.
<!-- [doc->REQ-HAZARD-ECHO-NEVER-WRITES-AGENT-DROP] -->

---

## 7. Boundary & delivery integrity (added 2026-05-31 — Stage A red-team)"""

edit(
    "docs/KNOWN-HAZARDS.md",
    [
        (
            "---\r\n\r\n## 7. Boundary & delivery integrity (added 2026-05-31 — Stage A red-team)",
            ENTRY.replace("\n", "\r\n"),
            "entry",
        ),
        (
            "| 6.11 | Brain respawn execs the applied bytes (canonical exe captured at broker start, not per-spawn current_exe) + promotion bytes-gate (exe_hash == artifact, else rollback) | daemon respawn path / self-update (ADR-0018 Q3) |",
            "| 6.11 | Brain respawn execs the applied bytes (canonical exe captured at broker start, not per-spawn current_exe) + promotion bytes-gate (exe_hash == artifact, else rollback) | daemon respawn path / self-update (ADR-0018 Q3) |\r\n| 6.12 | The echo-commune brief never transits the agent's `<id>-commune.md` (one writer on that path — the agent); the echo routes direct into the two-tier store | echo-commune seam / context tiering (releases#276) |",
            "row",
        ),
    ],
    "KNOWN-HAZARDS.md",
)
