fix(state): close the tracking leak and finish the audit of raw DB reads
Completeness pass over the previous commit. Registry leak (would have silently disabled the guard): the first version incremented on open but decremented only in SessionDB.close(). kanban's connect() hands raw connections to callers who close them directly (4 sites), so its counter only ever went up — after enough kanban operations every byte-probe on that path would be refused forever, disabling zeroed-file and header detection. Replaced manual track/untrack with a TrackedConnection subclass that untracks in close(), the one method every close path goes through. Verified across plain close, contextlib.closing, double close, nested lifetimes, and 100-cycle churn; `with conn:` (a transaction scope, not a close) correctly stays tracked. Also: a caller-supplied factory now wins instead of raising TypeError on a duplicate kwarg, since tracking is an optimisation for the probe guard, not a precondition for opening the database. Removed _apply_delete_for_wal_reset_bug, dead after the force-DELETE revert. Audit notes: - DELETE mode is still reachable via the NFS/SMB/FUSE fallback and remains correct there; those users are protected by the raw-read fix, not by the journal mode. - The remaining whole-file reads are on genuinely offline artifacts: _backup_db_file (DB won't open; bytes preserved for forensics), _backup_corrupt_db (quarantine path, now warns if a connection is live), and backup verification of snapshots. backup._safe_copy_db already uses the SQLite backup API rather than a byte copy. - The mechanism is POSIX-specific (Windows byte-range locks are handle-scoped, not process-scoped), so this is a Linux/macOS correctness fix; the change is platform-neutral and safe on Windows. Sibling tests updated: session recovery no longer expects a DELETE-mode recovered DB, and the kanban WAL-fallback test patches the connect site that actually runs now.
This commit is contained in:
@@ -3276,11 +3276,17 @@ def test_connect_falls_back_to_delete_on_locking_protocol(tmp_path, monkeypatch,
|
||||
return super().execute(sql, *args, **kwargs)
|
||||
|
||||
def wal_blocking_connect(*args, **kwargs):
|
||||
# connect_tracked supplies its own factory; the caller's wins, and it
|
||||
# skips tracking for non-TrackedConnection results.
|
||||
kwargs.pop("factory", None)
|
||||
return real_connect(
|
||||
*args, factory=_WalBlockingConnection, **kwargs
|
||||
)
|
||||
|
||||
with _patch("hermes_cli.kanban_db.sqlite3.connect", side_effect=wal_blocking_connect):
|
||||
with _patch(
|
||||
"hermes_cli.sqlite_safe_read.sqlite3.connect",
|
||||
side_effect=wal_blocking_connect,
|
||||
):
|
||||
with caplog.at_level("WARNING", logger="hermes_state"):
|
||||
conn = kb.connect()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user