feat(web): show repo, branch and target on a dispatch approval - #333
Open
saucam wants to merge 1 commit into
Open
feat(web): show repo, branch and target on a dispatch approval#333saucam wants to merge 1 commit into
saucam wants to merge 1 commit into
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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_sendtold you a message was going somewhere namedapi; 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
classifyFleetInputis split out ofclassifyFleetToolso the approval gate and the transcript share one classifier. The gate holds a raw(toolName, input)pair rather than aToolInfo, 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
ApprovalBarlint 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