Version line
v2 — Go rewrite (1.x), main-v2 (active development)
Exact version
A source build from main-v2 at or after merge commit 75537490abf2c8bfe18c431531676f7d530d9f73 (PR #8275).
What happened?
In a turn whose frozen TaskPolicy forbids workspace mutation (for example Plan mode, or a user request such as “read-only analysis; do not modify files”), the CLI blocks read-only branch inspection:
The command only lists local and remote-tracking branches, but it is classified as a workspace mutation and rejected before execution:
blocked: task policy forbids mutation
If the model sent later mutation/verification calls in the same provider batch, the dependency barrier may also report:
blocked: skipped because an earlier modification in this tool batch failed or was blocked
Expected behavior: branch-listing forms should be classified as workspace-non-mutating and remain available in read-only/Plan turns. Branch creation, deletion, rename, copy, forced ref movement, and upstream mutation must remain blocked or require the normal permission flow.
Root cause
The Go v2 classifier deliberately omits git branch from shellsafe.ReadOnlyPrefixes["git"] because the subcommand can mutate refs. evidence.ToolCallMutates reuses that conservative result through bashMayMutate; therefore it cannot distinguish git branch -a from git branch -D feature. PR #8275 then enforces TaskPolicy.ForbidMutation against that coarse classification, turning the false positive into a hard block.
This is a classification-boundary issue, not a request to broadly allow git branch. Permission auto-approval may remain conservative; workspace mutation accounting needs an argument-aware, host-proven listing classification.
Suggested fix
Add an argument-aware gitBranchIsListing classifier, analogous to the existing gitTagIsListing handling, and use it in the shared shell/workspace mutation path.
Allow only statically proven listing forms, including:
git branch
git branch -a / git branch --all
git branch -r / git branch --remotes
git branch --list [pattern...]
git branch --show-current
Keep fail-closed behavior for dynamic or malformed arguments and for mutating forms, including:
- branch creation:
git branch feature [start-point]
- deletion:
-d, -D, --delete
- move/copy:
-m, -M, -c, -C
- forced ref movement:
-f, --force
- upstream/description mutations:
--set-upstream-to, --unset-upstream, --edit-description
Compound commands must still be evaluated segment-by-segment; one unknown or mutating segment keeps the entire call mutating.
Regression coverage
Please cover the classification at all affected boundaries:
internal/shellsafe: listing vs mutating argument matrix
internal/permission: safe listing forms do not weaken destructive-argument handling
internal/evidence: ToolCallMutates("bash", ...)
internal/agent: TaskPolicy no-mutation turn allows listing and still blocks writers
- batch behavior: an allowed
git branch -a must not open the mutation dependency barrier
Related
Steps to reproduce
- Start the v2 CLI in a Git repository.
- Enter Plan mode, or ask for read-only analysis without workspace modifications.
- Ask the model to inspect all local and remote branches.
- Let it call
git branch -a.
- Observe
blocked: task policy forbids mutation.
The same false positive is visible when the listing call is embedded in an otherwise read-only inspection chain.
OS / platform
macOS (the exact OS version was not shown in the reproduction).
Relevant logs or output
Bash(git branch -a)
blocked: task policy forbids mutation
bash dependency · not executed
blocked: skipped because an earlier modification in this tool batch failed or was blocked
Version line
v2 — Go rewrite (1.x), main-v2 (active development)
Exact version
A source build from
main-v2at or after merge commit75537490abf2c8bfe18c431531676f7d530d9f73(PR #8275).What happened?
In a turn whose frozen TaskPolicy forbids workspace mutation (for example Plan mode, or a user request such as “read-only analysis; do not modify files”), the CLI blocks read-only branch inspection:
The command only lists local and remote-tracking branches, but it is classified as a workspace mutation and rejected before execution:
If the model sent later mutation/verification calls in the same provider batch, the dependency barrier may also report:
Expected behavior: branch-listing forms should be classified as workspace-non-mutating and remain available in read-only/Plan turns. Branch creation, deletion, rename, copy, forced ref movement, and upstream mutation must remain blocked or require the normal permission flow.
Root cause
The Go v2 classifier deliberately omits
git branchfromshellsafe.ReadOnlyPrefixes["git"]because the subcommand can mutate refs.evidence.ToolCallMutatesreuses that conservative result throughbashMayMutate; therefore it cannot distinguishgit branch -afromgit branch -D feature. PR #8275 then enforcesTaskPolicy.ForbidMutationagainst that coarse classification, turning the false positive into a hard block.This is a classification-boundary issue, not a request to broadly allow
git branch. Permission auto-approval may remain conservative; workspace mutation accounting needs an argument-aware, host-proven listing classification.Suggested fix
Add an argument-aware
gitBranchIsListingclassifier, analogous to the existinggitTagIsListinghandling, and use it in the shared shell/workspace mutation path.Allow only statically proven listing forms, including:
git branchgit branch -a/git branch --allgit branch -r/git branch --remotesgit branch --list [pattern...]git branch --show-currentKeep fail-closed behavior for dynamic or malformed arguments and for mutating forms, including:
git branch feature [start-point]-d,-D,--delete-m,-M,-c,-C-f,--force--set-upstream-to,--unset-upstream,--edit-descriptionCompound commands must still be evaluated segment-by-segment; one unknown or mutating segment keeps the entire call mutating.
Regression coverage
Please cover the classification at all affected boundaries:
internal/shellsafe: listing vs mutating argument matrixinternal/permission: safe listing forms do not weaken destructive-argument handlinginternal/evidence:ToolCallMutates("bash", ...)internal/agent: TaskPolicy no-mutation turn allows listing and still blocks writersgit branch -amust not open the mutation dependency barrierRelated
git branch -dbypasses review mode #257 — older argument-aware handling that kept listing forms fast while demoting destructive branch flagsSteps to reproduce
git branch -a.blocked: task policy forbids mutation.The same false positive is visible when the listing call is embedded in an otherwise read-only inspection chain.
OS / platform
macOS (the exact OS version was not shown in the reproduction).
Relevant logs or output
Bash(git branch -a) blocked: task policy forbids mutation bash dependency · not executed blocked: skipped because an earlier modification in this tool batch failed or was blocked