[FEATURE] A declarative team definition for Agent Teams, with enforced role guardrails

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 25, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Searched enhancement issues for write ownership, declarative team config, role-based teams, approval gates, per-agent budgets, and per-agent env allowlists. The closest existing work is different in kind: #67559 and #34535 both ask for a different-vendor model as reviewer, and #79507 asks for teams across machines. This request is about the team definition itself, and it holds with every teammate on an Anthropic model.

Problem Statement

Agent teams are formed by natural language and defined only by runtime state. The docs are explicit: the team config "holds runtime state such as session IDs and tmux pane IDs, so don't edit it by hand or pre-author it: your changes are overwritten on the next state update", and "There is no project-level equivalent of the team config."

That leaves no way to say, in a file I can commit and review, what my team is. Two consequences.

The team is not reproducible. The composition of the team depends on how I phrased the prompt this time. A team that worked well yesterday cannot be re-run, shared with a colleague, or diffed in a PR. .claude/agents/*.md gives me reusable roles, but nothing that says which roles form a team, who reviews whom, or what each one is allowed to touch.

The guardrails are advice, not enforcement. Several safety properties are currently the operator's job to maintain by prompting carefully:

  • File conflicts. The best-practice section says "Two teammates editing the same file leads to overwrites. Break the work so each teammate owns a different set of files." That is a rule with no mechanism. Nothing stops a teammate writing a file another teammate owns, and nothing reports it after the fact.
  • Self-approval. With plan approval enabled, "The lead makes approval decisions autonomously." The lead is the same model that decided the task and the team, approving a plan produced under its own framing. A fresh context does not make it an independent check; it shares the priors that produced the plan. There is no way to declare that a given approval must stop at a human.
  • Least privilege. "Permissions set at spawn: all teammates start with the lead's permission mode... you can't set per-teammate modes at spawn time", and if the lead runs --dangerously-skip-permissions, every teammate does. A read-only reviewer inherits the implementer's full environment, including whatever cloud credentials are exported in that shell.
  • Cost. Nothing bounds a teammate. #68110 (recursive unbounded fan-out) and #54393 (multi-agent coordination post-mortem) are what that looks like in practice; the docs' answer is "Monitor and steer".

Hooks (TeammateIdle, TaskCreated, TaskCompleted with exit code 2) are the current escape hatch, and they do work. But they push every one of these into imperative shell that each user writes, debugs, and maintains separately, for properties that are structural to the team rather than to the task.

Proposed Solution

I have been running this design in production for months in an orchestrator I built and ship (choragos, Go, Apache-2.0, v0.19.0), so what follows is a working config surface rather than a sketch. What I am proposing is that surface reshaped to fit Claude Code's existing subagent definitions. Implementation notes and the sharp edges are in Additional Context.

A project-level, version-controlled team definition, separate from the runtime state file, that declares the roles and the constraints the harness enforces. Something like .claude/team.json:

{
  "roles": [
    {
      "name": "coder",
      "agent": "implementer",
      "model": "opus",
      "judge": "reviewer",
      "judge_rounds": 3,
      "judge_pass": 7,
      "budget": "5.00"
    },
    {
      "name": "reviewer",
      "agent": "security-reviewer",
      "model": "opus",
      "env_allow": ["PATH", "HOME", "TERM"],
      "timeout": "45m"
    },
    {
      "name": "qa",
      "agent": "test-runner",
      "owns_files": ["defects.md"],
      "approve": true
    }
  ]
}

Semantics, each one failing closed to a human and inert when unset:

| Field | Enforced behaviour |
|---|---|
| owns_files | a write by any other role to a matching path is refused or reported at task completion |
| judge | the named role scores the work before it is accepted; a role may not judge itself |
| judge_pass, judge_rounds | numeric threshold out of 10, and a hard cap on retry rounds so a failing loop terminates |
| approve | this role's work stops at a human, not at the lead |
| env_allow / env_deny | the teammate process is spawned with a filtered environment |
| budget, timeout | notify or pause the role when it crosses the cap |

.claude/agents/*.md already defines what a role is. This defines what a team is and what each member may do, which is the piece with no home today. Referencing existing subagent definitions by name means no duplication of prompts or tool allowlists.

None of this depends on a non-Anthropic model. judge with both roles on Opus already buys the structural property that matters: the work is not accepted on the worker's own word, the reviewer is a distinct process with distinct state, and the acceptance is gated rather than asserted.

Alternative Solutions

  • Hooks. Works today and is what I would do without this. TaskCompleted exit 2 can implement a judge gate, and a PreToolUse hook can implement write ownership. Downsides: every user reimplements it; the hook sees a tool call, not the team's intent, so ownership has to be re-derived from paths; and none of it is visible in the team definition, so a reviewer reading the repo cannot tell what the guardrails are.
  • Prompting the lead. "Only approve plans that include test coverage" is documented and does influence behaviour. It is not a gate: the lead can still approve, and there is no record that it should not have.
  • Careful task partitioning. The documented answer to file conflicts. It relies on the human getting the partition right up front and on every teammate respecting it.
  • An external orchestrator. What I built (below). It works, but it sits outside Claude Code and re-implements process supervision, panes, and IPC to get at guardrails that would be a config file if the harness owned them.

Priority

Medium - Would be very helpful

Feature Category

Configuration and settings

Use Case Example

A three-role team on a service repo, every role on an Anthropic model:

  1. coder implements a change to the auth module.
  2. qa owns defects.md. It is the only role that may write it, so the coder cannot close a bug it opened. A coder write to that path is refused, not merely discouraged.
  3. reviewer is spawned with env_allow, so it never sees the AWS_* credentials exported in my shell. It reads the diff and the spec and returns a score.
  4. coder's work is not accepted until reviewer scores it 7 or better, up to 3 rounds. On the third failure the loop stops and hands me the open findings instead of grinding.
  5. The whole team is a committed file, so a colleague can check out the repo and run the same team, and a change to the guardrails shows up in a PR diff.

Today steps 2, 3, and 4 are prompt discipline plus bespoke hooks, and step 5 is not possible.

Additional Context

Every field above is implemented and shipping today in choragos, a multi-agent orchestrator I wrote: Go, Apache-2.0, v0.19.0 released 2026-08-18, 47 test files, CI and OpenSSF Scorecard on the repo. It drives real Claude Code sessions in owned PTY panes, with the guardrails above enforced by the orchestrator rather than by prompting. I am not asking you to adopt it; I am reporting what the config surface looks like after it survived contact with daily use, in case that shortens the design work.

Four details that turned out to matter more than the basic idea:

  1. Numeric verdict beats approve/reject. A binary verdict collapses toward approve. A score out of 10 with a threshold makes the reviewer commit to a degree and makes "barely passing" visible.
  2. The round cap is load-bearing. Builder/judge loops do not reliably converge. Default 3.
  3. Self-judging must be rejected at config load, not at runtime. It is easy to configure a judge that is the role itself and end up with a no-op that looks like a gate.
  4. Fail closed on an unparseable verdict. A judge that returns prose instead of a score must not count as a pass.

Source, if useful: internal/config/config.go:52 (judge), :104-107 (round cap), :113 (pass threshold), :477-479 (self-judge rejection), internal/deck/judge.go:167 (verdict parsing, fail-closed).

One further note from running it: the reviewer needs the spec, not only the diff. Reviewing a diff in isolation finds quality issues but not "this is not what was asked for", which is a large share of what a second reader actually catches.

Happy to help test if this is something you would consider.

View original on GitHub ↗