fix(merge): a branch whose edits net to zero can never be merged - #517
Merged
aaltshuler merged 6 commits intoAug 18, 2026
Merged
Conversation
ardacanuckan
force-pushed
the
fix/473-net-zero-merge
branch
from
August 16, 2026 01:22
be75428 to
eb53504
Compare
A branch can carry real commits whose content returns to the merge base: insert an edge, then delete it. Its manifest state still differs from the target's, because the Lance version advanced twice, so the table is not caught by the manifest-equality gate that skips untouched tables. Merging such a branch fails with "table version N already exists", and a retry recomputes the same publish and fails identically, so the branch is permanently unmergeable. These tests are red until the next commit. They cover fast-forward, non-fast-forward, mixed and branch-to-branch shapes, and assert more than the absence of an error: a second merge must report AlreadyUpToDate, the manifest must advance exactly once, and no table version may move. Refs ModernRelay#473
The adopt arm built its registration from the target's own version and metadata, producing a row field-for-field equal to the one __manifest already holds. The publisher's registry guard exempts only an owner-branch handoff, which requires a different branch, so it rejected the row. Plan the adopt once, purely, as AdoptPublish (Nothing / Pointer / Fork), and drop a candidate whose plan is Nothing at classification. That is the same disposition the manifest-equal tables get one gate earlier, and it keeps every set derived from the candidate map consistent: expected versions, recovery delta slots, effects, and published updates. Suppressing the update at publish time instead breaks recovery, which requires the confirmed updates to equal the sidecar's complete intended delta. Only an empty validation delta qualifies for the drop. A non-empty one is the evaluator's RI, uniqueness and cardinality input and is still checked. The condition is exact rather than conservative: compute_adopt_delta returns None only when every id on both sides carries an identical row signature and nothing was added or deleted, and classification is reached only when base equals target. Closes ModernRelay#473
The registry guard refused any second registration at an occupied (identity, version) unless it was an owner-branch handoff. That rejected the strictly benign case, a row field-for-field identical to the one already stored, while admitting the shape the guard exists to police: two branches claiming one version. The post-publish fold reaches the same state whether an identical re-registration is applied or skipped. The two refusals also shared a byte-identical message. A caller that batched one version twice is a different bug from a caller racing a stored registration, and the text now says which happened. test_batch_create_table_versions_is_atomic_on_conflict used an identical re-registration as its stand-in for a conflict; it now uses a genuinely differing row so it still pins batch atomicity under the corrected rule.
The matrix gains a netZeroEdge op: a side that commits real changes whose net content effect is zero. It is deliberately not folded into noop, because the two take different engine routes, noop being skipped on manifest equality while netZeroEdge reaches adopt classification. Crossing it with every other op turns one reported example into a covered interaction, and OpVariant's exhaustive match means a future op cannot be added without dispositioning its net-zero cells. 100 cells, 49 executable.
… sidecar The branch-merge sidecar requires the confirmed updates to equal its complete intended delta, and both sets derive from the merge candidates, so dropping a table at classification keeps them equal. This crashes between the durable effects and the confirmation on a branch carrying both shapes at once, one table netting to zero and one with a real delta, and proves recovery resolves the sidecar and leaves the dropped table untouched.
aaltshuler
force-pushed
the
fix/473-net-zero-merge
branch
from
August 18, 2026 18:56
eb53504 to
bced629
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
A branch whose edits return a table to its fork-point content could not be merged. The merge tried to publish a field-identical table registration, failed with a version collision, and failed the same way on every retry.
This fixes that path by planning adoption once and dropping a table candidate only when the planned registration is already the stored manifest row and the source and base rows are proven logically equal. The merge still publishes its graph lineage, while recovery intent, expected versions, and published table updates remain consistent.
The equality proof is deliberately typed rather than display-string based:
_row_idremain visible;The manifest publisher also accepts an idempotent re-registration of the exact stored row while continuing to reject a different row at an occupied identity/version.
Closes #473.
Recovery behavior
The crash test now pins the exact pre-confirmation outcome. Reopen rolls the unpublished sibling-table effect back, leaves the source delta available, and records one
RolledBackaudit entry. A clean retry must returnMerged, publish the source and target rows together, and continue to suppress the net-zero table.Coverage
_row_id, same-looking managed Blob descriptors from different files, and inherited managed Blob files across native branches.Local verification
cargo test --workspace --locked --features omnigraph-engine/failpoints,omnigraph-cluster/failpointscargo clippy --workspace --all-targets --locked -- -D warnings -W clippy::dbg_macrocargo clippy --workspace --all-targets --locked --features omnigraph-engine/failpoints,omnigraph-cluster/failpoints -- -D warnings -W clippy::dbg_macrocargo fmt --all --checkscripts/check-agents-md.shFocused merge, Blob-adopt, truth-table, planner, and recovery tests also pass. The only local diagnostic was the existing macOS linker warning about compact-unwind metadata size.
Greptile Summary
The PR makes net-zero branch edits mergeable by removing manifest-identical adopt candidates before recovery and publication, while permitting idempotent manifest-row registration.
Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Compare base and source table rows] --> B{Validation delta empty?} B -->|No| C[Retain merge candidate] B -->|Yes| D[Plan adopted source state] D --> E{Planned registration} E -->|Nothing| F[Drop candidate before recovery planning] E -->|Pointer| C E -->|Fork| C C --> G[Build expected versions and recovery slots] G --> H[Publish table updates and graph head atomically] F --> GReviews (3): Last reviewed commit: "fix(merge): prove adopt equality before ..." | Re-trigger Greptile
Context used (3)