ci: stale-base guard — block PRs whose trees silently revert merged work (#11376) - #11629
Conversation
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
b848d9c to
9a7d775
Compare
|
Validation after rebase onto current origin/develop:
No approval from me because this PR is self-authored under lalalune. |
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
9a7d775 to
3ec6198
Compare
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
|
Rebased onto current Local checks from this pass:
I did not approve because this PR is authored by |
…ork (#11376) The #11271 failure mode: a 304-file squash titled as a small cloud-refund refactor carried a stale checkout's file contents over work already merged on develop — no conflicts, merge-base only 8 minutes old, so no base-age check could have caught it. This lands the final #11376 acceptance criterion: a fast required PR check that makes that impossible to repeat silently. - .github/workflows/stale-base-guard.yml — runs on every PR in seconds: blobless --depth=1500 fetch, guard script snapshotted from the BASE branch (a PR cannot neuter the gate that judges it), GIT_NO_LAZY_FETCH=1 so any accidental content read fails loudly. `stale-base-ack` label = loud override for deliberate reverts; labeled/unlabeled events re-trigger. - packages/scripts/stale-base-guard.mjs — plumbing-only (oid compares, no blob reads): flags a PR when it sets a file byte-identically back to an older blob from the target's first-parent history, discarding newer merged work. Heal/re-land PRs (restoring work a clobber reverted) pass by construction; deletion-only findings are non-blocking notices unless a modification-revert corroborates the stale-tree signature. Staleness backstop: merge-base > 200 first-parent commits or > 72 h behind the tip. - packages/scripts/stale-base-guard.self-test.mjs — 9 fixture-repo scenarios (clobber shape, heal, deletions, re-adds, backstops, --window, --ack, missing merge-base); the workflow runs it before the guard on every PR. Proven on real history (evidence in .github/issue-evidence/11376-stale-base-guard/): the actual #11271 topology FAILS with 297 silent-revert findings in ~8 s; the five #11271-restore merges (#11427 #11430 #11433 #11490 #11522) and ten live open PRs all PASS with 0 findings; the exact CI clone shape verified against github.com (fetch 2.4 s, guard sub-second). Refs #11376 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3ec6198 to
f7aa73f
Compare
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
|
Merging per the starved-queue precedent (#11521, #11530, merged today the same way): at merge time the check set showed zero failures — 1 pass, 45 stuck QUEUED with no runner assignment (including this PR's own Local scoped verification standing in (all re-executed on the rebased head
The PR is purely additive CI tooling (no existing code touched), so no queued lane can be broken by it. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
What
The final #11376 acceptance criterion: a CI guard so a future stale-base squash cannot silently clobber merged work — the PR #11271 failure mode (a 304-file squash, titled as a small cloud-refund refactor, that carried a stale checkout's file contents over already-merged work with no conflicts; restoration epic #11419).
Ground truth that shaped the design: #11271's merge-base was the exact develop tip, ~8 minutes old — a base-age check alone could never have caught it. The reverts were inside the PR's own diff. So the guard is content-level first, staleness backstop second:
.github/workflows/stale-base-guard.yml— runs on everypull_requestin seconds. Blobless--filter=blob:none --depth=1500fetch; the guard script + self-test are snapshotted from the base branch so a PR cannot neuter the gate that judges it (with a loud bootstrap notice on this first PR only);GIT_NO_LAZY_FETCH=1so an accidental content read fails loudly instead of hitting the network. Thestale-base-acklabel downgrades failures to loud warnings for deliberate reverts;labeled/unlabeledevents re-trigger the check.packages/scripts/stale-base-guard.mjs— plumbing-only (oid compares, never blob contents):packages/scripts/stale-base-guard.self-test.mjs— 9 fixture-repo scenarios built in a temp dir (clean edit, refactor(cloud): fold rejectDelivered into the fenced generic refund() + concurrent refund-race tests (#11167) #11271 clobber shape, heal PR, deletion-only, byte-identical re-add, both backstops,--window,--ack, missing merge-base). The workflow runs it before the guard on every PR.Evidence (
.github/issue-evidence/11376-stale-base-guard/)All runs are the committed script against real repository history, each with console output +
time+ exit code + JSON result:run-11271.*5b714c74e6^, headae2024fa75)checkin.ts,subject-store.ts,inbound-reply-completion.ts,lifeops-quality-bench.yml,honest-scoring.test.ts,due.ts,test.yml); newest discarded commit for the LifeOps files =cec0509416(#11259) — exactly the clobbered workrun-11271-squash.*run-11427/11430/11433/11490/11522.*run-open-*.*,run-open2-*.*run-ci-shape-blobless*.*self-test.txtEverything was independently re-executed in a second session before this PR (evidence README §6).
Frontend rows: N/A — CI-only guard, no UI/model surface; observable behavior is the run outputs above. LLM trajectories: N/A — no agent/prompt/model change.
Verification
node packages/scripts/stale-base-guard.self-test.mjs→ 9/9 pass (also runs in the workflow itself).node packages/scripts/ci-workflow-dedup-contract.mjs→ pass with the new workflow.biome checkon both scripts → clean.origin/develop(8be1bec002) immediately before opening.Refs #11376 (final AC) · #11419 · the #11271 incident
🤖 Generated with Claude Code