Found while implementing #12633 (PR #12643); out of that card's scope and its file surface's defect class, so filed rather than fixed there.
The reading
renderReport in scripts/pm/check-governed-merges.mjs picks the attribution column on the presence of entry.attribution alone:
constwho=e.attribution
? `merged_by ${…} @ ${…} (via ${…})`
: `merged_by UNAVAILABLE — every channel failed; see the attribution note below`;But the attribution loop in main() skips an entry with no PR number entirely — if (entry.pr == null) continue; // its own loud entry; nothing to look up. So for that entry zero channels were tried, and the line asserts that every one of them failed. It also refers the reader to "the attribution note below", which summariseAttributionFailures does not produce for it either: that function groups only entries carrying attributionError, and this one has none. Measured on a constructed sweep (2026-08-27, PR #12643's QS-7 fixture, zero API lookups):
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below
0 API lookup(s) is printed four lines above it, in the same report.
Why it is worth a card rather than a shrug
This is the one entry shape the header calls out as more anomalous than any PR merge — a direct push to main touching a governed surface. Attaching to it a false claim about what was tried, plus a pointer to a note that does not exist, is exactly the kind of line a reader learns to discount; and the audit's whole product is a list a human reads entry by entry to decide "do I recognise this". The two facts differ and the report should keep them apart, the same way it keeps "nothing was found" apart from "nothing was looked at" everywhere else (#4690).
Shape of a fix (not taken here)
Split the column three ways rather than two: resolved · UNAVAILABLE, every channel failed (there is an attributionError) · NOT LOOKED UP, no PR number in the subject (nothing to query). Only the middle one should point at the attribution note. Small, report-only, no judgment moves — and --self-test already has the fixtures for the first two, so the third is one more case beside them.
Found while implementing #12633 (PR #12643); out of that card's scope and its file surface's defect class, so filed rather than fixed there.
The reading
renderReportinscripts/pm/check-governed-merges.mjspicks the attribution column on the presence ofentry.attributionalone:But the attribution loop in
main()skips an entry with no PR number entirely —if (entry.pr == null) continue; // its own loud entry; nothing to look up. So for that entry zero channels were tried, and the line asserts that every one of them failed. It also refers the reader to "the attribution note below", whichsummariseAttributionFailuresdoes not produce for it either: that function groups only entries carryingattributionError, and this one has none. Measured on a constructed sweep (2026-08-27, PR #12643's QS-7 fixture, zero API lookups):0 API lookup(s)is printed four lines above it, in the same report.Why it is worth a card rather than a shrug
This is the one entry shape the header calls out as more anomalous than any PR merge — a direct push to
maintouching a governed surface. Attaching to it a false claim about what was tried, plus a pointer to a note that does not exist, is exactly the kind of line a reader learns to discount; and the audit's whole product is a list a human reads entry by entry to decide "do I recognise this". The two facts differ and the report should keep them apart, the same way it keeps "nothing was found" apart from "nothing was looked at" everywhere else (#4690).Shape of a fix (not taken here)
Split the column three ways rather than two: resolved · UNAVAILABLE, every channel failed (there is an
attributionError) · NOT LOOKED UP, no PR number in the subject (nothing to query). Only the middle one should point at the attribution note. Small, report-only, no judgment moves — and--self-testalready has the fixtures for the first two, so the third is one more case beside them.