test(seed): pin the two unguarded terms of the torn-clone condition
hardlink_clone_into's torn-clone detection is a four-term condition, and a mutation sweep found two of the four unpinned: removing either `[ "$cp_rc" -eq 0 ]` (issue #5) or `[ "$i_before" != missing ]` left all five suites green. They were unpinned for the same reason — they mask each other. A source that vanishes mid-clone reads as `missing` at both ends AND fails `cp -al`, so with both terms present either one catches it and neither is individually necessary. Isolating them needs a state each term alone can see: 8c cp reports failure over a tree that is in fact whole. Neither inference sees anything — 0 entries short, one unchanged inode — so the exit status is the only witness. Forced with the PATH stub 8a/8b already use, on the consumer's own top-level clone. 8d both identity reads fail while the copy succeeds. `_dir_inode` folds every stat failure into the string `missing`, so two failed reads compare equal TO EACH OTHER; without the sentinel term the tree is published on the strength of two errors. Stubs `stat` narrowly — only the `%i` reads of this clone's own source — because taking the source away would fail `cp -al` too and pin 8c's property over again. The sweep also found `_unshare_files`'s xargs status unpinned, which is the guard that stops a staging tree whose dep-info files still point at the SOURCE's inodes from being renamed into place — not a tear, so all four clone checks pass it, and exactly the silent cross-branch stale-reuse the scheme exists to prevent. Scenario 10 pins it by refusing the dep-info unshares and asserting the clone discards rather than publishes. assert_tear gains an `unreadable` identity expectation, and its `same` case now demands a READ identity rather than two equal strings — `missing` equals `missing`, which is the exact confusion 8d exists to pin. Each of the five pinning scenarios was run in isolation against each mutation; the result is a clean diagonal, so every scenario fails only for its own term. Closes #5
This commit is contained in:
@@ -361,7 +361,7 @@ bash scripts/selftest.sh --fast # fixture-only suites, no compiler
|
||||
| suite | covers |
|
||||
|---|---|
|
||||
| `hardlink-clone-selftest.sh` | that a build in a clone cannot mutate its source — with a control proving a raw `cp -al` does. Needs a real compiler. |
|
||||
| `seed-target-dir-selftest.sh` | seed-source preference, lock-file stripping, two jobs racing on one cache key, **and a seed whose source is rotated — or silently loses a subtree — underneath its clone**: the two ways a hardlink clone tears |
|
||||
| `seed-target-dir-selftest.sh` | seed-source preference, lock-file stripping, two jobs racing on one cache key, **and one scenario per check a hardlink clone is validated against**: a source rotated wholesale, a subtree silently lost from the walk, a copy that reports failure over a tree both other checks read as whole, and a source identity that resolved at neither end — plus a staging tree that could not be privately owned being discarded rather than published |
|
||||
| `publish-snapshot-selftest.sh` | the atomic swap, that a live consumer survives a republish, and the publisher's side of the rotation race: deferred reclamation under a live reader, and its sweep once the reader is gone |
|
||||
| `prune-cache-selftest.sh` | liveness, protection, locking, eviction order, self-clear, **and that a cache a job claims *inside* the check-to-unlink window survives it** — against a real scratch `origin` |
|
||||
| `restore-mtimes-selftest.sh` | the merge hazard and the watermark that closes it, including the two-jobs-one-namespace case. Needs a real compiler. |
|
||||
|
||||
Reference in New Issue
Block a user