# Delegation Reference

Delegation is the only way to act on a repository other than the one you are in. Polygraph keeps local clones of the other repos in the session, but they are not yours to touch: never `cd` into them, read their files, or run git in them. Everything happens through `spawn_agent` and `show_agent`.

The flow is **pointer-based**. `spawn_agent` hands you a delegation id. A cheap background subagent watches that id and tells you when it stops moving. You then read the answer yourself, once. Nothing re-pastes the brief, and no log lines pass through a middleman.

## The delegation id

`spawn_agent` returns a short id (e.g. `frontend-1`) that names one child run. It is the handle for everything afterwards — polling, reading the result, following up, stopping. The id pins the repo AND the role, so once you have it you never re-specify either.

Keep every id you are given. Losing one means falling back to `repo` + `role` lookups, which are ambiguous the moment a repo hosts more than one agent.

## Spawning

Call `spawn_agent` directly from the main conversation. This is a fast, non-blocking call that returns an id — it is not polling and does not belong in a subagent.

```
spawn_agent(
  sessionId: "<sessionId>",
  repo: "<org/repo-name>",
  instruction: "<the task instruction>",
  role: "<optional role>",
  context: "<optional context>",
  agent: "<optional: claude | codex | opencode>",
  model: "<optional model override>"
)
```

`agent` picks the child's harness and `model` overrides its default model; include either only when the user named one.

Write the instruction as if to a competent engineer who cannot see your conversation: state the goal, the constraints, what "done" looks like, and what to report back. The child has its own repo and its own context; it inherits nothing from yours.

Delegate to several repos in parallel before waiting on any of them, and prefer one call over N. There are three shapes, the same three `polygraph agent spawn` takes:

- **One repo** — `repo` with an `instruction`.
- **Several repos, one task** — `repos` with one `instruction`, run verbatim in each.
- **Several repos, different tasks** — `specFile`, a JSON file you write, whose path you pass.

Batching pays because the shared text is transmitted once rather than once per repo, and every sibling starts from a byte-identical prompt prefix that can be served from cache. Each id still gets its own poller.

The spec file is the many-to-many form. Each entry names a repo and carries its own `instruction`, and optionally its own `context`, `role`, `agent`, and `model`. Whatever the entries share goes in `sharedInstruction` / `sharedContext`, which are prepended to each entry's own text with a blank line between — so the common brief stays one identical prefix and only the per-repo part varies. An entry may carry no `instruction` of its own and rely entirely on the shared one. Being a file, it also carries a brief too large to pass as an argument, and it is straightforward to generate programmatically.

```jsonc
{
  "sharedInstruction": "Brief every child gets, verbatim.",
  "sharedContext": "Optional background every child gets.",
  "agents": [
    { "repo": "api", "instruction": "Add the expand parameter to the endpoint." },
    { "repo": "people", "instruction": "Consume it in the client.", "context": "This repo still pins the old SDK." },
    { "repo": "planets", "role": "reviewer", "agent": "codex", "instruction": "Review the change against the shared brief." }
  ]
}
```

One qualifier is load-bearing: share only what is genuinely shared. Flattening several different tasks into one `sharedInstruction` to look efficient produces worse work, and the round trips to repair it cost more than the batch saved. Work that differs belongs in each entry's own `instruction`, which is what the file is for.

**Own-repo rule.** With the default role, `repo` must be a repository other than the one you are working in — never delegate into your own repo with the default role; work on it directly (ordinary local subagents are fine for that). Delegating into your own repo IS allowed with an explicit non-default `role`, because each (repo, role) pair is a separate agent slot and the child then runs alongside your own default-role work without colliding with it.

## The output contract

Children are told to be concise: another agent reads their final message and pays for it on every turn that carries it. Expect terse reports; brevity is not less work done.

Your half is the brief: state the output as well as the input — shape (fields, order), a cap where useful, the exact token for "nothing to report", what to omit. In communicating with child agents, maintain extremely high information density while being concise - describe everything needed in the fewest words possible.

Investigation is where it matters most: a fan-out leaves most repos with nothing to report, and without a named empty answer (`NONE`, `no matches`) each writes several thousand characters to say so.

Implementation still wants concision, but lost information is the worse failure: a missed detail costs a round trip, costlier than the prose. Cut narration, recap, hedging — never branch names, files touched, decisions taken, or anything contradicting the brief.

Prose only where necessary. Consumers are agents first, humans second: dense and structural, not narrative.

## Waiting

For each id, launch one background poller subagent whose entire job is to block until that child stops moving. Give it the `sessionId` and the `id`, and nothing else.

- **Claude Code** — a background `Task` with `subagent_type: "polygraph:polygraph-delegate-subagent"`, `run_in_background: true`, and description `Delegate to <repo>`. Fall back to the bare agent name only if the namespaced form is not found.
- **OpenCode** — invoke `@polygraph-delegate-subagent`.
- **Codex** — launch `agent_type: "polygraph-delegate-subagent"` via Codex's own `spawn_agent`, and collect it with `wait_agent`, passing a long `timeout_ms` (five minutes or more): `wait_agent` returns as soon as the poller stops, so a short timeout only adds wake-ups that burn tokens and fill the user-visible transcript with waiting noise.
- **Cursor** — a background `Task` with `subagent_type: "polygraph-delegate-subagent"`, `run_in_background: true`, and description `Delegate to <repo>`. Collect it with `Await`.

A collect step that returns while the poller subagent is still running has not failed. It has only reached the end of its collection window. Collect the same background-task id again, as many times as it takes for the poller subagent to stop. A poller that runs for several minutes is ordinary, and it is never a reason to take the wait back into the main conversation.

The background-task id is the handle your own harness returned when you launched the poller. It is not the Polygraph delegation id (`frontend-1`), which addresses the child agent. Once the poller subagent has finished, do not collect it again: read the child instead, as described below. A child that stops for attention also ends the poller, so treat that as a finished poller and not as a collection window running out.

The poller has exactly one tool and cannot read logs. It exits with a few lines naming the repo, the id, and the final status. That message is a doorbell, not a report — it tells you the child is worth reading, and nothing about what the child did.

**Routine polling never happens in the main conversation.** A waited `show_agent` loop run inline floods your context with status noise and is the single largest avoidable cost in a multi-repo session. That is what the poller exists to absorb.

## Reading the result

When a poller exits, read the child's answer yourself with a single **unwaited** `show_agent` — no `waitForTransitionMs`, no `tail`:

```
show_agent(sessionId: "<sessionId>", id: "<id>")
```

`result.text` is the child's final message: what it did and what it found, in the shape the instruction asked for. This is the payload. Read it once, in the main conversation, and act on it.

One-off unwaited reads like this are cheap and expected inline. It is the *waiting* that belongs in a subagent, not the reading.

When several pollers have exited, a **batch read** collects their results in one unwaited call: pass the list of ids and correlate each result by its delegation id. Batch when you spawned children together and have nothing to do until they all finish. Read one at a time when you act on each result as it lands, or only one child is in flight.

## When the result is not enough

Only if `result.text` is missing, truncated, or the child failed in a way you cannot explain from it:

- Pass an explicit `tail` to `show_agent` to pull recent log lines.
- Page further back with the `page` param: `tail: 5, page: 2` returns the 5 lines before the newest 5, `page: 3` the window before that.
- If the user wants to watch the run live, point them at `polygraph agent attach <repo>` (plus `--role <role>` for a non-default agent) — an interactive terminal view for humans, not a command for you to run.

These are deliberate, targeted follow-ups. None of them belongs in a polling loop, and none of them is a reason to go looking at transcript files, `~/.polygraph/sessions`, or anything a harness saved to disk because a tool result was too large. `show_agent` is the supported interface.

## Follow-ups

To send a child more work, call `spawn_agent` again with the SAME `repo` and the SAME `role`. If that (repo, role) still has a live task — working, or paused waiting on you — the orchestrator delivers your instruction to it as a follow-up instead of starting a second run. A (repo, role) pair therefore has at most one active child at a time.

A follow-up returns a **new** delegation id, linked to the previous one by a `continues` reference. The new id is the live handle: poll it, read it, follow up on it. The old id still addresses the earlier turn if you need to look back at it.

After a follow-up, launch a fresh poller subagent for the new id. The old poller has already exited; it does not resume.

## Input-required

When a child needs an answer from you, it stops and the poller exits with status `input-required` and "needs attention."

Read the child with an unwaited `show_agent` as usual. `inputRequiredQuestion` carries the child's verbatim question. Surface it to the user as the child asked it — do not paraphrase or answer on the user's behalf — then send the answer back as an ordinary follow-up `spawn_agent` for the same (repo, role). Poll the new id it returns.

`permission-required` is a different state and is not yours to resolve here; see "Handling permission requests" in the skill.

## Roles

A repository in a session can host several child agents at once, distinguished by **role**:

- **Omit by default.** Set a `role` only when the user very explicitly asked for a named one, or when a skill the user invoked prescribes one (e.g. `adversarial-review` uses `reviewer`). Never invent one.
- **Purpose.** Roles let independent streams of work run concurrently in one repo — a default agent implementing a feature while a `reviewer` or `ci-investigator` runs alongside. Each (repo, role) pair has at most one active child.
- **Default role.** An omitted `role` means the default role: `spawn_agent` without `role` starts or follows up with that repo's default-role agent.
- **Ids pin the role.** A delegation id already identifies one (repo, role) pair, so `show_agent` and `stop_agent` by id need no `role` argument. Pass `role` only when addressing an agent by `repo` instead of by id.
- **Logs.** Only default-role agents upload logs to the cloud and appear in the multiplexed stream (`polygraph session logs`). A non-default agent's transcript stays on this machine — the user can watch it with `polygraph agent attach <repo> --role <role>`.

## Stopping

Cancel a running child by id:

```
stop_agent(sessionId: "<sessionId>", id: "<id>")
```

The response reports `sessionPreserved: true`: the stopped agent's session is kept so its context can be restored later. Restoring is read-only. After a resume, do not continue the prior work or make further changes until the user explicitly asks for them.

## Before publishing

Every delegation must reach a terminal status — `completed`, `failed`, or `cancelled` — before you push branches or open PRs. A poller exiting on `input-required` is not terminal; it means the child is still waiting on you.
