Skip to content

feat(web): show repo, branch and target on a dispatch approval - #333

Open
saucam wants to merge 1 commit into
mainfrom
feat/p5-3-conductor-approval-card
Open

feat(web): show repo, branch and target on a dispatch approval#333
saucam wants to merge 1 commit into
mainfrom
feat/p5-3-conductor-approval-card

Conversation

@saucam

@saucam saucam commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

P5.3 (conductor-design R3, frontends §8). A send-class fleet dispatch now proposes itself with where it is going, not just what the tool is called.

Why

R3 names silent misrouting as the one failure that would kill trust in the conductor:

any send-class action to an existing user-owned session first proposes it with repo + branch + content shown, and acts only on confirm

The approval bar showed the tool name and its description — which is what the conductor called the target, not what the owner needs in order to verify it. Approving fleet_send told you a message was going somewhere named api; it didn't tell you which repo or branch that was.

Now the prompt carries the target name, its workdir, its worktree branch, and a clamped content preview.

Two rules that matter more than the layout

An ambiguous id prefix resolves to NOTHING. The conductor is told to name targets by name, but its tools also accept an id or an id prefix. Picking one of several prefix matches would be a coin flip presented as a fact — on the prompt whose entire job is preventing exactly that. An exact name beats an id prefix for the same reason.

An unresolvable target is shown, never hidden. That's precisely when an owner is at risk of approving the wrong thing, and a prompt that quietly shows no repo reads as "no repo involved" rather than "I could not tell you which".

It warns without blocking: the daemon resolves names itself and this client's view can legitimately be stale, so refusing the approval would be wrong.

A spawn is previewed by its workdir instead, and deliberately never flagged unresolved — there's no existing session to resolve, so flagging would cry wolf on every spawn.

One structural change

classifyFleetInput is split out of classifyFleetTool so the approval gate and the transcript share one classifier. The gate holds a raw (toolName, input) pair rather than a ToolInfo, and letting it grow its own parser is how the prompt and the transcript end up disagreeing about what a dispatch says.

Non-fleet approvals are untouched — the classifier returns null and the existing bar renders exactly as before.

Verification

14 dispatch-preview tests, 243 web tests total, typecheck and build clean. The 5 remaining ApprovalBar lint warnings are pre-existing — confirmed by linting the stashed baseline (5 before, 5 after).

Note on §8's fourth action

This is the "conductor-aware card" half of §8's approval work. The four-action grammar (Approve · Edit args · Respond · Reject, with the dispatch declaring which are permitted) still needs somewhere for that declaration to live — no wire field carries it today, so it's a protocol + daemon change rather than the "framing, not new plumbing" §8 describes. Left out deliberately.

🤖 Generated with Claude Code

P5.3 (conductor-design R3, conductor-frontends-design §8). A send-class
fleet dispatch now proposes itself with where it is going, not just what
the tool is called.

R3 names silent misrouting as the one failure that would kill trust in
the conductor: "any send-class action to an existing user-owned session
first proposes it with repo + branch + content shown". The approval bar
showed the tool name and its description, which is what the CONDUCTOR
called the target — not what the owner needs to verify.

Resolution lives in `lib/dispatch-preview.ts` as pure functions, since
turning a target name into a session is the decision worth testing.

Two rules matter more than the layout:

**An ambiguous id prefix resolves to NOTHING.** The conductor is told to
name targets by name, but its tools also accept an id or prefix. Picking
one of several prefix matches would be a coin flip presented as a fact,
on the prompt whose entire job is preventing that. An exact name beats an
id prefix for the same reason.

**An unresolvable target is shown, never hidden.** That is exactly when
an owner is at risk of approving the wrong thing, and a prompt that
quietly shows no repo reads as "no repo involved" rather than "I could
not tell you which". It warns without blocking: the daemon resolves names
itself and this client's view can legitimately be stale, so refusing the
approval would be wrong.

A spawn is previewed by its workdir instead, and deliberately never
flagged unresolved — there is no existing session to resolve, so flagging
would cry wolf on every spawn.

`classifyFleetInput` is split out of `classifyFleetTool` so the approval
gate and the transcript share ONE classifier. The gate holds a raw
(toolName, input) pair rather than a ToolInfo, and letting it grow its
own parser is how the prompt and the transcript end up disagreeing about
what a dispatch says.

Non-fleet approvals are untouched: the classifier returns null and the
existing bar renders exactly as before.

14 dispatch-preview tests; 243 web tests; typecheck and build clean. The
5 remaining ApprovalBar lint warnings are pre-existing — verified by
linting the stashed baseline.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant