Skip to content

fix(backend): resolve a sub-agent at the invoking Workspace's scope - #423

Merged
willdady merged 2 commits into
mainfrom
fix/415-scope-sub-agent-resolution
Aug 6, 2026
Merged

fix(backend): resolve a sub-agent at the invoking Workspace's scope#423
willdady merged 2 commits into
mainfrom
fix/415-scope-sub-agent-resolution

Conversation

@willdady

@willdady willdady commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Closes #415

getSubAgentsByIds looked sub-agents up by id alone — the one resource lookup in the Chat-turn query object taking neither orgId nor workspaceId. 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 whose workspaceId matched, 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.name is therefore optional, and a nameless entry renders as - Sub-agent \`: … — no delegation tool exists this turn.rather than deriving adelegateToUndefined` slug.

Save time

validateSubAgentAssignment takes 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 ChatTurnQueries double 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

willdady and others added 2 commits August 6, 2026 18:57
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>
@willdady
willdady merged commit 3197685 into main Aug 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.

getSubAgentsByIds is not scoped by org or Workspace, so a cross-Workspace sub-agent silently never loads

1 participant