feat(pre-push): pin the context table against branch protection #1329
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!1329
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/1327-pre-push-table-coverage"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
cmd/rt/pre_push.gocarries a hand-maintained table of status contexts, and nothing held it to the branch-protection rule it is a copy of. It was unpinned in both directions, so both a false pass and a wasted gate were available without any local signal.What was broken
The second is the one that costs something: a green pre-push battery is read as "the required set is covered", and it never was.
The fix
tableDrift()grades the table by set-difference against the live required contexts — the same complement form the reviewer procedure uses for status checks. A complete tally and a predicate that cannot be wrong look identical on the day they agree; a set-difference returns the informative value in every world.fails— the battery refuses.UNDECLARED, unless named inlocallyUnreproducible(17 entries) — the table's own record of what a local battery genuinely cannot run.What it found on its first run
The comment above the table said
changelog-body-checkwas not required. Both its contexts are required. The battery was already running that gate while its own comment said it did not have to. Fixed here.A half-stale comment is worse than an absent one — it made the omission read as deliberate, which is exactly the state a reader does not re-check.
Verification
Five arms: undeclared · stale · declared-is-not-undeclared · both-directions · a self-consistency arm over the real table that first asserts the table is non-empty, since a subset check over an empty table passes vacuously.
Four mutants, each verified applied against a saved original rather than against
HEAD— agit diff --numstatvs HEAD measures the whole uncommitted change and would have certified an inert mutation as applied:Scope — what this does NOT check
It pins the table against
status_check_contexts. It does not readenable_status_check, so on a repo where checking is disabled the required set is enforcement-free and this grades a table against a list that gates nothing.release-toolkithasenable_status_check=true, so the pin is live here; it would be vacuous onalcatraz-infra,emberorpurser.Intended-targets: #1327
Closes #1327
🤖 Generated with Claude Code
https://claude.ai/code/session_011VD4JoNbNqJkS8H1RdJfZj
pre_push.go carried a hand-maintained table of status contexts with nothing holding it to the branch-protection rule it is a copy of. It was unpinned in BOTH directions: a context dropped from the required set leaves a stale row that makes the battery run a gate nobody requires, and a context ADDED to the required set is simply absent, so the battery reports green on a set it never covered. tableDrift() grades the table by set-difference against the live required contexts, the same complement form the reviewer procedure uses for status checks -- a tally and a predicate that cannot be wrong look identical on the day they agree. stale in the table, not required -> adds to `fails` undeclared required, not in the table -> printed as UNDECLARED, unless named in locallyUnreproducible (17 entries), which is the table's own record of what a local battery cannot run Found on the first run and fixed here: the comment above the table said `changelog-body-check` was not required. Both its contexts ARE required, so the battery was already running that gate while its own comment said it did not have to. Half-stale comments are worse than absent ones -- the table read as deliberate. Arms: undeclared · stale · declared-is-not-undeclared · both directions · a self-consistency arm over the REAL table, which first asserts the table is non-empty (a subset check over an empty table passes vacuously). Mutation: four mutants, each applied against a saved original rather than against HEAD (a numstat vs HEAD measures the whole uncommitted change, not the mutation). All four redden with ran=5 and build clean -- M1 stale-never-reported 2 · M2 undeclared-never-reported 2 · M3 declared-map-ignored 2 · M4 stale-inverted 4. Closes #1327 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011VD4JoNbNqJkS8H1RdJfZjAPPROVE @
93c35fd8347aaf4a325b047fcdb5f3290c0dc4b9The set-difference framing is right, and the reason it is right is the one the body gives: a complete tally and a predicate that cannot be wrong look identical on the day they agree.
I reproduced M1 rather than reading the table. Saved original, mutated
stale = append(stale, c)into a no-op, confirmed the mutant differed from the saved original rather than fromHEAD, restored afterwards:Matches your M1 exactly, and the two arms that fire are the two that should.
selected=5is asserted rather than assumed — a-runtypo printsok … [no tests to run]at rc=0.It resolves a finding I raised on
#1320without being asked to. I flagged thatpre_push.go:117still namedfragment-check / toolkit-self gate (PR's own rt), a context that PR retired. On this head that row is gone andtoolkit-self-gates / …is declared unreproducible with a reason. Measured against the live rule,stale = 0.Live drift on this head, computed from
status_check_contexts(HTTP 200,enable_status_check=true):So the
UNDECLAREDline fires six times on the first real run. That is the feature working, not a defect.✅ And the
changelog-body-checkcomment is the best thing in the diff. It states the contexts are required, that the battery already runs the gate, that this prints them asUNDECLARED— and then declines to claim coverage, because the reusable posts two contexts from one job and whether one bare invocation closes both is unestablished. Refusing to earn coverage on a fraction of the evidence is the whole discipline, and the body's "Fixed here" undersells it: the comment was fixed, the coverage claim deliberately was not.Two findings. Neither blocks; the second is worth a follow-up.
①
summaryLinedoes not carryundeclared, so this PR's own distinction cannot be read by the surface it built for exactly that purpose.The comment above it is explicit that the tally exists because "a disclosure that cannot alter control flow is decoration" and that a wrapper should be able to "act on the NUMBERS without parsing prose." But the split this PR creates — decision vs gap — lives only in prose:
Today that
17is 11 declared decisions + 6 undeclared gaps, and a wrapper reading numbers sees one figure. The prose renders them differently; the machine surface renders them identically — which is the statelocallyUnreproducible's own header says it exists to end. One more field closes it.②
locallyUnreproducibleis a second hand-maintained list and this PR does not pin it — and six of its seventeen entries are already dead.Declared but not in the required set:
tableDriftgrades the table against branch protection in both directions and readsdeclaredonly as a filter, so nothing gradesdeclareditself. Today those six are inert.🔴 The hazard is not tidiness, it is a pre-suppressed warning.
undeclaredis built as required ∧ not-in-table ∧ not-in-declared, so if any of those six is ever re-added to the required set it arrives already declared unreproducible and never prints asUNDECLARED— a decision recorded for a context nobody made a decision about in its current form.base-divergence-check / checkis the live candidate; it landed mid-migration and is exactly the sort of thing that gets required later.The symmetric fix is the one this PR already wrote: a third return,
deadDeclarations = declared − required. I would not hold the PR for it — it is the same class of finding this change exists to make findable, which is the argument for landing this first.Scope disclosure is accurate.
enable_status_check=trueonrelease-toolkit, so the pin is live here; I confirmed it reads 200 rather than assuming an admin read succeeds.Reviewed at
93c35fd8347aaf4a325b047fcdb5f3290c0dc4b9;commit_idomitted so the read-back reports the substrate's head rather than my own input.New commits pushed, approval review dismissed automatically according to repository settings
APPROVE @
023d807525e866c0d02c6c34ca7467ed0c09022a— re-stamp; my earlier approval was bound to93c35fd8and no longer binds.The delta since that stamp is the changelog fragment and nothing else:
93c35fd8is an ancestor of this head (appended, not rebased), and the branch's merge-base is unchanged at0b1dd496— so the code I reviewed is byte-identical and the review stands in full. Both findings from it are unchanged, and finding ② is now#1333.⚠️ Note the head I stamped is not the one I was pointed at. The re-review request named
66263062; that commit is real and is an ancestor here, but it was superseded three minutes later by023d8075("cut the 1327 fragment to the density rules"). I read the head at submit time rather than taking the cited one — which is the only reason this stamp binds to something live.On the one red:
base-divergence-check / checkis failing, and it does not block.It is not in
status_check_contexts(23 required; this is not among them), so it posts and fails without gating. What it is telling you is true, though: main moved toe27b696ewhen#1319merged, and this branch's merge-base is still0b1dd496. A rebase clears it.🔑 And it is a live instance of
#1333's premise rather than a coincidence.base-divergence-check / checkis one of the six entries inlocallyUnreproduciblethat are declared-but-not-required. It is running, posting, and visibly useful right now — which is exactly why#1312's open decision to require it matters, and exactly why its pre-suppressedUNDECLAREDwarning would land the day that decision does.Reviewed at
023d807525e866c0d02c6c34ca7467ed0c09022a;commit_idomitted so the read-back reports the substrate's head rather than my own input.