fix(state): quarantine SessionDB handle after structural corruption
A bare SQLITE_CORRUPT/NOTADB on a live write (not FTS-scoped, not a replaced file) now sets a sticky per-instance flag: later writes fail fast with StateDbCorruptError, the handle never reopens after close(), and close() skips its explicit PASSIVE WAL checkpoint. Gateway and agent flush paths divert pending transcripts to JSONL/spool like the replaced case instead of retrying forever. Field evidence: a handle that kept writing for ~50 minutes after the first structural error checkpointed 15 pages under the wrong page numbers on shutdown (page 1 <- messages_fts_trigram_data leaf), turning "malformed" into "file is not a database". Refs #90837, #90950, #97940, #89332, #45383 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CNX8rNYHqA5pT4tAGSzXtb
This commit is contained in:
@@ -14,7 +14,7 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from hermes_state import SessionDB, _on_disk_journal_mode
|
||||
from hermes_state import SessionDB, StateDbCorruptError, _on_disk_journal_mode
|
||||
|
||||
|
||||
class _NotADbOnce:
|
||||
@@ -42,9 +42,12 @@ class TestFailClosedAfterNotADb:
|
||||
reopen = MagicMock()
|
||||
monkeypatch.setattr("hermes_state._connect_tracked_db", reopen)
|
||||
db._conn = _NotADbOnce(real_conn)
|
||||
with pytest.raises(sqlite3.DatabaseError, match="not a database"):
|
||||
with pytest.raises(sqlite3.DatabaseError, match="not a database") as excinfo:
|
||||
db.create_session(session_id="s2", source="cli", model="test")
|
||||
reopen.assert_not_called()
|
||||
# NOTADB on a live write is structural: the handle is quarantined.
|
||||
assert isinstance(excinfo.value, StateDbCorruptError)
|
||||
assert db._db_corrupt is True
|
||||
finally:
|
||||
db._conn = real_conn
|
||||
db.close()
|
||||
|
||||
Reference in New Issue
Block a user