# route

## route

`brigade route "<task>"` shows the route Brigade would take for a task without running anything. Before a `brigade run` dispatches workers, a deterministic engine reads the task, derives a set of signals, and composes the stages those signals require into dependency-ordered parallel waves. `brigade route` prints that composition so you can see what the run will cover before you spend any tokens.

The derivation is a pure function. `route_catalog.derive_signals` maps the task text (and, at run time, the git-changed paths) to signals, and a pure-function router (`brigade/router.py`, algorithm adapted from alp-river, MIT) composes the required stages into waves with `while`/`until` holds. Same task in, same route out, no model call.

```bash
brigade route "add oauth login to the dashboard"
brigade route "rewrite the QUICKSTART" --json
brigade route "make the change" --changed-path src/auth/session.py
brigade route "ship the release" --approve-ship
brigade route "review the migration" --template
```

`brigade route "<task>"` prints four things:

- **Signals** derived from the task.
- **Stages**, each shown with the signal that pulled it in.
- **Waves**: the stages grouped into dependency-ordered parallel waves. Stages in the same wave have no dependency between them and can run at once. A later wave depends on an earlier one.
- **Held** stages: stages behind a `while`/`until` hold that will not route until the hold is released.

Flags:

- `--changed-path <path>` seeds the route with a git-changed path (segment-matched) so you can preview how touched files steer the route.
- `--template` and `--route-template <hint>` supply the optional route-template hint the run also reads.
- `--approve-ship` releases a held ship stage in the preview, the same release `brigade run --approve-ship` performs.
- `--json` emits the full structure: `signals`, `approvals`, `route`, `waves`, and `held`, plus `triggered_by` (the live signal that pulled each stage in) and `overrides`.

## Signals

The engine derives up to seven signals from a task. Three are **path** signals that decide the shape of the route, and the rest are **lens** signals that pull review or verification stages onto that path:

| Signal | What it marks |
|---|---|
| auth surface | the task touches authentication, so it earns tests and a security review |
| UI | the task touches a `panel`, `dashboard`, `sidebar`, `widget`, `view`, or `tooltip`, so it pulls the UI review lenses |
| migration | a migration, which earns tests the way an auth change does |
| bug | a defect fix |
| docs | a prose or documentation change |
| system | a system-level change |
| ship request | the task asks to ship or release, which stages a ship step behind a hold |

A forced signal still pulls its dependents. Forcing `+auth-surface` also pulls in the tests and the security review that depend on it, so you get the full stage set, not the bare stage alone.

## Steering the route from run

`brigade run` derives the same route by default and carries it into the plan prompt. When the heuristic is wrong on a single task, `--route-signal` is the middle ground short of turning routing off:

```bash
brigade run "harden the token check" --route-signal +auth-surface
brigade run "note the release in the changelog" --route-signal ~ship-requested
```

- `--route-signal +<signal>` force-adds a derived signal, and `~<signal>` suppresses one.
- `--route-signal` refuses to add or suppress a **path** signal (`code`, `docs`, or `system`). The path is a derive-time decision, and stripping it collapses the route to a pathless remnant. Use it for lens and ship signals only.

Other run-time route controls:

- `brigade run --no-route` opts out of routing entirely for that run.
- `brigade run --approve-ship` releases a held ship stage so it routes.
- `brigade run --wait[=SECONDS]` waits for an active per-target run lock with a finite timeout. Without the flag, a lock conflict stays fail-fast.

## How the route reaches the plan

The derived route is carried into the plan prompt, and the plan tags each worker assignment with a `covers` tag naming the stage it handles. A plan that misses a required stage gets one corrective retry. If a gap remains, it is recorded in `plan-attempts.json` rather than failing the run. A `covers` tag naming a stage that is not in the route is surfaced as `unknown_covers` instead of being silently ignored.

The route lands in `run.json` telemetry, so a completed run records the signals, stages, waves, and holds it was planned against alongside the rest of the run metadata.

See [run, dogfood](/docs/commands/run-dogfood/) for the surrounding run flags, artifacts, and run locking.