fix: load and fork fail closed on unknown runs and stale projections (1.13.0) - #86
Draft
yoheinakajima wants to merge 2 commits into
Draft
yoheinakajima wants to merge 2 commits into
yoheinakajima wants to merge 2 commits into
Conversation
Runtime.load no longer registers an unknown run_id, and it refuses to replay into a GraphStore that already holds projection state. Creating a run stays an explicit Runtime(...) step. The package version stays 1.12.0 until the release tag. Co-authored-by: Yohei Nakajima <yoheinakajima@users.noreply.github.com>
Guard fork before any copied rows, register a catalog row for store=, and keep catalog probes from mutating an unrelated database. Co-authored-by: Yohei Nakajima <yoheinakajima@users.noreply.github.com>
cursor
Bot
force-pushed
the
cursor/load-integrity-fixes-d03d
branch
from
September 26, 2026 00:35
b2c34b4 to
bab57c1
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #81
Closes #82
Reported by @TrendpilotAI, including the reproductions and the #82 acceptance tests.
Governing issue and decision
status: ready-for-pr,priority: integrity)run_idfails and creates nothing. Replay — bothRuntime.loadandRuntime.fork— into a GraphStore that already holds projection state is refused before any event is applied and, for fork, before any fork row or copied events. The store is not cleared. An atomic rebuild-and-swap is explicitly out of scope. CONTRACT v1.13 Add README spec and v0 design contract #1 and Build v0.5 resumability: persistence, replay, fork, diff #2 amend v0.5 Build v0.8 Postgres + observability + operator CLI #5/Build v0.9 pack format + Diligence pack #6 and v1.2 Add README spec and v0 design contract #1.Invariant and scope
Runtime.loadobserves an existing canonical run and materializes that run's log into an empty projection. It does not mint run identity, repair a missingrunsrow, or leave facts in the projection that the log does not describe.Runtime.fork(..., graph_store=)follows the same emptiness rule.Out of scope:
clear()and a load-or-create flag.Runtime.fork. The CLI fork copies events and does not replay into a GraphStore, so there is no projection to refuse.Failure and compatibility model
Behavior change.
Runtime.load(path, run_id=...)used to replay an empty log andupsert_runthe requested id, so a typo showed up inlist_runs(). It now raisesRunNotFoundError(aStorageErrorand aFileNotFoundError) and does not insert a row or accept an event.reason="missing"— explicit id, the file exists, no catalog row, no events.reason="missing_file"— the SQLite file itself does not exist. The message says so. The file is not created.reason="orphan_events"— events exist for that id but the catalog row does not. Load does not repair the row. The message gives a one-line opt-in:SQLiteEventStore(path, run_id).upsert_run(created_at="<ISO-8601 timestamp>")(Postgres:PostgresEventStore(url, run_id).upsert_run(...)).reason="empty_catalog"—run_idwas omitted and an existing store has no runs. Nothing is inserted.Omitting
run_idstill loads the most recently appended-to run when one exists.Runtime(..., persist_to=)andRuntime(..., store=)both register the catalog row when the store implementsupsert_run. There is no load-or-create flag.except FileNotFoundErrorand the CLI not-found exit (code 3) still catch this.GraphStore.
Runtime.loadandRuntime.forkraiseNonEmptyGraphStoreError(aReplayError) whenstore.is_empty()is false, before any event is applied.operationis"load"or"fork". Fork checks beforefork_run, so a refusal leaves the run list and the event count unchanged. Neither call invokesclear().Unknown GraphStore backends.
GraphStore.is_empty()probes objects withquery_objects(ObjectQuery(result_mode="exists"))and still readsall_relations/all_patches. That default allows replay only when those reads are empty. A backend whose real state is invisible to them must overrideis_emptyand returnFalsewhile that hidden state would survive replay. FalkorDB overrides with oneMATCH (n) WHERE n:AGNode OR n:AGPatch RETURN 1 LIMIT 1, the same nodesclear()deletes, so a leftover placeholder is non-empty. Indexes are not projection state.Version. This adds public API (
RunNotFoundError,NonEmptyGraphStoreError,GraphStore.is_empty,catalog_status), so it is 1.13.0.pyproject.tomlandactivegraph.__version__are bumped here. The v1.12.0 bump landed in the API PR (#79), not in the release-prep PR (#85).Acceptance evidence
pytest -m "not slow" -q— 1096 passed, 14 skipped, 13 deselected. Skips are the Postgres suite (ACTIVEGRAPH_TEST_POSTGRES_URLis unset here). FalkorDB embedded conformance, includingis_emptyand the placeholder-only case, ran. Postgres cases for catalog identity andstore=registration are intests/test_load_integrity.pyand run on the CI Postgres service.mypy— success, 54 source filespython scripts/gate_docstrings.py— Ring 0 131/131, Ring 1 90.8% (floor 80%)Contributor credit
@TrendpilotAI reported both bugs, wrote the reproductions, and specified the #82 acceptance tests this PR follows.