Skip to main content
By default, PM uses a generic heuristic to assign a lane when you add an epic — routing by estimated effort, scope, and change type. /pm:lane-routing lets you define per-repo rules on top of that heuristic: keyword or glob patterns that are checked first, before the generic logic runs. When a pattern matches, the lane it specifies wins. Most repos never need this, but it becomes invaluable when your project has standing conventions the generic heuristic can’t know about.

Managing routing rules

Use set-lane-routing with --add, --remove, or --clear to manage the overrides list:
Each rule uses the format "<match>:<lane>":
  • <match> is either a plain, case-insensitive substring (hotfix matches “urgent hotfix for prod”) or a *-glob (billing-* matches “billing-refund-flow”).
  • <lane> must be one of openspec, superpowers, claude-code, decision, or external.
  • Adding a rule with the same <match> as an existing rule replaces the earlier rule.
  • --clear and --remove are applied before --add when combined in a single invocation.
  • Rules are checked in the order they were added; the first match wins.
Re-running set-lane-routing merges — only what you pass changes. At least one operation is required (0.44.0). A bare set-lane-routing used to write an empty laneRouting: {overrides: []} block where none existed and report success. It now exits 1 before reading or writing anything:
There is no read form; .conductor/state.json holds the overrides, and suggest-lane applies them. Quote the text you pass to suggest-lane. It reads ONE argument. Unquoted, suggest-lane fix a typo used to route fix alone; it is now refused before anything runs:
A quoted text that begins with -- but is not shaped like a flag (it contains a space, say) is still text. --help anywhere after either verb prints its help and writes nothing.

Looking up a lane suggestion

Before assigning a lane to a new epic, your agent should call suggest-lane with the epic title. If a rule matches, it returns the lane and the matched pattern; if nothing matches, it returns null and the generic heuristic applies as normal.

--ask=<text> — a text shaped like a flag (0.45.0)

A positional cannot carry a text that is itself flag-shaped: suggest-lane '--limit=5 ignored' is refused as an unknown flag, quoted or not, because the engine classifies the token rather than its position. --ask= is the same lookup for exactly that case — and it has to stay attached to its value:
The positional form is unchanged, and giving both together is refused. This is why the emitted tracker recipes route with suggest-lane --ask=<issue-title>, the title filled as one single-quoted word: an item title is third-party text, and a title like --help must register rather than act.

The suggestion is an input, not a verdict

New in 0.36.0. suggest-lane answers a mechanical question — does any configured pattern match this text — and the lane a change actually belongs in depends on things the engine cannot see: what the change touches, what it is for, whether it is on a milestone. That layer does not exist, so the emitted instructions now say the judgment half is absent rather than letting a pattern match read as a decision.
The tie-break is deliberately asymmetric. The lightest lane leaves no spec, no plan, no gate verdict and no stories behind. So the two errors are not equal: routing too heavily costs hours, and routing too lightly costs the record, permanently.An unresolved routing question therefore resolves away from the lightest lane, never into it. When you overrule the suggestion, record why on the epic — that note is the only thing that tells a later reader the lane was chosen rather than defaulted.

Use cases

Routing rules shine wherever your project has conventions that are obvious to your team but opaque to a heuristic:
  • "billing:openspec" — anything touching billing always goes through spec-driven review, regardless of estimated size. A two-hour billing change still needs the spec gate.
  • "hotfix-*:claude-code" — hotfixes skip spec and go straight to the claude-code lane. Speed matters more than formality here.
  • "infra*:decision" — infrastructure changes are recorded as architectural decisions, not built through a code-delivery lane.
Lane routing rules are stored in state.json under laneRouting[] and are re-applied by the CLAUDE.md rules block every session. You do not need to re-set them after a context compaction — the rules are durable from the moment you add them.