Six checks in 400-a-check-result-must-be-machine share three names, so the ledger holds three rows for them. One of the six going red would mark its namesake observed-red too, for a claim nothing attacked.
@jdatcmd spotted this in pgc_ledger.py merge's own output while regenerating for #978 — the tool says it out loud and we had both scrolled past it:
duplicate check name in one run, so one ledger row covers 2:
harness_selftest 400-a-check-result-must-be-machine and its human line is unchanged
harness_selftest 400-a-check-result-must-be-machine and its verdict is SKIP
Measured
On a harness_selftest run at main f69ed1e3 plus #981's eleven arms:
RESULT records in the log: 827
distinct (suite, part, name) keys: 824
keys carrying MORE THAN ONE check: 3
records lost to collapsing: 3
collapsed keys whose checks disagree: 0
The three keys, each covering two checks:
400-a-check-result-must-be-machine 'and its verdict is SKIP' PASS, PASS
400-a-check-result-must-be-machine 'and its human line is unchanged' PASS, PASS
400-a-check-result-must-be-machine 'and is counted as a pass, not a skip' PASS, PASS
Why they collide
Two hand-written blocks testing different helpers with identical names (400-...:386-411):
check "and its human line is unchanged" \
"$(_tmh 1 check_timing "a timing check" 1 1)" \
"SKIP a timing check (PGC_SKIP_TIMING: wall-clock measurement)"
check "and its human line is unchanged" \
"$(_tmh 1 check_ratio_needs_quiet_machine "a ratio check" 1 1 2)" \
"SKIP a ratio check (PGC_SKIP_TIMING: wall-clock ratio)"
One covers check_timing, the other check_ratio_needs_quiet_machine. Both are real, both are wanted, and the names do not say which is which.
Why it matters, and it is the same class as a fix already landed
pgc_record derives the part from BASH_SOURCE precisely because "a key of (suite, name) is not a key of checks, it is a key of check NAMES, and they differ by however many parts share a boilerplate premise" — and one going red "would then mark every sharer as observed red, a claim about a check nothing attacked."
That fixed collisions across parts. This is the same failure within one part, which the part dimension cannot separate.
No mixed verdicts today, so nothing is currently mis-recorded. The hazard is conditional and precise: if check_ratio_needs_quiet_machine's arm ever goes red, the row records ever red, and check_timing's arm inherits a red observation it never earned. checks_never_observed_red then falls by one for a check nobody attacked — and that census is the thing #925 and #918 exist to make trustworthy.
Why it matters for #432 specifically
It puts a wedge between two quantities the seeding design will need to keep straight:
checks that ran 827
rows the ledger can hold 824
Three today, in one part of one suite, found only because the merge tool printed it. The seeding design for 240 suites will be counting exactly this, and "the ledger has seen N checks" is not the same number as "N checks ran" whenever any two share a name inside one part. Nobody has measured the gap across the other 250 suites, because the ledger does not cover them yet — which means seeding is the moment it becomes visible, and a seed built on the larger number would be wrong.
The fix, and why it is not a one-line patch
Name them for the helper they exercise — and check_timing's human line is unchanged, and check_ratio's .... Six lines in one file.
It is not free: it renames three check names, so the ledger gains three rows and loses three, and the census must be re-derived. That is the regeneration tax again, and there are already PRs queued on that one line. Worth doing deliberately rather than wedged into something else.
Worth adding at the same time
A guard. pgc_ledger.py merge already detects this and prints it as a note; nothing fails on it. An arm asserting that no (suite, part, name) key covers more than one check in a single run would make the class impossible rather than merely visible — and it is a count over the RESULT records of a run the harness already produces.
That arm is the more valuable half: this instance is three benign rows, and the mechanism is what stops the next one being three rows where one hides a red.
Six checks in
400-a-check-result-must-be-machineshare three names, so the ledger holds three rows for them. One of the six going red would mark its namesake observed-red too, for a claim nothing attacked.@jdatcmdspotted this inpgc_ledger.py merge's own output while regenerating for #978 — the tool says it out loud and we had both scrolled past it:Measured
On a
harness_selftestrun at mainf69ed1e3plus #981's eleven arms:The three keys, each covering two checks:
Why they collide
Two hand-written blocks testing different helpers with identical names (
400-...:386-411):One covers
check_timing, the othercheck_ratio_needs_quiet_machine. Both are real, both are wanted, and the names do not say which is which.Why it matters, and it is the same class as a fix already landed
pgc_recordderives the part fromBASH_SOURCEprecisely because "a key of (suite, name) is not a key of checks, it is a key of check NAMES, and they differ by however many parts share a boilerplate premise" — and one going red "would then mark every sharer as observed red, a claim about a check nothing attacked."That fixed collisions across parts. This is the same failure within one part, which the part dimension cannot separate.
No mixed verdicts today, so nothing is currently mis-recorded. The hazard is conditional and precise: if
check_ratio_needs_quiet_machine's arm ever goes red, the row recordsever red, andcheck_timing's arm inherits a red observation it never earned.checks_never_observed_redthen falls by one for a check nobody attacked — and that census is the thing #925 and #918 exist to make trustworthy.Why it matters for #432 specifically
It puts a wedge between two quantities the seeding design will need to keep straight:
Three today, in one part of one suite, found only because the merge tool printed it. The seeding design for 240 suites will be counting exactly this, and "the ledger has seen N checks" is not the same number as "N checks ran" whenever any two share a name inside one part. Nobody has measured the gap across the other 250 suites, because the ledger does not cover them yet — which means seeding is the moment it becomes visible, and a seed built on the larger number would be wrong.
The fix, and why it is not a one-line patch
Name them for the helper they exercise —
and check_timing's human line is unchanged,and check_ratio's .... Six lines in one file.It is not free: it renames three check names, so the ledger gains three rows and loses three, and the census must be re-derived. That is the regeneration tax again, and there are already PRs queued on that one line. Worth doing deliberately rather than wedged into something else.
Worth adding at the same time
A guard.
pgc_ledger.py mergealready detects this and prints it as a note; nothing fails on it. An arm asserting that no (suite, part, name) key covers more than one check in a single run would make the class impossible rather than merely visible — and it is a count over theRESULTrecords of a run the harness already produces.That arm is the more valuable half: this instance is three benign rows, and the mechanism is what stops the next one being three rows where one hides a red.