feat(runtime): add durable append-only effect log with interrupted replay

Claims are fsynced to a per-lineage JSONL log before a caller may invoke a
backend; a persistence failure raises EffectPersistenceError (a
ResourceIdentityError) so dispatch fails closed. Outcomes and observations are
appended; nothing is rewritten. Reload validates every record strictly, ignores
only a torn final write, and fails closed on corruption, forgery or hardlink
aliasing. recover_interrupted appends INTERRUPTED/possible-impact outcomes for
claims that never settled and leaves RUNNING background effects alone.

The effect store is added to Wave 3 control-plane paths (prefix check only;
the log itself refuses aliased files), so filesystem tools cannot forge it.
Tests redirect the store to a session tmp directory.
This commit is contained in:
Alexandre Teixeira
2026-10-02 20:02:31 +01:00
parent c57aa7ad5c
commit 9c4ed24296
4 changed files with 371 additions and 0 deletions
+22
View File
@@ -227,6 +227,28 @@ def _serve_test_static():
server.server_close()
@pytest.fixture(scope="session")
def _effects_store_root(tmp_path_factory):
return tmp_path_factory.mktemp("effects")
@pytest.fixture(autouse=True)
def _isolated_effects_store(_effects_store_root):
"""Keep durable effect claims out of the developer's real data directory.
Restored manually: requesting the shared ``monkeypatch`` here would move
its teardown after ``_no_leaked_module_stubs`` and misreport test stubs.
"""
from src.agent_runtime import effect_log
previous = effect_log.EFFECTS_DIR
effect_log.EFFECTS_DIR = str(_effects_store_root)
try:
yield
finally:
effect_log.EFFECTS_DIR = previous
@pytest.fixture(autouse=True)
def _no_leaked_module_stubs():
"""Fail the test that leaves a bare ``src.*``/``core.*`` stub behind.