fix(lib.sh): load_state_json strict treats a missing DB table as unreadable (Issue #71)
A DB without its sessions table (or without both tables) was read as an empty registry in strict mode, so stop closed a registered session row-less and exited 0. Strict mode now fails on any OperationalError; the non-strict reader is unchanged. Refs #71 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a47a4683f3
commit
5f6065344f
2 files changed
+16
-8
No files matched your search
@@ -1592,8 +1592,8 @@ herdr() {
|
|||||||
# NOTE: Retained as small inline PYEOF block (39 lines) per Plan Rev.2 (Job 98393a97).
|
# NOTE: Retained as small inline PYEOF block (39 lines) per Plan Rev.2 (Job 98393a97).
|
||||||
# Small blocks have minimal overhead and high local cohesion; extraction into lib_py is reserved for large blocks (>200 lines).
|
# Small blocks have minimal overhead and high local cohesion; extraction into lib_py is reserved for large blocks (>200 lines).
|
||||||
# `load_state_json strict` (Issue #71) fails (stderr, exit 1) when the YAML or
|
# `load_state_json strict` (Issue #71) fails (stderr, exit 1) when the YAML or
|
||||||
# DB cannot be read, instead of printing {} - for a caller that acts on a
|
# DB cannot be read, a DB table missing included, instead of printing {} - for
|
||||||
# session being absent.
|
# a caller that acts on a session being absent.
|
||||||
load_state_json() {
|
load_state_json() {
|
||||||
env_python "$AGENT_SESSIONS_YAML" "MAM_STATE_READ=${1:-}" <<'PYEOF'
|
env_python "$AGENT_SESSIONS_YAML" "MAM_STATE_READ=${1:-}" <<'PYEOF'
|
||||||
import os, sys, sqlite3, json, yaml
|
import os, sys, sqlite3, json, yaml
|
||||||
@@ -1610,16 +1610,16 @@ try:
|
|||||||
row = conn.execute('SELECT data FROM state WHERE id=1').fetchone()
|
row = conn.execute('SELECT data FROM state WHERE id=1').fetchone()
|
||||||
if row:
|
if row:
|
||||||
d = json.loads(row[0])
|
d = json.loads(row[0])
|
||||||
except sqlite3.OperationalError as e:
|
except sqlite3.OperationalError:
|
||||||
if strict and 'no such table' not in str(e):
|
if strict:
|
||||||
raise
|
raise
|
||||||
try:
|
try:
|
||||||
cursor = conn.execute('SELECT data FROM sessions')
|
cursor = conn.execute('SELECT data FROM sessions')
|
||||||
for r in cursor.fetchall():
|
for r in cursor.fetchall():
|
||||||
db_sessions.append(json.loads(r[0]))
|
db_sessions.append(json.loads(r[0]))
|
||||||
d['herdr_sessions'] = db_sessions
|
d['herdr_sessions'] = db_sessions
|
||||||
except sqlite3.OperationalError as e:
|
except sqlite3.OperationalError:
|
||||||
if strict and 'no such table' not in str(e):
|
if strict:
|
||||||
raise
|
raise
|
||||||
conn.close()
|
conn.close()
|
||||||
elif os.path.exists(yaml_path):
|
elif os.path.exists(yaml_path):
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ def test_stop_does_not_take_an_unreadable_row_for_no_row(mam_sandbox, mock_herdr
|
|||||||
assert "orphan74" in json.loads(Path(mock_herdr).read_text())["agents"]
|
assert "orphan74" in json.loads(Path(mock_herdr).read_text())["agents"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("broken", ["yaml-without-db", "db"])
|
@pytest.mark.parametrize("broken", ["yaml-without-db", "db", "no-sessions-table", "no-tables"])
|
||||||
def test_stop_does_not_take_an_unreadable_registry_for_no_row(mam_sandbox, mock_herdr, broken):
|
def test_stop_does_not_take_an_unreadable_registry_for_no_row(mam_sandbox, mock_herdr, broken):
|
||||||
"""load_state_json's {} on a read error is not "no row": a registered session is not closed row-less."""
|
"""load_state_json's {} on a read error is not "no row": a registered session is not closed row-less."""
|
||||||
name = "orphan75-creator-claude"
|
name = "orphan75-creator-claude"
|
||||||
@@ -522,10 +522,18 @@ def test_stop_does_not_take_an_unreadable_registry_for_no_row(mam_sandbox, mock_
|
|||||||
for f in mam.glob("agent-sessions.db*"):
|
for f in mam.glob("agent-sessions.db*"):
|
||||||
f.unlink()
|
f.unlink()
|
||||||
(mam / "agent-sessions.yaml").write_text("herdr_sessions: [\n - {name: " + name + "\n")
|
(mam / "agent-sessions.yaml").write_text("herdr_sessions: [\n - {name: " + name + "\n")
|
||||||
else:
|
elif broken == "db":
|
||||||
for f in mam.glob("agent-sessions.db-*"):
|
for f in mam.glob("agent-sessions.db-*"):
|
||||||
f.unlink()
|
f.unlink()
|
||||||
(mam / "agent-sessions.db").write_bytes(b"this is not a database" * 64)
|
(mam / "agent-sessions.db").write_bytes(b"this is not a database" * 64)
|
||||||
|
else:
|
||||||
|
# a missing table is not an empty registry
|
||||||
|
conn = sqlite3.connect(str(mam / "agent-sessions.db"))
|
||||||
|
conn.execute("DROP TABLE sessions")
|
||||||
|
if broken == "no-tables":
|
||||||
|
conn.execute("DROP TABLE state")
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
_set_mock(mock_herdr, agents={name: _agent(mam_sandbox, "w1:p76")})
|
_set_mock(mock_herdr, agents={name: _agent(mam_sandbox, "w1:p76")})
|
||||||
res = _stop(mam_sandbox, name)
|
res = _stop(mam_sandbox, name)
|
||||||
assert res.returncode == 1, res.stdout + res.stderr
|
assert res.returncode == 1, res.stdout + res.stderr
|
||||||
|
|||||||
Reference in new issue
Block a user