Basekick-Labs / Basekick-Labs/arc
ListStaged reports partials its own DeleteStaged refuses
- Dominant language
- Go
- Stars
- 677
- Forks
- 53
- Avg merge
- 9h 14m
- Merged PRs (30d)
- 164
Description
Split out of #762 (item 3). The first half is sized for a first contribution; the second half is a design call and is explicitly out of scope here.
## Problem
`internal/storage/local.go` `ListStaged` (line 1163) walks for `*.part`, strips `PartSuffix`, and reports the result as the key the partial belongs to. `DeleteStaged` (line 1044) resolves that key through `stagedPath` (line 985), which validates the key body. Two consequences:
1. A partial whose key fails `validateKeyBody` (a legacy partial of a key that is illegal today) is reported by `ListStaged` and then refused by `DeleteStaged`: the enumerator's output is not accepted by its own deleter. `reclaimStagedPartials` (`internal/api/databases.go:790-809`) pipes one into the other, so `DROP DATABASE` logs a warning for that file on every run and never reclaims it.
2. A *committed* object whose real name ends in `.part` (legal before #744) is reported as a partial of a different key, and `DeleteStaged` on that key removes the committed object:
```
ListStaged("") -> "db/cpu/2026/09/12/13/legacy" (a key no object has)
DeleteStaged("db/cpu/2026/09/12/13/legacy") -> nil (removed legacy.part)
```
## What to change (this issue: consequence 1)
Make `ListStaged`'s contract match its consumer: validate the stripped key with the same rule `stagedPath` applies and do not report entries that `DeleteStaged` would refuse. Those files are not lost from view: `ListUnusable` (#765) already reports what `List` drops, and a `.part` file with an illegal key body is dropped by `List`; add a test asserting that the same file shows up there. Pin the invariant with a test: every key `ListStaged` returns is accepted by `DeleteStaged`.
## Out of scope (consequence 2, needs a design call)
On disk, a staging partial for key `k` and a committed object named `k.part` are the same path, so no enumerator can tell them apart by name. Fixing that means an unambiguous staging spelling (a distinct prefix or directory for partials, the way `.sync-staging/` is for edge sync), which touches the write path. Mention it in your PR description; do not attempt it there.
## Scope
One file plus tests. `go test ./internal/storage/...`. Release-notes entry per CONTRIBUTING.
Contributor guide
Research direction
Start in internal/storage/local.go with ListStaged, stagedPath, and DeleteStaged, then inspect ListUnusable and reclaimStagedPartials in internal/api/databases.go. Run go test ./internal/storage/... and add tests showing illegal-key partials are omitted from ListStaged, reported by ListUnusable, and that every returned key is accepted by DeleteStaged. Finish with the required release-notes entry from CONTRIBUTING; leave the naming collision out of scope.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100