Skip to content

fix(guards): pin origin/main to the run's build base, so a re-run judges the main it was built on - #4996

Merged
noahgift merged 3 commits into
mainfrom
PMAT-4861-guard-build-base
Oct 10, 2026
Merged

noahgift merged 3 commits into
mainfrom
PMAT-4861-guard-build-base

Conversation

@noahgift

@noahgift noahgift commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

What

Every ratchet in guard-tree and guard-cargo compares the PR's tree with origin/main. The setup step fetched main's tip at depth 1. The tip moves while the merge commit does not. So a run that starts after main shrank a baseline, or fixed a function, goes red on lines the PR never touched.

Fix

scripts/ci/pin_build_base.sh replaces the fetch step in both jobs. It points refs/remotes/origin/main at the commit the run was built on:

event origin/main is pinned to
pull_request the merge commit's first parent. HEAD must be a two-parent merge whose second parent is pull_request.head.sha, else it refuses
merge_group merge_group.base_sha
any other event the tip (deepened by one on push)

On any doubt it prints ::error::pin_build_base and exits 1, so the guards skip red, never green. Its --self-test (22 rows) runs in guard-tree before the pin.

The ratchet library's own first-parent comparand (FIRSTPARENT, from the #4983 commits on this branch) stays as a second line of defence. With the pin in place, the tip path and FIRSTPARENT name the same commit. The pin also covers readers of origin/main that do not use the library, and the merge queue.

Falsifier

scripts/tests/ratchet_semantics_test.sh --class complexity: 22 checks, 0 failed.

row case expected
C10 / C14 stale-merge re-run (main moved after the build), via the library / via the pin GREEN
C13 / C15 a real complexity rise, via the library / via the pin RED (NEW newbie)
C12 / C16 planted mutant of the library / of the pin the re-run turns RED again

Measured mutants, both caught:

  • MA: the library's first parent forced to HEAD. C9, C10, C12 and C13 fail.
  • MB: the pin takes the second parent. C14 and C15 fail; under MB the real rise went green.

Also green on this head:

  • ratchet_semantics_test.sh: readme class 12/0, satd class 10/0;
  • check_baseline_ratchets.sh --selftest and check_no_fabricated_baselines.sh;
  • fat_driver.py self-test 60/60;
  • check_sourced_libs_option_neutral.sh;
  • the 13 guards that read ci/sections.yml.

