Skip to content

Twenty-four checks across eight suites share a ledger key with another check, because a readable-log convention collides with a keyed record #982

Description

@OffgridwithJD

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions