# Run a multi-workspace dispatch loop

Fill the backlog, promote a stack of cards, and watch the orchestrator keep every workspace in your pool loaded with work in parallel.

One task at a time is the on-ramp. The reason Shelbi exists is the
other direction: half a dozen workspaces all running in their own
worktrees, the orchestrator keeping every free slot loaded with the
next ready card. This page walks the loop end-to-end.

You should already have a project with at least two workspaces declared.
If you only have one, edit `~/.shelbi/projects/<name>.yaml`, bump the
pool, and run `shelbi reload` before you start.

## Confirm the pool

From the orchestrator pane (or any shell):

```bash
shelbi workspace list
```

```
NAME      HOST      RUNNER   AGENT      STATE
alpha     hub       claude   -          idle
bravo     hub       claude   -          idle
charlie   hub       claude   -          idle
delta     devbox    codex    -          idle
echo      devbox    claude   -          idle
foxtrot   devbox    codex    -          idle
```

Every row should show `idle` under `STATE` and `-` under `AGENT`.
The `RUNNER` column is the configured `workspaces[].runner` value, so it
is where you confirm whether each slot will launch Claude, Codex, or
another declared runner. Nothing is loaded yet. If a pane is dead, jump
to the workspace (`Enter` on its sidebar row): the session may have
stalled on a permission dialog, and resolving it puts the workspace back
in the rotation.

You want at least three free slots to see the loop do its job. Two cards
on a one-slot pool is a queue, not a loop.

## Queue up a stack of work

The fastest way to fill the backlog is to talk to the orchestrator in
batches:

```
you: add three tasks:
     1. add CSV export to the reports page
     2. fix the cookie-domain bug on Safari SSO
     3. tighten the rate-limit error copy

Orchestrator: ✓ added 3 to backlog:
              - add-csv-export-to-reports
              - fix-cookie-domain-bug-on-safari-sso
              - tighten-rate-limit-error-copy
```

The CLI does the same thing in a loop if you'd rather script it:

```bash
shelbi task add "Add CSV export to the reports page"
shelbi task add "Fix cookie-domain bug on Safari SSO"
shelbi task add "Tighten the rate-limit error copy"
```

Each lands in `backlog` as its own markdown card under
`~/.shelbi/projects/<name>/tasks/`. Nothing dispatches yet. The
backlog is still your triage queue.

### Route a task to a specific machine

When a task is RAM-heavy, latency-sensitive, or you just want to keep
it off the hub, hint where it should run with `--prefers-machine`:

```bash
shelbi task add "Re-encode the marketing video assets" \
  --prefers-machine devbox
```

The orchestrator's dispatcher honors the hint when at least one
workspace on that machine is free. If `devbox` is fully busy when the
task becomes ready, the card stays in `todo`.

<Callout type="note" title="A machine preference is never overridden silently">

Shelbi will not re-route a `--prefers-machine` task to the hub just because
the preferred machine is busy. It parks the card in `todo` and waits. Free
a workspace on that machine, or drop the preference, to get it moving.

</Callout>

### Block one task on another

If task B reads files task A is about to rewrite, declare the order
explicitly:

```bash
shelbi task add "Rename the auth_v1 module to auth" --id auth-rename
shelbi task add "Update the OpenAPI spec for the new auth module" \
  --depends-on auth-rename
```

