0.45.0 — A sub-agent gets no commit, no merge, and no push
⚡ After you update
Restart any session that should be governed. A running session holds the plugin it loaded at start. An open conversation therefore keeps the old gate until it restarts.
No setup re-run is needed for this release. 0.44.0's requirement to run /workbench-dev-team:setup still stands if you have not run it.
🔴 The bug
0.44.0 made the gate deny, and routed approval through a permissions.ask rule. The reasoning: a permission rule is evaluated before the classifier in every mode. A rule therefore forces a real prompt.
That holds in a foreground session. It fails in a background sub-agent.
The sub-agent's request is background and non-interactive. The ask therefore resolves with nobody attached. The approval command is a Bash command the agent itself holds. The agent became both the requester and the approver.
Measured across every session transcript on one machine, during the gate's first day:
| Measurement | Value |
|---|---|
| Sub-agent self-approvals | 48 |
| Median delay, denial to approval | 3.4 seconds |
Unapproved commits on main from a single run |
17 |
Three seconds is not a human reading a diff.
✅ The fix
Three lanes, decided in this order.
| Lane | Signal | Verdict |
|---|---|---|
| Scheduled Index pipeline | WORKBENCH_DEV_TEAM_PIPELINE=1 |
Silent. Commits unattended, as before |
| Any sub-agent | Non-empty agent_id |
Refused, with no approval path offered |
| Foreground session | Empty agent_id |
Unchanged from 0.44.0 |
A sub-agent's denial prints no request id and names no command. It writes no pending record. The lane is decided before any record is read. An approval planted by hand therefore buys nothing either.
There is deliberately nothing for the agent to run. Any command an agent can run is not an approval.
The lane signal is the payload's agent_id. The harness supplies it, rather than the command. agent_type cannot serve. It is present for a scheduled run and an interactively dispatched one alike.
What is refused
A rule, not a list:
| Category | Verbs |
|---|---|
| Writes a commit | commit, revert, cherry-pick, am |
| Integrates another history | merge, rebase, pull |
| Publishes one | push, plus gh pr merge |
Matching the commit verb alone is what left merge and push open.
Reads stay open. A sub-agent can still fetch, diff, log, and view a pull request.
What a refused sub-agent does
It hands the work back. An uncommitted tree, the diff, and the proposed message go to the dispatching session. That session commits it, where a prompt reaches a human.
Watson's Direct mode, the develop standard, the orchestration guidance, and the README all say so.
One routing change
An Index item dispatched from a conversation now goes through bin/dispatch-agent.sh. That is the only path which can set the pipeline flag. The Agent tool keeps Direct mode.
🧪 Tests
83 gate cases and 31 approval cases. Every new guard is mutation-verified. Each one was broken in a sandbox copy and confirmed to redden. The control stayed green.
⚠️ Honest limits
- Still a protocol gate, not anti-evasion machinery. The gate reads the command the agent asked to run. A verb hidden inside
bash -cis not its subject. Neither is one written into a script. The observed failure was an agent following printed instructions. Removing the instruction removes that failure. - A commit message that discusses git commands can trip the gate. The command text includes heredoc bodies. A message line beginning with a gated verb is read as a command. Reflow the line.
git pullis refused, because it merges. A Direct-mode sub-agent cannot update its checkout.git fetchstays open, so it can still read remote state.