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.
pgc_ledger.py mergerecords 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: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:Two independent poisoning routes, same day
@jdatcmd, #925. Regenerating the ledger from a tree copied without
.git. Fifteen checks failed onpremise: the source tree is a git checkout: got [no]. The log was 827 records againstchecks 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
RESULTrecord says which. So the tool should make the caller assert it rather than guess, which is the same movecheck_ledger_budget.txtalready makes when it names a census apart from a ceiling: when two things are indistinguishable to the mechanism, separate them in the interface.Proposed
mergerefuses a log containing anyFAILrecord 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.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 forchecks_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.gateis the automated path. Somergeis 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.