# Author a custom Shelbi workflow

Edit the orchestrator agent's instructions to encode routing rules, project-specific Zen judgment, and reporting style — the workflow is two markdown files you own.

The orchestrator is your runner (`claude`, `codex`, …) booted with a
system prompt that explains the board, the workspaces, and the loop. That
prompt is the **workflow**: the contract for who initiates what, how
dispatch is decided, and how the agent reports back. Shelbi ships a
sensible default and seeds it into every project as an editable file.

This page customizes the orchestrator's prompt, encodes three concrete
tweaks, and applies them. The underlying machinery lives in
[the orchestrator concept page](/docs/concepts/orchestrator#customizing-the-prompt-per-project);
this is the practical version.

## Where the prompt lives

The orchestrator is a [project-local agent with shipped defaults](/docs/concepts/agents).
Its prompt is composed from two files, both under the project's `agents/`
directory (`~/.shelbi/projects/<name>/agents/` in the default
[global mode](/docs/concepts/config-modes), or `<repo>/.shelbi/agents/`
in in-repo mode):

- **`agents/orchestrator/instructions.md`** — the orchestrator-specific
  system prompt. Seeded with the shipped default on first load; your
  edits stay put on upgrade.
- **`agents/_shared/preamble.md`** — project-wide context prepended to
  every agent (developer, qa, orchestrator…). Put the repo layout,
  house style, and test command here so you write it once instead of
  pasting it into four prompts.

On every `shelbi reload` (and at session start) Shelbi composes
`preamble.md + instructions.md` and writes the rendered output to
`~/.shelbi/projects/<name>/CLAUDE.md`,
which is state, so it lives under `~/.shelbi/` in both modes. The
orchestrator launches in that directory and auto-loads the rendered
prompt. No flags, no env vars, no MCP.

<Callout type="warning" title="Don't hand-edit CLAUDE.md">

`CLAUDE.md` is a rendered artifact. Put project-wide bits in
`_shared/preamble.md` and orchestrator-specific bits in
`agents/orchestrator/instructions.md`. Every `shelbi reload` overwrites
`CLAUDE.md`, so hand-edits are silently lost.

</Callout>

## Start from the default

Don't rewrite from a blank file. The default prompt encodes
load-bearing contracts (bootstrap, reaction rules, reason strings)
that the rest of Shelbi expects the orchestrator to honor. Open the
seeded default and edit in place:

```bash
shelbi agent edit orchestrator
```

That opens `agents/orchestrator/instructions.md` in `$EDITOR` (and
materializes the shipped default first if you haven't touched it
yet). The file is plain markdown. Read it top to bottom before changing
anything. The comments explain why each section exists.

## What to keep when you fork

A handful of pieces are the contract between the orchestrator and the
rest of the system. Edit them carefully, but don't delete:

- **The bootstrap flow.** The first reply of every session runs
  `shelbi task list`, `shelbi workspace list`, starts
  `shelbi events tail --follow`, and reads `shelbi zen status`.
  Drop any of these and the orchestrator boots without state.
- **The reaction rules tied to specific reason strings.** The
  orchestrator dispatches on `user:*` promotions, rolls workspaces on
  `workspace:ready-marker`, and wakes on `heartbeat` lines. The rest
  of Shelbi emits those strings. Your prompt has to recognize them.
- **The "you are the scheduler" framing.** Without it, the agent
  reverts to chatbot mode and waits to be told what to do. The
  whole loop falls apart.
- **The Zen-Mode section** (if you intend to use Zen). Replace the
  judgment categories and the merge conditions if they don't fit
  your project, but keep the section structure. The wrapper around
  `shelbi zen probe` reads the same primitives regardless of the
  prose around them.

Everything else is fair game.

## Three example tweaks

A workflow override pays for itself the first time the agent stops
asking a question you've answered the same way a dozen times. Three
patterns worth stealing.

### 1. Pin certain task shapes to specific workspaces

The default scheduler picks the first free workspace in YAML
declaration order, honoring `prefers_machine` hints. If your project
has natural routing (infra tasks always to a beefy box, UI tasks
always somewhere with the design system pre-cached), bake it into
the workflow:

```markdown
## Routing rules

In addition to the project YAML's `prefers_machine` hints, apply
these project-specific rules when picking a workspace for a `todo`
card:

- **Infra tasks** — titles containing `infra:`, `terraform`,
  `helm`, `kubectl`, or paths under `infra/`. Always route to
  `devbox` (the beefier remote). If no `devbox` workspace is free,
  leave the card in `todo` rather than routing to the hub.
- **Docs and content** — titles containing `docs:` or paths under
  `site/content/**`. Route to the first free hub workspace; these
  are short, IO-light tasks that don't need the remote.
- **Migration touch-ups** — anything matching `db/migrate/**` or
  `crates/shelbi-state/src/migrations/**` always goes to
  `charlie` (so the same agent context accumulates across the
  series). If charlie is busy, hold in `todo`.

These rules take precedence over default declaration-order
selection. The YAML's `prefers_machine` hint still overrides
everything if set explicitly on a card.
```

The orchestrator now applies your routing on top of the default
free-workspace scan. No code change.

### 2. Tune Zen's judgment categories for this project's reality

The bundled default's judgment categories are deliberately
conservative: promote if you've routinely accepted this shape, or
the user just raised the issue, or it's part of a larger body of
work they kicked off. That's a sensible starting bar, but every
project has its own "always safe" pattern. Encode it:

```markdown
## Zen Mode

### Auto-promote judgment categories

Promote a backlog candidate to `todo` only if **at least one** is
true:

1. **Tagged `automation:` in the title.** Always in scope —
   these are bot-filed PR-bumps and lint sweeps we have years of
   acceptance history on.
2. **Touches only `docs/**` or `site/content/**`.** Content
   edits are reversible and never reach prod. If the task body
   names a file outside those globs, fall through.
3. **The user has accepted ≥3 tasks of the same shape (same
   verb-prefix, same target directory) without changes in the
   last week.** Track this against the `done` column via
   `shelbi task list --column done`.

If none of the above, leave the card in `backlog`, emit
`reason=orchestrator:zen-decline reason-text=<short>`, and surface
it in the next reply ("I considered promoting `<task>` but wasn't
sure — want me to?").

### Merge conditions

(Inherit from the default — see the bundled template.)
```

The Rust side (`shelbi zen scan`) never inspects this prose. It
just emits the mechanically-eligible candidates. Your override
decides which of those actually get promoted.

### 3. Change the reporting style

The default prompt errs toward terse one-line status updates ("✓
dispatched to delta. branch: shelbi/fix-login"). If you'd rather
get a richer rundown (or a quieter one), say so:

```markdown
## Reporting style

When dispatching, reply with:

- The card id and chosen workspace (always).
- The full first line of the task body (so the user sees what
  you're about to make the workspace work on).
- An estimated complexity tag (`xs` / `s` / `m` / `l`) based on
  the body and any `depends_on`. Don't overthink it — a one-token
  guess is better than nothing.

When reporting a finished review:

- The card id, the workspace, and the branch.
- A two-bullet summary of what the diff changed (read it via
  `git diff main..<branch> --stat`).
- Whether a relevant `done`-column precedent exists for this
  shape of change.

Background sweeps (heartbeat-triggered re-checks) report nothing
unless they take action. Silence is fine when the board is quiet.
```

This is the easiest place to start customizing. The behavior the
orchestrator gives you is downstream of the prose you write here.
Ask for the report you actually want.

## Apply the edits

<Steps>
  <Step title="Edit the orchestrator's instructions">

  ```bash
  shelbi agent edit orchestrator
  ```

  Opens `agents/orchestrator/instructions.md` in `$EDITOR`.

  </Step>
  <Step title="Re-render and respawn the dashboard">

  ```bash
  shelbi reload
  ```

  Composes the prompt against your edits and respawns the panes.

  </Step>
  <Step title="Verify what landed">

  ```bash
  shelbi agent show orchestrator
  ```

  Prints `instructions.md` followed by the agent's `skills/`.

  </Step>
</Steps>

The composed prompt (preamble + instructions) is written to
`~/.shelbi/projects/<name>/CLAUDE.md` on every `shelbi reload`. Read
that file to see exactly what the runner loads.

`shelbi reload` respawns the sidebar, Tasks, and Review panes against the
new config without restarting workspaces or losing chat history. It does
recycle the orchestrator pane, so its conversation context resets: the
cost of changing the system prompt mid-session. Heads up before the next
turn.

## Iterate from prose, not code

The whole point of putting the workflow in markdown is that you can
keep tightening it. Two practical habits:

- **Treat surprises as prompt bugs.** When the orchestrator does
  something you didn't expect (promotes a card you'd have held,
  reports in a style you find noisy, picks the wrong workspace), open
  `agents/orchestrator/instructions.md` and add the rule that would
  have produced the outcome you wanted. Reload. Try again.
- **Version-control the prompt.** Check `agents/orchestrator/` and
  `agents/_shared/` into the repo (or a dotfiles repo) and symlink
  them into place. These prompts are the most opinionated artifacts
  in your Shelbi setup, and the ones most worth preserving across
  machine moves and reinstalls.

What you should **not** do is try to encode policy in code. The
mechanical layer (the Rust CLI) is intentionally generous.
`shelbi zen scan` will hand the orchestrator everything that's
mechanically safe, and your prompt picks which of those to actually
run. That separation is what lets you raise or lower the bar by
editing prose.

## See also

- [Workflow config](/docs/configuration/workflow) — the YAML field
  reference for the status pipeline the orchestrator runs work through.
- [Orchestrator](/docs/concepts/orchestrator) — how the prompt
  template is loaded, the bootstrap flow it triggers, and the
  reaction rules tied to specific event reason strings.
- [Zen Mode](/docs/concepts/zen-mode) — the high-confidence bar
  your custom judgment categories sit on top of.
- [The events log](/docs/concepts/events-log) — the full reason
  string vocabulary the orchestrator emits and your override may
  want to recognize or extend.
- [`shelbi zen`](/docs/cli/zen) — the single-purpose primitives the
  orchestrator sequences during auto-merge, available to call by
  hand any time.
