feat(route): give the advisor a writer, on a need and per row

route.sh now emits `--advisor opus` into the startup command it prints.
The flag existed and worked, but nothing generated it, so it went unused:
the only mechanism that ever set an advisor here was `/advisor`, which
writes the global advisorModel setting -- every session, every repo -- and
was abandoned for burning quota. Nothing replaced it.

Two independent triggers, almost disjoint by construction:

  rows 1-2  always. Sonnet main model, so opus is a capability LIFT rather
            than a peer. Load-bearing: every fallback is one row cheaper and
            the cheap rows are Sonnet, so this makes the quota fallback safe.
  rows 3-4  only at reversibility=costly|one-way. Opus main model, so the
            advisor buys peer review where a mistake is not cheap to undo.
  rows 5-6  never. The CLI rejects every advisor for a Fable main model.

costly forces row 3 and one-way forces row 4, so a Sonnet row always has
reversibility=cheap and neither rule reaches the other's rows.
verification=none is deliberately not a third trigger: beyond the stakes
rule it adds only cheap-to-reverse mistakes, docs sessions among them.
Applied per ROW, so fallback-command carries its own correct answer.

route-selftest.sh 56 -> 73. Section 14 gates the three CLI facts the rule
rests on against the installed claude without spending a token: advisor
validation runs before the empty-prompt check, so `-p ""` reaches the
validator and stops. --help cannot gate this -- it short-circuits before
option validation, so an unknown flag would pass the gate untested.

Three pre-existing checks updated rather than worked around: two asserted
whole command strings that now carry the advisor, and section 11's effort
extraction swallowed the tail of the command line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8N8hQEJSWWtieWUx37txT
This commit is contained in:
Kjell Tore Guttormsen 2026-07-31 21:39:59 +02:00
commit 9cb405c2cd
10 changed files with 272 additions and 13 deletions

View file

@ -53,6 +53,30 @@
# overkill costs quota every session - but a wrong architecture decision in a
# published plugin costs more than either.
#
# THE ADVISOR is emitted into the command as '--advisor opus' - a second,
# stronger model consulted at key moments during the session. It is added on a
# NEED, never unconditionally: an always-on advisor is the global advisorModel
# setting, which burns quota on every session in every repo and is the thing
# this rule exists to replace. Two independent needs qualify:
#
# rows 1-2 ALWAYS. The main model is Sonnet, so opus is a capability LIFT
# rather than a peer - opus judgement at sonnet cost. This is what
# makes the FALLBACK safe to take: every fallback is one row
# cheaper, and the cheapest rows are the Sonnet ones.
# rows 3-4 only at reversibility=costly|one-way. The main model is already
# Opus, so the advisor buys peer review, worth paying for when a
# mistake is not cheap to undo.
# rows 5-6 NEVER, and not as a judgement call: the CLI rejects every
# advisor for a Fable main model.
#
# The two triggers barely overlap: costly forces row 3 and one-way forces row
# 4, so a Sonnet row always has reversibility=cheap. verification=none is
# deliberately not a third trigger - beyond the stakes rule it would only add
# mistakes that are cheap to reverse, docs sessions among them.
#
# Applied per ROW, so 'fallback-command' carries its own correct answer rather
# than the winning row's.
#
# WHERE IT DISAGREES WITH THE RUBRIC'S EXAMPLES. The rows are task-type labels;
# the traits are a different classification over the same six outcomes. They
# part company by one row on the two cheapest rows - documentation scores
@ -226,7 +250,7 @@ row_name() {
5) echo "Fable 5/high" ;; 6) echo "Fable 5/xhigh" ;;
esac
}
row_cmd() {
row_base_cmd() {
case "$1" in
1) echo "claude --model sonnet --effort high" ;;
2) echo "claude --model sonnet --effort xhigh" ;;
@ -237,6 +261,39 @@ row_cmd() {
esac
}
# THE ADVISOR is a second, stronger model consulted mid-task. It costs real
# tokens per session, so it fires on a NEED and nowhere else - an unconditional
# advisor is just the global advisorModel setting, which is the thing this
# replaces. Two independent needs qualify, and they are almost disjoint:
#
# rows 1-2 (Sonnet) ALWAYS. opus is a capability LIFT here, not a peer:
# opus judgement at sonnet cost. This half is what makes
# the fallback-command safe, since every fallback is one
# row cheaper and the cheapest rows are Sonnet.
# rows 3-4 (Opus) only at costly|one-way stakes, where the advisor is a
# peer review and being wrong is not cheap to undo.
# rows 5-6 (Fable) never. Not a judgement call: the CLI REJECTS every
# advisor for fable ("cannot be used as an advisor"), and
# opus is refused as under-capable for a fable main model.
# Gated against the installed claude by selftest 14.
#
# costly forces row 3 and one-way forces row 4, so a Sonnet row always has
# reversibility=cheap - the stakes rule can never reach rows 1-2, and the model
# rule never reaches rows 3-6. verification=none is deliberately NOT a trigger:
# beyond the stakes rule it would only add cheap-to-reverse mistakes, and it
# would put an advisor on every docs session (known/none/cheap/local).
#
# Applied per ROW rather than once, because the fallback is a real command the
# operator pastes under quota pressure and must carry its own correct answer.
row_advisor() {
case "$1" in
1|2) echo " --advisor opus" ;;
3|4) case "$2" in costly|one-way) echo " --advisor opus" ;; *) echo "" ;; esac ;;
*) echo "" ;;
esac
}
row_cmd() { printf '%s%s\n' "$(row_base_cmd "$1")" "$(row_advisor "$1" "$REVERS")"; }
FB=$((ROW - 1)); [ "$FB" -ge 1 ] || FB=1
# The rationale is free text from a session and lands inside an HTML comment on