import re

def load(p):
    return open(p, encoding='utf-8').read()

def save(p, s):
    open(p, 'w', encoding='utf-8', newline='\n').write(s)

def rep(s, a, b):
    n = s.count(a)
    assert n == 1, (n, a[:90])
    return s.replace(a, b)

# ---------------- resting.rs ----------------
p = 'crates/spt-daemon/src/resting.rs'
s = load(p)
s = rep(s, '''//! model** those mechanics consult. The deferred-message instance-state gate
//! (REQ-INST-6 — deferred rows hold while the instance rests) is
//! [`deferred_held`] (M5-D5a): state-keyed at the hook-channel consumer
//! sites, released by construction at the wake edge (the persisted record IS
//! the gate input).''', '''//! model** those mechanics consult. It answers WHICH INSTANCE a bare id
//! resolves to and whether a wake is owed first; it is never an input to a
//! delivery window. What a delivery window reads is the activity sentinel
//! (REQ-DELIVERY-WINDOW-IS-ACTIVITY-AXIS-ONLY, releases#341): this state
//! machine's events carry no activity input, so no reading of it can decide one.''')
i = s.index('/// The deferred-message resting gate (REQ-INST-6, CONTEXT §Instances):')
j = s.index('pub fn deferred_held(perch_path: &Path) -> bool {')
k = s.index('\n}\n', j) + 3
s = s[:i] + s[k:].lstrip('\n')
i = s.index('    // [unit->REQ-INST-6] the gate keys on the durable record: dormant and')
j = s.index('    fn deferred_held_keys_on_the_durable_rest_record() {')
k = s.index('\n    }\n', j) + 7
s = s[:i] + s[k:].lstrip('\n')
assert 'deferred_held' not in s, 'deferred_held still referenced in resting.rs'
save(p, s)

# ---------------- delivery.rs ----------------
p = 'crates/spt/src/api/delivery.rs'
s = load(p)
s = rep(s, '''    let perch_path = perch::resolve_perch_path(id, ParentHint::Infer);
    // The resting gate (REQ-INST-6): while the owner's durable rest record
    // reads dormant/suspended, deferred rows hold — the hook drain narrows to
    // non-deferred. The wake edge persists Active, so the very next drain
    // releases them through this same path (exactly once: delivered-marks).
    // [impl->REQ-INST-6]
    let include_deferred = include_deferred && !spt_daemon::resting::deferred_held(&perch_path);
    // ADR-0028 W2: the hook poll is the ACTIVE-window surface — it must SUPPRESS''', '''    let perch_path = perch::resolve_perch_path(id, ParentHint::Infer);
    // NO INSTANCE-STATE GATE HERE (releases#341, operator-ruled 2026-09-24): the
    // busy window is decided by the receiver's activity alone, so a deferred row
    // drains on this endpoint's own poll whatever its rest record says — dormant
    // (unwatched, possibly still working) included. The rest state machine has no
    // activity input, so it cannot answer a window question; the AND that read
    // it here is deleted, not relaxed.
    // [impl->REQ-DELIVERY-WINDOW-IS-ACTIVITY-AXIS-ONLY]
    // ADR-0028 W2: the hook poll is the BUSY-window surface — it must SUPPRESS''')
s = rep(s, '''    // `drain_active_window_at` reproduces the prior default/active_only behavior
    // (the resting-gate-narrowed `include_deferred` flows straight through) and
    // adds the idle_only suppression.''', '''    // `drain_active_window_at` reproduces the prior default/busy-only behavior
    // (`include_deferred` flows straight through) and adds the idle_only
    // suppression.''')
i = s.index('    // [unit->REQ-INST-6] the resting gate at the hook drain: a deferred row is')
j = s.index('    fn poll_drain_holds_deferred_while_resting_and_releases_once_on_wake() {')
k = s.index('\n    }\n', j) + 7
s = s[:i] + '''    // [unit->REQ-DELIVERY-WINDOW-IS-ACTIVITY-AXIS-ONLY] the busy window at the
    // hook drain reads NO rest record (releases#341). While the record reads
    // dormant, the away/return pair drains on the endpoint's own poll IN ORDER
    // (away first, the reason #341(a) exists), together with a peer's busy-window
    // message; while suspended, a busy-window row still drains on the next poll —
    // which, for a real suspended harness, is the first poll after its wake —
    // and exactly once. NEGATIVE CONTROL (pre-fix arm): re-adding
    // `&& !deferred_held(..)` holds every one of these rows and turns this red.
    #[test]
    fn poll_drain_delivers_the_busy_window_in_every_rest_state() {
        use spt_daemon::resting::{write_rest, RestState};
        let _h = isolated_home();
        establish("alice");
        let perch_path = perch::resolve_perch_path("alice", ParentHint::Infer);

        // Dormant (the driver detached): the agent may well be working.
        write_rest(&perch_path, RestState::Dormant, 1_000).unwrap();
        spool::spool_message_deferred_at(&perch_path, "spt-core", "away notice").unwrap();
        spool::spool_message_deferred_at(&perch_path, "bob", "peer while unwatched").unwrap();
        let got = poll_drain("alice", true);
        let bodies: Vec<&str> = got.iter().map(|m| m.body.as_str()).collect();
        assert_eq!(
            bodies,
            ["away notice", "peer while unwatched"],
            "dormant drains the busy window on its own poll, oldest first"
        );
        // The return notice only exists after reattach, so it can only follow.
        spool::spool_message_deferred_at(&perch_path, "spt-core", "return notice").unwrap();
        let got = poll_drain("alice", true);
        assert_eq!(got.len(), 1, "exactly the return notice: {got:?}");
        assert!(got[0].body.contains("return notice"));

        // Suspended: nothing holds on the record either.
        write_rest(&perch_path, RestState::Suspended, 2_000).unwrap();
        spool::spool_message_deferred_at(&perch_path, "bob", "peer while suspended").unwrap();
        write_rest(&perch_path, RestState::Active, 3_000).unwrap(); // the wake
        let got = poll_drain("alice", true);
        assert_eq!(got.len(), 1, "the first post-wake poll delivers it: {got:?}");
        assert!(got[0].body.contains("peer while suspended"));
        assert!(poll_drain("alice", true).is_empty(), "exactly once — never a replay");
        assert_eq!(spool::pending_count_at(&perch_path).unwrap(), 0);
    }
''' + s[k:]
save(p, s)

# ---------------- worker.rs ----------------
p = 'crates/spt/src/api/worker.rs'
s = load(p)
s = rep(s, '''    // The resting gate (REQ-INST-6): a resting worker's deferred rows hold,
    // exactly as `poll_drain`'s — the KH 1.4 "all sites agree" discipline.
    // [impl->REQ-INST-6]
    let include_deferred = include_deferred && !spt_daemon::resting::deferred_held(&perch_path);
    // ADR-0028 W2: the worker hook is the ACTIVE-window surface — suppress''', '''    // No instance-state gate, exactly as `poll_drain` (releases#341; the KH 1.4
    // "all sites agree" discipline): a worker's busy-window rows drain on its own
    // poll whatever its rest record says.
    // [impl->REQ-DELIVERY-WINDOW-IS-ACTIVITY-AXIS-ONLY]
    // ADR-0028 W2: the worker hook is the BUSY-window surface — suppress''')
i = s.index('    // [unit->REQ-INST-6] the resting gate agrees at the worker drain (the')
j = s.index('    fn worker_poll_holds_deferred_while_resting() {')
k = s.index('\n    }\n', j) + 7
s = s[:i] + '''    // [unit->REQ-DELIVERY-WINDOW-IS-ACTIVITY-AXIS-ONLY] the worker drain agrees
    // with the hook drain (KH 1.4 "all sites agree"): a suspended worker's
    // busy-window row drains on its own include-deferred poll — no rest record
    // holds it.
    #[test]
    fn worker_poll_delivers_the_busy_window_while_resting() {
        use spt_daemon::resting::{write_rest, RestState};
        let _h = isolated_home();
        establish_parent("alice");
        cmd_worker_start("alice", None, None, None);
        let child = perch::resolve_perch_path("alice-w1", ParentHint::Infer);
        spool::spool_message_deferred_at(&child, "alice", "deferred task").unwrap();

        write_rest(&child, RestState::Suspended, 1_000).unwrap();
        assert_eq!(cmd_worker_poll("alice-w1", true), 0);
        assert_eq!(
            spool::pending_count_at(&child).unwrap(),
            0,
            "drained through the resting worker's own poll"
        );
    }
''' + s[k:]
save(p, s)

# ---------------- inject.rs comment ----------------
p = 'crates/spt-daemon/src/inject.rs'
s = load(p)
s = rep(s, '''    // The claim is non-deferred by construction now (see `claim_idle_edge_at`), so the
    // resting gate no longer reads here: it existed to hold `active_only` rows back
    // while the owner rests, and this leg can no longer take one at all. REQ-INST-6
    // keeps the gate where a deferred row is still carried — the HOOK drain.''', '''    // The claim is non-deferred by construction (see `claim_idle_edge_at`), so this leg
    // never carries a busy-window row at all. No leg reads the rest record to decide
    // a window (releases#341, REQ-DELIVERY-WINDOW-IS-ACTIVITY-AXIS-ONLY).''')
save(p, s)
print('ok')
