8dc5608a78
A turn writing against a session already closed by compression died with session_persistence_failed and a misleading "this is often a full disk" dialog, even though the store was healthy and a live continuation existed (#82001). Depth-1 recovery (find_live_compression_child) could not resolve lineages with >=2 compression hops (root -> mid -> tip), reproduced independently on two- and three-hop chains. - run_agent.py flush chokepoint: on CompressionSessionClosedError, resolve tip = db.get_compression_tip(old_id) (canonical bounded transitive walk), adopt only when tip != old_id AND the tip row is live, retry the flush exactly once (adoption budget); otherwise fail closed. - gateway/session.py append_to_transcript: replace the depth-1 live-child lookup with the same tip + liveness contract, so gateway transcript reroutes follow full chains. - agent/conversation_compression.py _adopt_live_compression_child: turn-start recovery preflight now resolves via get_compression_tip with the same liveness check, closing the last depth-1 consumer in this family. - classify_persistence_error: new "compression_closed" bucket; the turn-end explanation names compression rotation and tells the client to refresh the session id instead of blaming a full disk. Tests: depth-1 adoption, multi-hop chain adoption (agent + gateway), fail closed with no continuation / stale-closed (ws_orphan_reap) tip, exactly-once adoption budget, and error-wording guards (compression-closed never mentions disk; real disk failures keep disk guidance). Closes #82001 Co-authored-by: Al3xand3r1987 <125030427+Al3xand3r1987@users.noreply.github.com> Co-authored-by: yuzilongleif-collab <235949691+yuzilongleif-collab@users.noreply.github.com>