Skip to content

Add multi-team context technical specs - #15347

Draft
IsaiahWitzke wants to merge 2 commits into
masterfrom
iw/multi-team-plan
Draft

Add multi-team context technical specs#15347
IsaiahWitzke wants to merge 2 commits into
masterfrom
iw/multi-team-plan

Conversation

@IsaiahWitzke

Copy link
Copy Markdown
Contributor

Description

Adds two coordinated technical specifications for safe multi-team request scoping:

  • specs/multi-team-context/TECH.md defines the owned TeamContext, borrowed TeamRenderContext, UserWorkspaces authority boundaries, and staged client migration.
  • specs/multi-team-api-context/TECH.md inventories network APIs by scope contract and divides implementation into parallel PRs 2A–2F.

The design prevents a window's mutable team selection from becoming ambient process state. Current-window operations capture TeamContext; explicitly targeted administration and ownership operations retain raw team UIDs; existing-resource operations remain resource-scoped. Requests that require selected-team identity use X-Warp-Team-Uid with server-side authorization and mismatch validation.

Planning artifact: Introduce an opaque team context capability

Linked Issue

None — this PR contains design specifications only.

Testing

  • git diff --check

  • Validated all 14 commit-pinned source links against their referenced files and line ranges.

  • No runtime testing was required because this PR changes documentation only.

  • I have manually tested my changes locally with ./script/run

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

CHANGELOG-NONE

Co-Authored-By: Warp agent@warp.dev

Co-Authored-By: Warp <agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Aug 20, 2026
@warp-agent-staging

Copy link
Copy Markdown
Contributor

@IsaiahWitzkePR 2F scope contracts

Recommendations

  1. Runners: keep the existing cross-team lookup for CLI CRUD and existing-run resolution, and add a selected-team collection for picker surfaces. Do not change the existing method in place.
  2. GET /agent?repo=... skills: define the skill as a repository-scoped resource. GitHub access is user-authorized, but team/environment links are annotations, not ownership.
  3. External conversations: keep creation task-scoped and inherit the task owner; do not add TeamContext. Make the task ID a request-local argument so the client cannot rely on mutable ambient task state.

1. FactoryClient::get_runners: split lookup from selection

Today. The client contract explicitly says “all runners visible to the caller” and sends no selector (client). The server authorizes by principal and returns every accessible runner (query); each result already carries its owner Space (assembly). This global shape serves three different jobs:

What breaks if unchanged. A multi-team user sees same-named runners from unrelated teams in a current-window picker, with no owner label, and can select the wrong UID. Making the existing method selected-team instead would fix the picker but break existing-run display and global CLI management when the selected window is on another team.

Options/tradeoff.

  • One global collection: preserves lookup/CLI behavior, but every picker must add owner labels and still exposes irrelevant choices.
  • Replace it with selected-team semantics: simplest picker, but silently narrows UID lookup and CLI behavior.
  • Split contracts (recommended): preserve a clearly named global accessible-runner lookup; add a TeamContext-scoped collection for new selection surfaces. This costs one additional client/server query shape but avoids overloading one collection with incompatible jobs.

Implementation touch: add a team-filtered GraphQL/client method; migrate only the two picker callers; keep CLI and details on the global method. Preserve scope.uid in the global DTO and show team identity in global CLI output (today it collapses scope to only Team/Personal: formatting). Add two-team/same-name tests.

2. GET /agent?repo=...: repository-scoped skill, user-authorized

Today. The caller explicitly supplies a repository and the client checks GitHub access before listing (CLI flow, request). The stable skill identity is {owner}/{repo}:{skill_path} (identity). The server fetches that repo with the user OAuth client, or only permits public repos with the shared client (access gate). It also loads all accessible environments (handler, environment query), but uses them only to annotate which environments reference the repo; the repo itself remains the source (repo listing).

What breaks if unchanged. Skill contents remain correct, but environment annotations aggregate across teams and carry only environment UID/name. Duplicate environment names become ambiguous. Making the skill selected-team would be worse: the same owner/repo:path definition would appear/disappear as the window changes even though GitHub authorization and source content did not change.

Options/tradeoff.

  • Global: only accurate for the special oz-skills catalog.
  • User-level: describes authorization, not resource identity.
  • Selected-team: useful for environment-driven discovery, but incorrect for an explicit repo query.
  • Repository-level (recommended): identity and content come from the requested repo; the caller’s GitHub credentials authorize access. Environment/team associations are metadata only.

Implementation touch: keep repo= independent of TeamContext for content. Add owner/team metadata to returned environment annotations (or omit those annotations on the explicit-repo route). Separately, the no-repo “skills from my environments” mode should use selected-team environments; do not let that mode redefine the repo= contract.

3. Harness external conversation: task owner is authoritative

Today. /harness-support/external-conversation is in the cloud-only group behind task validation and workload-token enforcement (route, missing-task rejection). Validation resolves the run and binds the authenticated principal to its executor (middleware). Creation then fails if the task has no owner and passes info.Task.Owner directly to conversation creation (ownership, persist). Calls without a task fail closed; the client already notes local CLI creation is unsupported (caller).

What breaks if unchanged. Ownership does not become ambiguous: the task supplies it. The weak point is transport: create_external_conversation(format) uses generic POST and inherits a mutable “task ID for all subsequent requests” from ServerApi (ambient setter, generic call). Concurrent runs/windows can therefore route a request through the wrong ambient task. Also, upstream local task ownership still has teamIDs[0] shortcuts (owner resolution); that must be fixed by the planned CreateAgentTask TeamContext migration, not compensated for at conversation creation.

Options/tradeoff.

  • Add TeamContext: creates a second source of truth and requires mismatch validation against the task.
  • Allow taskless creation with selected team: supports local fallback, but produces an orphan conversation contract and weakens cloud-only authorization.
  • Task-scoped (recommended): require a valid task and always inherit its owner. A future taskless/local conversation API can be designed separately and would need explicit scope.

Implementation touch: change the client method to create_external_conversation(task_id, format) and use the existing request-local post_public_api_response_for_task helper (helper). Thread the task ID into Claude/Codex/Gemini harness runners. Keep the server owner derivation unchanged. Add tests proving missing/wrong task fails and a two-team task creates the conversation under exactly Task.Owner.

Responding as wilson: Open session · View factory task

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants