"""Cell 1 — prefix continuation + recovery determinism under real SIGKILL. Contract clause (arXiv:2608.03836): every append the store *acknowledged* survives a hard crash; recovery yields a contiguous prefix (no holes, no duplicates); two independent recovery passes see the identical transcript. Adapted from the spot-probe in the tracking issue (#80921), which ran ~29.5K messages to a SIGKILL with zero lost-after-return. Scaled down for CI: the parent kills the writer once >= 200 acknowledged appends are journaled — the property assertions are identical, only the exposure window is shorter. """ from __future__ import annotations import json import sqlite3 from pathlib import Path import pytest from tests.conformance.persistence._harness import ( effective_mode_or_skip, integrity_ok, kill9_and_reap, make_hermes_home, spawn_child, wait_for, ) WRITER = r""" import json, sys from pathlib import Path from hermes_state import SessionDB db_path = Path({db_path!r}) journal = Path({journal!r}) db = SessionDB(db_path=db_path) db.create_session("cell1", source="conformance") i = 0 with journal.open("a", buffering=1) as j: while True: rowid = db.append_message("cell1", "user", content=f"m{{i}}") # fsync-journal the acknowledged index AFTER append returns — # exactly the probe's definition of "claimed durable". j.write(json.dumps({{"i": i, "rowid": rowid}}) + "\n") j.flush() i += 1 """ def _acknowledged(journal: Path) -> list[dict]: if not journal.exists(): return [] out = [] for line in journal.read_text().splitlines(): try: out.append(json.loads(line)) except json.JSONDecodeError: # A torn final line is expected under SIGKILL: the write to the # journal itself was interrupted. That index was never fully # acknowledged to the harness, so it is out of scope. continue return out def _recover(db_path: Path) -> list[tuple]: """One independent recovery pass in a fresh connection/process context.""" conn = sqlite3.connect(str(db_path)) try: return conn.execute( "SELECT id, content FROM messages WHERE session_id='cell1' ORDER BY id" ).fetchall() finally: conn.close() @pytest.mark.parametrize("requested_mode", [None, "DELETE", "WAL"]) def test_acknowledged_appends_survive_sigkill(tmp_path, requested_mode): db_path = tmp_path / "state.db" journal = tmp_path / "acked.jsonl" child_env = {} if requested_mode is not None: # Steer the CHILD's own resolver via an isolated HERMES_HOME config: # pre-seeding the file alone is not enough — SessionDB.__init__ runs # apply_wal_with_fallback(), which upgrades a non-WAL file to WAL # whenever the configured mode says so (the resolver's downgrade # gates may still refuse; effective_mode_or_skip audits post-run). child_env["HERMES_HOME"] = str( make_hermes_home(tmp_path, requested_mode.lower()) ) child = spawn_child( WRITER.format(db_path=str(db_path), journal=str(journal)), env=child_env ) try: wait_for( lambda: len(_acknowledged(journal)) >= 200, what=">=200 acknowledged appends", child=child, ) # The kill must interrupt a LIVE writer — a child that already # exited would turn this into a clean-shutdown test. assert child.poll() is None, ( f"writer exited early (rc={child.returncode}): " f"{(child.stderr.read() if child.stderr else b'').decode(errors='replace')[-1500:]}" ) finally: kill9_and_reap(child) # A leg that ran in a different mode than requested is no evidence for # the requested mode — skip it rather than silently double-counting WAL. mode = effective_mode_or_skip(db_path, requested_mode) acked = _acknowledged(journal) assert len(acked) >= 200, ( f"[journal_mode={mode}] only {len(acked)} acknowledged appends " "journaled before the kill — harness window too small" ) pass1 = _recover(db_path) pass2 = _recover(db_path) # Zero lost-after-return: every acknowledged index is present. recovered_contents = {row[1] for row in pass1} lost = [a for a in acked if f"m{a['i']}" not in recovered_contents] assert not lost, ( f"[journal_mode={mode}] {len(lost)} acknowledged appends lost after " f"SIGKILL (first: {lost[:3]})" ) # Contiguous prefix: indices 0..N-1 with no holes and no duplicates. indices = sorted(int(row[1][1:]) for row in pass1) assert indices == list(range(len(indices))), ( f"[journal_mode={mode}] recovered transcript is not a contiguous " "prefix (holes or duplicates present)" ) # Recovery determinism: two independent passes identical. assert pass1 == pass2, f"[journal_mode={mode}] recovery passes diverge" assert integrity_ok(db_path), f"[journal_mode={mode}] integrity_check failed"