rule(B-0746)+backlog: GitHub auto-closes PR on force-push to base-SHA + refuses reopen — industry-wide trap (bit Zeta on #4997 + ServiceTitan per Aaron)#5011
Conversation
… + refuses reopen — industry-wide trap (bit Zeta on #4997 + ServiceTitan per Aaron) Aaron 2026-05-25, after I hit the trap on PR #4997 squashing via force-push: 'save that lesson it's not obvious even to human devs it's bit us too at ServiceTitan.' Trap shape: squashing a PR's branch via force-push must NEVER land the branch ON the base ref's SHA, even briefly. GitHub treats 'head==base' as terminal (no diff = no PR) + auto-closes; the reopenPullRequest GraphQL mutation refuses the reopen even after the head ref is restored to a diff-having SHA. Two correct patterns documented: - Pattern A (recommended): cherry-pick onto fresh branch + open new PR; old branch never modified - Pattern B: verify HEAD-moved-before-pushing via pre-push check Clarifies that --force-with-lease IS Aaron-authorized + safe in itself (per his 2026-05-25 'force least is always fine i never look at it is destructive' standing authorization); the trap is the SPECIFIC failure mode of pushing a base SHA, not force-push. Empirical anchor: 2026-05-25 session PR #4997 → squash attempt → silent commit failure OR rev-parse misread → pushed base SHA → GitHub auto-closed → reopen refused → substrate carried over to fresh PR #5010 + cross-link comment on closed #4997. Industry-wide per Aaron's ServiceTitan disclosure; rule landing in .claude/rules/ ensures future-Otto + future-AI + (transitively) future human contributors inherit the trap-avoidance at cold-boot. Composes with B-0737 (the substrate that was on the trapped PR) + B-0741 fork framing (rule travels to any fork that adopts the rule library — concrete value of 'AI-native project adopts Ace conventions for free'). Co-Authored-By: Claude <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR lands an operational “rule + backlog row” documenting a GitHub PR state-machine trap: if a force-push briefly makes a PR’s head SHA equal the base SHA (no diff), GitHub auto-closes the PR and (per the write-up) refuses reopening even after restoring the head SHA. It captures an empirical anchor (PR #4997 → #5010) and regenerates the backlog index.
Changes:
- Added a new
.claude/rules/rule describing the trap and recommended mitigation patterns. - Added backlog row B-0746 documenting the lesson + empirical anchor.
- Updated
docs/BACKLOG.mdto include B-0746.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
.claude/rules/github-pr-auto-closes-on-force-push-to-base-sha-refuses-reopen.md |
New operational rule describing the trap + mitigation patterns. |
docs/backlog/P2/B-0746-github-pr-auto-closes-on-force-push-to-no-diff-state-irreversibly-rule-landing-empirical-anchor-pr-4997-to-pr-5010-aaron-2026-05-25.md |
New P2 backlog row capturing the lesson and empirical anchor. |
docs/BACKLOG.md |
Regenerated index entry adding B-0746. |
| ## Pattern C — `--force-with-lease` is Aaron-authorized + safe in itself | ||
|
|
||
| Per Aaron 2026-05-25: *"force least is always fine i never look at it is destructive"* — `--force-with-lease` is the safe form of force-push (refuses if the remote SHA isn't what you expected, which catches concurrent pushes from peer agents). Aaron explicitly authorized it. | ||
|
|
||
| The classifier wrongly blocked `--force-with-lease` as destructive in one session; clarification: Aaron's standing authorization covers `--force-with-lease`. Plain `--force` (without `-with-lease`) remains discretionary + needs explicit per-use authorization. |
| # 3. Commit + VERIFY HEAD moved | ||
| cd /tmp/squash-prep |
|
Closing as substrate-stale (DIRTY-conflict) per .claude/rules/pr-triage-tiers.md Tier 3 + the discriminator pass below. Discriminator pass:
Disposition: close. The branch content is preserved in git history; re-land path is cherry-pick onto a fresh branch off current main with any ID-collision renumbering needed. This is the same Tier 3 disposition applied to today's #5038 + #5032 (same root cause: 2026-05-25 evening session left ~9 backlog/rule PRs DIRTY when the next morning's iter-5.x + iter-6 work landed and moved main forward). This close is NOT a punt — it's explicit ownership classification per .claude/rules/fighting-past-self-vs-peer-agent-distinguisher-fix-your-own-coordinate-on-peers-dont-punt-by-default.md (recurrence anchor landed today via #5126). The substrate-honest re-land path is documented; the operator-tax of indeterminate DIRTY state is cleared. |
Pull request was closed
Summary
Aaron 2026-05-25, after I hit the trap on PR #4997 squashing via force-push:
The trap shape
Squashing a PR's branch via force-push must NEVER land the branch ON the base ref's SHA, even briefly. GitHub treats
head == baseas terminal (no diff = no PR) + auto-closes; thereopenPullRequestGraphQL mutation refuses the reopen even after the head ref is restored to a diff-having SHA.Two correct patterns documented
Pattern A (recommended): Cherry-pick onto fresh branch + open new PR. Avoids the trap entirely; old branch never modified.
Pattern B: Verify HEAD-moved before push:
--force-with-leaseclarificationPer Aaron's standing authorization:
--force-with-leaseis safe + authorized. The trap is NOT force-push being destructive; it's the SPECIFIC failure mode of pushing a base-SHA to a PR branch.Empirical anchor
2026-05-25 session:
origin/main, cumulative diff applied,git commitrangit rev-parse HEADreturnedorigin/main's SHA — silent commit failure OR rev-parse misread--force-with-lease(succeeded; remote branch went to no-diff state)gh pr reopen 4997refused via GraphQL even after head ref restoredIndustry-wide
Aaron's disclosure: "it's bit us too at ServiceTitan" — not Zeta-specific. Any team using GitHub PRs + occasionally force-pushing for squash + having automation that could silently fail at commit step is susceptible.
Composes with
.claude/rules/zeta-expected-branch.md(branch-state verification discipline; sibling).claude/rules/codeql-no-source-on-docs-only-pr-is-broken-commit-canary.md(sibling failure mode shape — commit-tree corruption vs PR-state corruption)Test plan
.claude/rules/<name>.mdmatches loading-taxonomy)🤖 Generated with Claude Code