Repository navigation
fix(#1323): skip pre-push replay when origin/branch is an ancestor - #1793
Conversation
post-fix replayed local commits onto the freshly fetched origin/<branch> unless one of the narrow, flag- or instruction-gated skip arms fired. When the agent reconciled a stale base by merging the target (no rebased_onto_target / merged_target flag, or a forge state that was not CONFLICTING), origin/<branch> was already an ancestor of HEAD, yet the default `git rebase` still ran. It drops the merge commit and replays target-side history onto the old PR tip: it either re-hits the conflict the agent already resolved (push-rejected, as on fullsend#7308) or applies cleanly and pushes a silently inflated PR (as on fullsend#8755). Add a general skip arm just before the replay: when nothing else has skipped it and `git merge-base --is-ancestor origin/<branch> HEAD` holds, skip the replay and push directly (a fast-forward). The check uses runner-computed ancestry only, so it does not widen trust in sandbox-written flags; it is a no-op for linear histories; and GitLab reconstructions (diverged SHAs) never satisfy it, so they keep falling through to the existing replay. Tests: add push-preserves-unflagged-target-merge (clean and conflicting replay, plus a signed-commits-off variant), which fail on the old script. Narrow push-history-rewrite-ordinary-append to assert only that the preservation skips do not fire, since a linear append now takes the fast-forward skip. The optional richer push-rejected comment from the issue is not included. Note: pre-commit could not fetch hook repos (TLS failure in the sandbox); the configured hooks were run directly instead (shellcheck, pre-commit-hooks fixers/checks, lint-release-cases) and passed, and gitleaks ran via scan-secrets. Three post-fix-test.sh checks fail identically on main because yq is not installed in the sandbox. Closes #1323 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
🤖 Finished Review · ✅ Success · Started 3:18 PM UTC · Completed 3:27 PM UTC Commit: Runtime: pi · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $1.70 |
|
<!-- fullsend:risk-assessment --> DetailsSmall, well-tested bug fix precisely matching linked issue scope, tempered by protected script modifications and historical churn without a rollback toggle. |
ReviewFindingsMedium
Labels: PR fixes a defect in the fix-agent post-script push logic. |
|
This should cover most cases. If a concurrent update causes the post-script to rebase, though, it can still drop the agent’s merge and inflate the PR diff. I’ve tracked that edge case separately in #1797. |
waynesun09
left a comment
There was a problem hiding this comment.
LGTM. Checked at a72dadd:
post-fix-test.shpasses against the source and the bundled script (195 tests), andmake check-bundleis clean.- The three new
push-preserves-unflagged-target-mergetests fail against main's script, so they catch #1323. origin/<branch>is force-fetched right before the ancestry check. The plain push, its lease fallback and the signed path all reject a remote that moved, so nothing is overwritten.
Non-blocking test and comment nits for a follow-up:
- No bot-triggered variant, and no case where the remote moved while HEAD has a merge.
- The negative greps at
post-fix-test.sh:2525/2620/2796still match the shared "skipping rebase onto" prefix. - The header comment at
post-fix-test.sh:3395and themerged_targetwording inskills/fix-result-contract/SKILL.mdare now stale.
Summary
scripts/post-fix.src.sh(and the regenerated bundlescripts/post-fix.sh) now skips the pre-pushgit rebase origin/<branch>replay whenever the freshly fetchedorigin/<branch>is already an ancestor of localHEAD. In that state the plain push is a fast-forward and there is nothing to reconcile.Why
The replay was only skipped through narrow arms tied to the agent's
rebased_onto_target/merged_targetflags, the human instruction wording, or a forge-reported conflict. When the agent reconciled a stale base with an ordinary merge of the target,origin/<branch>was already an ancestor ofHEAD, but the defaultgit rebasestill ran. That rebase drops the merge commit and replays target-side history onto the old PR tip, with two outcomes:Approach
A new arm just before the replay, active only when nothing else has already set
SKIP_REMOTE_REBASE:git merge-base --is-ancestor "origin/${BRANCH}" HEADHEADdoesn't have, the replay still runs (covered bypush-rebase-conflict-fails-closed).Testing
push-preserves-unflagged-target-mergetests inscripts/post-fix-test.sh:clean: the replay would apply cleanlyconflict: the replay would conflictsigned-commits-offvariant ofcleanpush-history-rewrite-ordinary-appendnow checks only that the "to preserve" skip arms don't fire, because a linear append now takes the fast-forward skip.post-fix-test.shpasses against both the source and the bundled script. The exception is 3 harness-config checks that fail the same way onmainbecauseyqisn't installed in the sandbox.post-fix.src.sh.skillsaw --strictpasses, and shellcheck plus the pre-commit-hooks checks pass when run directly.pre-commititself couldn't fetch its hook repos because of a sandbox TLS error.Not included
The issue's optional richer push-rejected comment, which would mention that the agent had already recorded a verified resolution. With this skip in place, the replay only fails when the remote really did gain commits the agent never saw, and the existing message is accurate for that case.
🤖 Generated with Claude Code
Closes #1323
Post-script verification
agent/1323-skip-replay-when-ancestor)f589f2e719ba9bdda0a5915b8d4a86c45e7694e7..HEAD)