LeVCS/crates
Levi Neuwirth ee886f6138
Tell a legacy root apart from one this store cannot read
Startup states 3 and 4 shared a refusal that named both, because the
recognizer separating them had not been written. That under-classification
was honest but it cost the operator the answer: a legacy instance holding
real data and a directory belonging to something else got the same
"refusing to modify", and only one of them has a next step.

State 3 is the scope's signature and nothing looser: at least one entry
whose name is 64 hexadecimal characters and which holds a `.levcs`
directory. It is recognized, never inferred -- a missing FORMAT is
evidence of nothing, and most roots without one are state 4.

Neither half of the signature may be reached through a symlink. The entry
type comes from the directory entry itself and `.levcs` is checked with
`symlink_metadata`, because the claim being made is about what this root
*holds*, not what a link in it can reach. `Path::is_dir` follows links, so
a `.levcs` pointing at any directory anywhere satisfied it, and a root
that borrowed the shape from elsewhere was answered with a migration
command for repositories it does not have.

The recognizer opens no file and creates nothing, and runs last -- only
for a root already known non-empty and unformatted. A probe that wrote so
much as a directory would destroy the byte-identity guarantee state 4
promises, and destroy it before the refusal that promises it.

State 3 carries the exact command with the source filled in and
`--destination` left as a placeholder: only the operator knows where the
v2 root goes, and inventing one produces a command that runs and writes
somewhere nobody chose. State 4 reports what was observed and suggests
nothing, because the directory may be another application's, a partial
backup, or a root whose FORMAT was deleted, and a migration taken on faith
costs data. A test asserts state 4's message does not mention migration.

`tree_image` no longer follows links either. It records a symlink as its
own kind carrying its target, which is both what let the new tests be
written and strictly more sensitive than before: it now notices a symlink
replaced by a real directory of the same name, the one substitution a
contents-only image called unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JGdH5V43XWnj1PdHqiPktQ
2026-08-09 17:49:46 +02:00
..
levcs-cli Robustness fixes 2026-05-03 20:56:46 -04:00
levcs-client integrate CI and docs 2026-05-01 11:29:18 -04:00
levcs-core integrate CI and docs 2026-05-01 11:29:18 -04:00
levcs-identity Freeze Wave A: Phase 1 storage spine 2026-07-26 19:47:03 -04:00
levcs-instance Robustness fixes 2026-05-03 20:56:46 -04:00
levcs-merge integrate CI and docs 2026-05-01 11:29:18 -04:00
levcs-protocol Condition benchmark claims on the run that produced them 2026-07-29 17:38:03 -04:00
levcs-store Tell a legacy root apart from one this store cannot read 2026-08-09 17:49:46 +02:00
levcs-tui integrate CI and docs 2026-05-01 11:29:18 -04:00