--plan shipped with dir= and command= as separate fields. That is right for a driver, which moves the pane itself and then types the command, and unusable for a person: the first operator to read a block could not act on it at all. Joining an absolute path from one field with a command from another is not a saved line of output. It is the step where a session gets started in the wrong repository - the same failure the absolute dir= exists to prevent, moved from the machine to the human. paste= is the two already joined, cd <dir> && <command>, one thing to select. Both forms stay: the two consumers want the same fact shaped differently, and neither shape serves both. Emitted only alongside command=, never once per block. A `paste=cd X && ` with nothing after it would run the cd and then a bare newline, which fails SILENTLY - the operator ends up in the right directory with no session started and no error to notice. board-selftest: 86 -> 89 checks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eKRzhD1RqY2MxvDEkrJAM
204 lines
11 KiB
Markdown
204 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.17.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" \
|
|
[--opus-xhigh-failed] \
|
|
[--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.
|
|
|
|
## The last-session record
|
|
|
|
Rows 5 and 6 (Fable) are history-dependent, so without a record they are dead
|
|
policy. 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.
|
|
|
|
Read the previous `route-last` line out of STATE.md before overwriting it.
|
|
Pass `--opus-xhigh-failed` **only** when it says an `Opus 5`/`xhigh` session ran
|
|
at this same next step and did not complete it. A session that ran out of
|
|
context, was interrupted, or stalled waiting on another repo is not a model
|
|
failure — and Fable runs without an advisor, so a wrong escalation here is
|
|
expensive. When it is ambiguous, leave the flag off.
|
|
|
|
## 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, `--advisor opus` included.** The calculator decides
|
|
the advisor per row, and it is not decoration: on a Sonnet row it is what lifts
|
|
the session to Opus judgement at Sonnet cost, which is what makes the cheaper
|
|
`fallback-command` safe to take under quota pressure. Dropping it because it
|
|
looks like noise silently removes that. Equally, never *add* it to a command
|
|
that came back without one — an unconditional advisor is the global
|
|
`advisorModel` setting, which costs quota in every session in every repo and is
|
|
the failure mode this rule replaces. `route.sh --help` carries the full rule.
|
|
|
|
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.
|
|
|
|
**`--advisor` is part of that comparison, not an afterthought.** It is a launch
|
|
flag, so `/clear` reuses the process and keeps whatever advisor the session
|
|
started with. If `command` carries `--advisor opus` and this session was not
|
|
launched with it, `/clear` is *not* enough — the operator needs `/exit` and the
|
|
full command, or the advisor silently never appears.
|
|
|
|
Do not paste the whole output block. One row, the rule that produced it, the
|
|
command.
|