Problem
When a contract's mirror mode flips (snapshot → history or vice versa), bundles published under the old mode become dead-weight: no current ref points at their tip, no clone can be served from them, but they stay in object_index forever because the contract is append-only by design.
After the flip, auto-detect reads the new mode and turns off the dead-weight filter for the bundle class that's now legitimate. Rescue then iterates all bundles — including the old-mode orphans — every cron cycle. Rescue cost grows monotonically with mode-flip count over the contract's lifetime.
Filed as a refinement that avoids the contract-migration risk of #47 (now closed in favour of this).
Approach
Add a client-side --prune-dead-weight flag to freenet-git rescue that computes the dead-weight set from local state alone and SKIPS rescuing those bundles — same rescue-cost effect as a real object_index prune, without touching the contract.
The dead-weight check builds on reachable_bundle_ids (already implemented for --only-current-tips). The difference:
--only-current-tips says "rescue ONLY bundles whose tip is in refs." Correct for snapshot mode; wrong for history mode (would skip ancestor commits reachable via parent chain).
--prune-dead-weight says "rescue everything EXCEPT bundles whose tip is NOT in refs AND NOT in any reachable ancestor path." Correct for history mode after a mode-flip cleanup.
The second predicate needs a way to identify "ancestor commits of current refs" — which requires walking the commit graph. Two implementations:
- Local-clone-assisted (
--prune-dead-weight --from <git-dir>): operator points the helper at a local clone. Helper does git merge-base --is-ancestor checks. Cheap but requires operator to have a clone.
- Network-walked: helper fetches the branch tip's bundle, parses commits, walks parents using bundle-tip extensions to identify which old bundles are still ancestor-reachable. Slow and complex but works without local clone.
Option 1 is cleaner; Option 2 is similar in shape to walk_unresolved_parents from PR #33. Start with Option 1.
Constraints
- No contract change.
object_index stays as-is; rescue just skips work.
- Bundles still occupy contract bytes. Acceptable cost; the rescue-iteration savings is the real win.
- Other rescuers without the flag still pay the full cost. Document this — the flag is opt-in cleanup, not a contract-wide fix.
Related
[AI-assisted - Claude]
Problem
When a contract's mirror mode flips (snapshot → history or vice versa), bundles published under the old mode become dead-weight: no current ref points at their tip, no clone can be served from them, but they stay in
object_indexforever because the contract is append-only by design.After the flip, auto-detect reads the new mode and turns off the dead-weight filter for the bundle class that's now legitimate. Rescue then iterates all bundles — including the old-mode orphans — every cron cycle. Rescue cost grows monotonically with mode-flip count over the contract's lifetime.
Filed as a refinement that avoids the contract-migration risk of #47 (now closed in favour of this).
Approach
Add a client-side
--prune-dead-weightflag tofreenet-git rescuethat computes the dead-weight set from local state alone and SKIPS rescuing those bundles — same rescue-cost effect as a realobject_indexprune, without touching the contract.The dead-weight check builds on
reachable_bundle_ids(already implemented for--only-current-tips). The difference:--only-current-tipssays "rescue ONLY bundles whose tip is in refs." Correct for snapshot mode; wrong for history mode (would skip ancestor commits reachable via parent chain).--prune-dead-weightsays "rescue everything EXCEPT bundles whose tip is NOT in refs AND NOT in any reachable ancestor path." Correct for history mode after a mode-flip cleanup.The second predicate needs a way to identify "ancestor commits of current refs" — which requires walking the commit graph. Two implementations:
--prune-dead-weight --from <git-dir>): operator points the helper at a local clone. Helper doesgit merge-base --is-ancestorchecks. Cheap but requires operator to have a clone.Option 1 is cleaner; Option 2 is similar in shape to
walk_unresolved_parentsfrom PR #33. Start with Option 1.Constraints
object_indexstays as-is; rescue just skips work.Related
[AI-assisted - Claude]