Skip to content

bd purge: live-dependent protection is one level deep, so a closed root with an open grandchild is deleted (#6883) #7031

Description

@bee-ghosttrack

Post-merge audit of #6883 at main 448c4b8. The same code is on hotfix/1.3.1 (cherry-picked in #6905, patch-identical), so this is in v1.3.1-rc.2.

1. The protection looks one level down (major)

#6883 says a sweep never deletes the root, container or gate of work that is still in progress. That holds for a two-level hierarchy only.

SweepLiveDependentTargets (internal/workapi/sweep.go:337) protects a candidate when the source of a protecting edge is in live, and live is built from the dependents' not-done statuses (BuildSweepLiveDependentScanFilter). A closed candidate that is itself protected is not in live, so it does not protect its own parent. The callers (internal/storage/issueops/sweep.go:97, internal/storage/uow/sweeper.go:151) run the check once.

Reproduced on a scratch embedded workspace with a bd built at 448c4b8:

  1. Create ephemeral R, then S1 with --parent R, then S2 with --parent S1.
  2. Close S1 and R. S2 stays open.
  3. bd purge --force --json reports purged_count: 1, live_dependent_skipped: 1.

Afterwards R is gone, S1 is still there (closed, protected by S2) with no parent, and the open S2 hangs off an orphaned S1. --dry-run reports the same deletion, so the two modes agree with each other; both are wrong about R.

A fix that fits the current shape: iterate to a fixed point, adding each protected candidate to live until the protected set stops growing.

2. --wisps-plane deletes cited no-history beads that bd prune keeps (minor)

cmd/bd/purge.go:103-114 switches the tier to SweepWispsPlane and keeps purge's scope otherwise, so the reference protection that bd prune applies is off. --wisps-plane reaches --no-history beads, which are durable-tier.

With a closed --no-history bead N and an open bead whose text cites N:

  • bd prune --pattern N --dry-run → referenced_skipped: 1, pruned_count: 0
  • bd purge --wisps-plane --pattern N --dry-run → purge_count: 1

If that difference is intended, the help text should say so; if not, the wisps-plane scope wants protectReferenced.

3. Test gap (nit)

Custom statuses count as live in the code, and nothing pins it: passing nil for the custom statuses in the live-dependent filter leaves the embedded TestSweeperContract green.

What was checked and found sound

Edges consulted (parent-child, tracks, blocks, from both dependency tables, direction "points at the candidate"); the check and the delete share one transaction in all three backends; negative --limit is refused; --wisps-plane requires --older-than or --pattern in both the CLI and the role; --force does not bypass the protection; JSON counts come from the actual delete. Mutation: turning protectLiveDependents off in the CLI turns TestEmbeddedPurgeWispsPlaneRetention red. Not executed: the dolt-server and uow TestSweeperContract variants and TestProxiedServerPurgeWispsPlaneRetention.

bee, beads-lane steward

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugBroken existing behaviorpriority/p0Critical: data, security, or basic operation blocked

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions