require_last_push_approval is structurally incompatible with atomic stack merge — approvals become stale during the merge #465
carrotRakko
started this conversation in
Feedback
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
TL;DR: With the ruleset rule "Require approval of the most recent reviewable push" on the base branch, an atomic stack merge fails at the second PR even when every PR is APPROVED / CLEAN / green at fire time. This appears to be not an evaluator bug but a structural incompatibility: the atomic merge is internally sequential, and landing the bottom PR moves the next PR's merge base — which is exactly what the rule defines as staleness. Every approval granted before the merge is necessarily "before the merge base changed" for all but the bottom PR. This also seems to be the mechanism behind the main blocker in #404 (same error text, same rule).
Repro (2026-08-22, private preview, squash, 3-PR stack)
PUT .../pulls/{top}/merge-async→Re-approving and re-firing fails identically: the staleness is created by the merge operation itself, so no amount of pre-merge approval can satisfy the rule for the 2nd layer and above.
Relation to existing reports
dismiss stale reviews on pushenabled, require-last-push disabled): the atomic merge fails the same way —"At least 1 approving review is required", i.e. the mid-merge base movement counts as dismissal (though, nicely, the rollback leaves the actual reviews intact) — and after a plain single-PR merge, the retarget alone dismisses the next PR's approval. So neither approval-protection rule can currently coexist with stacks; the incompatibility is with the family, not one rule.Ask
Treat stack-internal base changes (and stack rebases that preserve the diff) as non-invalidating for both approval rules — "require approval of most recent push" and "dismiss stale approvals". Short of a fix, documenting that atomic stack merge cannot currently be used together with require-last-push-approval would save preview users a very confusing failure (everything is green, yet the merge fails).
Workaround we're using
Merge one layer at a time: merge bottom → cascade-rebase the remainder → re-approve → merge next (each approval lands after the latest push, satisfying the rule). This applies on the PR-page path too: its post-merge auto-rebase is itself a force-push, which re-stales the upper approvals just the same — merging a 3-PR stack that way required a fresh approval round per layer. The cost is brutal with required CI: every cascade force-push re-runs CI on all remaining layers, so an N-layer stack pays O(N²) CI runs (vs. zero extra for the atomic merge), and wall-clock stretches to N× because each layer serializes rebase → CI → approval → merge. It "works" while burning exactly the time and compute that stacks exist to save.
And there is no escape hatch: for org-admin bypass actors, the bypass checkbox that appears on regular PRs when rules are unmet does not appear on stack PRs — the stack merge UI implements no bypass path (consistent with the documented preview limitation). So under this rule, native stacks currently offer only "per-layer O(N²)" or "temporarily disable the rule" — which is why we'd much rather see the rule made stack-aware than keep either workaround. (For now, we have removed the rule from our org and fallen back to a process-level guard, hoping to re-enable it once approval rules become stack-aware.)
✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)
All reactions