Skip to content

push_to_pull_request_branch: trusted-base merge handling (#68459) never activates because pulls.get base.sha is the merge base #68690

Closed

Description

@envygit-bot

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: pullRequest comes from githubClient.rest.pulls.get(...).
  • L1102–1107: trustedBase = pullRequest.base.sha (fetched with git fetch origin <sha>).
  • L145–148: getBundleValidationBase returns the fallback (old head) unless git merge-base --is-ancestor trustedBase oldHead exits 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:

  1. For open PRs, gh api repos/<o>/<r>/pulls/<n> --jq .base.sha equals git merge-base <head> origin/main in every PR we checked (3 of 3, including one PR whose branch had merged main the same day and one that never had).
  2. base.sha does 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.
  3. Replaying the three merge-base --is-ancestor calls from L145–148 locally, with oldHead = current PR head, trustedBase = the API base.sha, and candidate = PR head merged with current main (one clean merge, one with conflicts): oldAncestor=0 baseAncestor=0 alreadyMerged=0 in 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.

Refs #68448, #68459

Activity

  1. locked and limited conversation to collaborators on Oct 14, 2026
  2. unlocked this conversation on Oct 14, 2026
  3. envygit-bot commented on Oct 16, 2026

    @envygit-bot
    Author

    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 trustedBase from pullRequest.base.sha (push_to_pull_request_branch.cjs around L1107–1112 at v0.92.0), and none of the base-tip / GITHUB_SHA code 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:

    • The handler only needs the base-tip commit object plus enough ancestry to answer merge-base --is-ancestor against the old head and the candidate bundle. A shallow git 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.
    • Alternatively, try the workflow run's GITHUB_SHA first, 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 whose GITHUB_SHA is not on the base branch.

    Either would make the merge-aware baseline described in safe-outputs-pull-requests.md effective for agents that merge the base branch into the PR branch. Until then we keep a local patch to the handler.

  4. locked and limited conversation to collaborators on Oct 16, 2026
  5. unlocked this conversation on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions