Skip to content

[safeoutputs] push_to_pull_request_branch: document auto-pinned HEAD and reject concurrency-control fields (expected_head_sha) #45821

Description

@github-actions

Improve push_to_pull_request_branch Description to Reject Concurrency-Control Fields

Summary

Analysis of the last 24 hours of workflow runs found the push_to_pull_request_branch safe-output tool rejecting an invented optimistic-concurrency field (expected_head_sha) for the second time, in the same workflow and engine. The workflow prompt is correct — the agent invented the field on its own — so this is a tool-description gap. This recurrence meets the escalation trigger that the 2026-06-27 audit recorded for this exact pattern.

🔍 Error Analysis Details

Error Pattern: push_to_pull_request_branch + expected_head_sha

Occurrences: 2 total (2026-06-27 → 2026-07-15), both Design Decision Gate 🏗️ on the claude engine.

What the agent did wrong: passed expected_head_sha (a git optimistic-concurrency / compare-and-swap SHA, as in the GitHub git-refs update API and git push --force-with-lease) to push_to_pull_request_branch, which accepts only message, branch, pull_request_number (plus secrecy/integrity).

Example — workflow Design Decision Gate 🏗️, Run §29442707009 (PR #45799):

{
  "tool": "push_to_pull_request_branch",
  "expected_head_sha": "7ef6fc3c84aa490f82a77e904e49968627be3215",
  "pull_request_number": 45799
}

Server response:

-32602 Invalid arguments: unknown parameter 'expected_head_sha'.
Supported parameters for this tool: 'message', 'branch', 'pull_request_number', 'secrecy', 'integrity'.

Expected (what the third, successful attempt used):

{
  "tool": "push_to_pull_request_branch",
  "branch": "copilot/add-nilctxpassed-linter",
  "message": "docs(adr): add draft ADR-45799 for nilctxpassed linter",
  "pull_request_number": 45799
}

Recovery path in this run (3 calls): (1) expected_head_sha rejected → (2) agent dropped the field but also dropped message/branch → rejected missing or empty 'message' → (3) full branch+message+pull_request_number → success. The invented field cost two round-trips before recovery.

Why this happened: pushing to a branch semantically resembles a git ref update, so the agent reached for a compare-and-swap guard. But the tool auto-pins the branch HEAD itself — the same run's server log shows Pinned branch 'copilot/add-nilctxpassed-linter' to SHA a024a338... — so no concurrency-control parameter is needed or accepted. The current description states force-push is unsupported (APPEND-ONLY) but never tells the agent that HEAD is auto-pinned and that compare-and-swap / expected-SHA fields are rejected.

Current Tool Description

Current definition from safe_outputs_tools.json (push_to_pull_request_branch)

Description (excerpt):

Push committed changes to a pull request's branch. APPEND-ONLY: this tool adds new commits on top of the existing PR branch — force-push is NOT supported and will be rejected. [...] IMPORTANT: always supply the 'branch' argument [...]

Accepted parameters: message (required; already carries x-synonyms: ["commit_message"] and a "named message, NOT commit_message" note), branch, pull_request_number, secrecy, integrity. additionalProperties: false.

Neither the source copy (pkg/workflow/js/safe_outputs_tools.json) nor the runtime copy (actions/setup/js/safe_outputs_tools.json) mentions expected_head_sha or auto-pinning — verified with grep -c "expected_head_sha" = 0 in both. No deployment gap: the fix is genuinely absent from both copies and must be added to both.

Root Cause Analysis

  1. The description tells the agent force-push is unsupported, but does not map that to the agent's mental model of "pass an expected-SHA to guard the push."
  2. There is no statement that the tool auto-pins the branch HEAD, so a concurrency-control parameter is unnecessary.
  3. Sibling git operations elsewhere accept a SHA guard, so the agent assumes parity (same invented-field / cross-tool-parity class as the accepted message vs commit_message fix).

Recommended Improvement

Add one sentence to the push_to_pull_request_branch description in both pkg/workflow/js/safe_outputs_tools.json and actions/setup/js/safe_outputs_tools.json, following the accepted "named X, NOT Y" disambiguation style already used for message:

This tool auto-pins the PR branch HEAD before pushing, so it takes no concurrency-control / compare-and-swap parameter — do NOT pass expected_head_sha, head_sha, or base_sha; the only accepted fields are message, branch, and pull_request_number.

Optionally add "x-synonyms": ["expected_head_sha", "head_sha", "base_sha"]-style guidance is not appropriate here (these are not synonyms of an accepted field); the explicit prohibition sentence in the description is the right lever.

Affected Workflows

  • Design Decision Gate 🏗️ (claude) — 2 occurrences (2026-06-27 run 28298881067; 2026-07-15 run 29442707009).

Implementation Checklist

  • Add the "auto-pins HEAD / no concurrency-control parameter" sentence to push_to_pull_request_branch in pkg/workflow/js/safe_outputs_tools.json
  • Mirror the change into the runtime copy actions/setup/js/safe_outputs_tools.json
  • make build and make recompile
  • make test (verify TestSafeOutputsToolsJSONInSync still passes)
  • Monitor push_to_pull_request_branch rejections for 1–2 weeks

Secondary Observation (not re-filed)

submit_pull_request_review + pr was rejected once this window (Test Quality Sentinel, Run §29442706755, PR #45799): unknown parameter 'pr' (closest: 'repo'). pr is an accepted alias on add_comment/update_pull_request but not on submit_pull_request_review. This is the known pr_number_alias_inconsistency pattern (tracked since 2026-06-13; the generalize-prohibition remediation was previously declined). Agent recovered with pull_request_number. Tracked, not escalated.

References

  • Tool schema (source): pkg/workflow/js/safe_outputs_tools.json
  • Tool schema (runtime): actions/setup/js/safe_outputs_tools.json
  • MCP server loader: actions/setup/js/safe_outputs_tools_loader.cjs
  • Validator: actions/setup/js/safe_output_validator.cjs

Run IDs with the error: §29442707009 (2026-07-15), §28298881067 (2026-06-27)

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by ⚡ Daily Safe Output Tool Optimizer · 429.4 AIC · ⌖ 33 AIC · ⊞ 8.9K ·

  • expires on Jul 17, 2026, 1:31 PM UTC-08:00

Metadata

Metadata

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions