fix: make bundle-tip extensions propagate (write them at update_seq 1) - #62
Merged
Conversation
A repo's `extensions` map arrived empty on any peer that synced by summary+delta, while its refs and object_index propagated normally. That silently disables the dead-weight bundle filter (#32), so a fetch downloads every bundle in object_index instead of just the reachable ones -- worst for snapshot-mode mirrors, where most bundles are dead weight by design. Cause is in the contract's delta computation: summary.extension_seqs.get(k).copied().unwrap_or(0) < v.update_seq `unwrap_or(0)` reads a key the peer has NEVER SEEN as seq 0, which is indistinguishable from a key it already holds at seq 0. `bundle-tip:<id>` entries are written at seq 0 on purpose -- each key is unique per bundle, so there is no monotonicity to track -- so the test is `0 < 0` and the entry is never put in a delta and never reaches that peer. Refs escape only because the push path always writes seq >= 1. Two changes, because the obvious fix cannot reach the network: 1. `get_state_delta` now distinguishes absent from zero, for both extensions and refs. This is the root fix, but it lives in the contract WASM, and `contracts/repo-contract.wasm` is a checked-in artifact only rebuilt deliberately -- a rebuild re-keys every repo and needs a legacy_contracts.toml migration entry. So it takes effect on the next contract bump, not now. Filed separately. 2. The push path writes bundle-tip extensions at seq 1 (`BUNDLE_TIP_UPDATE_SEQ`). Under the deployed rule `0 < 1` is true, so the entry propagates today. It stays correct after a contract rebuild: a peer missing the key still needs it, one holding it at 1 still does not. Verified as a controlled A/B on the live network. Two repos pushed minutes apart, differing only in this seq, then read from a third node that had to obtain both from the network: ext-probe (seq 0) -> extensions (0) <- lost seqfix-probe (seq 1) -> extensions (2) <- propagated with refs and object_index intact in both, reproducing the original symptom exactly. Cloning the control reports every bundle as "legacy" (filter disengaged); cloning the fixed repo takes the tipped path. The unit test for the seq deliberately replicates the DEPLOYED delta rule rather than calling `get_state_delta`, which has now been fixed in source -- calling it would test the future and stop testing what is actually running. [AI-assisted - Claude]
sanity
added a commit
that referenced
this pull request
Aug 4, 2026
Ships the #62 bundle-tip propagation fix. Tips written at update_seq 0 were undeliverable under the deployed contract's delta rule, so a repo's extensions map arrived empty on peers that synced by summary+delta, silently disabling the dead-weight bundle filter and making fetches download every bundle in object_index. The fix is in the push path, so it only takes effect for pushes made by a published binary -- hence the release. Repos heal as they are pushed; previously-written seq-0 tips stay unpropagatable, which is the status quo (those bundles are treated as legacy and downloaded). The root fix in `get_state_delta` also landed in #62 but is inert until the contract WASM is rebuilt, which re-keys every repo. Tracked in #63. Only the freenet-git crate changed; the sibling crates are unchanged and stay at their published versions. [AI-assisted - Claude]
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.
Follow-up to the loose end noted after #61: a repo's
extensionsmap was arriving empty on peers, while its refs andobject_indexpropagated normally. That silently disables the dead-weight bundle filter (#32), so fetches download every bundle inobject_indexrather than the reachable ones — worst for snapshot-mode mirrors, where most bundles are dead weight by design.Cause
In the contract's delta computation:
unwrap_or(0)reads a key the peer has never seen as seq 0, which is indistinguishable from a key it already holds at seq 0.bundle-tip:<id>entries are written at seq 0 deliberately — each key is unique per bundle, so there is no monotonicity to track. So the test is0 < 0, the entry is never included in a delta, and it never reaches that peer. Refs escape only because the push path always writesupdate_seq >= 1, which is a property of a different crate that nothing inget_state_deltaenforces.Two changes, because the obvious fix cannot reach the network
1.
get_state_deltadistinguishes absent from zero (extensions and refs). This is the root fix — but it lives in the contract WASM, andcontracts/repo-contract.wasmis a checked-in artifact last built in April. Rebuilding it re-keys every repo and needs alegacy_contracts.tomlmigration entry, so it is a deliberate operation, not a side effect of this PR. This fix therefore takes effect on the next contract bump, not now. Filed as #63.2. The push path writes tips at seq 1 (
BUNDLE_TIP_UPDATE_SEQ). Under the deployed rule0 < 1holds, so tips propagate today. It remains correct after a contract rebuild: a peer missing the key still needs it, one holding it at seq 1 still does not.Verification — controlled A/B on the live network
Two repos pushed minutes apart, differing only in this seq, then read from a third, fresh node that had to obtain both from the network:
update_seqext-probe(control)extensions (0)— lostseqfix-probe(fixed)extensions (2)— propagatedrefsandobject_indexintact in both, reproducing the original symptom exactly.User-visible effect, cloning each from that node:
Note on the seq test
bundle_tip_seq_survives_the_deployed_contracts_delta_ruledeliberately replicates the deployed rule instead of callingget_state_delta. That function is fixed in source now, so calling it would test the future and quietly stop testing what is actually running on the network. The test says so, and says to delete the replica when the contract is rebuilt — not before.Scope note
Existing seq-0 tips already written stay unpropagatable; new pushes write seq 1 and propagate, so repos heal as they are pushed. No regression either way — those bundles are treated as legacy and downloaded, which is exactly what happens today.
[AI-assisted - Claude]