Version in all seven places (plugin.json, package.json, README badge, four SKILL.md) and CHANGELOG [0.35.0] dated 2026-09-18. 0.35.0 is the PLAN column - a display-only filesystem reading showing where a repo keeps no destination, or one nobody touches - plus the --dispatch, --inbox-plan and no-STATE.md record fixes found while building it, and the selftest section that kills all 16 review mutants. Suites on the staged tree: coord 257, board 478, route 73, orders 116, guard 54 (badge 978); npm test 12/12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
210 lines
11 KiB
Markdown
210 lines
11 KiB
Markdown
---
|
||
name: route
|
||
description: >-
|
||
Decide which model and reasoning effort the NEXT session should run with, by
|
||
scoring four traits of the next task and running them through the rubric row
|
||
table (`route.sh`). Use at session end, whenever STATE.md's next step is
|
||
written or rewritten, and whenever the operator asks what to launch the next
|
||
session with: "what model should I use next", "which effort level", "close
|
||
the session", "wrap up", "update STATE", "what should the next session run
|
||
with", "is opus overkill here", "give me the startup command". Also triggers
|
||
on Norwegian phrasings: "hvilken modell neste økt", "hvilken effort", "avslutt
|
||
sesjonen", "oppdater STATE", "hva skal neste økt kjøre med", "er opus
|
||
overkill", "gi meg oppstartskommandoen", "modell og effort". Trigger even when
|
||
the operator names no model and no tool — choosing the model for the next
|
||
session IS this skill. Not for choosing which REPO gets the next session:
|
||
that is the `board` skill.
|
||
version: "0.35.0"
|
||
---
|
||
|
||
# route — what the next session should run with
|
||
|
||
The operator's closing line requires two things every single session: the model
|
||
and effort for the next session, and a pasteable startup command. This skill
|
||
produces both from evidence instead of from a hunch, and leaves the scoring
|
||
behind in STATE.md so a wrong call can be found later.
|
||
|
||
**The division of labour is the whole design.** Scoring the four traits is
|
||
judgement and belongs to you. Turning scores into a model is a lookup, and
|
||
`route.sh` does it deterministically — same scores, same answer, every time,
|
||
at zero token cost. Never "reason your way" to a model. If you find yourself
|
||
weighing whether the task feels hard enough for Opus, you have skipped the
|
||
scoring step and are doing the thing this skill replaces.
|
||
|
||
## The engine
|
||
|
||
ROUTE="${CLAUDE_PLUGIN_ROOT}/scripts/route.sh"
|
||
|
||
"$ROUTE" --path <known|partial|undetermined> \
|
||
--verification <strong|weak|none> \
|
||
--reversibility <cheap|costly|one-way> \
|
||
--scope <local|multi-file|cross-cutting> \
|
||
--rationale "why these four scores" \
|
||
[--last-model <name> --last-effort <level> \
|
||
--last-completed <yes|no> --last-corrections <n>]
|
||
|
||
There is no deployed copy anywhere else and no fallback path — one entry point
|
||
is deliberate. Exit 2 means a bad or missing argument; read stderr and fix the
|
||
call rather than dropping the flag. `route.sh --help` carries the full row table
|
||
and the reasoning behind it.
|
||
|
||
The script writes nothing. It prints `row`, `rule`, `next-cost`, `command`,
|
||
`fallback`, `fallback-command`, `route-line` and (when the record is given)
|
||
`route-last`. **You** paste those into STATE.md.
|
||
|
||
## Scoring the four traits
|
||
|
||
Score the task the NEXT session will do — the one in the `👉 NESTE` block —
|
||
not the one that just finished. Read the next step as written before scoring;
|
||
if you cannot score it from what is written, that is a finding (see below).
|
||
|
||
| Trait | Score it by asking |
|
||
|---|---|
|
||
| `path` | Is the solution route already described? `known` = the steps are written down or the pattern exists in this repo. `partial` = the shape is clear, one real decision is open. `undetermined` = it has to be found first. |
|
||
| `verification` | Will a machine catch the error? `strong` = tests, types or a compiler fail on it. `weak` = a smoke test or manual run would probably surface it. `none` = prose, API shape, a security judgement — a wrong answer just sits there. |
|
||
| `reversibility` | `cheap` = a commit away. `costly` = touches published state, needs a migration or a follow-up release. `one-way` = a pushed tag, a public interface, a deletion. |
|
||
| `scope` | `local` = one file or one function. `multi-file` = several files, one repo. `cross-cutting` = many subsystems, or more than one repo. |
|
||
|
||
`verification` carries the most signal and is the trait most often skipped.
|
||
Strong verification means a cheap model's mistakes get caught and corrected —
|
||
cheap model plus tight feedback beats an expensive model without it. When
|
||
nothing verifies the output, model quality is the only defence left.
|
||
|
||
Three rules that keep the scoring honest:
|
||
|
||
- **Score the task, never the feeling.** "Hard", "complex" and "important" are
|
||
not traits here. They are unfalsifiable, and they always resolve upward.
|
||
- **`--rationale` is required and is the point.** It is where a misscore is
|
||
caught weeks later, when the recommendation turns out to have been wrong. One
|
||
sentence naming the evidence: "the pattern exists in handlers/, but the error
|
||
handling is undecided" — not "medium difficulty".
|
||
- **When torn between two scores, take the more expensive one.** Escalation is
|
||
asymmetric by design: any one trait escalates, and row 1 needs all four at
|
||
the cheap end.
|
||
|
||
## The specification check you get for free
|
||
|
||
If the next step is scored `path=undetermined` and no design phase is planned,
|
||
**the task description is underspecified — the model is not too small.** Say so,
|
||
and rewrite the next step until it can be scored. Upgrading the model to
|
||
compensate for a vague specification is the most expensive form of
|
||
procrastination available, and it hides the real defect.
|
||
|
||
This check is worth more than the tokens the routing saves. Do not skip it by
|
||
scoring `partial` to keep things moving.
|
||
|
||
## Fable rows are never this calculator's output
|
||
|
||
`route.sh` only ever emits rows 1-4. Rows 5-6 (Fable) fired from an explicit
|
||
`--opus-xhigh-failed` flag until 2026-08-06, when the operator removed that
|
||
policy; nothing replaced it as a trait-derived outcome. Choosing Fable is now
|
||
always a deliberate deviation from the rubric — CLAUDE.md is explicit that the
|
||
rubric stays the only deterministic lookup and a departure from it is recorded
|
||
in STATE as an **override**, never as something this skill produces. If Fable
|
||
is the right call for the next step's *form* (big-picture, review, planning),
|
||
write the board line and the `rule` by hand — `board.sh` still parses
|
||
"Fable 5/high" and "Fable 5/xhigh" — and say so plainly in the rationale rather
|
||
than scoring the four traits to land there.
|
||
|
||
## The last-session record
|
||
|
||
Write it whenever all four fields are actually known, from what happened in
|
||
the session that is ending — never from what STATE.md prescribed:
|
||
|
||
- `--last-model` / `--last-effort` — what this session actually ran with. Both
|
||
are closed sets (`Sonnet 5|Opus 5|Fable 5`, and the verified effort levels),
|
||
because the next session compares these values rather than just displaying
|
||
them.
|
||
- `--last-completed yes|no` — did this session finish the next step the previous
|
||
STATE.md set out? Answer about that step, not about the session in general.
|
||
- `--last-corrections <n>` — how many rounds of rework it took. This is the
|
||
cheap proxy for whether the routing was right.
|
||
|
||
**Measure the effort, never infer it.** Read what this session actually
|
||
resolved:
|
||
|
||
```bash
|
||
echo "$CLAUDE_EFFORT"
|
||
```
|
||
|
||
`CLAUDE_EFFORT` is Claude Code's own *current* effort level, exported into every
|
||
tool-use context — which is why a Bash call can read it. Pass it verbatim as
|
||
`--last-effort`. The model is not in the environment (there is no
|
||
`CLAUDE_MODEL`); take it from what this session knows itself to be running as.
|
||
|
||
Two sources are **wrong on purpose**, and both fail the same way. The previous
|
||
board line holds what was *prescribed*, not what was *run* — the two come apart
|
||
exactly when the record would be most interesting. Asking the operator launders
|
||
that same prescription through a human, who is reading it off the startup
|
||
command they typed rather than off the running process. Confirming a measured
|
||
value with them is fine; sourcing it from them is not.
|
||
|
||
So the record no longer waits on anyone: all four fields are knowable from
|
||
inside the session that is ending. Still omit it entirely — all four or none —
|
||
if any one of them is genuinely unknown. A guessed value is worse than a missing
|
||
one, because it reads back later as a measurement.
|
||
|
||
**This file must never declare an `effort:` frontmatter field.** Skill
|
||
frontmatter overrides the session effort while the skill is active, so the
|
||
reading above would report *this skill's* effort instead of the session's — a
|
||
measurement measuring itself, with nothing in the output to show it happened.
|
||
Pinned by `route-selftest.sh` section 13.
|
||
|
||
One honest limit: `CLAUDE_EFFORT` is the *current* level, so if the operator
|
||
changed it mid-session with `/effort`, "the effort this session ran with" is not
|
||
a single value. Record the level the work was actually done at and say so.
|
||
|
||
## Writing it into STATE.md
|
||
|
||
Three single-line HTML comments sit directly under the `👉 NESTE` heading, in
|
||
this order:
|
||
|
||
<!-- board: status=in-progress; blocked-on=-; next-cost=Opus 5/high -->
|
||
<!-- route: path=partial; verification=strong; ...; rationale=... -->
|
||
<!-- route-last: model=Opus 5; effort=xhigh; completed=yes; corrections=1 -->
|
||
|
||
Splice the emitted `next-cost` value into the existing board line — leave
|
||
`status` and `blocked-on` alone, they answer a different question and this skill
|
||
knows nothing about them.
|
||
|
||
**They must stay single-line comments.** `board.sh` reads the first line under
|
||
the heading that is not blank, not a heading, and does not *start* with `<!--`,
|
||
and shows it as that repo's next step across every repo. A YAML block or a
|
||
comment broken across lines therefore replaces the repo's next step on the board
|
||
with `next_task:`. Measured, not assumed — `route-selftest.sh` section 7 pins it.
|
||
|
||
## Reporting it
|
||
|
||
Give the operator the two closing-line items and nothing more:
|
||
|
||
- **Modell neste økt:** the `next-cost` value, plus the `fallback` one row
|
||
cheaper for quota pressure. Name the `rule` that fired — "path=partial" — so
|
||
the call is auditable rather than asserted.
|
||
- **Oppstartskommando:** the `command` string, in its own code block, with
|
||
`/exit` named. **Never prefix it with `cd`:** one repo per terminal tab, so
|
||
the working directory is already right. If the next step belongs in a
|
||
different repo, say so in plain words — that is a different tab, not a `cd`.
|
||
|
||
**Paste `command` verbatim.** It is the row's whole spelling: model and effort,
|
||
and nothing else. Do not add flags the calculator did not emit.
|
||
|
||
**The advisor is not this skill's to decide.** Until 2026-09-12 the rubric
|
||
appended an opus advisor on some rows; that rule is struck, on a measurement —
|
||
of 54 dispatches issued 08.–12.09 not one carried it, because sessions are
|
||
started by hand from the model and effort. The advisor is now an operator
|
||
decision per session. Never add `--advisor` to an emitted command on your own
|
||
initiative; if the operator asks for one, that is their call and it belongs in
|
||
the handover as what it is — an operator choice, not a rubric outcome.
|
||
|
||
If `command` and `fallback-command` are the same as the current session's model,
|
||
say `/clear` is enough instead — but only if no newly installed plugin or skill
|
||
needs a fresh process to be picked up.
|
||
|
||
**An advisor the operator asked for is part of that comparison.** It is a
|
||
launch flag, so `/clear` reuses the process and keeps whatever advisor the
|
||
session started with — a session that is to gain or lose one needs `/exit` and
|
||
a full command, never `/clear`. The rubric never puts it there; the operator
|
||
does.
|
||
|
||
Do not paste the whole output block. One row, the rule that produced it, the
|
||
command.
|