Order 20260912T202210Z-7588027378-from-.claude, operator decision 2026-09-12 (helhetlig vurdering av arbeidssystemet, cut row 3 and the board.sh --row improvement row). One order, two parts, one version bump. THE ADVISOR RULE IS STRUCK. route.sh and board.sh --dispatch emit no --advisor at all. The rule fired per ROW on a need - always on the Sonnet rows (a capability lift, which is what made the quota fallback safe to take), and on the Opus rows at reversibility=costly|one-way - and it read well. It was killed by a MEASUREMENT, not by taste: of 54 dispatches the PM issued 08.-12.09, ZERO carried the flag, because sessions are started by hand from the model and effort rather than from the whole emitted line. A rule nothing honours is not a policy, and an emitted value nobody acts on is decoration in a field whose only job is to be evidence. The advisor is now what it already was in practice: an operator decision per session, said in one sentence in route.sh --help. The comments that rested on the rule were REWRITTEN, not left standing. board.sh --dispatch still refuses a --model/--effort pair, but the reason is no longer "the advisor is a property of the ROW": it is that the rubric has exactly one copy, and a dispatch taking the model directly would be a second, unscored way to reach the same decision - recording no traits, no rationale and no next-cost, so nothing afterwards could say whether the routing or the scoring was wrong. A comment defending a removed mechanism is how the next session restores it. Both skills carry the correction. Pinned as an ABSENCE over the whole trait space - 81 combinations, every line of output, with a known-positive control proving the sweep's grep can find a planted advisor - rather than on four sampled rows, because the claim is that no path emits it. board.sh --dispatch at reversibility=costly is pinned separately: that is the exact input a reintroduced rule would fire on. The literal string is absent from route.sh entirely, including the paragraph recording what was struck (it says "an opus advisor flag" in words), because a blunt grep cannot tell a description from a specification. Backward compatibility is pinned rather than assumed: a route line carrying a legacy advisor= field still parses and still yields a command - measured, 0 of 48 route lines in ~/repos carry one, but a reader that broke on an unknown field would turn last month's STATE.md into "that repo has no route line". The three CLI gates section 14 carried went with the rule; the suite no longer depends on the installed claude at all. board.sh --row <repo> IS THE SEVENTH RENDERING of the same scan, never a second scan, read-only like every other one. (The order calls it the sixth; by this file's own numbering --inbox-plan is the fourth and --dispatch the fifth. Corrected rather than carried wrong.) It exists because the columns WERE misread: on 11.09 the PM read FLY off the table by eye and got it wrong, while every other rendering a program consumes is already key=value. inn, ordre and fly are three separate fields because they are three separate facts; status is the bare token, never the table's blocked>target display, with blocked-on beside it; neste is last and uncut. An unknown repo exits 2 and writes NOTHING to stdout - an empty block would read as a repo whose every column is blank, which is a real and different state. upushet is the ONE field that is not a rendering of the scan, and it is named rather than blended in: nothing in the scan measures it, so it is read once, for the named repo only, and never enters the table, the plan or the briefing. It reads the remote-TRACKING ref, not the remote, so upushet=N honestly means "the local ref says N"; a repo with no upstream reports ?, never 0. The row fixture's three counts are three DIFFERENT integers (3/2/1), and that is the finding worth recording. Built first with 2/1/1, it was mutation-tested by making fly read the ORDRE field - the exact 11.09 misreading - and the check stayed GREEN, because the two fields held the same digit. A fixture that cannot tell two columns apart is the defect wearing a passing test, inside the section written to prevent it. Suites under /bin/bash 3.2, before -> after: coord 257 -> 257, board 393 -> 427, route 73 -> 73 (13 advisor checks and 3 CLI gates out, 15 absence/legacy checks in, and it no longer varies with claude being on PATH), orders 116 -> 116, state-line-guard 54 -> 54. Sum 893 -> 927, README badge updated to the measured sum. npm test 12/12, fail 0. Verified live against the real tree, not only fixtures: --row repo-mailbox reports fly=1 beside ordre=0 (the distinction that was misread), --row on the nested key from-ai-to-chitta/content-sadhguru resolves, and an unknown repo exits 2. No tag, no push, no catalog change - that is the operator's release-plugin.mjs run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 KiB
| name | description | version |
|---|---|---|
| route | 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. | 0.34.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.
--rationaleis 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:
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-costvalue, plus thefallbackone row cheaper for quota pressure. Name therulethat fired — "path=partial" — so the call is auditable rather than asserted. - Oppstartskommando: the
commandstring, in its own code block, with/exitnamed. Never prefix it withcd: 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 acd.
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.