fix(#6083): only grace-evict CONFIRMED never-persisted shells (Codex finding #2)

Codex re-gate asked to distinguish a confirmed-absent sidecar from an
indeterminate/unreadable one before entering the abandoned-shell grace path.
Add _session_sidecar_exists() (True/False/None) and only grace-evict when the
sidecar is confirmed absent (False); a present-or-indeterminate sidecar keeps
the session resident. Age-window bound retained (fully closes the leak, no
data-loss edge); an SSE-mounted-lease refinement is noted as optional follow-up.
This commit is contained in:
nesquena-hermes committed 2026-07-15 19:06:10 +00:00
1 parent 02e5acb890
commit 30059085c2
1 file changed
+33 -5
+33 -5
View File
@@ -4043,9 +4043,27 @@ def _persisted_session_meta_prefix(sid) -> dict | None:
return None
def _session_sidecar_exists(sid) -> bool | None:
"""Return whether *sid*'s sidecar file exists on disk.
True = the sidecar is confirmed present.
False = the sidecar is confirmed absent (a truly never-persisted session).
None = existence is indeterminate (unsafe id, or the stat raised).
``_session_is_evictable`` uses this to distinguish a genuinely
never-persisted empty shell (safe to grace-evict once abandoned) from a
session whose count merely could not be read this pass (stay resident).
"""
if not is_safe_session_id(sid):
return None
try:
return (SESSION_DIR / f'{sid}.json').exists()
except OSError:
return None
# Grace window (seconds) during which a never-persisted, empty, draftless session
# shell is protected from LRU eviction. new_session() defers the first disk write
# until the first message (#1171), so a just-opened "New Conversation" tab lives
# only in the SESSIONS cache; evicting it there permanently 404s the chat (#6083).
# After this window a still-empty, still-draftless, never-saved shell is treated as
# abandoned and becomes evictable again, so these shells can't grow unbounded past
@@ -4110,15 +4128,25 @@ def _session_is_evictable(s) -> bool:
in_memory_count = len(getattr(s, 'messages', None) or [])
disk_count = _persisted_message_count(sid)
if disk_count is None:
# Never persisted. Keep it resident ONLY while it is a fresh or
# actively-composed shell; an old, draftless, never-saved shell is an
# abandoned "New Conversation" tab and must remain evictable so these
# shells cannot accumulate unbounded past the cache cap (#6083 follow-up).
# disk_count is None for TWO distinct reasons: the sidecar is confirmed
# absent (truly never persisted → this cache is the only copy), OR the
# sidecar exists but its count could not be read this pass (transient I/O,
# mid-write). Only the CONFIRMED-ABSENT case is eligible for grace-based
# shell eviction; an indeterminate existing-sidecar session stays resident
# (conservative — never grace-evict something we cannot prove is gone).
if in_memory_count > 0:
return False # holds unsaved messages → never drop
composer_draft = getattr(s, 'composer_draft', None)
if composer_draft:
return False # user is composing (draft present) → keep resident
if _session_sidecar_exists(sid) is not False:
# Sidecar present or existence indeterminate → not a never-persisted
# shell; do not enter the abandoned-shell grace path.
return False
# Confirmed never-persisted empty draftless shell. Protect it while fresh
# (the compose window right after "New Conversation"); once stale it is an
# abandoned tab and becomes evictable so these shells cannot accumulate
# unbounded past the cache cap (#6083 follow-up).
created_at = getattr(s, 'created_at', None)
if isinstance(created_at, (int, float)):
if (time.time() - created_at) <= _UNSAVED_SHELL_GRACE_S: