Skip to content

pgc_ledger.py merge records a red from any log that reconciles, and two of us poisoned the ledger that way on day one #946

Description

@jdatcmd

pgc_ledger.py merge records a red from any log that reconciles, and two of us poisoned the ledger that way on the day it landed.

What it does today

Measured on d05e3c3:

a log that does NOT reconcile              rc=2   already refused
a log that reconciles PERFECTLY, one FAIL  rc=0   ACCEPTED, red recorded

So the refusal that exists is about self-consistency, and self-consistency is not the property that matters. Both of the logs that poisoned the ledger reconciled.

All three ways a red can arrive are rc=0, differing only in how well they are labelled:

red, no flags                 check two  unknown     -
red + --date                  check two  2026-09-10  -
red + --date + --mutation     check two  2026-09-10  M

Two independent poisoning routes, same day

@jdatcmd, #925. Regenerating the ledger from a tree copied without .git. Fifteen checks failed on premise: the source tree is a git checkout: got [no]. The log was 827 records against checks run: 827 — flawless reconciliation — and would have written fifteen environment reds into an artifact whose only subject is which checks have ever been red. Caught by noticing the failures, not by the tool.

@OffgridwithJD, #944. A red selftest log merged during an unfinished change, recording a red for part 350's doc-coverage check that the check never earned. One FAIL, perfect reconciliation, and the red belonged to the work in progress rather than to the code.

A third route is easy to imagine and nobody has hit it yet: a run against a stale .so. The rate is the argument here, not either instance — two independent routes on day one, from the two people who knew the tool best.

Why the tool cannot infer this

A red from an environment fault and a red from a real regression are identical in the log. Nothing in a RESULT record says which. So the tool should make the caller assert it rather than guess, which is the same move check_ledger_budget.txt already makes when it names a census apart from a ceiling: when two things are indistinguishable to the mechanism, separate them in the interface.

Proposed

merge refuses a log containing any FAIL record unless the caller says which kind of red it is:

  • --mutation NAME — already exists, and already means "I broke this deliberately". Implies the reds are intended.
  • a new explicit opt-in — "these reds are a genuine observation of the code under test", for a real CI red or a reproduced regression.

Default: refuse, naming the failing checks.

Not "refuse FAILs unless --mutation". A genuine CI red is the most valuable row the ledger can hold and has no mutation to name; that rule would refuse precisely the entry the ledger exists for, which is the deadlock shape the budget file already argues against for checks_never_observed_red.

Why a default refusal is affordable

Nothing automated calls merge. grep -rn 'pgc_ledger.py merge' .github/ test/ finds exactly one hit, and it is the gate's own advice string telling a human what to run. gate is the automated path. So merge is always a deliberate act, and a default refusal breaks no CI path.

The red-first test is already written

@jdatcmd's .git-less log is a ready-made fixture: it reconciles, it holds fifteen FAILs, and it must be refused. @OffgridwithJD's 350 run is the same shape from the other direction — one FAIL, perfect reconciliation, red belonging to an unfinished change.

Both harnesses, independently implemented, per the standing rule.

Out of scope

Deciding whether a red is real. The tool cannot know and this issue does not ask it to; it asks the caller to say.

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