fix(backend): resolve a sub-agent at the invoking Workspace's scope - #423
Merged
Conversation
A sub-agent was looked up by id alone — the one resource lookup in the Chat-turn query object taking neither orgId nor workspaceId. An Agent from another Workspace resolved, its name and description reached the parent's system prompt, and its Provider then failed to resolve against the parent's Workspace, so the delegate tool was silently never built. A sub-agent now resolves the way the single-Agent lookup does: Workspace-scoped here, or Organization-scoped where attached. An assigned id that does not resolve is reported to the parent as unavailable, identified by the id its own configuration holds — reading a name off the row is the boundary crossing being closed. Save-time validation had the mirror-image defect: it accepted only Agents whose workspaceId matched, so promoting a sub-Agent left its parent unsavable from the Workspace surface. It now accepts an Agent visible at either scope. Closes #415 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pace Review follow-ups on the sub-agent scoping fix: the org-scoped branch now requires a null workspaceId, so a row carrying both scope columns cannot borrow another Workspace's Attachment to resolve — the columns are mutually exclusive by convention, not by a database constraint. Also dedupes the Agent visibility rule in the in-memory query double, pins the save-time error message in the route test, and corrects the Agents doc to say "attached only to the first". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 6, 2026
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.
Closes #415
getSubAgentsByIdslooked sub-agents up by id alone — the one resource lookup in the Chat-turn query object taking neitherorgIdnorworkspaceId. An Agent belonging to another Workspace resolved, its name and description reached the parent's system prompt, and its Provider then failed to resolve against the parent's Workspace, so the delegate tool was never built. Save-time validation had the mirror-image defect: it accepted only Agents whoseworkspaceIdmatched, so once a sub-Agent was Promoted its parent could no longer be saved from the Workspace surface at all.Run time
A sub-agent now resolves the way the single-Agent lookup does: Workspace-scoped in the invoking Workspace, or Organization-scoped where attached (ADR-0007). The org-scoped branch also requires a null
workspaceId— the two scope columns are mutually exclusive by convention, not by a database constraint, so a row carrying both must not borrow another Workspace's Attachment to resolve. Rows come back in assignment order, so the prompt lists sub-agents the way the Operator configured them.An assigned id that does not resolve is reported to the parent as unavailable by id, alongside the build failures #413 already surfaced. It carries no name, deliberately: reading one off the row is the boundary crossing being closed.
SubAgentFailure.nameis therefore optional, and a nameless entry renders as- Sub-agent \`: … — no delegation tool exists this turn.rather than deriving adelegateToUndefined` slug.Save time
validateSubAgentAssignmenttakes the Workspace's scope context and accepts any Agent visible there at either scope, so a reference to an attached Shared Agent succeeds and anything invisible is still rejected. Promotion is untouched — a Shared Agent may still reference only Organization-scoped sub-Agents.Tests
The in-memory
ChatTurnQueriesdouble enforces the same visibility rule, so the new tests exercise the boundary rather than a hole in it. Covered: a workspace-scoped sub-agent stays callable; an attached org-scoped one becomes callable; an unattached Shared one and a foreign-Workspace one resolve to nothing, with neither name nor description reaching the prompt; each unresolved id is named in the unavailable block; and both Agent routes accept an attached Shared sub-agent while still rejecting one that is not visible.Docs
The Agents page's "When a sub-agent can't run" section now lists three reasons, leading with a detached or never-attached Shared sub-Agent, and notes that a parent attached to a second Workspace loses a sub-agent attached only to the first.
🤖 Generated with Claude Code