test(dst): model edges as physical rows, not a set of pairs - #684
Merged
azimafroozeh merged 2 commits intoSep 8, 2026
Merged
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
azimafroozeh
force-pushed
the
sibling-merge-readopts-deleted-edge
branch
from
September 8, 2026 07:40
645b3a6 to
7963511
Compare
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.
What & why
Closes #681. The DST nightly (run 9, seed 221206) fired its first
WorldDifferentialon a delete-vs-readd fork:mainholdsalice -> bob,b0inserts the pair again,b1deletes it; mergingb1and thenb0leaves the pair onmain. The engine is right under the multiset default and the DST model was wrong.idalone.b0's insert is a fresh row,b1's delete removes the inherited one, so the second merge adopts the fresh row. That is the multiset contract for an unkeyed edge type: re-inserting a pair is a second row, and a delete removes only the rows it matched (dedup deferred to bug: merge duplicates an edge added on both sides of the fork, and plain traversal hides the extra row #583, identity opt-in per type via@key(src, dst)in feat(engine): add edge keys with derived identity #593).Model.edgeswas a set of pairs, one entry per pair, so it sawb0unchanged and predicted absence. It is now a map from a minted row id to the pair, ids drawn from one counter onWorldModeland cloned across a fork, andpredict_mergeruns the same per-key three-way over persons and edges.export_jsonl) keeps duplicate rows andassert_physical_matchescompares rows ∪ ghosts at the final reopen, so a model whose row count drifts from the engine's goes red; the bound-edge reader gains a row-grain form (knows_rows_bound_target).query+bound); the widened keep-serving arbitration narrows its ties the same way..gqt, no Rust test:issue_681_sibling_merge_readopts_deleted_edge.gqtpins the engine outcome (no rows after theb1merge, one row after theb0merge);keyed_edge_delete_wins_over_readd.gqtpins the keyed contract (the pair stays deleted after both merges, RFC 0044 via feat(engine): add edge keys with derived identity #593);selfloop_rows_gated_once.gqtpins that two self-loop rows read once on the gated traversal and count 2 bound, the grain the model's membership oracles rely on. The model's own agreement is exercised by the nightly fleet (seed 221206 is the arm that found the gap) and by every existing pinned universe under the row-grain physical oracle.Backing issue / RFC
issue_681_sibling_merge_readopts_deleted_edge.gqtis the regression evidence: its# red_on:records the red it witnessed under the expectation the issue filed, its# notes:the ruling, and it expects the engine's answer. The keyed twin takes RFC 0045's feature shape (# issue: none): no build was ever wrong on it.Checklist
.gqtcases and the RFC 0055 record; no engine edit).gqtcases; the DST suite's existing pinned universes run under the row-grain physical oracle, 50 passed / 27 ignored)Local verification
cd crates/omnigraph-dst && cargo test— scenarios 50 passed / 27 ignored (dst_keep_serving_wedge_issue_554included), lane_b 1 passed, torn_init 1, lib 26cargo test -p omnigraph-gqt --test gq_logic_tests— 51 cases green on main with feat(engine): add edge keys with derived identity #593 (on the pre-feat(engine): add edge keys with derived identity #593 base the keyed twin is refused at parse, as expected)cargo fmt --all -- --check— cleancd crates/omnigraph-dst && cargo clippy --workspace --all-targets— cleanStore(Query)/WorldDifferential; green under this model[(Alice,Bob),(Alice,Charlie),(Alice,Charlie),(Bob,Diana)], export three rows — the phantom row the bound-row tie-break now rules outcargo test -p omnigraph-gqt --lib— not run to green:branch_list_shape_and_rows_are_blessed_like_a_read_stepoverflows its stack on5f94a741without this diff tooNotes for reviewers
edge Knows: Person -> Person { @key(src, dst) }the identical insert is an upsert of the same row and the pair stays deleted after both merges; the keyed twin case pins that on main with feat(engine): add edge keys with derived identity #593 merged.Worldkeeps its own set model; it never merges.export_jsonl: an export read inside a reconcile shifted the issue-554 panel's lance-realm universes by test order (the panel is green when run first and red after most predecessors with the export read; deterministic with--test-threads=1), while the bound query, the same read class the reconcile already issues, leaves them unchanged. The export channel keeps its role as the final-reopen physical oracle.Milestonerecipe that reaches the sibling delete-vs-readd shape deliberately (today it is seed luck), and a count-aware differential at every oracle site; the final-reopen row-grain oracle covers the count claim once per universe.