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
|
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
|
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
|
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
|
unlink. `seed-target-dir-selftest.sh` uses the shape four times over, on two
|
||||||
consumer's own clone is what rotates the snapshot underneath it, or what
|
different commands. Scenarios 8a, 8b and 8c stub `cp`, so the consumer's own
|
||||||
loses a subtree of its own source, strictly inside the identity window. 8a
|
clone is what rotates the snapshot underneath it, loses a subtree of its own
|
||||||
does start a second real process — the actual `publish-snapshot.sh` — but the
|
source, or reports a failure over a tree that is in fact whole — each strictly
|
||||||
stub is what fixes where its swap lands; the concurrency is incidental to the
|
inside the window that clone's checks cover; scenario 10 stubs `cp` one level
|
||||||
determinism. Every stub asserts that it fired, because a scenario whose
|
down instead, refusing the per-file copies that unshare the mutable paths.
|
||||||
interference silently did not happen passes for the wrong reason.
|
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
|
**A synthetic stand-in for the other side, where that artefact *is* the
|
||||||
contract.** `publish-snapshot-selftest.sh` scenarios 6 to 8 hold a
|
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
|
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
|
*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
|
green because a sibling fires in its place. Scenarios 8a to 8d and 10 of the
|
||||||
suite do; scenario 9 of the same suite does not yet, which is why a mutation
|
seed suite do, and each is reddened by exactly one mutation of the clone's
|
||||||
survives it (issue #5).
|
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
|
The action YAML holds no logic beyond wiring; everything testable lives in
|
||||||
`scripts/`. A composite action needs `shell: bash` on every `run:` step, and
|
`scripts/`. A composite action needs `shell: bash` on every `run:` step, and
|
||||||
|
|||||||
@@ -52,7 +52,9 @@
|
|||||||
# identity read is reported as the string `missing`, so two of them
|
# identity read is reported as the string `missing`, so two of them
|
||||||
# compare equal to each other; without the term that rejects the
|
# compare equal to each other; without the term that rejects the
|
||||||
# sentinel, a clone whose source could not be identified at either end
|
# sentinel, a clone whose source could not be identified at either end
|
||||||
# is published on the strength of two errors.
|
# is published on the strength of two errors. A regression guard on a
|
||||||
|
# defensive term rather than a reproduction of a reachable state — see
|
||||||
|
# the scenario's own comment.
|
||||||
# All four scenarios force their interleaving rather than racing for it,
|
# All four scenarios force their interleaving rather than racing for it,
|
||||||
# and each asserts WHICH check caught the tear, so none stays green if
|
# and each asserts WHICH check caught the tear, so none stays green if
|
||||||
# the check it exercises is removed.
|
# the check it exercises is removed.
|
||||||
@@ -486,6 +488,15 @@ echo "=== 8d: an identity that could not be read at either end ==="
|
|||||||
# would differ from `missing` and the identity COMPARISON would become the
|
# would differ from `missing` and the identity COMPARISON would become the
|
||||||
# witness instead — 8a's property, not this one. The assertion that the stub
|
# witness instead — 8a's property, not this one. The assertion that the stub
|
||||||
# fired is therefore a count rather than a flag.
|
# fired is therefore a count rather than a flag.
|
||||||
|
#
|
||||||
|
# So be clear about what this scenario is. It is NOT a reproduction of a state
|
||||||
|
# a CI job reaches: every route to it is closed off — a rotation hands the
|
||||||
|
# witness to 8a, and a source that is genuinely gone fails `cp -al` and hands
|
||||||
|
# it to 8c. It is a REGRESSION GUARD ON A DEFENSIVE TERM, and the thing it
|
||||||
|
# defends against is a sentinel comparing equal to itself, which is a property
|
||||||
|
# of the code rather than of the filesystem. That is worth pinning precisely
|
||||||
|
# because nothing else can reach it: a term no fixture exercises is the one a
|
||||||
|
# refactor drops without argument.
|
||||||
IDENT=$(cache_key feat/identity-unreadable)
|
IDENT=$(cache_key feat/identity-unreadable)
|
||||||
IDENT_BASE=$(cache_key release/3)
|
IDENT_BASE=$(cache_key release/3)
|
||||||
make_wide_tree "$root/snapshot-$IDENT_BASE" identgen 4
|
make_wide_tree "$root/snapshot-$IDENT_BASE" identgen 4
|
||||||
|
|||||||
Reference in New Issue
Block a user