feat(route)!: Fable rows are a hand-written override, never a route.sh outcome
The operator removed the "Fable can only be suggested after a failed Opus 5/xhigh session" policy on 2026-08-06 - it stood in the way too often. Until now that policy was implemented as code: --opus-xhigh-failed was the ONLY way route.sh's SELECTION chain could reach rows 5-6 (Fable), so the rubric still enforced a policy the operator had already dropped. Given a choice between (A) deleting the branches and the flag outright, so the calculator's output range closes at row 4, or (C) keeping a path that renders a Fable row on explicit instruction with rule=operator-override, the operator chose A (AskUserQuestion) - CLAUDE.md is explicit that the rubric stays the only deterministic lookup and a Fable choice is now always a deviation from it, recorded in STATE as an override rather than produced here. board.sh is untouched and still parses "Fable 5/high"/"Fable 5/xhigh" written by hand into a board line (route-selftest.sh section 6 now pins that half directly, since route.sh can no longer produce the strings itself). TDD: every affected selftest check was rewritten to fail against the unmodified route.sh first (72->68/69, confirmed red), then route.sh was edited to match. --opus-xhigh-failed is gone outright - passing it now exits 2 like any other unknown argument, not silently accepted as a no-op. route-selftest.sh: 73 -> 69 checks (three checks tested command shapes row_advisor() can no longer produce; the two row-5/row-6 reachability checks in section 1 collapsed into one "the flag is gone" check). coord 191, board 142 unaffected. Suite total 406 -> 402. Also folds in a standalone fix already pushed this session: section 14 was gating the wrong CLI fact (whether "claude --advisor fable" is rejected, which row_advisor() never depends on) instead of the one it actually rests on (whether opus/sonnet can advise a FABLE main model). Re-pointed and verified against the installed CC 2.1.226. Verified before committing: grepped every repo under ~/repos for a route line carrying --opus-xhigh-failed (none - one repo has it in prose only, not in its <!-- route: --> comment) and ran board.sh --plan/--brief over the real tree to confirm no repo's command line broke. Sent a follow-up coord-send to catalog superseding an earlier now-stale "406" stat-line correction with the current 402. skills/route/SKILL.md: usage block, "last-session record" framing, and the closing --opus-xhigh-failed paragraph rewritten to match. CLAUDE.md, README and CHANGELOG updated (checks 73->69, badge 406->402, new 0.21.0 entry). Version bumped 0.20.3 -> 0.21.0 across plugin.json, package.json and all three skill frontmatters (breaking CLI removal at 0.x -> minor, per v0.20.0 precedent). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ap1WKHCDPcSfpjo4ds2dQX
This commit is contained in:
parent
4821ba8d4b
commit
dd8f3ce042
10 changed files with 172 additions and 124 deletions
|
|
@ -8,17 +8,31 @@
|
|||
# and no writer, so its value was typed by hand every session and drifted into
|
||||
# several competing spellings. Cleaning the data could not fix that.
|
||||
# A writer with a CLOSED output range can: this script can only ever emit one
|
||||
# of six strings, so a seventh cannot enter circulation.
|
||||
# of four strings, so a fifth cannot enter circulation.
|
||||
#
|
||||
# THE ROW TABLE IS THE POLICY, and it is the operator's rubric verbatim -
|
||||
# moved here so there is one copy rather than one per repo:
|
||||
# moved here so there is one copy rather than one per repo. It has six rows;
|
||||
# this calculator only ever computes four of them:
|
||||
#
|
||||
# 1 Sonnet 5/high reading, summarizing, docs, mechanical refactor
|
||||
# 2 Sonnet 5/xhigh TDD cycle, known-root-cause bugfix, one-file change
|
||||
# 3 Opus 5/high multi-file feature, architecture choice, hard debugging
|
||||
# 4 Opus 5/xhigh long autonomous run, big refactor, cross-repo migration
|
||||
# 5 Fable 5/high Opus 5/xhigh has ACTUALLY failed on this same step
|
||||
# 6 Fable 5/xhigh open problem, no known solution path, longest horizon
|
||||
# 5 Fable 5/high deliberate choice for big-picture/review/planning work
|
||||
# 6 Fable 5/xhigh same, open-ended or longest horizon
|
||||
#
|
||||
# ROWS 5-6 ARE NEVER COMPUTED HERE. Until 2026-08-06 they fired only from an
|
||||
# explicit --opus-xhigh-failed flag; that policy is REMOVED (operator
|
||||
# decision), and nothing replaces it as a rubric outcome. A Fable choice is
|
||||
# now always a deliberate deviation from this calculator - 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 produced here.
|
||||
# Write "Fable 5/high" or "Fable 5/xhigh" into the board line by hand when
|
||||
# that is the right call; board.sh still parses both (route-selftest.sh
|
||||
# section 6 pins that half). The one fact worth carrying: a Fable session
|
||||
# runs without an advisor (row_advisor() below, and gated against the
|
||||
# installed claude by route-selftest.sh section 14) - informational, never a
|
||||
# gate on reaching the row, since there is no longer a gate to reach.
|
||||
#
|
||||
# Cheapest first, so the rubric's "always name one row cheaper as the quota
|
||||
# fallback" is row minus one, floored at row 1, correct by construction.
|
||||
|
|
@ -40,9 +54,8 @@
|
|||
# and a default would be indistinguishable from a real score when the log is
|
||||
# read back to find out whether the ROUTING was wrong or the SCORING was.
|
||||
#
|
||||
# SELECTION - first match wins, most expensive first:
|
||||
# row 6 history says Opus 5/xhigh failed here, and path=undetermined
|
||||
# row 5 history says Opus 5/xhigh failed here
|
||||
# SELECTION - first match wins, most expensive first. Rows 5-6 do not appear:
|
||||
# they are never a trait-derived outcome (see above).
|
||||
# row 4 reversibility=one-way OR scope=cross-cutting
|
||||
# row 3 path=partial|undetermined OR reversibility=costly OR scope=multi-file
|
||||
# row 2 verification=weak|none
|
||||
|
|
@ -66,8 +79,11 @@
|
|||
# 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.
|
||||
#
|
||||
# Rows 5-6 never reach this logic at all - the calculator cannot select them
|
||||
# (see above). Informational only: were the operator to hand-write a Fable
|
||||
# command, it would carry no advisor either way, since 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
|
||||
|
|
@ -95,18 +111,12 @@
|
|||
#
|
||||
# Usage:
|
||||
# route.sh --path <v> --verification <v> --reversibility <v> --scope <v>
|
||||
# --rationale <text> [--opus-xhigh-failed]
|
||||
# --rationale <text>
|
||||
#
|
||||
# route.sh ... --last-model <Sonnet 5|Opus 5|Fable 5>
|
||||
# --last-effort <low|medium|high|xhigh|max>
|
||||
# --last-completed <yes|no> --last-corrections <n>
|
||||
#
|
||||
# --opus-xhigh-failed is the ONLY way rows 5 and 6 can fire, and it means one
|
||||
# narrow thing: an Opus 5/xhigh session already ran at THIS SAME next step and
|
||||
# did not complete it. Not "a session failed" - context exhaustion, an operator
|
||||
# interrupt or a block on another repo are not this flag. It escalates to a
|
||||
# model that runs without an advisor, so when in doubt, leave it off.
|
||||
#
|
||||
# THE LAST-SESSION RECORD (the four --last-* fields, all or none) is what makes
|
||||
# any of this falsifiable. It records how the session that just ran actually
|
||||
# went, so the policy can later be judged against outcomes instead of against
|
||||
|
|
@ -114,9 +124,10 @@
|
|||
# high counts on row 1 mean the cheap row is too easy to reach, systematically
|
||||
# zero on row 4 means escalation fires too readily.
|
||||
#
|
||||
# All four fields are closed sets or numbers, and required together, because the
|
||||
# NEXT session reads the record back to decide --opus-xhigh-failed. Every one of
|
||||
# them must be MEASURED by the caller: --last-effort comes from CLAUDE_EFFORT,
|
||||
# All four fields are closed sets or numbers, and required together, so the
|
||||
# record reads back later as evidence rather than a guess - a partial record
|
||||
# would emit an empty value indistinguishable from a real measurement. Every
|
||||
# one of them must be MEASURED by the caller: --last-effort comes from CLAUDE_EFFORT,
|
||||
# which Claude Code exports into every tool-use context as the session's current
|
||||
# effort level. It is deliberately NOT defaulted from that variable here - a
|
||||
# calculator that reads its own environment stops being deterministic from its
|
||||
|
|
@ -126,18 +137,17 @@
|
|||
# off the startup command they typed. Omit the record rather than guess - a
|
||||
# guessed value reads back as a measurement.
|
||||
#
|
||||
# It is deliberately NOT wired to the Fable rows. Inferring "the model failed"
|
||||
# from "the session did not finish" would fire on context exhaustion and on
|
||||
# operator interrupts, which say nothing about the model. The record is
|
||||
# evidence; --opus-xhigh-failed is a judgement. Keeping them apart is why the
|
||||
# evidence stays worth reading.
|
||||
# It is pure telemetry and never changes what the calculator outputs.
|
||||
# Inferring "the model failed" from "the session did not finish" would fire on
|
||||
# context exhaustion and on operator interrupts, which say nothing about the
|
||||
# model - so the record stays descriptive, never a trigger.
|
||||
#
|
||||
# Exit 0 on a decision, 2 on any bad or missing argument. ASCII only,
|
||||
# bash 3.2 safe.
|
||||
set -u
|
||||
export LC_ALL=C
|
||||
|
||||
PATH_T=""; VERIF=""; REVERS=""; SCOPE=""; RATIONALE=""; RAT_SET=0; FAILED=0
|
||||
PATH_T=""; VERIF=""; REVERS=""; SCOPE=""; RATIONALE=""; RAT_SET=0
|
||||
L_MODEL=""; L_EFFORT=""; L_DONE=""; L_CORR=""; L_SET=0
|
||||
|
||||
die() { echo "route: $1" >&2; exit 2; }
|
||||
|
|
@ -152,7 +162,6 @@ while [ $# -gt 0 ]; do
|
|||
--reversibility) need "$@"; REVERS="$2"; shift 2 ;;
|
||||
--scope) need "$@"; SCOPE="$2"; shift 2 ;;
|
||||
--rationale) need "$@"; RATIONALE="$2"; RAT_SET=1; shift 2 ;;
|
||||
--opus-xhigh-failed) FAILED=1; shift ;;
|
||||
--last-model) need "$@"; L_MODEL="$2"; L_SET=1; shift 2 ;;
|
||||
--last-effort) need "$@"; L_EFFORT="$2"; L_SET=1; shift 2 ;;
|
||||
--last-completed) need "$@"; L_DONE="$2"; L_SET=1; shift 2 ;;
|
||||
|
|
@ -196,7 +205,7 @@ if [ "$L_SET" -eq 1 ]; then
|
|||
[ -n "$L_DONE" ] || die "--last-completed is required with a last-session record"
|
||||
[ -n "$L_CORR" ] || die "--last-corrections is required with a last-session record"
|
||||
# Model and effort are closed sets here, not free text. The next session READS
|
||||
# this record back to decide --opus-xhigh-failed, so a drifted spelling
|
||||
# this record back as evidence months from now, so a drifted spelling
|
||||
# ("opus 5" for "Opus 5") rebuilds the reader-versus-writer drift this whole
|
||||
# script exists to remove, one field over.
|
||||
case "$L_MODEL" in
|
||||
|
|
@ -217,11 +226,9 @@ if [ "$L_SET" -eq 1 ]; then
|
|||
fi
|
||||
|
||||
# --- Selection: first match wins, most expensive first ---------------------
|
||||
if [ "$FAILED" -eq 1 ] && [ "$PATH_T" = "undetermined" ]; then
|
||||
ROW=6; RULE="opus-xhigh-failed + path=undetermined"
|
||||
elif [ "$FAILED" -eq 1 ]; then
|
||||
ROW=5; RULE="opus-xhigh-failed"
|
||||
elif [ "$REVERS" = "one-way" ]; then
|
||||
# Rows 5-6 (Fable) never appear: they are a hand-written operator override,
|
||||
# never a trait-derived outcome (see the header note).
|
||||
if [ "$REVERS" = "one-way" ]; then
|
||||
ROW=4; RULE="reversibility=one-way"
|
||||
elif [ "$SCOPE" = "cross-cutting" ]; then
|
||||
ROW=4; RULE="scope=cross-cutting"
|
||||
|
|
@ -242,12 +249,12 @@ fi
|
|||
# goes in the command the operator pastes. Emitting both from one table is the
|
||||
# point - two hand-maintained spellings of one decision is how they disagree.
|
||||
# Aliases are gated against the installed claude by route-selftest.sh section
|
||||
# 11, never assumed here.
|
||||
# 11, never assumed here. Rows 5-6 have no entry: $ROW can never be 5 or 6
|
||||
# (see SELECTION above), so a case arm for them would be dead code.
|
||||
row_name() {
|
||||
case "$1" in
|
||||
1) echo "Sonnet 5/high" ;; 2) echo "Sonnet 5/xhigh" ;;
|
||||
3) echo "Opus 5/high" ;; 4) echo "Opus 5/xhigh" ;;
|
||||
5) echo "Fable 5/high" ;; 6) echo "Fable 5/xhigh" ;;
|
||||
esac
|
||||
}
|
||||
row_base_cmd() {
|
||||
|
|
@ -256,8 +263,6 @@ row_base_cmd() {
|
|||
2) echo "claude --model sonnet --effort xhigh" ;;
|
||||
3) echo "claude --model opus --effort high" ;;
|
||||
4) echo "claude --model opus --effort xhigh" ;;
|
||||
5) echo "claude --model fable --effort high" ;;
|
||||
6) echo "claude --model fable --effort xhigh" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
|
@ -272,14 +277,17 @@ row_base_cmd() {
|
|||
# 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.
|
||||
#
|
||||
# Rows 5-6 (Fable) never reach this function - $ROW can only be 1-4 (see
|
||||
# SELECTION above). Informational only: the only advisor this script ever
|
||||
# emits is opus (pinned by selftest 14's "the only advisor value ever emitted
|
||||
# is opus"), and opus is refused as under-capable for a fable main model -
|
||||
# measured against the installed claude, still true at CC 2.1.226 - so a
|
||||
# hand-written Fable command carries no advisor either way.
|
||||
#
|
||||
# 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:
|
||||
# rule never reaches rows 3-4. 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).
|
||||
#
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue