Repository navigation
Description
Summary
The trusted-base merge handling added in #68459 (follow-up to #68448) never activates in practice, because pullRequest.base.sha from pulls.get is the merge base between the PR head and the base branch, not the current tip of the base branch. That commit is by definition already contained in the old PR head, so getBundleValidationBase always returns the PR-head fallback, and size and protected-file checks still count every base-branch commit that the agent merged in.
Where
actions/setup/js/push_to_pull_request_branch.cjs at v0.91.12:
- L752–759:
pullRequestcomes fromgithubClient.rest.pulls.get(...). - L1102–1107:
trustedBase = pullRequest.base.sha(fetched withgit fetch origin <sha>). - L145–148:
getBundleValidationBasereturns the fallback (old head) unlessgit merge-base --is-ancestor trustedBase oldHeadexits 1, i.e. unless the trusted base is not already in the old head.
The unit tests (push_to_pull_request_branch.test.cjs L2340–2347) set pr.base.sha to an arbitrary SHA and mock the git calls, so they do not exercise the real meaning of base.sha.
What we observed
In a private repository using push-to-pull-request-branch with patch-format: bundle, signed-commits: false, where the agent merges the latest default branch into the PR branch to resolve conflicts:
- For open PRs,
gh api repos/<o>/<r>/pulls/<n> --jq .base.shaequalsgit merge-base <head> origin/mainin every PR we checked (3 of 3, including one PR whose branch had merged main the same day and one that never had). base.shadoes not move when the default branch advances, and does not move to the base tip on a head push either: a PR created at 07:30 UTC still reported the 07:23 main commit after a head push made after 10:32, although main had advanced to 08:39 and later commits by then. It only moved when the PR branch itself merged main, and then it became the merged main commit, which again equals the merge base.- Replaying the three
merge-base --is-ancestorcalls from L145–148 locally, witholdHead= current PR head,trustedBase= the APIbase.sha, andcandidate= PR head merged with current main (one clean merge, one with conflicts):oldAncestor=0 baseAncestor=0 alreadyMerged=0in both cases → fallback to PR-head validation.
So for any PR, the condition that enables the merge-aware baseline cannot hold when the agent merges the base branch: the commit the handler treats as "newly incorporated" is the one the branch already had.
Note: GitHub's REST docs do not define base.sha precisely; the merge-base reading is from the observations above.
Expected
When the agent's bundle newly incorporates base-branch commits, validation should use a baseline that includes them, as described in safe-outputs-pull-requests.md ("compare against a validation-only merge of the old PR head and that trusted base").
Suggested fix
Resolve the trusted base from the base branch tip rather than pull_request.base.sha, e.g. git fetch origin refs/heads/<pullRequest.base.ref> (or repos.getBranch on base.ref) in the safe_outputs job, then keep the existing ancestry checks: base tip must be an ancestor of the candidate and not of the old head. If the branch moved after the agent started, the tip may not be an ancestor of the candidate; trying the GITHUB_SHA of the workflow run (the default-branch commit the run started from) before falling back to the PR head would cover agents that merged the commit they were started on.
Workaround we use
On v0.89.21 we patch the installed handler in a safe-outputs.steps step to validate the fetched bundle against the workflow run's GITHUB_SHA when the bundle newly contains it. We would like to drop that patch once the upstream baseline works with real PR data.
Activity
envygit-bot commentedon Oct 16, 2026
The fix for this issue (#68717, 96b2d13) was reverted four minutes after merging by #68795, so the problem is still present in released versions. v0.92.0 (current Latest) and later pre-releases still resolve
trustedBasefrompullRequest.base.sha(push_to_pull_request_branch.cjsaround L1107–1112 at v0.92.0), and none of the base-tip /GITHUB_SHAcode from #68717 is present. Could this issue be reopened?The revert message cites bandwidth: fetching the live base branch tip without a depth bound in large shallow monorepos. A possible way to keep that bounded:
merge-base --is-ancestoragainst the old head and the candidate bundle. A shallowgit fetch --depth=1 origin refs/heads/<base.ref>gives the tip commit; when ancestry checks are inconclusive because history is missing, deepen incrementally (--deepen=N) up to a fixed cap, and fall back to PR-head validation if the cap is reached. That keeps the transfer proportional to how far the PR is behind, with a hard upper bound.GITHUB_SHAfirst, before fetching the base tip, when the run was triggered on the default branch. In our case the agent merges exactly that commit, and the safe_outputs job checkout already contains it, so no extra fetch is needed in the common case. The base-tip fetch (bounded as above) would then only be a fallback for runs whoseGITHUB_SHAis not on the base branch.Either would make the merge-aware baseline described in
safe-outputs-pull-requests.mdeffective for agents that merge the base branch into the PR branch. Until then we keep a local patch to the handler.