You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fail Closed on a Source-Only Push After an Upstream Change in the Same Command in the Write Guard #3322
Under push.default=upstream, Git sends a source-only refspec such as git push origin feature/example to the branch named by branch.feature/example.merge. The write guard reads that mapping from the persisted config, but a command that changes the upstream earlier in the same tool call is not seen. The guard then reads the old mapping, falls back to the literal source branch, and allows a push that Git sends to a ruled branch.
Raised by CodeRabbit on the promotion pull request #3321 (host-setup/agent-safety/claude/gh-write-guard.py, the source-only mapping around _mapped_src_branches and the command classification _head_may_move uses).
Reproduction
Constructed repository: origin at example/alpha, where any release/ branch requires a pull request. The checkout is on feature/example, its upstream is origin/feature/example, and push.default is upstream. Each command was classified by the guard at main and at develop:
Git sends each of the last five pushes to release/1. Main allows all of them too, so this is a gap the two branches share, not one the promotion added.
Expected
An upstream-setting option on branch, checkout, or switch (-u, -t, --track, --set-upstream-to, and their abbreviated long spellings) counts as a configuration change, the same as a config write. A source-only refspec read while the configuration is unreliable is recorded as undetermined rather than mapped to its literal source branch, so the push fails closed.
Acceptance
Each command in the table above is denied, and each control keeps its current result.
A develop-versus-branch differential over the guard's self-test cases shows no deny-to-allow flip.
Closed as out of the write guard's scope under the maintainer's decision (ptr727, 2026-10-23) on #3391, item 1. The guard bounds a good-faith agent's mistake rather than an adversary, so a parse gap reached only by a command shape no agent plausibly types in a coding session is closed rather than fixed, and the guard's own growth is a cost each later review pays again. The scope statement lands in the guard's docstring and README under #3391. Reopen only with a command a session actually produced.
Activity
ptr727 commentedon Oct 23, 2026
Closed as out of the write guard's scope under the maintainer's decision (ptr727, 2026-10-23) on #3391, item 1. The guard bounds a good-faith agent's mistake rather than an adversary, so a parse gap reached only by a command shape no agent plausibly types in a coding session is closed rather than fixed, and the guard's own growth is a cost each later review pays again. The scope statement lands in the guard's docstring and README under #3391. Reopen only with a command a session actually produced.