Problem
Two residual file-lifecycle races remain:
- backup reads use separate stat/read opens, so replacement or growth between calls can bypass the 64 MiB bound; no-force destination checks use existsSync followed by rename, so a destination created in the race window may be overwritten;
- bundle-cache cleanup can delete generic .tmp-* directories created by another cache/process without proving staleness or ownership.
Acceptance criteria
- Backup opens once, fstats and bounded-reads the same handle.
- No-force publication uses an atomic no-replace primitive; force replacement remains explicit.
- Bundle cleanup skips locally pending fingerprints and deletes only age-gated stale candidates.
- Fault/concurrency tests cover replacement, growth, competing destination creation, and active materialization cleanup.
Evidence
Review anchors: packages/core/src/cli/storage.ts around backup read/publish (~812-849) and packages/core/src/storage/bundle-cache.ts around cleanup (~170).
Problem
Two residual file-lifecycle races remain:
Acceptance criteria
Evidence
Review anchors: packages/core/src/cli/storage.ts around backup read/publish (~812-849) and packages/core/src/storage/bundle-cache.ts around cleanup (~170).