# Set up review workspaces

Tag a workspace, require that tag on your review status, and give the workflow a `review:` serve recipe — the end-to-end path from a `review` tag in your YAML to clicking through a change on a live server and accepting or rejecting it from the review interface.

The [multi-workspace loop](/docs/guides/getting-started/multi-workspace) gets
finished branches back to you. But reading a diff isn't the same as *running*
the change. Most real review of an app or a site means booting the dev server,
opening a URL, and clicking through it.

The *routing* that gets a task to a review slot is assembled from generic
primitives: a **workspace carries a tag** and a **status requires that tag**.
On top of that, the workflow's **`review:` block** tells the Review agent how
to boot the branch, and Shelbi's **review interface** lets you run the change
and accept or reject it. This page wires them together end to end. For the model
underneath, see the reference:
[Review workspaces](/docs/concepts/review-workspaces).

<Callout type="note" title="You need a working pool first">

This page assumes you already have a project with a workspace pool and can
promote a task through the board. The [first task](/docs/guides/getting-started/first-task)
and [multi-workspace](/docs/guides/getting-started/multi-workspace) pages get
you there. A review workspace is an ordinary slot with a tag, not a new kind of
thing.

</Callout>

## Set up and use the review flow

<Steps>
<Step title="Tag a workspace for review">

Add a `review` tag to a slot in your project YAML
(`~/.shelbi/projects/<name>.yaml` in [global mode](/docs/concepts/config-modes),
`<repo>/.shelbi/project.yaml` in-repo). Tag one workspace directly, or tag the
machine so every slot on it inherits the tag:

```yaml
workspaces:
  - { name: alpha,    machine: hub, runner: claude }
  - { name: bravo,    machine: hub, runner: claude }
  - { name: review-0, machine: hub, runner: claude, tags: [review], slot: 3000 }
```

`tags` accepts a bare string as shorthand (`tags: review`). The `slot: 3000`
sets the numeric slot the `review:` recipe below resolves `$PORT` to — so the
dev server binds `:3000`. Leave `slot:` out and it defaults to the slot's
zero-based index among the machine's workspaces, which is why an explicit,
port-shaped value is worth setting on a review slot.

<Callout type="tip" title="One review slot is plenty to start">

Each review slot holds one running server bound to its port, so give a second
review slot a different `slot:` value (`3010`) to avoid a collision. One is
enough for a first pass.

</Callout>

</Step>
<Step title="Require the tag and add a review: serve recipe">

In your [workflow file](/docs/configuration/workflow), require the `review` tag
on the review status so tasks route to the tagged slot, then add a
[`review:`](/docs/configuration/workflow#review) block telling the Review agent
how to boot the branch:

```yaml
statuses:
  - { id: in-progress, owner: agent, agent: developer }
  - { id: review,      owner: user,  agent: review, tags: [review] }
  - { id: done,        owner: user }

transitions:
  - from: review, to: done, actions: [merge, delete_branch]

review:
  setup: npm install --no-audit --no-fund
  serve: npm run dev -- -p $PORT
  ready: curl -sf http://localhost:$PORT
  url: http://localhost:$PORT
```

Shelbi resolves `$PORT` (equivalently `$SLOT`) to the review slot's port —
`3000` here — and injects the recipe into the Review agent's prompt. The agent
runs `setup`, launches `serve`, polls `ready` until the server answers, and
hands back the `url`. Swap the `npm` lines for whatever your project needs —
`cargo run`, a `Makefile` target, a `Procfile web:` line. For a monorepo, add
`workdir: site` (or `app`, `docs`) so each workflow serves its own
subdirectory on the slot's port.

The `review → done` transition is the accept edge: it merges the branch and
deletes it. Omit the `review:` block entirely and the Review agent falls back
to a **diff-only** review, booting nothing.

</Step>
<Step title="Reload to pick it up">

```bash
shelbi reload
```

The tagged slot does **not** show up under **Workspaces** — a review slot's
capacity surfaces through the review sections instead, and only once a task is
routed to it.

</Step>
<Step title="Get a task to the review status">

Run a task the way you normally would: promote a card and let a dev workspace
pick it up, or dispatch one through the orchestrator. When the dev workspace
finishes it writes its review-ready marker; the hub poller rebases the branch
onto the base branch and moves the task into the **Review** status.

Because that status requires `tags: [review]`, the orchestrator routes the task
onto a free `review`-tagged slot (preferring the one it ran on) and the Review
agent boots the server from the `review:` recipe. If every matching slot is
busy the task waits under **Queued for Review** until one frees — nothing is
preempted.

The finishing dev workspace closes its own session and returns to `idle`. Its
work is now waiting to be *run*, not edited.

</Step>
<Step title="Open the review interface and click through">

The task appears in the sidebar under **Ready for Review** (`✓`) with a
`machine:workspace` badge showing the slot it's loaded on. Select it (or press
`Enter` on its row) to open the **review interface**: a two-column layout with
the **review panel** on the left (its own navigation, with a back button at the
top that returns you to the dashboard) and the swappable review content on the
right.

From the review panel you can:

- **🤓 Chat with Reviewer** — talk to the Review agent (the default view).
- **🔀 View Diff** — open your system diff tool over the review branch's
  changes in the main pane. This uses git's configured diff tool
  (`diff.tool`, or `diff.guitool` for a GUI tool); if none is configured the
  panel shows a short error instead of launching.
- **✍️ Edit in `<editor>`** — open the review worktree in your
  [configured editor](/docs/configuration/global#configyaml).
- **🌐 Open Browser** — open the served `url` in your system browser. This
  action only appears when the workflow declares a review URL.

The built-in `review` agent's charter is narrow: **load and serve, don't
code.** It won't modify the branch or move the card. Point `agent:` at an agent
you've authored to change what runs there.

</Step>
<Step title="Approve or reject">

You own the decision, and you make it from the review panel's **Actions**:

- **✅ Approve** — moves the card one column forward (`review → done`), firing
  that edge's `actions`: per the workflow above, it merges the branch and
  deletes it. The interface tears down and focus returns to the dashboard.
- **❌ Reject** — opens a type-the-reason dialog. On submit, the reason is
  appended to the task body and the card bounces back to the ready status for
  another pass. An empty reason can't submit.

Either decision frees the review slot for the next task routed for review, if
any.

</Step>
</Steps>

## Next

Routing a task to a review workspace is the *human* path: the whole point is
human eyes on the running app. Keeping `merge` on the *accept* edge and off the
*enter*-review edge means the orchestrator holds a review task for you rather
than auto-merging it. When you're ready to hand the orchestrator the cases it
*can* judge on its own,
[enable Zen Mode](/docs/guides/getting-started/enable-zen-mode) to auto-promote
backlog and auto-merge finished branches that clear a confidence bar you define.

## See also

- [Review workspaces](/docs/concepts/review-workspaces) — the full model: tags,
  status-tag routing, the `review:` serve recipe, the review sidebar, and the
  review interface.
- [Workflow config](/docs/configuration/workflow) — status `tags`, the
  `review:` block, and the transition command fields.
- [Project config](/docs/configuration/project) — machine and workspace `tags`,
  and workspace `slot`.
- [Global config](/docs/configuration/global) — the hub-wide `editor` the
  "Edit in `<editor>`" view launches.
