docs(readme): correct the scenario census and the #5 citation
Review of #9 found the methodology section falsified by that PR and owned by nobody — the scoping that fenced it off was wrong, #8 never touches these paragraphs. Three fixes: * The PATH-stub paragraph named 8a and 8b as the seed suite's stubs. It is now four scenarios on two commands: 8a/8b/8c stub `cp` at the clone, 10 stubs it one level down at the per-file unshare, and 8d stubs `stat` — a mechanism the paragraph did not mention at all, and the only way to make an identity that could not be READ the sole witness. * The assert-which-guard-fired paragraph cited issue #5 as a live example of a surviving mutation. #5 is the issue this PR closes, so a reader following that citation landed on "removing it leaves every suite green", which is no longer true. Scenario 9's own sentence stands — the matrix confirms it survives every mutant — so it now says WHY it survives (an unreadable source leaves the staging dir at mode 000, and the unshare pass aborts the clone before the copy's exit status is consulted) instead of citing a closed issue. * Added the mutual-masking hazard the sweep turned up, since it is the general lesson rather than a fact about two particular terms: two guards that can each catch the same fault make each other unnecessary, so no fixture built around that fault pins either one. Also names scenario 8d for what it is in its own comment — a regression guard on a defensive term, not a reproduction of a reachable state. Every route to the state it constructs is closed off (a rotation hands the witness to 8a, a genuinely absent source hands it to 8c), which is the reason it is worth pinning rather than a reason to doubt it.
This commit is contained in:
@@ -381,13 +381,20 @@ key and asserts only what must be true whichever of them wins the rename.
|
||||
which places the interference inside the window rather than hoping it lands
|
||||
there. `prune-cache-selftest.sh` scenario 12 stubs `du`, so the pass's own
|
||||
measurement publishes a reader marker strictly between its check and its
|
||||
unlink; `seed-target-dir-selftest.sh` scenarios 8a and 8b stub `cp`, so the
|
||||
consumer's own clone is what rotates the snapshot underneath it, or what
|
||||
loses a subtree of its own source, strictly inside the identity window. 8a
|
||||
does start a second real process — the actual `publish-snapshot.sh` — but the
|
||||
stub is what fixes where its swap lands; the concurrency is incidental to the
|
||||
determinism. Every stub asserts that it fired, because a scenario whose
|
||||
interference silently did not happen passes for the wrong reason.
|
||||
unlink. `seed-target-dir-selftest.sh` uses the shape four times over, on two
|
||||
different commands. Scenarios 8a, 8b and 8c stub `cp`, so the consumer's own
|
||||
clone is what rotates the snapshot underneath it, loses a subtree of its own
|
||||
source, or reports a failure over a tree that is in fact whole — each strictly
|
||||
inside the window that clone's checks cover; scenario 10 stubs `cp` one level
|
||||
down instead, refusing the per-file copies that unshare the mutable paths.
|
||||
Scenario 8d stubs `stat`, because the check it pins fires on an identity that
|
||||
could not be READ rather than on one that changed, and the only way to make
|
||||
that the sole witness is to fail the identity reads while the copy between
|
||||
them succeeds. 8a also starts a second real process — the actual
|
||||
`publish-snapshot.sh` — but the stub is what fixes where its swap lands; the
|
||||
concurrency is incidental to the determinism. Every stub asserts that it
|
||||
fired, because a scenario whose interference silently did not happen passes
|
||||
for the wrong reason.
|
||||
|
||||
**A synthetic stand-in for the other side, where that artefact *is* the
|
||||
contract.** `publish-snapshot-selftest.sh` scenarios 6 to 8 hold a
|
||||
@@ -404,9 +411,19 @@ does it any more.
|
||||
|
||||
Where more than one guard could catch a fault, a scenario should assert
|
||||
*which* one did — otherwise deleting the guard under test leaves the suite
|
||||
green because a sibling fires in its place. Scenarios 8a and 8b of the seed
|
||||
suite do; scenario 9 of the same suite does not yet, which is why a mutation
|
||||
survives it (issue #5).
|
||||
green because a sibling fires in its place. Scenarios 8a to 8d and 10 of the
|
||||
seed suite do, and each is reddened by exactly one mutation of the clone's
|
||||
checks. Scenario 9 does not, and a mutation still survives it: with its source
|
||||
unreadable, `cp -al` leaves the staging directory at mode `000`, so the
|
||||
unshare pass aborts the clone before the copy's own exit status is ever
|
||||
consulted, and the assertion is satisfied down a path it was not written for.
|
||||
|
||||
That is the standing hazard here, and it is not hypothetical. Two guards that
|
||||
can each catch the same fault mask each other, so **neither** is individually
|
||||
necessary and no fixture built around that fault can pin either one — which is
|
||||
how both `[ "$cp_rc" -eq 0 ]` and `[ "$i_before" != missing ]` sat unpinned
|
||||
(issue #5) while looking well covered. Isolating a check means constructing
|
||||
the state only it can see, not the state that trips several at once.
|
||||
|
||||
The action YAML holds no logic beyond wiring; everything testable lives in
|
||||
`scripts/`. A composite action needs `shell: bash` on every `run:` step, and
|
||||
|
||||
Reference in New Issue
Block a user