Skip to content

fix(conductor): let the fleet read surface run without prompting - #328

Merged
saucam merged 2 commits into
mainfrom
fix/fleet-read-tools-auto-approve
Sep 6, 2026
Merged

fix(conductor): let the fleet read surface run without prompting#328
saucam merged 2 commits into
mainfrom
fix/fleet-read-tools-auto-approve

Conversation

@saucam

@saucam saucam commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

The bug

conductor-design §3 specifies that the fleet read verbs run silently:

The fleet read surface (fleet_list / fleet_find / fleet_summary / fleet_recall / fleet_tasks / machine_map) runs silently.

They don't. isSafeTool knows the memory and blackboard mounts but not the fleet, so in guarded mode every one of them raises an approval prompt.

Found by driving a real conductor rather than reading code — asking it to resolve a session reference produced an approval request for fleet_find, which is a read. An assistant that asks permission to look something up is not an assistant, and it's the first friction anyone meets in the conductor pane from #325.

Worth noting the two layers disagreed: the daemon already puts these verbs in the provider's allowedTools (the SDK even warns it will auto-approve them), but codeoid's own gate is consulted independently and didn't recognise them.

The fix

Follows the established shape exactly — match the namespace prefix, then require the suffix to be a known read verb. Never a bare prefix match: an over-broad match here is a prompt bypass, which is why a look-alike segment (mcp__evil_codeoid_fleet__fleet_find) is asserted to gain nothing.

The read list is FLEET_READ_TOOLS from the shared protocol package (#323), so the send half cannot leak in by someone editing one of two copies.

Why this is safe

Send-class verbs are hard-gated before this function is ever consulted — isFleetSendTool in Session#shouldAutoApprove, checked ahead of any mode logic, as the R3 invariant. This change is defence in depth, not the fence.

Verified live, both directions

With the fix, on a real conductor:

mcp__codeoid_fleet__fleet_list:  executing
mcp__codeoid_fleet__fleet_find:  executing
mcp__codeoid_fleet__machine_map: executing

prompts raised: 0

And the half that must never regress — same daemon, asking it to spawn:

⚑ PROMPTED: mcp__codeoid_fleet__fleet_spawn
mcp__codeoid_fleet__fleet_spawn: waiting_confirmation

prompts raised: 1

Reads run silently; the owner still confirms every dispatch.

Also

Lists the bare (non-mcp__) namespacing alongside the in-process one, so a mounted fleet (#245) doesn't silently regress to prompting on every read.

Verification: 10 tool-safety tests (4 new, covering both namespacings, every send verb, an unknown verb, and look-alike prefixes), 2452 daemon tests, typecheck and lint clean.

🤖 Generated with Claude Code

conductor-design §3 specifies that the fleet READ verbs — fleet_list,
fleet_find, fleet_summary, fleet_recall, fleet_tasks, machine_map — "run
silently". They did not. `isSafeTool` knew the memory and blackboard
mounts but not the fleet, so in guarded mode every one of them raised an
approval prompt.

Found by driving a real conductor rather than reading the code: asking it
to resolve a session reference produced an approval request for
`fleet_find`, which is a read. An assistant that asks permission to look
something up is not an assistant, and this is the first friction anyone
meets in the new conductor pane (#325).

The daemon already put these verbs in the provider's `allowedTools`, so
the SDK warned it would auto-approve them — but codeoid's own gate is
consulted independently and did not recognise them, which is why the two
disagreed.

Follows the established shape exactly: match the namespace prefix, then
require the suffix to be a known read verb. Never a bare prefix match — an
over-broad match here is a prompt bypass, which is why a look-alike
segment (`mcp__evil_codeoid_fleet__…`) is asserted to gain nothing.

The read list is `FLEET_READ_TOOLS` from the shared protocol package, so
the send half cannot leak in by someone editing one of two copies. Send
verbs remain hard-gated BEFORE this function is consulted
(`isFleetSendTool` in Session#shouldAutoApprove, checked ahead of any mode
logic); this is defence in depth, not the fence.

Verified live, both directions. With the fix, fleet_list / fleet_find /
machine_map go straight to `executing` with zero prompts; fleet_spawn
still lands in `waiting_confirmation` and waits for the owner. The R3
invariant is intact.

Also lists the bare (non-`mcp__`) namespacing so a mounted fleet (#245)
does not silently regress to prompting on every read.

10 tool-safety tests; 2452 daemon tests; typecheck and lint clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@saucam
saucam merged commit 6da2d44 into main Sep 6, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants