Teams and specialists

For bigger tasks, Guild Stack splits the work into lanes and gives each lane to a specialist: an AI helper with one focused job. Guild Stack proposes the team; you decide it — that approval is never automated away. When a specialist finishes, it writes a short receipt you can read.

Try it. After /guild:ideate writes a spec, ask for a plan. Guild Stack proposes the team and stops until you approve it:

/guild:plan

The Power layer covers composing teams and controlling cost. The Architect layer covers dispatch, the handoff envelope, and the artifact bus underneath.

Teams and the team gate

A specialist is an agent with one domain. A team is the set of specialists for one phase of one task. Guild Stack proposes the team; you decide it.

How a team is composed

guild-team-compose runs inside /guild:plan:

  1. It matches the spec's domains against your project's specialists in .guild/agents/*.md. An existing specialist is reused, never re-created.
  2. It matches the gaps against Guild Stack's shipped specialist templates. A template that fits is minted into .guild/agents/.
  3. It shows you what exists and what is missing, with options A/B/C/D.
  4. It proposes the full team for the task. There is no size cap. Each role comes with a reason, and excluded roles are listed too (guild.team_proposal.v2).
  5. It waits at the team decision gate (guild.team_decision.v1).
  6. On approval, it gives each specialist a default_tier from its definition and writes .guild/team/<slug>.<phase>.yaml.

The team decision gate

The gate verbs are approve, restructure, add, remove, substitute, and edit_dependencies. The gate blocks on a missing decision, on a restructure (it is not an approval), and on a stale or tampered trail. --auto-approve never covers a team decision.

Shipped specialist templates

Guild Stack ships 15 templates under templates/specialists/:

  • Engineering: architect, researcher, backend, frontend, devops, qa, mobile, security.
  • Content and communication: copywriter, doc-writer, technical-writer, social-media, seo.
  • Commercial: marketing, sales.

A template becomes a project specialist only when team composition mints it.

Machinery agents

Four agents ship registered with the plugin (agents/*.md). They run the machinery; they are not domain roles.

AgentJob
advisorAnswers one escalated sub-question at the powerful tier. Never sees the raw project context.
context-managerAssembles bounded context bundles and run-scoped evidence.
developerThe generic mid-tier implementer for lanes with no domain home.
team-leadOwns one task cell. Dispatches its specialists, reads their receipts, and reports upward only as a goal status. Specialists never message the orchestrator or each other.

Minting a new specialist

When no template fits, guild-create-specialist mints a new one. It interviews you, drafts the role under .guild/agents/proposed/, scans its boundaries against neighbor roles, gates any boundary edits, gates the new role, and then registers it live. It runs only on your explicit request (or team-compose option A), or when evolution proposes it with every required signal.

For a missing capability rather than a missing role, guild-create-skill mints a skill the same gated way.

Cost and tiering

Guild Stack dispatches each lane at the lowest viable tier. The default biases cheap. A powerful lane needs a reason: the score, an explicit override, or an advisor request.

Model routing: work type by tier, with advisor escalation.
Open full size

The three tiers

TierScoreOn Claude
cheap0haiku
mid1–2sonnet
powerful3 or moreopus

The cutoffs come from models.thresholds (default {mid:1, powerful:3}). The tier-to-model map is models.tiers; the Claude column is its binding on that host.

How a lane is scored

A script scores the lane. It calls no model, so it costs zero tokens and the same signals always give the same tier. The signals:

  • the work type: read or summarize 0, draft or extract +1, architect, review, or schema +2;
  • the blast radius and file count;
  • an upstream depends-on: contract;
  • security or correctness sensitivity;
  • a prior escalation on this lane: +1, sticky for the run.

The plan's complexity_score and tier are an estimate. The scorer confirms or replaces them at dispatch. Each dispatch prints one line: lane <task-id> · score N · tier <tier> · model <model>, and the run record keeps it.

Overrides

From strongest to weakest:

  1. --model-tier= on the command line. It pins every lane in the run.
  2. A tier: pin on one lane in the plan.
  3. models.tiers and models.thresholds in config.
  4. The built-in default.

Research floor. A lane whose specialist has purpose: research always runs powerful. No override, retry, or resume goes below it.

If a re-score differs from the tier you approved in the team proposal, Guild Stack does not pick one silently. It asks you to approve a restructured team.

Advisor escalation

When a low-tier lane hits a question above its tier, it emits escalate. Guild Stack then asks the advisor (a powerful machinery agent) that one sub-question. The advisor sees only the draft, the question, and a short critique instruction. It never sees the raw file context, which keeps the call small. The lane folds in the answer and continues. It is not re-run on the expensive model.

Where to see the cost

/guild:status stats reports usage over .guild/runs/**. See Status, stats, and the dashboard.

Dispatch and execution

/guild:build runs guild:execute-plan. For each lane it assembles a context bundle, scores a model tier, dispatches one agent on the backend chosen at intake, collects a handoff receipt, then runs the lane review gate. This page covers the execution half. Identity records are on Dispatch and run identity. Bundle contents are on Context assembly.

The agent_mode ladder. Run-start preflight reads agent_mode, then resolves team to cmux surfaces, then tmux panes, then a subagent floor. Explicit agent and subagent pins skip the team rungs. The result is frozen in the run's settings snapshot.
Open full size
Lead to lane specialists to receipts.
Open full size

The backend is chosen once

The backend is resolved once, at command intake, by runStartPreflight. The result is frozen in .guild/runs/<run-id>/resolved-settings.json. execute-plan reads snapshot.effective.agent_mode and honors it. It never re-picks, and it does not read the backend from team.yaml, whose backend field is only an audit mirror.

The agent_mode setting takes team, agent, subagent, or auto. The default is auto. The ladder:

agent_modeResolves to
agentIn-process independent agents. Explicit pin, never overridden.
subagentSubagents through the Agent tool. Explicit pin, never overridden.
team or autoFirst match wins: a cmux workspace (CMUX_WORKSPACE_ID set) gives visible cmux surfaces. Otherwise tmux on PATH gives tmux panes. Otherwise the subagent floor.

Team is primary whenever tmux or cmux is present. Subagent is the fallback, not the default.

Per dispatch.md, the agent and subagent rungs fail closed for capability-scoped lanes. The host contract does not yet prove that the child process receives the scope environment, so the launcher refuses rather than emit a production dispatch plan. Scoped dry runs are non-dispatchable previews.

The run-start preflight

Preflight is a library that returns a structured result. It does not write files. The run lifecycle persists the snapshot at run start. It:

  1. Resolves settings and records the source of each key.
  2. Validates them.
  3. Probes tmux, and probes for a cmux workspace once.
  4. Resolves the dispatch backend with the ladder above.
  5. Detects the host and review providers, and resolves the per-run roles (including the advisory role).

If tmux is available and agent_mode is not team, preflight asks once whether to use tmux agent teams for all runs. Note that auto triggers the prompt. A yes persists agent_mode: team through config-cmd set. A no persists nothing, and the prompt can return next run.

The tmux backend

When the ladder resolves to tmux, scripts/agent-team-launcher.ts spawns one session. It has one pane for the orchestrator and one pane per specialist, each with CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 exported. A real launch writes .guild/runs/<run-id>/agent-team/session.json. Limits: experimental, one team per session, no nested teams, higher token cost. Use --dry-run to preview commands without spawning anything.

If the snapshot resolves to the team backend and CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 is not set, execution refuses and surfaces the blocker. It does not fall back silently, because that would change execution semantics under the plan.

A PreToolUse guard enforces the refusal at runtime. It compares each specialist dispatch through the in-session Agent tool against the snapshot:

Snapshot agent_modeTeam substrateOutcome
team or autoavailableDenied. The run committed to panes, so an in-session lane is a downgrade.
teamunavailableRecorded, not blocked. The launcher downgrades with a warning.
autounavailableUntouched. The ladder already resolved to agent or subagent.

A deny needs structured lane evidence in the dispatch's own env map. Evidence that comes only from prompt text is recorded, never blocked. GUILD_ALLOW_BACKEND_DEGRADE=1 allows a conscious downgrade and still records it. Every non-pass decision writes a backend-degradation receipt to .guild/runs/<run-id>/logs/backend-degradation.jsonl.

Ephemeral one-agent-per-task cells

Each lane is an ephemeral agent: spawn, work, extract, dismiss. This composes with the backend ladder and does not replace it. The ladder picks the backend. The lifecycle is the same on all of them.

  1. Spawn. A new agent at the lane's resolved tier, with its task-scoped context bundle. Concurrent lanes get distinct agents, never a shared one.
  2. Work. The agent runs, and may escalate (below).
  3. Extract. Learnings go into the agent's handoff envelope. The coordinator records them in the run. Learnings are candidates only. Nothing is auto-promoted.
  4. Dismiss. The agent terminates. No idle agents persist. The next task is a new agent.

Before dispatch, the lane's task-run descriptor is written. A retry is a fresh agent with the same bundle and tier, up to defaults.retry.max_attempts total attempts.

On the tmux backend, --dismiss-completed --run-id <id> terminates a lane only when a durable handoff-acceptance record authorizes it. A receipt on disk alone never does. A specialist's shared pane is killed only once all of that specialist's task cells are terminal.

Context bundles

Before each dispatch, guild:context-assemble builds one bundle per specialist at .guild/context/<run-id>/<specialist>-<task-id>.md. It has three layers: Universal (about 400 tokens), Role-dependent, and Task-dependent. The target is about 3k tokens with a hard cap of 6k. A post-write linter, lint-context-bundle, enforces the cap in code: exit 0 passes, exit 2 fails.

The Universal layer is assembled once per run and reused byte-identical across lanes, so provider prompt caches can match the shared prefix. Recall runs through the single entry point scripts/lib/recall.ts. When recall returns chunks and the top score is at least models.recallScoreThreshold (default 0.4), the agent receives the chunks and skips the full file read.

Tier auto-scoring

Each lane runs at the lowest viable tier. scripts/score-tier.ts is a pure scorer with no model call, so a dispatch trace is reproducible. Signals include the work-type verb, blast radius, an upstream depends-on: contract, security sensitivity, and a sticky +1 after a prior escalation on the lane.

ScoreTierClaude model
0cheaphaiku
1 to 2midsonnet
3 or morepowerfulopus

The cutoffs are models.thresholds, default {mid: 1, powerful: 3}. Precedence, highest first:

  1. --model-tier= pins every lane in the run.
  2. A per-lane tier: pin in the plan.
  3. settings.json models.tiers and models.thresholds, if present. These are settings-schema keys, not policy keys: config set refuses models.* in v3.0.0. See Config reference.
  4. The built-in default.

A lane whose participant has purpose: research is floored at powerful (research_always_hard) above this ladder, and the floor carries through retries and advisor consults. The chosen tier is printed as lane <task-id> · score N · tier <tier> · model <model>, recorded in the run, and written back to the per-phase team.yaml so tmux panes route at the lane score. With models.enabled: false, scoring is skipped.

Advisor escalation

A low-tier agent that hits something above its tier gets one powerful sub-answer, then continues. It is not re-run on the expensive model. Any of three triggers starts it:

  • The agent emits status: "escalate" with an escalate_reason in its handoff envelope.
  • Its output contains a phrase from models.escalationMarkers.
  • Its output is anomalously short. This fires only when a models.shortOutputThreshold value exists for that task type and tier.

The advisor agent sees the draft, the question, and a short critique instruction, never the raw file context. It returns through the same envelope and the original agent folds the answer in. The advisory role is read from snapshot.roles.advisory, never re-resolved. models.advisorRounds (default 2) caps consults per lane. On exhaustion the lane is recorded inconclusive: advisor budget exhausted. The escalation trail is recorded in the run.

The envelope format is on Agent communications. Cost settings are on Cost and tiering.

Agent communications

Guild Stack agents do not talk to each other in chat. Every lane writes a file, and the lead reads the file. This keeps handoffs deterministic and replayable. Specialists never message the orchestrator or each other, and the orchestrator never sees a specialist transcript. Work is reported upward only as a handoff envelope or a goal-status roll-up.

Handoff flow. A lane agent writes a receipt file under .guild/runs/run-id/handoffs, then sends a one-line pointer message. The lead reads the file, validates the embedded envelope, and records the result. Chat never carries the envelope.
Open full size

The handoff envelope

The envelope is guild.handoff.v2. It is a strict JSON object. The validator rejects any key not on this list.

KeyRule
schema_versionExactly "guild.handoff.v2".
task_idNon-empty string.
tiercheap, mid, or powerful.
statusdone, blocked, or escalate.
summaryNon-empty, at most 600 characters.
artifactsArray of strings. May be empty.
issuesArray of strings. May be empty.
escalate_reasonRequired and non-empty when status is escalate.
learningsOptional array of strings.
notesOptional, at most 200 characters.
injection_cleanOptional: clean, flagged, or unverified.

Receipts

Each agent writes its receipt as its final action, on every backend, to:

.guild/runs/<run-id>/handoffs/<specialist>-<task-id>.md

The receipt is a guild.handoff_receipt.v1 Markdown wrapper. Its YAML frontmatter and five sections (changed_files, opens_for, assumptions, evidence, followups) are context for a human reviewer. The machine truth is exactly one fenced guild.handoff.v2 JSON block inside it. A receipt with no block is invalid. A receipt with two or more blocks is rejected as a duplicate-block defect.

In team mode, the agent then sends one line to the lead and nothing else:

done · <task-id> · status:<done|blocked|escalate> · receipt:<absolute-path>

Pasting the envelope into chat is forbidden. The lead reads the file, never the message body. The message is a liveness signal.

A communication-format linter checks receipts under .guild/runs/<id>/handoffs/. It flags a receipt with no embedded block, two or more blocks, or a block that fails the envelope validator. It also flags new hand-rolled YAML readers and new communication artifacts that lack an artifact_category:. In the source it runs in warn mode and exits 0 unless --enforce is passed.

Result contracts

A closed registry lists six result contracts. Each has a validator, and none are deferred. A host-side normalizer targets exactly these. A value that matches none is rejected with a schema error and is never accepted as prose.

Wire schema_versionValidationPurpose
guild.handoff.v2strictSpecialist lane result.
review_result.v1lenientAdvisory or adversarial review result for the gate-pass check.
guild.phase_result.v1strictPhase close summary and gate predicate.
guild.permission_receipt.v1strictRequested and selected host mode, and gate policy.
guild.host_event.v1strictNormalized hook, tool, or session event.
guild.qa_result.v1strictTest matrix execution, gaps, failures, release predicate.

review_result.v1 has no guild. prefix on the wire. Use the exact string.

Required fields of the four orchestration contracts. Every one also requires schema_version set to its own wire string, run_id, and ts.

ContractOther required fields
guild.phase_result.v1phase (init, ideate, plan, build, qa, ops); status (complete, blocked, partial); gate_passed (boolean); outputs (string array).
guild.qa_result.v1checks (array of {id, status}, status pass, fail, or skip); gaps and failures (string arrays); release_ok (boolean).
guild.host_event.v1host_id; event_type (hook, tool, session); name; optional detail string.
guild.permission_receipt.v1requested_mode; selected_mode; gate_policy (prompted_inline, autonomous, forbidden); scope (string array).

Extra keys are tolerated on these four. The required core is checked strictly.

The review_result.v1 reader requires packet_id, artifact_sha256_reviewed, verdict, and a blocking_findings array. A present schema_version must equal review_result.v1. An absent one is tolerated.

The artifact bus

The artifact bus is a file-based publish and consume layer inside a run directory. It is filesystem-canonical: hosts that share one .guild/ tree share the bus, with no network transport. It is separate from the handoff receipts above, and uses its own schemas and log.

Path under .guild/runs/<run-id>/bus/SchemaHolds
log.jsonlguild.bus_event.v1One line per publish, each with a monotonic seq.
cas/<aa>/<sha256>.meta.yamlguild.cas_meta.v1Header for a content-addressed artifact.
subscribers/<id>.yamlguild.bus_subscriber.v1A subscription: topics and callback.
fanout.jsonlnot named in sourceOne delivery record per matched hook subscriber.

A topic is <type>/<scope>/<resource>, where type is one of handoff, status, context, review, approval, heartbeat. Subscribers match with globs: * matches one segment, and a trailing ** matches the rest. An event is one of artifact.published, artifact.streaming, artifact.closed, artifact.retracted.

Publishing is serialized by an exclusive lock file, bus/.lock, so concurrent publishers never reuse a seq. Lock timeout fails closed, and an invalid topic returns null. The publish path checks the publisher's tier against durable state, and refuses a publisher that asserts a tier it does not hold.

Subscriber callbacks are hook, poll, or webhook-url. The TaskCompleted hook processes new events and records a delivery for each matching hook subscriber. poll subscribers tail the log themselves. webhook-url is a recorded remote seam, and the hook never calls it.

For where these files sit in a run, see System architecture. For how lanes are dispatched, see Dispatch and execution.