fix(langgraph): don't guess subagent attribution when it's a coin flip - #864
Merged
Conversation
LangGraph's tools:<uuid> namespace is a checkpoint id assigned independently of the parent's call_* tool-call id. I checked the wire for a link between them and there is none — no metadata field, no co-occurrence in any payload of a live run. When a delegation tool carries no matchable description, position is genuinely the only signal left. That is fine with one outstanding child, and it is what every graph here produces: cockpit/chat/subagents dispatches ONE tool call per assistant turn, three sequential ToolNode round-trips. With several outstanding at once, arrival order is not dispatch order, and claiming the first unmapped call silently cross-wires them. Reproduced: two children whose streams arrive in reverse dispatch order, and call_ALPHA's card renders beta's output. A booking card showing research text is worse than an empty one. The fallback now fires only when exactly one candidate is outstanding. Ambiguous streams stay buffered rather than mis-attributed, and can still resolve later — as siblings complete, the candidate set shrinks back to one. The description rungs are untouched and still preferred. Verified: 344/344 (new cross-wiring guard included). Mutation-tested — restoring the greedy fallback fails exactly that guard. Sequential attribution re-checked against a live model: all three cards populate, each on its own call_* id. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
blove
enabled auto-merge (squash)
August 30, 2026 01:41
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.
Follow-up to the attribution work in #847, which noted the positional fallback "works for sequential dispatch; a graph fanning out parallel look-alike children would lean on it hard." It does worse than lean — it cross-wires them.
Is there a real identity link? No.
I checked before assuming. LangGraph's
tools:<uuid>namespace is a checkpoint id, assigned independently of the parent'scall_*tool-call id:No
call_*anywhere in the child's metadata, and no payload in a full live run carries both a namespace uuid and a tool-call id. (One grep looked like a hit —call_appearing inside prose content.) So when a delegation tool carries no matchable description, position is genuinely the only signal left. This can't be fixed client-side by finding a better key; there isn't one.The actual bug
Position is sound with one outstanding child — and that's what every graph here produces.
cockpit/chat/subagentsdispatches one tool call per assistant turn, three sequential ToolNode round-trips (verified on the wire).With several outstanding at once, arrival order is not dispatch order. Reproduced in a test: two children whose streams arrive in reverse dispatch order, and
call_ALPHA's card rendersbeta output. A booking card showing research text is worse than an empty one — it's confidently wrong, and a reader has no way to tell.The fix
The last-resort fallback now fires only when exactly one candidate is outstanding. Ambiguous streams stay buffered instead of mis-attributed, and can still resolve later: as siblings complete, the candidate set shrinks back to one. The description rungs are untouched and still preferred when a graph provides them.
The trade-off is deliberate — for a parallel fan-out with no descriptions, cards may stay empty. That's the honest failure mode. Silent cross-wiring is not.
Verification
>= 1fallback fails exactly that guard and nothing else.call_*id.cockpit-chat-subagentse2e green; lint 0 errors.🤖 Generated with Claude Code