# Coordination
URL: /docs/alineod/guides/coordination

Wait on agents in different ways, wait for a whole subtree to finish, and tell a running agent when another one finishes.



Beyond the plain `waitFor` of [Fan-out and gather](/docs/alineod/guides/fan-out-gather), alineod gives you three
ways to coordinate agents. All of them are declared by you (the operator) or in a spawn request. An agent can't
start them from inside its own sandbox yet.

| Need                                                                | Use                                          |
| ------------------------------------------------------------------- | -------------------------------------------- |
| Start a child on the first result, a quorum, or a deadline          | [`waitFor` modes](#waitfor-modes)            |
| Wait until everything under an agent has finished                   | [Subtree completion](#waiting-for-a-subtree) |
| Tell a running agent when another one finishes, without blocking it | [`notifyOn`](#notifyon)                      |

## `waitFor` modes [#waitfor-modes]

`waitFor` accepts an object as well as a list of IDs:

```json
{
  "parentAgentId": "a_7b2e44d1",
  "spec": { "name": "judge", "...": "..." },
  "prompt": "Pick the best of the drafts in /inputs.",
  "waitFor": {
    "agents": ["a_c01d9e3a", "a_5e8f2b71", "a_91aa0c4d"],
    "mode": "quorum",
    "k": 2,
    "deadlineSec": 600
  }
}
```

| `mode`              | The child starts when                          | Inputs written          |
| ------------------- | ---------------------------------------------- | ----------------------- |
| `settled` (default) | every agent has finished, whatever the outcome | all of them             |
| `all`               | every agent succeeded                          | all of them             |
| `any`               | the first agent finishes                       | that one                |
| `quorum`            | `k` agents succeeded                           | the first `k` successes |

A plain list is the same as `mode: "settled"`.

* **`onDepFailure`** (`all`, `quorum`): `fail` (the default) ends the child `failed` without forking it when a
  dependency fails, or when a quorum can no longer be reached. `proceed` starts it anyway with what there is.
* **`deadlineSec`**: stop waiting after this long. With `onDeadline: "proceed"` (the default) the child starts with
  whatever has finished, and the wait is marked `partial`. With `"fail"` the child ends `failed`. The deadline is
  stored, so it survives a daemon restart.
* **Agents still running after the child starts**, like the losers of an `any` or `quorum`, keep running. Stop
  them yourself if you don't need them, e.g. with a subtree stop.

For any wait other than a plain `settled` one that fully succeeded, `/inputs.json` also has a `__wait` entry, so the
child can tell a partial or race result from a complete one:

```json
{
  "a_c01d9e3a": { "path": "/inputs/a_c01d9e3a.txt", "outcome": "success" },
  "a_5e8f2b71": { "path": "/inputs/a_5e8f2b71.txt", "outcome": "success" },
  "__wait": { "mode": "quorum", "outcome": "satisfied", "pending": ["a_91aa0c4d"] }
}
```

Each resolved wait emits `wait_resolved`. While a dependency the wait still needs is **paused**,
`wait_blocked_on_paused` is emitted once for it, so a stalled wait shows up instead of hanging silently.

## Waiting for a subtree [#waiting-for-a-subtree]

A subtree is **quiescent** when every agent in it has finished and none is still being spawned. A paused agent
counts as not finished, and is listed in `blockedOnPaused`.

```bash
curl -s 'localhost:4600/agents/a_7b2e44d1/await?scope=subtree&wait=120'
```

```json
{
  "rootAgentId": "a_7b2e44d1",
  "quiescent": true,
  "asOf": 214,
  "members": [
    {
      "agentId": "a_7b2e44d1",
      "state": "done",
      "outcome": "success",
      "resultRef": "fs://a_7b2e44d1/result.md"
    },
    {
      "agentId": "a_c01d9e3a",
      "state": "done",
      "outcome": "success",
      "resultRef": "fs://a_c01d9e3a/result.md"
    }
  ],
  "blockedOnPaused": []
}
```

The request is held for up to `wait` seconds (maximum 240) and returns as soon as the subtree is quiescent. A
finished agent that you prompt again makes the subtree active again, so quiescence can come and go.
`subtree_quiescent` is emitted each time a subtree someone is waiting on becomes quiescent.

To wait on a set of agents without spawning anything, use the same modes as `waitFor`:

```bash
curl -s -X POST localhost:4600/runs/r_3f9a1c20/await \
  -H 'content-type: application/json' \
  -d '{"agents": ["a_c01d9e3a", "a_5e8f2b71"], "mode": "any", "wait": 60}'
```

```json
{
  "outcome": "satisfied",
  "selected": ["a_5e8f2b71"],
  "settled": [{ "agentId": "a_5e8f2b71", "outcome": "success" }],
  "pending": ["a_c01d9e3a"]
}
```

If it's still undecided when `wait` runs out, `outcome` is `pending`. `{"subtree": "<agentId>", "wait": N}` in the
same body waits for that subtree instead.

## `notifyOn` [#notifyon]

`waitFor` holds a child until its dependencies finish. `notifyOn` doesn't block anything: the agent keeps working
and is **told** when an agent it watches finishes.

```json
{
  "parentAgentId": "a_7b2e44d1",
  "spec": { "name": "integrator", "...": "..." },
  "prompt": "Start integrating; the reviewers' findings will arrive as they finish.",
  "notifyOn": ["a_c01d9e3a", "a_5e8f2b71"]
}
```

Or subscribe an agent that already exists:

```bash
curl -s -X POST localhost:4600/agents/a_2d4c9e10/notify-on \
  -H 'content-type: application/json' -d '{"agents": ["a_c01d9e3a"], "wake": true}'
```

When a watched agent finishes, however it finishes, the subscriber gets a line like this:

```text
[alineo] Update from agents you're watching:
- a_c01d9e3a (reviewer-auth) finished: success. Result: fs://a_c01d9e3a/result.md — "Found two issues in …"
```

How it arrives depends on what the subscriber is doing:

| Subscriber is   | Delivery                                                                                                                     |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| running         | Steered into its current turn, after its running tool call.                                                                  |
| paused          | Held; delivered as soon as it's resumed.                                                                                     |
| idle            | Added to the start of its next prompt. With `wake: true`, or `POST /agents/:id/inbox/deliver`, it starts a new turn instead. |
| not forked yet  | Held; added to its first prompt.                                                                                             |
| stopped or lost | Dropped.                                                                                                                     |

* **Merged:** everything pending when a delivery happens goes out as one message.
* **Already finished:** subscribing to an agent that has already finished notifies at once.
* **Finishing again:** an agent prompted again that finishes again notifies again, as "finished again".
* **Durable:** notifications are stored in the ledger, so pending ones survive a daemon restart.

`GET /agents/:id/inbox` lists pending and delivered notifications. The events are `notify_registered`,
`inbox_queued`, `inbox_delivered` and `inbox_dropped`.

<Callout type="info" title="A notification can't interrupt a running tool call">
  Delivery into a running turn uses steer, so it lands after the agent's current tool call. If a
  long build is running, the notification waits for it. Use `waitFor` when the agent can't do
  anything useful until the result arrives.
</Callout>
