Merge origin/main into ethie/pm-clean

- tools/browser_tool_install.py: keep pm-clean's frozen old-updater stub; main's
  UTF-8 decode fix touched only the npx prefetch body it replaces.
- tests/hermes_cli/test_update_scoped_reconciliation.py: keep pm-clean's test
  subset (catch-up rides the PM completion owner) and take main's gateway-less
  host evidence (#120740): the updated seed that holds the host at a running
  gateway, and the two gateway-less matrices for the source change that merged
  cleanly into update_cmd_fleet.py.
This commit is contained in:
ethernet
2026-09-23 20:04:27 -04:00
46 changed files with 1256 additions and 145 deletions

View File

@@ -979,14 +979,7 @@ class SessionDB(
# mutations, not just idempotent UPSERTs.
ioerr_begin_retried = False
while True:
self._raise_if_db_corrupt()
if storage_state(self.db_path) == STORAGE_CORRUPT:
# Another handle in this process already saw structural damage on this file.
# Quarantine this one before it touches SQLite; the error type is the same
# StateDbCorruptError, so every transcript-diversion owner handles it unchanged.
self._halt_db_corrupt(sqlite3.DatabaseError(
"database disk image is malformed (reported earlier in this process: "
f"{storage_corrupt_reason(self.db_path)})"))
self._raise_if_db_corrupt(storage=True)
# NOTE: the replaced/generation live probe runs INSIDE the lock below,
# not here. close() mutates _conn and _db_sidecar_identity under that
# same lock, ending the WAL generation (SQLite unlinks the -wal/-shm
@@ -1078,7 +1071,7 @@ class SessionDB(
self._raise_if_db_replaced()
# Corrupt FTS shadow tables fail every write via the sync triggers while canonical
# rows are intact: detach the derived indexes atomically and retry (never rebuild here).
if self._enter_fts_fail_open(exc):
if self._enter_fts_fail_open(exc, deadline=deadline, patience_s=patience_s):
continue
# What survives both checks is structural damage: quarantine.
if self._is_structural_corruption_error(exc):
@@ -1397,9 +1390,16 @@ class SessionDB(
)
return retire_without_close
def _raise_if_db_corrupt(self) -> None:
def _raise_if_db_corrupt(self, *, storage: bool = False) -> None:
if self._db_corrupt:
raise self._corrupt_error()
if storage and storage_state(self.db_path) == STORAGE_CORRUPT:
# Another handle in this process already saw structural damage on this file.
# Quarantine this one before it touches SQLite; the error type is the same
# StateDbCorruptError, so every transcript-diversion owner handles it unchanged.
self._halt_db_corrupt(sqlite3.DatabaseError(
"database disk image is malformed (reported earlier in this process: "
f"{storage_corrupt_reason(self.db_path)})"))
def _sleep_before_write_retry(self, deadline: float, patience_s: float) -> bool:
"""Sleep one jitter interval if the budget allows; True = retry, False = deadline passed. Small