import io

p = 'traceable-reqs.toml'
s = io.open(p, encoding='utf-8').read()

# ── 1. REQ-TERM-7 amended BY REPLACEMENT (the subtype vocabulary is the record) ──
old7 = 'title = "Two-origin digest merge: spt-owned context-injection entries (psyche_download | echo_mirror | owl_message) appended by spt to the endpoint `digest.log`, timestamp-interleaved with the adapter\'s extracted activity records into one ordered timeline, via a distinct context-injection record category. Data model only this milestone; GUI collapse/expand and the echo-reads-digest delta loop are deferred to the surfaces that consume them."'
new7 = 'title = "Two-origin digest merge: spt-owned context-injection entries (psyche_download | echo_commune | owl_message) appended by spt to the endpoint `digest.log`, timestamp-interleaved with the adapter\'s extracted activity records into one ordered timeline, via a distinct context-injection record category. Data model only this milestone; GUI collapse/expand and the echo-reads-digest delta loop are deferred to the surfaces that consume them. THE MIDDLE SUBTYPE WAS `echo_mirror` UNTIL releases#113, AND THE NAME WAS A PHANTOM: nothing ever mirrored an echo-commune into a running context. run_echo_commune writes the brief as a commune DROP FILE whose ingest routes it into the DURABLE context tiers a later session resumes from; record_context_injection only appends a digest line; ADR-0019 defers the read-back loop. The name plus a comment reading `record that spt mirrored an echo-commune brief into the agent\'s context` were the ENTIRE basis on which releases#113 reported a behavioural divergence from legacy, and the ticket ordered the removal of a behaviour that was never built. Measured at 343df76e (todlando). Re-kinded to `echo_commune` for what it is -- a record that a brief was PRODUCED for this agent -- and the comment corrected in the same commit, because a name that asserts a behaviour is how the phantom gets re-derived. Distinct namespace from spt_proto EVENT_TYPE_ECHO_COMMUNE and the manifest [session.echo_commune] role, which spell the same word for different things."'
assert s.count(old7) == 1, 'REQ-TERM-7 title not matched'
s = s.replace(old7, new7)

# ── 2 + 3. the two new #113 REQs, minted before the code that satisfies them ──
anchor = '[[requirements]]\nid = "REQ-ECHO-DROP-DIR-RESOLVE"'
assert s.count(anchor) == 1

age_title = (
    "THE ECHO GATE SPLITS BY ARM INTO TWO SENTINEL FILES, AND ONLY THE WORK-DRIVEN ONE IS "
    "AGE-GATED (releases#113 fork 1, re-ruled 2026-08-30 on a corrected premise). MEASURED FIRST, "
    "AND THE MEASUREMENT INVERTED THE TICKET: the W0 report recorded the gate as armed by "
    "TRANSITION EDGES ONLY (arm_transition_echo) and concluded that an agent working for hours "
    "without a transition edge never echoes. FALSE -- cmd_state's idle arm (api/delivery.rs) writes "
    "the same sentinel UNCONDITIONALLY unless --no-gate, and that is published contract "
    "(harness-contract api.md: reporting idle also arms the echo gate). `api state idle` IS the "
    "turn-end report, so a working agent arms at EVERY turn end and the pulse loop fires within "
    "one period. spt-core was never missing the work-driven trigger; it was missing the AGE GATE. "
    "THE RULED SHAPE IS TWO FILES, NOT A CARRIER INSIDE ONE: (a) the EDGE sentinel, written by "
    "arm_transition_echo, fires UNGATED as today -- detach / attention-shift / suspend are rare and "
    "attention-correctness wants timeliness, and gating them would drop exactly the fire that "
    "matters at detachment risk; legacy supports the split, its boundary and session-edge fires "
    "were not age-gated either. (b) the IDLE sentinel, written by the idle arm, fires only at "
    "AGE >= 15 MINUTES, carrying the three legacy mechanics: a fresh sentinel answers "
    "SkipSentinelFresh{remaining} and the caller schedules a SHORT NEXT PULSE to wake near the "
    "crossing (this file only); age reads BIRTHTIME with an MTIME FALLBACK and the degradation is "
    "DECLARED rather than silent (birthtime = oldest un-fired turn end, mtime = most recent); and "
    "a metadata failure or clock skew FAILS OPEN -- it FIRES. Two files rather than one carrier is "
    "the property: an idle write can never overwrite an armed edge (no last-writer-wins downgrade), "
    "each file has one writer class, read-and-clear stays per-file, and the fire-time distinction "
    "is STRUCTURAL rather than parsed out of a byte someone has to remember to write. THE ARMING "
    "CONTRACT IS UNCHANGED and the docs sentence stays true -- reporting idle still arms; what "
    "acquires the age discipline is the FIRE."
)
boundary_title = (
    "A SESSION BOUNDARY FIRES AN ECHO WHOSE INPUT IS CAPTURED BEFORE THE SESSION ID ROTATES "
    "(releases#113 fork 3, ruled 2026-08-30 off the measurement it ordered). MEASURED at 343df76e: "
    "spt-core caught NO delta at a boundary, on three independent arms. (1) cmd_boundary fires no "
    "echo and arms no gate -- it rotates the sid, carries an engine-room empowerment, re-stamps "
    "ready, appends the session ledger, publishes one IO boundary event, nudges the project index "
    "and resurfaces notifs; the three production fire_echo triggers are pulse, rest-transition and "
    "signoff, and a boundary is none of them, nor does the departing session's soft api session-end "
    "fire one. (2) Even a fire placed after a boundary could not SEE the departing work: fire_echo "
    "feeds history(session_id) resolved through the CURRENT sid, and [history] resolves a single "
    "{session_id} file -- ADR-0019 states that property in as many words -- so the input after a "
    "rotation is the new, empty session. Session-spanning exists in this codebase but belongs to "
    "the DIGEST (ledger + last-K, REQ-TERM-6); the echo does not read it. (3) No last-echo "
    "watermark exists anywhere (zero hits across crates/), so the delta since the last echo is not "
    "a quantity spt-core holds. THE CONSTRAINT IS INPUT-BEFORE-ROTATION, and the shape delivering "
    "it (fire pre-rotation, or snapshot the input then rotate) is the implementer's. NO WATERMARK "
    "THIS PHASE, ruled: the whole current-session input stands -- it is a SUPERSET of the delta and "
    "the Psyche ingest consumes communes as content-deltas anyway; delta-since-last-echo semantics "
    "would be its own request. The boundary echo must not be able to FAIL the boundary: the "
    "harness has already rotated by the time core is told, so an echo problem is loud and "
    "best-effort, never a refusal that desyncs spt's record from the reality it describes."
)

block = (
    '[[requirements]]\n'
    'id = "REQ-ECHO-IDLE-AGE-GATE"\n'
    f'title = "{age_title}"\n'
    'required_stages = ["doc", "impl", "unit"]  # ACTIVATED in the delivering lane (todlando, releases#113 fork 1, 2026-08-30).\n'
    '[[requirements]]\n'
    'id = "REQ-ECHO-BOUNDARY-INPUT-BEFORE-ROTATION"\n'
    f'title = "{boundary_title}"\n'
    'required_stages = ["doc", "impl", "unit"]  # ACTIVATED in the delivering lane (todlando, releases#113 fork 3, 2026-08-30).\n'
)
s = s.replace(anchor, block + anchor)

assert '"' not in age_title and '"' not in boundary_title, 'a quote in a TOML basic string kills the parse'
io.open(p, 'w', encoding='utf-8', newline='\n').write(s)
print('ok')
