Parent: #1936
Problem
performAction's "update_branch" case (src/services/agent-action-executor.ts) passes action.expectedHeadSha directly into updatePullRequestBranch with no ?? ctx.headSha fallback (unlike approve/merge). updatePullRequestBranch (src/github/pr-actions.ts) spreads it in conditionally: ...(expectedHeadSha ? { expected_head_sha: expectedHeadSha } : {}). When expectedHeadSha is absent, the param is simply OMITTED from the PUT .../update-branch call — GitHub then applies the update against whatever the live head happens to be, with zero server-side staleness comparison at all (not even a tautological one).
This is lower severity than the approve/merge cases (#2377, #2422) since rebasing a PR onto its current base is generally idempotent/safe regardless of exactly which head it's applied against — it doesn't itself ratify or merge unreviewed code, just brings the branch up to date. Flagged for completeness/consistency by the same adversarial review pass that found the approve and merge gaps, not because it's an equivalent-severity finding.
Requirements
- Decide whether
update_branch needs any pinning enforcement, or whether its current "GitHub applies the update against whatever's current" behavior is acceptable given the operation's low-risk nature.
Deliverables
Acceptance criteria
- A documented, deliberate decision exists for
update_branch's pinning behavior, rather than an unexamined asymmetry with approve/merge.
Expected outcome
Either explicit documentation that this is safe-by-design, or parity with the approve/merge fixes if a real risk is found.
Parent: #1936
Problem
performAction's"update_branch"case (src/services/agent-action-executor.ts) passesaction.expectedHeadShadirectly intoupdatePullRequestBranchwith no?? ctx.headShafallback (unlikeapprove/merge).updatePullRequestBranch(src/github/pr-actions.ts) spreads it in conditionally:...(expectedHeadSha ? { expected_head_sha: expectedHeadSha } : {}). WhenexpectedHeadShais absent, the param is simply OMITTED from thePUT .../update-branchcall — GitHub then applies the update against whatever the live head happens to be, with zero server-side staleness comparison at all (not even a tautological one).This is lower severity than the approve/merge cases (#2377, #2422) since rebasing a PR onto its current base is generally idempotent/safe regardless of exactly which head it's applied against — it doesn't itself ratify or merge unreviewed code, just brings the branch up to date. Flagged for completeness/consistency by the same adversarial review pass that found the approve and merge gaps, not because it's an equivalent-severity finding.
Requirements
update_branchneeds any pinning enforcement, or whether its current "GitHub applies the update against whatever's current" behavior is acceptable given the operation's low-risk nature.Deliverables
update_branchintentionally has no staleness guard because the operation is safe regardless of target head, or (b) add the same accept-flow-level denial pattern used for approve (fix(agent-actions): pin a staged approve to its reviewed head SHA #2377) and merge (fix(agent-actions): merge has the same unpinned-legacy-row head-fallback gap as approve #2422) if a real risk scenario is identified.Acceptance criteria
update_branch's pinning behavior, rather than an unexamined asymmetry with approve/merge.Expected outcome
Either explicit documentation that this is safe-by-design, or parity with the approve/merge fixes if a real risk is found.