Build the promised artifact placement convergence pass
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 4
- Forks
- 1
- Avg merge
- 3h 7m
- Merged PRs (30d)
- 523
Description
Problem
artifact-source.md promises a repair pass that reconciles artifact placement with the content store. It does not exist — there is no repair_artifact_placements anywhere in the tree.
That matters because placement completeness cannot be a write-side invariant. reclaim_guard.rs deliberately deletes the placement row before the bytes, documenting that an interruption leaves a digest whose row claims nothing while the bytes survive — the safe direction, since the reverse would promise bytes that are gone. So a correct system produces absent rows, and only convergence can restore the table.
There is a second problem with the pass as documented: it describes iterating placements. A pass that iterates placements can never add a row that was never written. To converge, it has to iterate the content store and reconcile against the table.
Required change
Build the convergence pass the documentation already promises, driven by the content store rather than by the table.
It must add rows for digests the store holds and the table does not, and retire rows for digests the table claims and the store does not. Both directions occur: the first from the eleven byte-commit paths that never recorded, the second from the reclaim window.
Bound it. The content store is the largest thing in the system, so this is a cursor pass with a resumable position, not a single transaction — the retention scan's batching is the local precedent, and the checkpoint work in peryx-storage is the more recent one.
Say what a concurrent reader observes mid-pass, and what happens when the pass races the reclaim window it exists to repair. Repairing a row that reclaim is deliberately removing would resurrect a promise the system just retired.
PlacementEvent is decided here, not before
PlacementEvent has four variants and only BytesVerified is constructed in production. The obvious move — delete the unconstructed ones — is wrong: Repaired { present: false } is precisely the transition that absorbed BytesRemoved when #2140 removed it, so deleting it deletes the eviction transition. And with only BytesVerified remaining, after() collapses to a one-arm match and the enum earns nothing.
So there are two honest outcomes, and this issue is where the evidence to choose exists:
- Wire the lifecycle up. This pass constructs
Repaired, eviction constructsRepaired { present: false }, and the enum becomes real. - Delete
PlacementEventoutright and replaceapply_placement_eventwith a directmark_local, if the lifecycle it models is not one the system actually has.
"Keep only the constructed variants" lands on neither and should not be the answer.
Acceptance criteria
- A digest the store holds with no placement row gains one; a row whose digest the store lacks is retired.
- The pass is resumable and bounded, and an interrupted run leaves the table no worse than it found it.
- Racing the reclaim window does not resurrect a row reclaim is removing; a test drives that interleaving by construction.
PlacementEventends this issue either fully wired or deleted, with the reasoning recorded.- The documentation in
artifact-source.mdmatches what was built, including the iteration direction.
Boundary
The convergence pass and the event model it settles. Blocked by #2151, which defines what an absent row means — the pass cannot decide what to repair until absence has one meaning. #2141 adds the missing writes and should land first, so this converges a smaller gap.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with artifact-source.md and reclaim_guard.rs, then compare the retention scan's batching with the checkpoint work in peryx-storage. Trace PlacementEvent, apply_placement_event, and the content-store and placement-table entry points, while checking the dependency decisions in #2151 and #2141. Done means a bounded resumable pass, a constructed reclaim-race test, a resolved event model, and matching documentation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, database
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100