test(disk-accounting): a gate for the root-only bash nothing ever ran β plus the live defects three audit rounds found in it - #1366
Conversation
β¦ no gate ever ran
`scripts/diagnose-disk-accounting.sh` had no test file, in a repo with no
shellcheck gate. That is why a root command injection, an E2BIG abort and a hard
`0.0 GiB` all shipped invisibly: nothing here was ever executed by a gate, so
nothing here could be SEEN to be wrong.
The script refuses non-root (rc 2) by design, so the suite could not run it. It
now carries a sourceable seam β `BASH_SOURCE[0] != $0` returns before the root
check β and nine helpers hold the transforms verbatim. Sourcing measures
nothing; executing is byte-for-byte the same report as before.
Covered, each with a positive control and each watched red:
* lsof column resolved from the HEADER, pinned with TWO fixtures (+L1 -> col
7, plain -n -P -> col 9) whose every other column sums to 0.0, so a fixed
index cannot pass both
* root command injection, driven by a directory named
`evil";echo PWNED-AS-$(id -un) >&2;"x` and asserted on the EXPANSION
* E2BIG, with the fixture sized from the live ARG_MAX and the glob form
watched to die rc 126 first
* the foreign-mount listing, the device split, the denial counts, the
`grep -c` two-line zero, and the never-negative lsof count
Three defects found while writing them, and fixed:
a) `OUT=$(lsof β¦); RC=$?` is a CHECKED command under `set -e` and lsof exits 1
when it finds nothing, so section 7's no-rows message β written precisely
to stop a zero being read as a measurement β was unreachable, and sections
7 and 8 never printed.
b) `<producer> | sort | head -N` under pipefail is a SIZE-DEPENDENT abort:
measured 40 entries survive rc 0, 20,000 die rc 141, silently. The real
/tmp had 171,886. Replaced by head_n (awk reads to EOF) at all nine sites.
c) a second glob-into-du in section 5.
19 mutants, all KILLED by their own guard, in
`scripts/tests/mutants-diagnose-disk-accounting.sh`.
Claude-Session-Id: c8eedffa-36a5-41b3-b250-99d5d2851435
β¦guard it is `du` never starts a shell, so no mutation of `size_breakdown` short of adding `sh -c` could make that assertion go red. It pins a real property β the pipeline stays shell-free β but it is not a test of a bug that ever existed, and counting it as regression coverage overstates what this suite guards. Named so a reader stops at the right line. Claude-Session-Id: c8eedffa-36a5-41b3-b250-99d5d2851435
β¦e whole run, and three guards were narrower than their own descriptions Round-1 audit findings on #1366. Five payload fixes, two guard fixes, 56 new assertions (71 -> 127) and 22 new mutants (19 -> 41). PAYLOAD F1 A vanished entry mid-scan aborted the report, at three sites. GNU find exits 1 when an entry disappears between readdir and stat; the target host's /tmp holds ~270,000 churning top-level entries and section 4's own counter already calls those ENOENTs "benign, transient". 2>/dev/null ate the message, pipefail promoted it, set -e killed the run: sections 6d-inodes, 7 and 8 never printed and the report ended with NO error. Third route to the same truncated-scan failure (E2BIG and SIGPIPE were the first two). MEASURED A FOURTH ROUTE the audit did not name and the prescribed fix does not cover: xargs exits 123 when any du it ran exited 1 β the same vanished entry, one step later. Both stages are now `|| true` at all three sites (size_breakdown, inode_breakdown, section 5) plus section 6c's own xargs. Isolated fixtures: a mode-0400 directory makes find exit 1 while still listing (route a); a mode-000 subdirectory makes du exit 1 with find at 0 and the readable sibling still reported (route b). F3 The seam was reachable from the environment and the comment denied it. `[ "${BASH_SOURCE[0]}" != "$0" ]` β bash imports BASH_SOURCE from the env as a scalar, so `env 'BASH_SOURCE=(nope)' bash <script>` took the SOURCED branch and returned at top level: rc 2, no report. rc 2 is also the script's "you forgot sudo" status, so two causes shared one exit code β which is why only a guard asserting the refusal MESSAGE can see it. `bash < script` was the mirror image: BASH_SOURCE unset, set -u, dead on the guard's own line, rc 1 (base ran fine that way, so that half was a genuine regression). Replaced with `(return 0 2>/dev/null)`, which reads no variable. The header comment now states what was measured instead of asserting immunity. F4 LSOF_BIN was a test seam that widened what a ROOT run executes. `LSOF_BIN=${LSOF_BIN:-lsof}` sat ABOVE the seam, so an inherited LSOF_BIN=/anything was what root would run; base hardcoded `lsof`. The override now lives inside the sourced branch; the execute path pins `LSOF_BIN=lsof`. Observed through `bash -x` on the real script, since the execute path exits 2 before printing anything of its own. F5 Defect 7 (`du -x` is not "only this filesystem") was fixed at /home and left standing at /tmp. `-xdev` stops find DESCENDING past a mount but still LISTS the mountpoint at depth 1, so a mount under /tmp arrived as a starting point for `du -sh -x` β the exact case du -x cannot handle β and its whole size landed in a figure labelled root-fs /tmp usage. Section 6d now applies the same per-candidate device comparison section 6c uses, in both breakdowns, via a single `_on_device` filter over `find -printf '%D\t%p\0'` (one process, not a stat fork per entry). `_dev_of` moved from `stat -c '%D'` (HEX) to `stat -c '%d'` (DECIMAL) so it matches find's own %D β one format everywhere. `du -x` itself is now pinned structurally, labelled as the invariant pin it is: a second filesystem needs root, so it cannot be checked behaviourally. π΄ THE FILTER CREATES ITS OWN BLIND SPOT, so section 6d now REPORTS what it excluded (`foreign_entries`), the way 6c already does for /home. Dropping a foreign mount is right; dropping it silently is the floor-presented-as-a- total failure this whole file catalogues. The device id is also validated as DIGITS in one place (`_dev_is_valid`) before it is interpolated into a `sed` script β `stat -c '%d'` cannot produce a `/` today, which is an argument for checking it once rather than trusting it at three call sites. F7 split_by_device wrote the foreign list one-per-line and read it with `while IFS= read -r`, so a directory name containing a newline split into two rows in the report. Both lists are NUL-separated now. GUARDS F2 The `head` ban was SPELLED, not structural: `| *head -[0-9]` requires a digit right after `head -`, so `head -n 20` β the POSIX-preferred spelling β walked straight through. Widened to `| *head *-`; `head -n 20`, `head -n 30`, `head -n 15` and `head -15` are all now caught, each as its own mutant. The canary line deliberately spells it `head -n 20`, so narrowing the pattern back trips the SCANNER-BROKEN branch instead of reporting ok. F2 The E2BIG fixture was 17,550 touched FILES and inode_breakdown filters `-type d`, so it returned ZERO rows and the `inode_breakdown "$BIG"` call in the SIGPIPE probe was vacuous β the whole battery row was killed by size_breakdown alone. The fixture now also builds 600 directories (~140 KiB of sort output against a 64 KiB pipe buffer; under ~258 rows the SIGPIPE abort does not fire at all), the probes are split one per breakdown, and a non-vacuity assertion fails loudly if the row count ever returns to 0. Also fixed while doing the above: adding `2>/dev/null` to inode_breakdown's new xargs stage silently disarmed the injection guard β the historical defect's own proof of execution is a planted name printing to STDERR. Caught by the battery scoring that row WRONG-KILLER, not by a green suite. EVIDENCE new suite vs the base script (491fc44): 43 FAIL, 84 ok new suite vs HEAD: 0 FAIL, 127 ok mutation battery: 41 mutants, every one KILLED by its own guard; baseline green (127 ok / 0 FAIL), negative control red (6 FAIL), SURVIVES control unkilled NOT FIXED, deliberately: `stat -f /` measures the real root fs while `dumpe2fs` measures $DEV and nothing cross-checks they are the same filesystem. Reported, not touched β it only executes under root and I cannot verify a change to it. Claude-Session-Id: c8eedffa-36a5-41b3-b250-99d5d2851435
Round 1 audit β claims blockRound 1 was a first, full adversarial audit (7 findings: 1 π΄, 4 π‘, 2 π’). Its fixes landed in Independently re-verified before round 2 (not accepted from the fix round's report)Re-ran in a detached worktree at
Ledger β |
β¦ntly DROPPED the entries it could not stat, and said "none" Round 2 of the audit ladder found 2 π΄ and 3 π‘. The headline one was created by round 1's own fix, which is the third consecutive round where a fix round produced the next round's finding. ## π΄ F-1 β `-printf '%D\t%p\0'` emits NOTHING for an unstattable entry Round 1 swapped `-print0` for `-printf '%D\t%p\0'` to get the device id for the new per-candidate device filter. But `%D` forces find to STAT each entry to format the record, and when that stat fails GNU find emits no record at all β where `-print0` still emitted the name. Measured on two implementations: -print0 345 bytes, rc 0 (bfs 4.1.1) -printf '%D\t%p\0' 0 bytes, rc 1 (bfs 4.1.1) findutils 4.10.0 0 bytes; 4.11.0 (nix dev shell) 119 / 0 bytes Consequence: an entry root cannot stat β a FUSE mountpoint without `allow_other` (AppImage `/tmp/.mount_*`, gvfs, sshfs), or a device returning ESTALE/EIO β vanished from `size_breakdown`, from `inode_breakdown` AND from `foreign_entries`, which then printed the affirmative sentence "none β every depth-1 entry is on the same filesystem". All three finds discarded stderr and `report_denials` only reads section 2's log, so nothing counted it. π΄ That is the floor-presented-as-a-total failure this script exists to catalogue, produced by the function added to prevent it β the FIFTH route to it in this one file (E2BIG, SIGPIPE, find rc 1, xargs rc 123, now this). It also falsified the comment round 1 wrote in the same commit claiming "a total failure is still visible, as an empty section". Fixed by capturing each enumeration's stderr, counting and reporting the unstattable entries, and changing `foreign_entries` to say only that none is VISIBLE rather than asserting none exists. ## π΄ F-2 β a bare statement-level `du -sh` under `set -e` `du -sh /var/lib/rancher/k3s/storage` sat directly beneath two carefully `|| true`-guarded pipelines, under a comment saying the same treatment was needed. A pod unlinking a file between du's readdir and stat made it print an UNDER-COUNTED total, exit 1, and kill sections 6, 6b, 6c, 6d, 7 and 8 with no error. Round 1's site enumeration covered pipelines and missed the one direct `du`; that is twice an enumeration missed a member, so this round swept mechanically rather than by eye. Now `|| echo "COULD NOT MEASURE β¦"`, which labels the floor as a floor instead of leaving it to read as a total. ## π‘ F-3 β `foreign_entries` reintroduced F7 in the commit that fixed F7 `| tr '\0' '\n' | head_n 15`: a directory name containing a newline became two report rows, one reading as a real path, and consumed two of the fifteen slots. `/tmp` is mode 1777, so any user can create one. Now one row per NUL record, matching `report_foreign_mounts`. ## π‘ F-4 / F-5 β two stale claims, each in a file the scanner cannot see `scripts/run-tests.sh` still described the sourceable seam this PR REMOVED and now BANS, and called the script "282 lines" (567 at head). The banned-pattern scanner only reads `diagnose-disk-accounting.sh`, so nothing caught the copy. And `test_no_real_launchers_all_targets.py` pinned a justification saying the suite "does execute the script itself ONCE" β it is now seven times, and section 2c ran the real root script OUTSIDE the suite's root guard, so under root it would have run `find / -xdev` and both breakdowns over a 78-million- entry /tmp for hours and then reported green. Both corrected, and each now records what it previously said so the next reader is not sent hunting. ## Verification suite at HEAD 162 ok / 0 FAIL suite vs the round-2 script 4d8228c 27 FAIL / 135 ok mutation battery 54 mutants, every one KILLED by its own guard; baseline 162/0, negative control red at 6 FAIL, SURVIVES control unkilled The red-at-4d8228c3 run is the evidence that matters: the new guards fail against the exact code they were written for, including "foreign_entries does NOT claim every entry is on the same filesystem". NOT verified: sections 1, 2, 3, 5, 6, 6b and 8 execute only under root and remain unguarded; F-2 is demonstrated on an isolated reconstruction of section 5's code, not on a live root run. The device filter's behaviour against a real second filesystem is asserted nowhere β the suite stubs `_dev_of`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UvuSKVRMV7KEmXH8KVe4gb Claude-Session-Id: c8eedffa-36a5-41b3-b250-99d5d2851435
Round 2 audit β claims blockRound 2 was a delta re-audit of Independently re-verified before round 3 (not accepted from the fix round)The round-2 fix agent was stopped mid-run by the operator and had committed and pushed nothing β all 728 insertions were uncommitted in its worktree. Its work was preserved, checked for
Gate status β one red, not attributable to this branch
The single failure is
It is a signal-delivery race under saturation, and Merged-tree gate in flight: this branch is 22 commits behind Ledger β |
β¦ed into the directory under diagnosis, and a du under-count the new blind-spot report made read as complete
Round 3 found no π΄: five π‘/π’ and one out-of-range item. Two of the π‘s were
the round-2 commit's own CORRECTIVE PROSE being false β the fourth consecutive
round in which the fix round's correction was itself wrong β so every number in
this commit was re-derived in the tree it is written about, and every count that
would drift again next round has been REMOVED rather than updated.
## π‘-1 β three `mktemp` files in NO trap, in /tmp
`size_breakdown`, `inode_breakdown` and `foreign_entries` each opened
`errf=$(mktemp)` β an anonymous `/tmp/tmp.XXXXXXXXXX` β removed only by that
function's last statement. The realistic way this run ends is SIGINT, not an
abort: the run it was written for took ~3 h over 78 million entries. MEASURED on
bash 5.3.15: an EXIT trap DOES run when the shell is killed by an untrapped
SIGINT, so the trap removed the NAMED $DENIED_LOG and left up to three files
nothing could attribute to anything β in the directory under diagnosis.
Fixed structurally, not by extending a list. Every temp file goes through
`_scan_mktemp <kind>` (`/tmp/disk-accounting-<kind>.XXXXXX`) and is removed by
`_cleanup_temps`, which reads its variables at CALL time β so ONE
`trap _cleanup_temps EXIT`, installed BEFORE the first mktemp, covers every file
opened later. That deletes the hand-widened second trap at section 6c, which is
the mechanism the three files fell out of.
## π‘-5 β du's stderr was still discarded, and silence had started to mean something
`_report_unstattable` fires only on FIND's stderr. An entry find can stat but du
cannot fully read is listed with an under-counted figure and no marker β and
once `!! UNSTATTABLE` existed, the ABSENCE of a blind-spot line began reading as
"nothing was missed". MEASURED over `base/{open,locked/inner}`, each holding one
4 KiB file with `inner` mode 000: `du -sh -x` prints 8.0K for `locked` against a
true 12K, a 33% under-count presented as a total, rc 1 (xargs 123).
du's stderr is now kept at all three du sites and reported as
`!! PARTIALLY READ: du could not read N path(s)`. The two sinks are separate
FILES on purpose: one blind spot erases an entry, the other shortens its number,
and one count standing for two claims is how both stop meaning anything. A
mutant that points the du report at find's stderr is in the battery for that.
## π’-1 β the sweep's framing was wider than the sweep
The script said "Everything that scan reports is guarded above EXCEPT these" and
listed two sites; the suite header said Β§7b "sweeps the whole file". The scan
keys on a line's FIRST WORD and drops any line containing `||`, so a pipeline
headed by `for`/`done`/`printf` is invisible, and so is one guarded only in an
early stage. FIVE trailing `sort` stages are unguarded, not two β three of them
statement-level pipelines the sweep cannot reach. Both halves fixed: the claims
are narrowed to what the scan does, and Β§7b gained a SECOND ledger over trailing
`sort` stages that fails when the set GROWS or SHRINKS, with a canary exercising
both branches. Nothing is a live bug; the exception list read as exhaustive.
## π’-2 β the `%D` evidence disagreed with itself in three places
Re-measured: ONE fixture (a 35-character mode-0400 base holding three entries),
THREE implementations, each run twice.
GNU findutils 4.10.0 (the bash PATH) -print0 114 B rc 0 | %D 0 B rc 1
GNU findutils 4.11.0 (nix dev shell) -print0 114 B rc 0 | %D 0 B rc 1
bfs 4.1.1 (interactive alias) -print0 114 B rc 0 | %D 0 B rc 1
Two things were wrong, not one: the builds AGREE (the old "116 B / 119 B" over
one fixture could not both be true β `-print0` emits the paths it FOUND, not the
binary's path), and `-print0` exits 0, not 1, because it needs no stat. bfs was
measured this round, so the script's "not measured on bfs" is gone. The
load-bearing half β 0 bytes under `%D` β is identical on all three.
π΄ This commit message SUPERSEDES eb4e3a8's figures (345 B for bfs, "two
implementations" above three rows). That message is immutable; the script, the
suite and the PR body now carry the re-measured table.
## π‘-2 / π‘-3 β the round's own corrective prose, false twice
`run-tests.sh` said "282 at the merge base (567 after round 1, 730 after round
2)". The file was 766 β a stale count inside the sentence explaining that stale
counts get quoted downstream. `test_no_real_launchers_all_targets.py` said the
suite sources the script "ten more times, counting the FOUR probe scripts";
there are FIVE, and `nodev-probe.sh` was added by that same commit. A third of
the same kind turned up in the suite's own header: "282 lines when this suite
was written" β 282 is the count at the MERGE BASE, before this suite's commit;
by the end of that commit the file was 414.
All three now state no drifting count: the historical figure is anchored to
`c1169e3b`, the current ones are `wc -l`. No grep recipe is offered for the
source/execute counts either β the obvious ones are wrong in opposite directions.
## Out of range, DECLARED not fixed β `split_by_device`
Its `[ -d "$p" ] || continue` silently drops a directory root cannot stat, with
no count, feeding `report_foreign_mounts`. Round 2's "every caller reports the
count" is true of `_depth1_nul`'s three callers only; that over-claim is
corrected in place. The site is RECORDED, not fixed: `[ -d "$p" ]` says no for
three different reasons β not a directory, stat refused, or an unmatched glob
left its own pattern β and bash's file tests cannot separate them, so a count
needs a different enumeration. Inventing one inside an audit-fix round is
precisely how every previous round produced the next round's finding.
## Verification
suite vs diagnose-disk-accounting.sh @ 491fc44 (pre-audit) : 93 FAIL, 106 ok
suite vs @ 4d8228c (round 1) : 52 FAIL, 147 ok
suite vs @ eb4e3a8 (round 2) : 20 FAIL, 179 ok
suite vs HEAD : 0 FAIL, 199 ok
mutation battery 64 mutants, every one KILLED by its
own guard; baseline 199/0, negative
control 6 FAIL, SURVIVES control unkilled
The 20 red at eb4e3a8 are exactly this round's guards: 4 absent helpers, 3
route-(b) du assertions, 5 temp-file rows, 7 structural pins and 1 sort-ledger
row. One of them is worth naming: "an interrupt mid-breakdown leaves NO temp
file behind" PASSES at eb4e3a8 β vacuously, because the probe records no paths
there. Its positive control is what goes red.
π΄ That control also caught a harness bug in this round's own new guard. A first
draft signalled with `kill -INT` alone; it was green in the foreground and
produced THREE FAILs the moment the mutation battery ran the suite from
`nohup β¦ &`, because bash sets SIGINT to SIG_IGN for a command started
asynchronously without job control and a non-interactive shell cannot reset a
signal ignored on entry. Without the leak positive control it would have gone
VACUOUSLY GREEN there. The probe now falls back to SIGTERM and reports which
signal it delivered.
NOT verified: sections 1, 2, 3, 5, 6, 6b and 8 execute only under root and
remain unguarded arithmetically. Section 5's and section 6c's du-stderr captures
are INVARIANT PINS with no behavioural half, and are labelled as such; only
`size_breakdown`'s is measured against a fixture.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XLiNARJLqsDf6EhaGnEhe
Claude-Session-Id: c8eedffa-36a5-41b3-b250-99d5d2851435
β¦instead of honouring $TMPDIR The round-3 fix replaced a bare `mktemp` β which HONOURS `$TMPDIR` β with a `/tmp/disk-accounting-<kind>.XXXXXX` template that does not. That is a behaviour change and it was undocumented, which is how the next round "fixes" it back. It is deliberate and it goes the safer way for this file: `$DENIED_LOG` already hardcodes /tmp, and the script's whole premise is that a local unprivileged process must not influence a root run β an inherited `TMPDIR=/anything` choosing where a root run writes its scan stderr is the `LSOF_BIN` lesson under a different name. The cost is recorded rather than hidden: a NON-root caller (the suite) now writes into the system /tmp instead of its own sandbox, and a deliberately broken copy that dies between the `mktemp` and the `rm` leaves the file there. MEASURED: a clean suite run leaks nothing; a full mutation sweep left ~60 empty files, because ~a dozen mutants exist precisely to make the function abort mid-way. They are empty, harmless and β the entire point of the round-3 change β NAMED, which is the only reason they were noticed and swept. Re-verified after this comment-only edit: suite 199 ok / 0 FAIL, red-at-eb4e3a81 still 20 FAIL / 179 ok, mutation battery 64/64 KILLED by their own guard with baseline 199/0 and the SURVIVES control unkilled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XLiNARJLqsDf6EhaGnEhe Claude-Session-Id: c8eedffa-36a5-41b3-b250-99d5d2851435
Round 3 audit β claims blockRound 3 was a delta re-audit of Independently re-verified before round 4
Ledger β Gate β both tiers red, and NONE of it is this PREstablished by control, not by reasoning:
|
β¦read as "nothing was missed", and two set -e-fatal truncations in neither ledger
Round 4 found no π΄: one π‘ and three π’. Nearly all of it is again THIS
LADDER'S OWN CORRECTIVE PROSE being wrong β the fifth consecutive round in which
that is the most reliable finding. So this commit REMOVES claims rather than
restating them. No new count is written anywhere in it; where a sentence needed
a number to be true, the number is gone and the command that derives it is in
its place.
## π‘-A β "du's stderr is kept at all three du sites" was FALSE, and one of the
## uncovered sites printed a figure with NO marker at all
The file holds more `du` invocations than the ones that route stderr into a
file. Some of the others at least print a `|| echo` fallback. Section 6's
per-tree row printed nothing:
printf '%10s %12d inodes %s\n' \
"$(du -sh -x "$d" 2>/dev/null | awk '{print $1}')" \
A `du` that cannot fully read a tree β a stale NFS/CSI mount under
/var/lib/kubelet is the realistic case β prints a PARTIAL total on stdout, exits
1, and with its message at /dev/null and its status discarded the row rendered
that floor exactly like a complete figure. Since `_report_unreadable` landed the
ABSENCE of a marker reads as an affirmative "nothing was missed" β that is
`_report_unreadable`'s own thesis β so this site was MORE misleading after
round 3 than before it, at a site the round's prose said was covered.
Two fixes, neither of them a new count:
* The claim is narrowed to the callers of `_report_unreadable`
(`size_breakdown`'s capture, section 5's PVC listing, section 6c's /home
listing), named structurally rather than counted, and the rest are DECLARED
in that function's comment the way `split_by_device`'s blind spot is
declared β carrying the `grep` that enumerates them instead of a figure that
goes stale next round. The suite row that called section 5 "the third du
site" now says it is about that pipeline and no other.
* Section 6 BRANCHES on du's status and prints a FLOOR marker. MEASURED
2026-09-08 over a fixture holding a mode-000 subdirectory: 20K printed
against a true 24K, now marked, and the loop continues. `if VAR=$(β¦)` and
not a bare assignment β a command in an `if` condition is not checked by
`set -e`.
Two SEPARATE `required` rows with two ISOLATED mutants, because the site has two
independent ways to be wrong: the branch can be removed, or the marker can be
computed and never reach the output. Section 6 is root-only, so both are
INVARIANT PINS; the fixture exercises the expression, not the script's own line.
## π’-D β `: > "$DU_ERR"` is set -e-FATAL and was in NEITHER ledger
MEASURED 2026-09-08 on bash 5.3.15: `set -euo pipefail; : > /absent/x` ends the
shell, rc 1, with nothing after it running; the same line with `||` is caught and
the run continues. The line is headed by `:`, so Β§7b's first-word sweep cannot
see it, and it carries no `| sort`, so the trailing-sort ledger cannot either. It
was in neither, and absent from the "left open on purpose" list β and round 3's
own π’-1 was "the exception list read as exhaustive and was not", in the same
commit that added this site. Section 6c's copy fires AFTER sections 1..6b have
printed, which fails the file's own criterion for leaving a site unguarded.
GUARDED rather than listed, so the exception list does not have to grow.
Deliberately NOT `|| true`: a file that did not truncate still holds an earlier
site's paths, and `_report_unreadable` would then attribute them here β the "one
count standing for two claims" the two-sinks comment is about.
Pinned as a RELATIONSHIP, not a literal: the suite counts `: > "$DU_ERR"` lines
and guarded ones and asserts they are equal and non-zero, so it goes red when a
guard is dropped AND when a new unguarded truncation is added. Both directions
have a mutant. A single `required` row would have stayed green with one of the
two sites unguarded, which is why this counts instead of pinning one literal.
## π’-B β the recorded cost of hardcoding /tmp said "empty", and omitted the
## consequence that matters
MEASURED 2026-09-08 on this host: leftovers of both kinds were sitting in /tmp
and NOT all of them were empty β some held a fixture's captured stderr, e.g.
`du: cannot read directory '/tmp/tmp.XXXXXXXXXX/du-errors/locked': Permission
denied`. Mode 0600, so "harmless" holds; "empty" did not. The comment says so and
quotes `ls -l /tmp/disk-accounting-*` rather than a number, because the number is
whatever the last interrupted sweep happened to leave. The stale "~60 times per
full sweep" in the same paragraph is gone for the same reason.
The half that was never recorded: hardcoding /tmp also removes TMPDIR as an
ESCAPE ROUTE, on exactly the failure this script is pointed at. $DENIED_LOG and
$DU_ERR are opened unguarded at the top of the executable region, so a root
filesystem out of inodes ends the run on those lines with a blank report β which
the exception list accepts as the honest outcome. What it did not say, and now
does, is that the bare `mktemp` this replaced would have honoured an inherited
$TMPDIR and the template cannot.
## π’-C β the PR body's own control miscounted the tests and files it rested on
PR body only, no code. The counts are deleted and the table is the record; the
conclusion is unchanged and is now stated in terms the table itself carries.
## The battery's occurrence check earned its keep again
A first draft of section 6's new comment quoted `ROOT_DEV=$(_dev_of /) ||
ROOT_DEV=` verbatim while explaining the checked-assignment reasoning. The
mutator matches RAW text, so that made an EXISTING mutant ambiguous:
`root-dev-reading-unguarded` reported `occurrences=2` and scored MUTATION DID NOT
APPLY. Without that check it would have silently measured the unmutated file β
"the guard held", the most flattering possible wrong answer. The comment now
paraphrases, and says why.
## Verification
Every row re-run with THIS round's suite, not quoted from a previous round β
which is why the first three are three higher than round 3 published:
suite vs diagnose-disk-accounting.sh @ 491fc44 (pre-audit) : 96 FAIL, 106 ok
suite vs @ 4d8228c (round 1) : 55 FAIL, 147 ok
suite vs @ eb4e3a8 (round 2) : 23 FAIL, 179 ok
suite vs @ 530a5c5 (round 3) : 3 FAIL, 199 ok
suite vs HEAD : 0 FAIL, 202 ok
The 3 red at 530a5c5 are exactly this round's three new guards, one per fix:
section 6's branch, section 6's printed marker, and the `: > "$DU_ERR"` ledger
(which reports `0 of 2` guarded there).
mutation battery baseline 202 ok / 0 FAIL; negative control 6 FAIL;
EVERY mutant KILLED BY ITS OWN GUARD, 0 problems;
SURVIVES control (comment reword) killed nothing.
Round 4's four new rows all KILLED by their own guard:
section6-du-status-not-read,
section6-marker-computed-but-not-printed,
duerr-truncation-unguarded-6c (ledger shrinks),
duerr-truncation-added-unguarded (ledger grows).
Both tiers, read as COUNTS:
dev-host pytest TOTAL collected=22003 passed=21992 skipped=3 failed=8
RESULT: FAIL (exit=1) β the 8 are the DECLARED toolchain
drift (age 1.3.1->1.3.2, opencode 1.18.29 vs pinned
1.18.21) plus the browser-bridge [INT] signal race; none is
in a file this PR touches. This suite inside that run:
PASS, 202 ok / 0 FAIL.
dev-host node TOTAL suites=5 files=41 tests=1449 pass=1449 fail=0
RESULT: PASS (exit=0)
sandbox pytest TOTAL collected=22003 passed=22000 skipped=3 failed=0
RESULT: PASS (exit=0) β fully green; this suite 202 ok / 0
FAIL. The sandbox's previously-declared flake did not recur.
sandbox node TOTAL suites=5 files=41 tests=1449 pass=1449 fail=0
RESULT: PASS (exit=0)
NOT verified: sections 1, 2, 3, 5, 6, 6b and 8 execute only under root and their
arithmetic remains unguarded. Section 6's FLOOR marker and both `: > "$DU_ERR"`
guards are INVARIANT PINS with no behavioural half β what the fixture above shows
is the expression, not the script's own line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XLiNARJLqsDf6EhaGnEhe
Claude-Session-Id: c8eedffa-36a5-41b3-b250-99d5d2851435
scripts/diagnose-disk-accounting.shis root-privileged bash that had no test file, in a repo with no shellcheck gate. That combination is why an E2BIG abort and a hard0.0 GiBshipped invisibly β nothing here was ever executed by a gate, so nothing here could ever be seen to be wrong.This adds
scripts/tests/test_diagnose_disk_accounting.sh, registers it inrun-tests.shSHELL_TESTS, and shipsscripts/tests/mutants-diagnose-disk-accounting.shso the "mutation-verified" claim below can be re-derived rather than believed.βοΈ Correction 1 β this PR did not fix the root command injection
An earlier revision listed "root command injection" among the defects this PR fixed. It was already fixed before this PR. Measured on the merge base
c1169e3b: the file contains zeroxargs -Ioccurrences in code β the only match is inside a comment describing the historical defect β and the inode walk was alreadyfind β¦ -exec sh -c 'β¦' _ {} \;. The fix landed ind8fe0bc(#1227).What this PR adds is the test for it: a fixture directory literally named
evil";echo PWNED-AS-$(id -un) >&2;"x, asserted against the expansionPWNED-AS-<whoami>and never the wordPWNED(the filename contains it), with the old vulnerablexargs -I{}pipeline run first as a positive control and confirmed to leak the expansion.That guard earned its keep in round 1: adding a
2>/dev/nullto the newxargsstage silently disarmed it β the historical defect's own proof of execution is a planted name printing to stderr β and the mutation battery caught it asWRONG-KILLERwhile the suite stayed green.βοΈ Correction 2 β the seam was reachable from the environment
This description used to quote the seam as
[ "${BASH_SOURCE[0]}" != "$0" ]and claim "the guard is not reachable from the environment". Measured false. bash importsBASH_SOURCEfrom the environment as an ordinary scalar:env 'BASH_SOURCE=(nope)' bash scripts/diagnose-disk-accounting.shβ takes the sourced branch,returns at top level, rc 2, no report at all β and rc 2 is this script's own "you forgot sudo" status, so two unrelated causes shared one exit code. Only a guard asserting the refusal message can tell them apart.bash < script/bash -sβBASH_SOURCEunset,set -u, dead on the guard's own line, rc 1 β a genuine (narrow) regression the refactor introduced.It is now
if (return 0 2>/dev/null); then, which reads no variable, andBASH_SOURCEis a banned pattern in the suite's own scanner.βοΈ Correction 3 β section 5 is not a verbatim move
This description said "Everything else in the diff is a verbatim move into a helper." Not quite. Section 5's enumeration changed from a glob to
find β¦ -print0 | xargs -0, andfindenumerates dotfiles that the glob skipped. That is a behaviour change β an improvement for an accounting tool, and it is kept, but it should not have been filed under "verbatim".βοΈ Correction 4 β the
%Dcross-implementation evidence disagreed with itselfRound 2's evidence for the
%Ddefect was quoted in three places and no two agreed: the script said "the two GNU findutils builds β¦ (Not measured onbfs)", the round-2 commit message said "Measured on two implementations" while printing bfs numbers, and a PR comment said three. The byte counts could not all have been true either β "-print0116 B (4.10.0) / 119 B (4.11.0)" over one fixture, when-print0emits the paths it found, not the binary's path.Re-measured in round 3 β one fixture (a 35-character mode-0400 base holding three entries), three implementations, each run twice:
-print0-printf '%D\t%p\0'bash, and so asudorun, resolves)Two things were wrong, not one: the builds agree (114 B is a figure about that fixture's path length and nothing else), and
-print0exits 0, not 1 β it needs nostat, which is exactly why it still emits the names. The load-bearing half β 0 bytes under%Dβ reproduces identically on all three and was never in doubt. The script and the suite now carry these figures; the round-2 commit message is immutable and is superseded by them.The seam I refactored
The script refuses to run as non-root (rc 2) by design, so the only way to gate it as an ordinary user is to make the transforms callable. Sourcing it defines the helpers and takes the
(return 0 2>/dev/null)branch, running nothing.LSOF_BINexists so the suite can drive both the not-on-PATH branch and the rows branch without planting a binary β and it is inside the sourced branch only (round 1, F4).Defects the suite covers
All eight from
claudedocs/handoff-nix-disk-cleanup.mdrank 5 / cairndevrc/diagnose-disk-accounting:+L1putsSIZE/OFFat col 7, plainlsof -n -Pputs it at col 9. Both carry the same two sizes summing to 2.5 GiB and every other column in both sums to 0.0, so 2.5 is reachable only from the right column. Plus: an absentSIZE/OFFcolumn must refuse.getconf ARG_MAX, with the glob form run as a positive control and confirmed to dieArgument list too long.none found, plus a positive control that thenonebranch can fire at all.grep -cprints 0 and exits 1 β an empty denial log must produce exactly three clean zero lines and must not trip the broken-counter guard.NR-1β β1 β empty input, header-only input and a missinglsofbinary are three different answers, none of them a bare zero.du -xis not "only this filesystem" β per-candidate device comparison, in both section 6c and section 6d.FLOORS, not totalsasserted.Round 1 β defects found while writing the tests
(a) The no-rows message in section 7 was unreachable dead code.
LSOF_OUT=$(lsof +L1 2>/dev/null); LSOF_RC=$?is a checked command underset -e, and lsof documents exit 1 when it finds nothing β the ordinary case. Now|| rc=$?, with a probe that runs the realset -euo pipefailand demands a marker printed after section 7.(b)
<producer> | sort | head -Nunderpipefailis a live, size-dependent abort. Measured at two points: 40 entries survive rc 0, 20,000 die rc 141. The real/tmphad 171,886. Fixed withhead_n() { awk -v n="$1" 'NR<=n'; }.(F1) A vanished entry mid-scan killed the whole run β two routes, not one. GNU
findexits 1 when an entry disappears between readdir and stat;xargsexits 123 when anyduit ran exited 1. Both are promoted bypipefailand fatal underset -e. Two deterministic fixtures isolate one route each (a mode-0400directory; a directory holding a mode-000subdirectory), so neither guard is scored on the other's failure and neither needs a concurrent deleter.(F5) Defect 7 was fixed at
/homeand left standing at/tmp.-xdevonly stopsfinddescending past a mount β it still lists the mountpoint at depth 1, so it arrived as a starting point fordu -sh -x, the one casedu -xcannot handle. Section 6d now applies the same per-candidate device comparison, through a single_on_devicefilter overfind -printf '%D\t%p\0'._dev_ofmoved fromstat -c '%D'(hex) tostat -c '%d'(decimal) to match find's own%D.(F2a) The
headban was SPELLED, not structural. The banned pattern was| *head -[0-9], demanding a digit afterhead -β sohead -n 20,head -n 30andhead -n 15all SURVIVED a fully green suite. Widened to| *head *-, with all four spellings in the battery.(F2b) The E2BIG fixture was files-only, so
inode_breakdownmeasured nothing. It filters-type dand the fixture was 17,550touched files, so its probe call returned zero rows and the whole SIGPIPE battery row was killed bysize_breakdownalone. The fixture now also builds 600 directories, the probes are split one per breakdown, and a non-vacuity assertion fails loudly if the row count returns to 0.Round 2 β the round-1 fix created the headline defect
(π΄ F-1)
-printf '%D\t%p\0'emits NOTHING for an unstattable entry. Round 1 swapped-print0for-printf '%D\t%p\0'to get the device id for the new filter.%Dforces astatper entry, and a failed stat emits no record at all where-print0still emitted the name. So an entry root cannot stat β a FUSE mountpoint withoutallow_other, gvfs, sshfs, or a device answering ESTALE/EIO β vanished fromsize_breakdown,inode_breakdownandforeign_entriesat once, andforeign_entriesthen printed the affirmative "none β every depth-1 entry is on the same filesystem". Fixed by keeping each enumeration's stderr in a file, counting it, reporting it (!! UNSTATTABLE), and downgrading the affirmative "none" to "none VISIBLE". Numbers in Correction 4.(π΄ F-2) A bare statement-level
du -shunderset -e. Section 5's total sat directly beneath two carefully|| true-guarded pipelines, under a comment describing the treatment it did not have. Round 1's site enumeration was done by eye and missed it; round 2 replaced that with a mechanical sweep, which is now section 7b of the suite.(π‘ F-3)
foreign_entriesreintroduced F7 in the commit that fixed F7 β| tr '\0' '\n' | head_n 15turned a directory name containing a newline into two report rows. Now one row per NUL record.(π‘ F-4/F-5) Two stale claims, each in a file the scanner cannot see β
run-tests.shandtest_no_real_launchers_all_targets.py. The banned-pattern scanner only readsdiagnose-disk-accounting.sh, so both copies walked past it.Round 3 β this round
(π‘ 1) Three
mktempfiles in NO trap, opened into the directory under diagnosis.size_breakdown,inode_breakdownandforeign_entrieseach openederrf=$(mktemp)β an anonymous/tmp/tmp.XXXXXXXXXXβ removed only by that function's last statement. The realistic way this run ends is SIGINT, not an abort: the run it was written for took ~3 h over 78 million entries. MEASURED: an EXIT trap does run when bash is killed by an untrapped SIGINT, so the trap removed the named$DENIED_LOGand left up to three unattributable files in/tmp.Fixed structurally rather than by extending a list: every temp file now goes through
_scan_mktemp <kind>(template/tmp/disk-accounting-<kind>.XXXXXX) and is removed by_cleanup_temps, which reads its variables at call time β so onetrap _cleanup_temps EXIT, installed before the firstmktemp, covers every file opened later. That also deletes the hand-widened second trap at section 6c, which is the mechanism the three files fell out of in the first place.Guarded end-to-end: a probe stubs
_report_unstattable(called after bothmktemps and before therm) to record the paths and signal itself. The positive control runs first and must LEAK β with no trap, both files survive β because a "nothing was left behind" assertion is otherwise indistinguishable from a probe that never opened a file. That control caught a real harness bug: a first draft usedkill -INTalone and produced three FAILs the moment the mutation battery ran the suite fromnohup β¦ &, since bash sets SIGINT toSIG_IGNfor a command started asynchronously without job control and a non-interactive shell cannot reset a signal ignored on entry. The probe now falls back to SIGTERM and says which signal it delivered.(π‘ 5)
du's stderr was still discarded, and the new blind-spot report made the silence mean something it hadn't._report_unstattablefires only when find's stderr is non-empty. An entryfindcan stat butducannot fully read is listed with an under-counted figure and no marker of any kind β and once!! UNSTATTABLEexisted, the absence of a blind-spot line started reading as "nothing was missed". MEASURED onbase/{open,locked/inner}, each holding one 4 KiB file withinnermode 000:du -sh -xprints 8.0K forlockedagainst a true 12K β a 33% under-count presented as a total β and exits 1.du's stderr is now captured the way find's is at the sites that call
_report_unreadableβsize_breakdown's own capture, section 5's PVC listing and section 6c's/homelisting β and reported as!! PARTIALLY READ: du could not read N path(s). (This sentence used to read "at all threedusites", which asserted a count of the file'sduinvocations and was wrong; see round 4 π‘ A below. The scope is now stated structurally, and_report_unreadable's own comment declares that it says nothing about any otherduin the file.) The two sinks are deliberately separate files: one blind spot erases an entry, the other shortens its number, and a merged count would be one number standing for two different claims. A mutant that points the du report at find's stderr is in the battery for exactly that.(π’ 1) The sweep's framing was wider than what the sweep can see. The script's comment read "Everything that scan reports is guarded above EXCEPT these" and listed only the two
out=$(β¦ | sort β¦)captures; the suite header said 7b "sweeps the whole file". The scan keys on a line's first word and drops any line containing||, so a pipeline headed byfor/done/printfis invisible to it, and so is one whose early stages are guarded while its last stage is not. Five trailingsortstages are unguarded, not two β three of them statement-level pipelines the sweep cannot reach.Both halves fixed: the claims are narrowed to what the scan actually does, and Β§7b gained a second ledger over trailing
sortstages that fails when the set GROWS or SHRINKS, with its own canary exercising both branches (a guarded{ β¦ } || truegroup must be excluded). Nothing here is a live bug βsortover β€30 lines will not spill β but the exception list read as exhaustive and was not.(π‘ 2, π‘ 3) The round's own corrective prose was false, twice.
run-tests.shsaid "282 at the merge base (567 after round 1, 730 after round 2)" β the file was 766, i.e. the sentence explaining that stale counts get quoted downstream carried a stale count. Andtest_no_real_launchers_all_targets.pysaid the suite sources the script "ten more times, counting the four probe scripts" β there are five, andnodev-probe.shwas added by the same commit. Both now state no drifting count at all: the historical figure is anchored to a sha, and the current ones are re-derivable withwc -l/grep -c.While correcting them I found a third of the same kind, in the suite's own header: "It was 282 lines when this suite was written" β 282 is the count at the merge base, before this suite's commit; by the end of that commit the file was 414.
(Out of range, declared not fixed)
split_by_deviceis a known fourth site. Its[ -d "$p" ] || continuesilently drops a directory root cannot stat, with no count, feedingreport_foreign_mounts. Round 2's comment claimed "every caller reports the count", which is true of_depth1_nul's three callers only; that over-claim is corrected. The site itself is recorded, not fixed:[ -d "$p" ]says no for three different reasons β not a directory, stat refused, or an unmatched glob left its own pattern β and bash's file tests cannot separate them, so a count needs a different enumeration. Inventing one inside an audit-fix round is precisely how each round of this ladder produced the next round's finding. What limits it is thatreport_foreign_mounts's empty branch already tells the reader an empty list is a BUG rather than a clean result.Round 4 β this round
No π΄: one π‘ and three π’, and nearly all of it is again this ladder's own corrective prose being wrong β the fifth consecutive round in which that is the most reliable finding. So this round removes claims rather than restating them. Where a sentence needed a number to be true, the number is gone and the command that derives it is in its place.
(π‘ A) "du's stderr is kept at all three
dusites" was false β and one of the uncovered sites printed a figure with no marker at all. The file has moreduinvocations than the ones that route stderr into a file. Some of the others at least print a|| echofallback. Section 6's per-tree row printed nothing:"$(du -sh -x "$d" 2>/dev/null | awk '{print $1}')"renders a partial total exactly like a complete one, which is what a stale NFS/CSI mount under/var/lib/kubeletproduces. Since_report_unreadablelanded, the absence of a marker reads as an affirmative "nothing was missed" β its own thesis β so that site was more misleading after round 3 than before it, at a site the round's prose said was covered.Two fixes, and neither is a new count. The claim is narrowed to the callers of
_report_unreadable, named structurally; the rest are declared in that function's comment the waysplit_by_device's blind spot is declared, with thegreps that derive both sets instead of a figure that goes stale. And section 6 now branches on du's status βif du_out=$(du β¦); then du_mark=; else du_mark=' !! FLOOR β du could not read all of it'; fiβ with the marker printed as part of the row. MEASURED 2026-09-08 over a fixture holding a mode-000 subdirectory: 20K printed against a true 24K, now marked, and the run continues.if VAR=$(β¦)rather than a bare assignment, because a command in anifcondition is not checked byset -e.Guarded by two separate
requiredrows with two isolated mutants, because the site has two independent ways to be wrong: the branch can be removed, or the marker can be computed and never reach the output. Section 6 is root-only, so both are INVARIANT PINS; the fixture above exercises the expression, not the script's own line, and is reported as such.(π’ D)
: > "$DU_ERR"isset -e-fatal and was invisible to both ledgers. A redirection failure on a special builtin ends the shell β MEASURED 2026-09-08, bash 5.3.15:set -euo pipefail; : > /absent/xexits 1 with nothing after it running; the same line with||is caught and the run continues. The line is headed by:, so 7b's first-word sweep cannot see it, and it carries no| sort, so the trailing-sortledger cannot either. It was in neither ledger and absent from the "left open on purpose" list β and round 3's own π’ 1 was "the exception list read as exhaustive and was not", in the same commit that added this site. Section 6c's copy fires after sections 1β6b have printed, which fails the file's own criterion for leaving a site unguarded.Guarded rather than listed, so the exception list does not have to grow:
: > "$DU_ERR" || echo "COULD NOT MEASURE: β¦". Deliberately not|| trueβ a file that did not truncate still holds an earlier site's paths, and_report_unreadablewould then attribute them here. Pinned as a relationship rather than a literal: the suite counts: > "$DU_ERR"lines and guarded ones and asserts they are equal and non-zero, so it goes red when a guard is dropped and when a new unguarded truncation is added. Both directions have a mutant; a singlerequiredrow would have stayed green with one of the two sites unguarded.(π’ B) The recorded cost of hardcoding
/tmpsaid "empty", and omitted the consequence that matters. MEASURED 2026-09-08 on this host: leftovers of both kinds were sitting in/tmpand not all of them were empty β some held a fixture's captured stderr (du: cannot read directory '/tmp/tmp.XXXXXXXXXX/du-errors/locked': Permission denied). Mode 0600, so "harmless" holds; "empty" did not. The comment now says so, and quotesls -l /tmp/disk-accounting-*rather than a number, because the number is whatever the last interrupted sweep happened to leave.The half that was never recorded: hardcoding
/tmpalso removesTMPDIRas an escape route, on exactly the failure this script is pointed at.$DENIED_LOGand$DU_ERRare opened unguarded at the top of the executable region, so a root filesystem out of inodes ends the run on those lines with a blank report β which the exception list accepts as the honest outcome. What it did not say, and now does, is that the baremktempthis replaced would have honoured an inherited$TMPDIRand the template cannot: pointing this run's scratch at another filesystem now means editing the file.(π’ C) The control in "the pytest tier is RED" miscounted the tests and files it rests on. Fixed above, by deleting the counts and pointing at the table. The conclusion is unchanged and is now stated in terms the table itself carries.
Red-at-old-code / green-at-HEAD
The current suite run against each earlier revision of the script, unmodified:
Every row was re-run with the round-4 suite, not quoted from a previous round β which is why the first three are each three higher than the figures round 3 published (
93 / 52 / 20): round 4's three new guards are red at every earlier revision too. The three red at530a5c56are exactly those guards, one per fix β section 6's branch on du's status, section 6's printed marker, and the: > "$DU_ERR"ledger, which reports0 of 2guarded there.The 20 red at
eb4e3a81as round 3's suite reported them β the table above now reads 23 there, the extra three being round 4's guards β are exactly round 3's new guards, and they account for all of it: 4 absent helpers (_errline_count,_report_unreadable,_scan_mktemp,_cleanup_temps), 3 route-(b)duassertions, 5 temp-file rows (the name, the file's existence, the leak positive control, and the two_cleanup_tempsempty-slot rows), 7 structural pins in section 7, and 1 sort-ledger row.One of them is worth naming: "an interrupt mid-breakdown leaves NO temp file behind" passes at
eb4e3a81β vacuously, because the probe records no paths there. Its positive control is what goes red. A leak assertion without one is a claim about a probe that never ran.Mutation battery
bash scripts/tests/mutants-diagnose-disk-accounting.shβ one row per mutant plus a SURVIVES control, each an exact single-occurrence replacement whose application is checked, each scored on whether its own guard'sFAIL:line appears. A mutant killed only by some other guard reportsWRONG-KILLER, not ok. (The roster size used to be written here as a number and is not any more: it is one of the counts that drifts every round. Run the battery β it prints one line per mutant.)Round 3's ten new rows, all
KILLED by its own guard:Round 4's new rows, all
KILLED by its own guard, and deliberately isolated in pairs:The occurrence check earned its keep again, on this round's own edit. A first draft of section 6's new comment quoted
ROOT_DEV=$(_dev_of /) || ROOT_DEV=verbatim while explaining the checked-assignment reasoning. The mutator matches raw text, so that made an existing mutant ambiguous:root-dev-reading-unguardedreportedoccurrences=2and scoredMUTATION DID NOT APPLY. Without that check it would have silently measured the unmutated file β "the guard held", the most flattering possible wrong answer. The comment now paraphrases, and says why.It also earned it in round 3:
unstattable-report-suppressedreportedoccurrences=2the moment_report_unreadablegained the same[ "$n" -gt 0 ] || return 0line, so the mutation now carries thelocalline above it as an anchor. Acount=1replace on a two-occurrence pattern would have scored the wrong function.Both tiers, read as counts rather than as "BUILD OK"
nix develop β¦ -c scripts/gate.sh --tier bothTOTAL collected=22003 passed=21992 skipped=3 failed=8,RESULT: FAIL (exit=1).PASS scripts/tests/test_diagnose_disk_accounting.sh (script)β 202ok:, 0FAIL:, read from the gate's own log.TOTAL suites=5 files=41 tests=1449 pass=1449 fail=0 skipped=0(floor 1367),RESULT: PASS (exit=0)nix build -L .#checks.x86_64-linux.pytestsTOTAL collected=22003 passed=22000 skipped=3 failed=0,RESULT: PASS (exit=0). My suite ran here too: 202ok:, 0FAIL:.nix build -L .#checks.x86_64-linux.nodetestsTOTAL suites=5 files=41 tests=1449 pass=1449 fail=0 skipped=0,RESULT: PASS (exit=0)Both
nix builds were run with-L, so the counts above were read from the streamed build log rather than from a silent exit 0 β a quietnix buildis cached, not a verdict.π΄ The pytest tier is RED, and none of it is this PR
The two tiers disagree, and the disagreement is itself the useful reading: the dev host is red, the sandbox is fully green (
failed=0), and every dev-host failure is a toolchain the sandbox pins away. (Round 4 update: the sandbox previously reported one failure βtest_git_repo_isolation.py::test_live_cotenantsβ¦, called a flake at the time. It did not recur in this round's sandbox run, which is the only evidence available that the "flake" reading was right; it is still the reading, not a proof.)test_analyze_service_index_escrow_verify.py::test_a_TAMPERED_or_TRUNCATED_artifact_is_ARTIFACT_CORRUPT_not_EMPTYΓ3eb4e3a81β¦::test_every_decrypt_family_VERDICT_is_pinned_WHOLEeb4e3a81test_analyze_service_index_backup.py::test_the_LEAKING_manglers_really_DO_make_age_keygen_echo_the_secreteb4e3a81β¦::test_resolve_recipient_NEVER_quotes_age_keygens_INPUT_ECHOING_stderreb4e3a81test_opencode_engine.py::test_engine_is_the_version_every_measurement_is_keyed_toeb4e3a81test_browser_agent.py::test_the_release_handler_EXITS_rather_than_resuming[INT]test_git_repo_isolation.py::test_live_cotenants_sees_another_process_in_the_repoThe control was run properly rather than reasoned about:
git archive eb4e3a81 | tar -xinto a scratch directory β no worktree, no shared-repo write β then the dev-host rows re-run there. Each one reproduced, which is what the table'scontrolcolumn records row by row. (Round 4 correction: this paragraph used to summarise that as "the six named tests β¦ the two test files involved are byte-identical", and neither number matched the table directly above it β "six" is only reachable by counting table rows, which would include the[INT]row the table itself calls a signal race, and that reading gives four files. The counts are gone; the table is the record.) The visible causes are toolchain drift the dev host has and the sandbox pins:age1.3.1 β 1.3.2 changed the stderr these guards assert on, andopencodeis 1.18.29 against a pinned 1.18.21.No file in that table is one this PR touches β
git diff --name-only c1169e3b..HEADlistsscripts/diagnose-disk-accounting.sh,scripts/run-tests.sh,scripts/tests/test_diagnose_disk_accounting.sh,scripts/tests/mutants-diagnose-disk-accounting.shandscripts/tests/test_no_real_launchers_all_targets.py, and nothing else. This PR does not fix these failures, and it should not be read as having made the gate green.What I could NOT cover without root
Sections 1, 2, 3, 5, 6, 6b and 8 do privileged whole-filesystem measurement β
dumpe2fs -h /dev/nvme0n1p2,find / -xdevacross/root,/var/lib/docker,/var/lib/kubelet,/var/lib/private,/var/lib/rancher/k3s/storage,findmnt /mnt/rootcheck. Their arithmetic is unguarded here: the hardlink dedup in section 2's awk, the residual subtraction in section 3, the inode-table arithmetic in section 1.du -xis in the same category, which is why its pin is structural and labelled as such β as are section 5's and section 6c's du-stderr captures, section 6's FLOOR marker, and the: > "$DU_ERR"ledger.Reported, not fixed:
stat -f /measures the real root filesystem whiledumpe2fsmeasures$DEV, and nothing cross-checks that they are the same filesystem.split_by_device's silent drop, above β declared, not closed. And theduinvocations outside_report_unreadable's callers still send stderr to/dev/nulland produce no path count. That scope is declared in_report_unreadable's comment, with the twogreps that derive it, rather than counted anywhere.Registration
SHELL_TESTSinrun-tests.sh, plus the deliberate ledger intest_no_real_launchers_all_targets.py. The suite touches no launcher, no spool and no git: it sources the script β directly, and once inside each probe script it writes β and executes it only on the non-root refusal path, where every run exits 2 at the root check before the script opens a temp file. The suite aborts if it is itself running as root, since several fixtures are inert under uid 0. ItsEXITtrapchmod -R u+rwXs beforerm -rf, because section 4b deliberately builds unreadable fixture directories.