Repository navigation
Description
Problem
FileOps::rename and FileOps::copy archive a file they replace under the name <name>.archive.<unix-ts>, where the timestamp has one-second resolution and the archive write is an unconditional tag set. Two replaces of the same destination within the same second produce the same archive name, so the second archive silently overwrites the first — the first replaced version loses its only archive reference (and, once its tag is gone, its protection from blob garbage collection). The web save path has the identical pattern in archive_original_tag.
Evidence (verified on main 63ba170)
pkgs/id/src/fileops.rs:153-162—archive_existing:let archive = format!("{name}.archive.{ts}"); store.tags().set(&archive, existing.hash)— no existence check, no uniquifier;setreplaces an existing tag (the same replace semanticsput/renamerely on for the destination name itself).pkgs/id/src/fileops.rs:238(rename) and:319(copy) —let ts = now_unix();taken once per call;pkgs/id/src/tags.rs:1735-1740—now_unix()isduration_since(UNIX_EPOCH).as_secs(), i.e. second resolution.pkgs/id/src/fileops.rs:248— the rename-original archive uses the same{from}.archive.{ts}shape.pkgs/id/src/web/routes.rs:2170-2176— sibling:archive_original_tagbuildsformat!("{name}.archive.{timestamp}")withas_secs()andstore.tags().set(...)— the web document-save path, where successive saves in one second are routine.pkgs/id/src/fileops.rs:388-410—deleteenumerates archives by exactly this<name>.archive.<digits>pattern, so an overwritten archive is not merely mislabelled: from the store's point of view it never existed.
Verification: verbatim-logic probe crate run ON THE LAPTOP (probe ~/git/fleet/probe-2026-10-28-laneB-archive there; fileops.rs sha256 d1e49762 and routes.rs sha256 2667e857 identical in the sandbox clone at main 63ba170 and the laptop clone; probe models tags().set with its documented replace semantics):
BUG arch1=Some("b.md.archive.1730000000") arch2=Some("b.md.archive.1730000000")
BUG archive_tag_value=Some("HASH_A") (V1 archive survives? false)
BUG archive_count=1 tags={"b.md.archive.1730000000": "HASH_A", "b.md": "HASH_C"}
CTRL archive_count=2 v1_kept=true a_kept=true
Two replaces of b.md in the same second (V1 → A → C) leave one archive, pointing at A; V1's archive is gone. The same two replaces one second apart (control) leave two archives and both versions survive.
Why it matters
The archive is the safety net the module doc promises: "rename and copy archive a file they replace" (fileops.rs:14-18). Scripted/bulk renames, REPL loops, test suites, and rapid web saves routinely replace the same name twice in one second — exactly the bursts where an operator most needs the earlier version back. The loss is silent: both operations report success, each returns an archived_replaced name, and the two names are identical strings, so even the archive.replace metadata tag cannot reveal that one archive replaced the other. Related but distinct from #1549 (web image-upload naming collision) and #1605 (mcpx exec artifact overwrite): this is the blob-tag archive namespace in FileOps plus its routes.rs sibling.
Acceptance criteria
- Archiving a replaced file never overwrites an existing archive: if
<name>.archive.<ts>is taken, a distinct archive name is chosen (e.g. a suffix/counter) while staying inside the<name>.archive.<...>enumeration shapedeleteandclassify_tagunderstand — or the enumeration/classification is updated in the same change. - The same fix (or a shared helper) covers
archive_original_taginweb/routes.rs, so the save path cannot collide either. - Unit/integration tests: two replaces of the same destination with the same timestamp produce two distinct archives holding the two distinct hashes; the returned
archived_replaced/archived_originalnames are the names actually written. - Existing rename/copy/delete archive tests still pass; single-replace behaviour and archive naming for the common case are unchanged.
Activity
developing-today commentedon Oct 28, 2026
CODE-636
dezren39 commentedon Oct 28, 2026
🤖 Agent note · filed
75 remaining items
id(fileops): unique archive tag names so same-second replaces never o…
dezren39 commentedon Oct 28, 2026
🤖 Agent note · done