Not in this PR

  • A second pin. The refinement gate in workspace-test pins origin/main with its own event map (batch/ont-10: every remaining ONT-10 row for 0.70.0 (one PR, one quorum, one CI) #4502, ci/sections.yml:430-440): pull_request.base.sha, merge_group.base_sha, push before. This PR leaves it alone. On the two real runs read, it names the same commit as this pin:

    run merge commit its first parent refinement base
    fix(gpu): a test filling the device let neighbours' allocations OOM in-process (#4956) #4970, 37945794011 attempt 2 60a5903 845004a 845004a
    fix(gpu): a shared cubin temp file let a reader load a partial cubin (#4956) #4964, 37978589934 (reopened) 24eb969 9259972 9259972

    On merge_group both read base_sha. Two maps with no row comparing them can still drift apart without anyone noticing. Follow-up: one guard row that asserts both give the same commit per event, or the refinement step calling pin_build_base.sh. Changing a second gate here would widen this gate change.

  • Self-test gaps. The second review's mutation run tried 27 mutants and killed 21. The six survivors are missing rows, not wrong results on the plants:

    • the pin without its read-back of origin/main;
    • the pin with a failed tip fetch swallowed by || true;
    • the library's FIRSTPARENT arm without its origin/main condition, and without its empty-merge-base condition (two mutants);
    • check_no_fabricated_baselines.sh without its origin/main condition;
    • check_census_derived.sh without FIRSTPARENT in its accepted modes.

    They go in the same follow-up as the comparing row.

Gate change

This changes what a gate accepts on PR runs. Before merge it needs:

  • a sign-off round from a non-author session, bound to the final commit;
  • 2 non-author reviews;
  • green CI.

keep-open: #4861 and #4983 stay open for the follow-up named under "Not in this PR" (the row comparing the two pins, and the six self-test rows). #3997 is the parent epic.

Refs #4861
Refs #4983
Refs #3997

🤖 Generated with Claude Code

noahgift and others added 3 commits October 9, 2026 20:24
… first parent, not main's moving tip

Refs #4983

A pull_request run checks out GitHub's merge commit at depth 1, so there
is no merge-base and every baseline ratchet fell back to the origin/main
TIP. A re-run keeps the old merge commit but fetches main fresh, so once
main moves, main's own shrinks read as this PR's growth and an unchanged
PR turns red on files it never touched (#4970 run 37945794011, m17).

- lib_baseline_ratchet.sh: new FIRSTPARENT comparand, ahead of TIP, only
  for origin/main, only with no merge-base, only when the event is
  pull_request and HEAD is GITHUB_SHA with two or more parents. The
  parent is read from the commit object (HEAD^1 does not resolve under a
  depth-1 graft). Fail-closed: parent not fetched -> UNRESOLVABLE with a
  fetch-by-sha hint, never the tip; parent lacks the path -> BOOTSTRAP or
  ABSENT, never the tip.
- ci/sections.yml (guard-tree and guard-cargo setup steps): on
  pull_request, fetch that first parent at depth 1.
- census, complexity and readme callers: FIRSTPARENT admitted by census;
  UNRESOLVABLE hints name the fetch that resolves the ref.

Red/green: ratchet_semantics_test.sh --class complexity C8 (control: the
tip path reproduces the false red), C9 (parent unfetched -> RED at
preflight, nothing measured), C10 (re-run GREEN on FIRSTPARENT), C11
(first run gives the same verdict on both paths), C12 (registered
mutation at RATCHET-FIRSTPARENT-MUTATION-POINT turns C10 RED again).
check_baseline_ratchets.sh --self-test gains 10 resolver/end-to-end rows;
the same mutation turns 5 of them RED.

Agent: aprender-a7n

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…first-parent comparand too

The q-4983 sign-off round's minority lane found a second resolver with the same
defect in the same guard job: check_no_fabricated_baselines.sh had its own
resolve_base_ref, merge-base then the origin/main TIP. On a pull_request re-run
after main shrank the Rust ledger, the PR's merge commit still carried the
removed entries, so the ledger read as GROWN on a PR that never touched it.

It now sources scripts/lib_baseline_ratchet.sh and takes the same FIRSTPARENT
step: only with no merge-base, ref origin/main, a pull_request event and
HEAD == GITHUB_SHA with two or more parents. Fail-closed as before: the first
parent not fetched is UNRESOLVABLE, and fetched without the ledger it is ABSENT
(this ledger has no bootstrap). Neither falls through to the tip. The
UNRESOLVABLE text prints the fetch that is actually missing.

Red/green, --selftest comparand table 3 -> 12 rows, all green with the event
unset and with GITHUB_EVENT_NAME=pull_request. Rows: push keeps TIP and its
verdict reds on main's shrink (the defect, reproduced); HEAD != GITHUB_SHA keeps
TIP; parent unfetched -> UNRESOLVABLE, rc 1; parent fetched -> FIRSTPARENT and
the re-run verdict is green; a single-parent head keeps TIP; a parent without
the ledger -> ABSENT. Mutation: blanking p1 at
FABBASE-FIRSTPARENT-MUTATION-POINT turns 4 rows RED (UNRESOLVABLE, FIRSTPARENT,
re-run verdict, ABSENT all read TIP). The full scan still passes (36 entries,
0 added, 0 removed vs the merge-base).

Refs #4983

Agent: aprender-a7n
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ges the tree it built against the main it was built on

Every ratchet in guard-tree and guard-cargo compares the PR's tree with
origin/main, and the setup step fetched main's TIP at depth 1. The tip moves
while the merge commit does not, so a run that starts after main shrank a
baseline or fixed a function goes red on lines the PR never touched:

- #4813: the pipe-into-grep-q ledger read 62 on the PR against 60 on a tip
  that had moved on.
- #4983: #4970's complexity step, run 37945794011 attempt 2, checked out the
  unchanged merge commit 60a5903 and judged it against main's moved tip
  acd599e. Attempt 1 and attempt 2 of the same commit disagreed.

scripts/ci/pin_build_base.sh now replaces both jobs' fetch step. It points
refs/remotes/origin/main at the commit this run was BUILT on:

- pull_request: HEAD's first parent. HEAD must be a two-parent merge whose
  second parent is pull_request.head.sha, else the pin refuses.
- merge_group: merge_group.base_sha.
- any other event: the tip, deepened by one on push.

On any doubt it prints ::error::pin_build_base and exits 1, so the guards
skip RED, never green. Its --self-test (22 rows) runs in guard-tree before the
pin.

The library's own first-parent comparand (FIRSTPARENT, the commits before
this one) stays as defence in depth: with the pin in place the tip path and
FIRSTPARENT name the same commit. The pin also covers readers of origin/main
that do not use the library, and the merge queue.

Falsifier (ratchet_semantics_test.sh --class complexity, 22 checks, 0 failed):
- C14: a stale-merge re-run, pinned, goes GREEN on the tip path (TIP = B1).
- C15: a real complexity rise, pinned, stays RED (RED NEW newbie, TIP = B1).
- C13: the same rise stays RED on the library's FIRSTPARENT path.
- C16: a planted mutant of the pin (its pull_request arm disabled) turns the
  stale re-run RED again, against B2.

Measured mutants:
- MA (library first parent forced to HEAD): C9, C10, C12, C13 fail.
- MB (pin takes parents[1]): C14 and C15 fail. C15 wanted 1, so the real rise
  went green under it.

Also green on this head: the readme (12/0) and satd (10/0) classes,
check_baseline_ratchets --selftest, check_no_fabricated_baselines, the
fat_driver self-test (60/60), check_sourced_libs_option_neutral, and the 13
ci/sections.yml guards.

Refs #4861
Refs #4983
Refs #3997

Agent: aprender-df
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=4996 head=0527930f15a2eb0e43d7dbcf3b85e297845fd811 verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

@noahgift
noahgift added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 5a3b047 Oct 10, 2026
29 of 32 checks passed
@noahgift
noahgift deleted the PMAT-4861-guard-build-base branch October 10, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant