def patch(p, pairs):
    s = open(p, encoding='utf-8', newline='').read()
    crlf = '\r\n' in s
    s = s.replace('\r\n', '\n')
    for a, b in pairs:
        assert s.count(a) == 1, (p, a, s.count(a))
        s = s.replace(a, b)
    if crlf:
        s = s.replace('\n', '\r\n')
    open(p, 'w', encoding='utf-8', newline='').write(s)


patch('docs-site/src/messaging/overview.md', [(
'''    it — and **never by its instance state**. A **dormant** instance has two
    arms: **its driver detached** (nobody is watching; the agent may still be
    working) or **a sibling took attention** (another instance of the id is
    the active one). Either way it drains its busy-window messages on its own
    next poll. A **suspended** instance receives them on its first poll after
    it wakes.''',
'''    it — and **never by its instance state**. A **dormant** instance (a
    sibling of the id is the active one) drains its busy-window messages on
    its own next poll. A **suspended** instance receives them on its first
    poll after it wakes. A busy-only message also never makes its target the
    active instance; a default or idle-only message from a different
    endpoint does (see [Instances](../instances/overview.md)).''')])

patch('docs-site/src/lifecycle/overview.md', [(
'''Endpoints rest instead of dying: **dormant** (warm — zero idle compute,
instantly wakeable) or **suspended** (cold), explicitly via
`spt endpoint suspend` or on attention-shift. Resting instances stay''',
'''Endpoints rest instead of dying: **dormant** (warm — zero idle compute,
instantly wakeable) when a sibling instance becomes the active one, or
**suspended** (cold) via `spt endpoint suspend` or auto-suspend (on by
default for dormant, uncontrolled, idle instances after 15 minutes — see
[Instances](../instances/overview.md)). Detaching a controller does not rest
an instance. Resting instances stay''')])

patch('docs-site/src/concepts/overview.md', [(
'''One endpoint can have **instances** on several nodes. Instances rest when
unused — **dormant** (warm, zero idle cost, instantly wakeable) or
**suspended** (cold) — and remain addressable while resting: messages for''',
'''One endpoint can have **instances** on several nodes. One of them is the
**active** instance; the others rest — **dormant** (warm, zero idle cost,
instantly wakeable) or **suspended** (cold) — and remain addressable while
resting: messages for''')])

patch('docs/DORMANCY-BUDGET.md', [(
'''2. **Auto-suspend stays opt-in, default OFF globally** — confirmed, not
   amended. The knob chain shipped in D9-2 (`auto_suspend_after_ms`: global
   default OFF → `daemon.json` node leg → `info.json` endpoint leg, `0` =
   explicit endpoint OFF) is the policy surface.
3. **Constrained nodes should default the node leg ON.** ~300 MiB/seat is
   real on a Pi/handheld-class node: set `auto_suspend_after_ms` in
   `daemon.json` there. The threshold counts from **dormancy onset** (the
   `dormant_since_ms` anchor), never from last activity.''',
'''2. ~~Auto-suspend stays opt-in, default OFF globally.~~ **AMENDED
   2026-09-25 (INSTANCE-AXES, releases#348):** auto-suspend is a
   `{mode, after}` pair — `disable` / `dormant-enable` / `enable` — and the
   default is **`{dormant-enable, 15 min}`**: an uncontrolled, idle, dormant
   instance suspends after 15 minutes unbroken. The measured budget above
   still stands; what changed is the default, because a warm seat nobody is
   using (dormant = a sibling holds attention) is ~300 MiB of LLM-harness
   RSS for no reader. The retired bare `auto_suspend_after_ms` knob migrates
   to the new default. The chain is node default (`spt daemon config
   auto-suspend`) → endpoint override (`spt endpoint auto-suspend`).
3. **Constrained nodes may choose `enable`** (suspend the active instance too
   when it is uncontrolled and idle) — `spt daemon config auto-suspend
   enable` on a Pi/handheld-class node. The timer counts from the latest of
   dormancy onset, going uncontrolled and going idle — any break restarts
   it.''')])

patch('PRD.md', [(
'''- ~~Warm-vs-cold dormancy default policy + suspend command surface~~ — **resolved**: active = most-recently-interacted + driver-attached (a linked Shell counts as a driver); no idle timer; auto-suspend opt-in (default OFF, node + endpoint override) counted from dormancy onset.''',
'''- ~~Warm-vs-cold dormancy default policy + suspend command surface~~ — **resolved** (the active/auto-suspend half **superseded 2026-09-25 by INSTANCE-AXES, releases#348**: the controlled axis is separate and never moves the instance state; active is reached only by an activation trigger and ordered by a replicated activation counter; the only edge to dormant is a sibling becoming active; auto-suspend is `{mode, after}`, default `{dormant-enable, 15 min}` — see CONTEXT §dormant/suspended). Originally: active = most-recently-interacted + driver-attached (a linked Shell counts as a driver); no idle timer; auto-suspend opt-in (default OFF, node + endpoint override) counted from dormancy onset.''')])
print('ok')
