fced39c798
Three blobs survived the sweep. Five seconds of async-unlink polling did not remove them, so they were never queued — GC had not judged them collectible, and the loop had already exited. It broke on the FIRST zero-reap pass. A single zero only says nothing was collectible at that instant: releases cascade, since reaping a source drops the references its derived and attached rows held and `on_blob_deleted` does that from spawned tasks, so a pass can land in the gap between "source reaped" and "dependents released" and report zero with work outstanding. The import jobs added a level to that chain, which is when it started biting. Now two consecutive zeros, with the bound raised to match — one extra trigger over an empty store is cheaper than a false pass reporting a clean disk. The rest is diagnosis, because a list of paths cannot tell the three causes apart and they need opposite fixes: a positive refcount means a release was missed, an orphan means the reap predicate has a gap, and a row without a manifest means the registry is inconsistent. Each leftover now reports its manifest and blob refcounts plus how many files, derived rows and attached rows point at it — so if this is a real leak rather than the race, the next run names it instead of costing another full pass through the suite.