fix(update): ignore cron/executions.db sidecars, not just the base file
`/*.db-wal` / `/*.db-shm` only match at the checkout root, so on a flat install `git stash push --include-untracked` still swept `cron/executions.db-wal` / `-shm` while the scheduler held the WAL-mode database open (cron.executions._connect opens it via open_db in WAL mode). The base file stayed put but its WAL vanished under a live writer, so the next `_connect()` failed with `disk I/O error` (review finding on #111175). Use `/cron/executions.db*` like the gateway recovery db rule already does, covering -wal/-shm/-journal and retired-WAL dirs. Every other non-root db rule in the block already uses the glob. The regression tuple gains the two sidecars, and the stash test gets the reviewer's repro against the real `_stash_local_changes_if_needed`: an open WAL connection with one committed row must still be readable from a fresh connection afterwards.
This commit is contained in:
+1
-1
@@ -188,7 +188,7 @@ docs/superpowers/*
|
|||||||
/gateway/discord_message_recovery.db*
|
/gateway/discord_message_recovery.db*
|
||||||
/sessions/
|
/sessions/
|
||||||
/browser-profile/
|
/browser-profile/
|
||||||
/cron/executions.db
|
/cron/executions.db*
|
||||||
/cron/jobs.json
|
/cron/jobs.json
|
||||||
/cron.pid
|
/cron.pid
|
||||||
/gateway.lock
|
/gateway.lock
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ tracked .gitignore must cover the runtime state set, mirroring the
|
|||||||
.hermes-bootstrap-complete / .install_method precedent (#38529 / #66189).
|
.hermes-bootstrap-complete / .install_method precedent (#38529 / #66189).
|
||||||
"""
|
"""
|
||||||
import shutil
|
import shutil
|
||||||
|
import sqlite3
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ FLAT_INSTALL_RUNTIME_STATE = (
|
|||||||
"sessions/2026-09-14_06-00-00_abcd123d.jsonl",
|
"sessions/2026-09-14_06-00-00_abcd123d.jsonl",
|
||||||
"browser-profile/Cookies",
|
"browser-profile/Cookies",
|
||||||
"cron/executions.db",
|
"cron/executions.db",
|
||||||
|
"cron/executions.db-wal",
|
||||||
|
"cron/executions.db-shm",
|
||||||
"cron/jobs.json",
|
"cron/jobs.json",
|
||||||
"cron.pid",
|
"cron.pid",
|
||||||
"gateway.lock",
|
"gateway.lock",
|
||||||
@@ -126,3 +129,33 @@ def test_untracked_autostash_cannot_sweep_runtime_state(flat_install_repo):
|
|||||||
if not (flat_install_repo / rel).exists()
|
if not (flat_install_repo / rel).exists()
|
||||||
]
|
]
|
||||||
assert missing == []
|
assert missing == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_untracked_autostash_leaves_open_wal_database_readable(flat_install_repo):
|
||||||
|
"""The updater's real stash step must not unlink the -wal/-shm sidecars of a
|
||||||
|
WAL-mode database the scheduler holds open (cron/executions.db). With the
|
||||||
|
base file ignored but its sidecars swept, the next ``cron.executions._connect()``
|
||||||
|
finds a database whose WAL vanished under a live writer and fails with
|
||||||
|
``disk I/O error`` (the review repro on #111175)."""
|
||||||
|
from hermes_cli.update_cmd_stash import _stash_local_changes_if_needed
|
||||||
|
|
||||||
|
db_path = flat_install_repo / "cron" / "executions.db"
|
||||||
|
db_path.unlink() # the fixture's placeholder is not a database
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
try:
|
||||||
|
conn.execute("PRAGMA journal_mode=WAL")
|
||||||
|
conn.execute("CREATE TABLE executions (id INTEGER PRIMARY KEY, status TEXT)")
|
||||||
|
conn.execute("INSERT INTO executions (status) VALUES ('ok')")
|
||||||
|
conn.commit()
|
||||||
|
assert (db_path.parent / "executions.db-wal").exists()
|
||||||
|
# A tracked local change makes the updater actually enter its stash step.
|
||||||
|
(flat_install_repo / "app.py").write_text("print('changed')\n")
|
||||||
|
|
||||||
|
stash_ref = _stash_local_changes_if_needed(["git"], flat_install_repo)
|
||||||
|
|
||||||
|
assert stash_ref
|
||||||
|
# Fresh connection, exactly as every cron.executions call opens one.
|
||||||
|
with sqlite3.connect(db_path) as reader:
|
||||||
|
assert reader.execute("SELECT status FROM executions").fetchall() == [("ok",)]
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|||||||
Reference in New Issue
Block a user