The write-guard refusal (#123583) covers delete_session/delete_sessions/
prune_sessions, but two deletion paths still mutate a row a live turn
could own:
- prune_never_active_keyed_sessions called delete_session without
exclude_active_write_guards and dropped the routing entries up front.
The never-active predicate reads committed state, so a keyed row whose
first turn lease is already held — its messages not yet flushed — looks
empty and is deleted mid-turn; its gateway routing entry died with it.
Now the prune skips guarded rows (guard errors are per-row, not fatal),
and routing entries are removed only for rows actually deleted.
- delete_session_if_empty had no guard at all: its emptiness predicate
(no messages/title/children) shares the same committed-state window.
The in-transaction _guarded_ids check refuses like the guarded deletes;
the CLI's broad except already treats any refusal as keep-the-row.
Tests: two new prune invariants (guarded row survives with its routing
entry; compression lock protects too) and an empty-delete refusal case in
the write-guard suite. hermes_state: 1386 passed; 4 pre-existing macOS
mount/barrier/snapshot failures reproduce on pristine main.
(cherry picked from commit aa579f0cf9a7bc991b675a6d45e1b320fa208234)