fix(sessions): let guarded delete remove idle compression-ended rows

The new entry-side guard in delete_session/delete_sessions called
_write_guards_reject without allow_closed_compression_parent=True, so
_check_transcript_write_guards raised CompressionSessionClosedError for any
row with end_reason='compression'. That type is not caught by
_write_guards_reject, so every user-facing delete of a compressed parent
500'd and a bulk delete containing one rolled back the whole batch.

Pass the flag at both sites, matching prune (hermes_state_maintenance.py).
The lease is keyed on the lineage root, so a live turn on the tip still
blocks deleting its ancestor. Test 1 gains a compression-ended case (red on
the pre-fold file).
This commit is contained in:
kshitijk4poor
2026-09-27 15:56:50 +05:30
committed by kshitij
parent 3402644764
commit adacbcc5fc
2 changed files with 14 additions and 2 deletions

View File

@@ -1576,7 +1576,9 @@ class SessionSessionsMixin:
def _do(conn):
if conn.execute("SELECT 1 FROM sessions WHERE id = ? LIMIT 1", (session_id,)).fetchone() is None:
return False
if exclude_active_write_guards and self._write_guards_reject(conn, session_id):
if exclude_active_write_guards and self._write_guards_reject(
conn, session_id, allow_closed_compression_parent=True,
):
raise SessionActiveWriteGuardError(
f"session '{session_id}' has an active turn lease or compression lock"
)
@@ -1649,7 +1651,10 @@ class SessionSessionsMixin:
if not existing:
return 0
if exclude_active_write_guards:
active_ids = {sid for sid in existing if self._write_guards_reject(conn, sid)}
active_ids = {
sid for sid in existing
if self._write_guards_reject(conn, sid, allow_closed_compression_parent=True)
}
existing = [sid for sid in existing if sid not in active_ids]
if skipped_ids is not None:
skipped_ids.extend(sorted(active_ids))

View File

@@ -39,6 +39,13 @@ def test_delete_session_refuses_when_write_guard_active(tmp_path):
assert db.delete_session("sess-cmp", exclude_active_write_guards=True) is True
assert db.get_session("sess-cmp") is None
# 4. An idle compression-ended row is a closed parent, not a live write: delete must succeed
# (no CompressionSessionClosedError leaking out of the guard check).
db.create_session("sess-compressed", source="test")
db.end_session("sess-compressed", "compression")
assert db.delete_session("sess-compressed", exclude_active_write_guards=True) is True
assert db.get_session("sess-compressed") is None
db.close()