The dependent card carries a `🔒` badge in the Kanban view and the
orchestrator skips it during dispatch until every id in its
`depends_on` list reaches `done`. See
[dependent tasks](/docs/guides/getting-started/workflows#dependent-tasks-with-depends_on)
for the full state model.

## Promote everything to todo

Promotion is the start signal. Move all three cards into **todo** in
the order you want them picked up:

- **From the Kanban view** — `Ctrl+P` → Tasks, then `H`/`L` to walk
  cards into the next column. `K`/`J` to reorder within `todo`. The
  orchestrator dispatches from the top.
- **From the orchestrator** — just ask:

  ```
  you: promote all three.
  ```

- **From the CLI** — one move per card:

  ```bash
  shelbi task move add-csv-export-to-reports --to todo
  shelbi task move fix-cookie-domain-bug-on-safari-sso --to todo
  shelbi task move tighten-rate-limit-error-copy --to todo
  ```

The orchestrator is already tailing `~/.shelbi/events.log`. Each
promotion arrives as a `task=<id> backlog -> todo reason=user:*` line
and triggers an immediate dispatch attempt.

## Watch the loop in action

Within a second or two, the sidebar lights up. Workspace badges flip
from `·` to `⏵` in YAML declaration order: `alpha`, then `bravo`,
then `charlie`, until either the `todo` column is empty or every
workspace is busy. The sidebar groups slots under the machine they're
pinned to, and each busy row shows the agent it's running:

The orchestrator's chat shows the same thing in prose:

```
Orchestrator: ✓ dispatched 3:
              - add-csv-export → alpha
              - fix-cookie-domain → bravo
              - tighten-rate-limit → charlie
```

To see the raw stream, open a second pane and tail the log:

```bash
shelbi events tail --follow
```

You'll see the column moves and the workspace state changes interleaved,
exactly what the orchestrator is reacting to:

```
2026-06-22T14:11:02+00:00 task=add-csv-export-to-reports backlog -> todo reason=user:cli
2026-06-22T14:11:02+00:00 task=add-csv-export-to-reports todo -> in_progress reason=orchestrator:auto-dispatch_workspace=alpha
2026-06-22T14:11:03+00:00 workspace=alpha none -> working
2026-06-22T14:11:04+00:00 task=fix-cookie-domain-bug-on-safari-sso backlog -> todo reason=user:cli
2026-06-22T14:11:04+00:00 task=fix-cookie-domain-bug-on-safari-sso todo -> in_progress reason=orchestrator:auto-dispatch_workspace=bravo
…
```

Jump between workspace panes any time. `Enter` on `bravo` drops you
straight into its pane to watch the Safari fix happen live. Pop back
to the orchestrator with `Ctrl+P` → Chat when you're done.

## Review one branch while the others keep running

Workspaces finish at their own pace. When the first one writes its
review-ready marker, three things happen at once:

1. The hub poller moves the task from `in_progress` to `review` and
   clears the marker.
2. The workspace's badge flips from `⏵` to `✓` and the **Ready for
   Review** list in the sidebar grows a row.
3. The orchestrator, watching the event stream, sees the workspace free
   up and immediately dispatches the next ready `todo` card to that
   slot (if any).

That last step is the loop. You don't have to ask. The workspace that
just handed off a finished branch is already on its next task by the
time you've clicked into the review pane.

Open the review:

```
✓ add-csv-export   alpha     ← Enter
```

Shelbi checks the branch out into the project's working directory on
the machine that ran the task and spawns a fresh agent session pointed
at the diff. Ask it to walk you through the change, run tests, or
request edits. The workspace that produced the branch is busy on
something else, but the review pane is its own conversation in the
same worktree.

Accept when you're ready:

```
you: merge it.
```

Then move the card from `review` to `done` (`L` in the kanban view,
or `shelbi task move <id> --to done`). The card is yours; the
orchestrator only takes the `review` → `done` step when you've
explicitly authorized it as part of [Zen
Mode](/docs/guides/getting-started/enable-zen-mode).

While you've been reviewing, the orchestrator has been working the
backlog. By the time you finish accepting one branch, two more may be
in review. The board state is the single source of truth: the
sidebar mirrors it, the events log records every move, and the
orchestrator keeps every free slot loaded.

## When the loop stalls

A few things stop the dispatcher from filling a free slot, in
intentional ways. They're worth recognizing so you don't mistake
intent for breakage:

- **The card is blocked.** Anything in `depends_on` is not yet `done`.
  The card renders with `🔒` in the kanban view. Land the dependency
  first.
- **The card prefers a machine and no workspace on it is free.** The
  orchestrator parks it in `todo` rather than mis-routing. Free a
  workspace on the target machine (kill another task, or remove the
  preference).
- **The workspace's pane died.** The orchestrator surfaces it
  (`workspace=<name> pane_alive=false`) and waits for you. Re-launch with
  `Enter` on the workspace
  row, or `shelbi workspace stop <name>` to release the in-flight task
  back to `todo`.
- **The mid-conversation guard.** If you're actively chatting with
  the orchestrator about a specific task, it holds off on
  back-to-back dispatches that would talk over you. The next event
  resumes the loop.

Everything else (diff size, branch contents, file ownership) is the
orchestrator's job to figure out from the prompt. Move the card and
move on.

## Next

The orchestrator is now keeping every workspace fed without your
involvement past triage and review. Reviewing a diff is one thing.
The next step is *running* the change: [set up review
workspaces](/docs/guides/getting-started/review-workspaces) to load a
finished branch onto a live dev server and click through it before you
accept. After that, [enable Zen Mode](/docs/guides/getting-started/enable-zen-mode)
to let the orchestrator auto-promote eligible backlog cards and
auto-merge finished branches that clear a project-defined confidence
bar.
