feat(route,board): strike the advisor rule, add board.sh --row <repo>
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>
This commit is contained in:
parent
671e275a97
commit
5e5bc4a66e
13 changed files with 689 additions and 271 deletions
|
|
@ -244,8 +244,8 @@ for n in 1 2 3; do echo "msg" > "$CLAUDE_COORD_DIR/repo-a/inbox/2026-msg$n-from-
|
|||
echo "old" > "$CLAUDE_COORD_DIR/repo-a/archive/2026-old-from-x.md"
|
||||
|
||||
# repo-owes: unhandled inbox AND a route line, so the briefing can derive the
|
||||
# EXACT startup command - advisor flag included - from the repo's own four
|
||||
# traits instead of guessing from next-cost alone. Its NESTE runs deliberately
|
||||
# EXACT startup command from the repo's own four traits instead of guessing
|
||||
# from next-cost alone. Its NESTE runs deliberately
|
||||
# past the table's 38-character column: carrying that line whole is the reason
|
||||
# the briefing exists at all.
|
||||
mkrepo "$ROOT/repo-owes"
|
||||
|
|
@ -581,10 +581,15 @@ printf '%s' "$BRIEF" | grep -q 'check-versions'
|
|||
check "brief prints the full NESTE line, not the 38-char table excerpt" $?
|
||||
|
||||
# Derived by CALLING route.sh with the repo's own four traits - deliberately
|
||||
# not spelled out in board.sh - so the rubric keeps exactly one copy. Row 1
|
||||
# carries the advisor, which is what makes the quota fallback safe to take.
|
||||
printf '%s' "$BRIEF" | grep -q 'claude --model sonnet --effort high --advisor opus'
|
||||
# not spelled out in board.sh - so the rubric keeps exactly one copy.
|
||||
printf '%s' "$BRIEF" | grep -q 'claude --model sonnet --effort high'
|
||||
check "brief derives the exact startup command from the repo's route line" $?
|
||||
# Anchored to end-of-line, because the loose grep above would pass just as
|
||||
# happily on a command that grew a flag back. The rubric emits no advisor
|
||||
# since 2026-09-12 and the briefing is a rendering of the rubric, not a
|
||||
# second policy.
|
||||
printf '%s' "$BRIEF" | grep -q 'claude --model sonnet --effort high$'
|
||||
check "brief's startup command ends at the effort - no advisor is appended" $?
|
||||
|
||||
# The command must start its OWN line. `fold` copies its input's missing
|
||||
# trailing newline, which ran the command onto the tail of the NESTE prose and
|
||||
|
|
@ -900,8 +905,8 @@ n_b="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-b$' | cut -d: -f1)"
|
|||
check "uavklart repos rank below every repo that declared a status" $?
|
||||
|
||||
# The command comes from route.sh, same single copy of the rubric the briefing
|
||||
# uses - advisor flag included, since that is a property of the ROW.
|
||||
printf '%s' "$PLAN" | grep -q '^command=claude --model sonnet --effort high --advisor opus$'
|
||||
# uses.
|
||||
printf '%s' "$PLAN" | grep -q '^command=claude --model sonnet --effort high$'
|
||||
check "plan derives the exact startup command from the repo's route line" $?
|
||||
|
||||
# Both no-command causes must degrade to a marker. A bare `command=` would be
|
||||
|
|
@ -932,7 +937,7 @@ check "plan prints the full NESTE line, not the 38-char table excerpt" $?
|
|||
# because they answer different questions: a driver cd's the pane itself and
|
||||
# would choke on a compound line, while the operator needs ONE thing to copy.
|
||||
# Assembling it by hand from two fields is where a tab lands in the wrong repo.
|
||||
printf '%s' "$PLAN" | grep -q "^paste=cd $ROOT/repo-owes && claude --model sonnet --effort high --advisor opus\$"
|
||||
printf '%s' "$PLAN" | grep -q "^paste=cd $ROOT/repo-owes && claude --model sonnet --effort high\$"
|
||||
check "paste= is the whole line: cd into the repo, then the startup command" $?
|
||||
|
||||
printf '%s\n' "$PLAN" | grep -A7 '^repo=repo-typo$' | grep -q '^paste='; [ $? -ne 0 ]
|
||||
|
|
@ -1530,7 +1535,7 @@ check "ip-repo-no-state reports 1 owed (no reply-expected: no declared)" $?
|
|||
# class=repo still derives the exact startup command from the repo's own
|
||||
# route line - same single copy of the rubric every other rendering uses.
|
||||
printf '%s\n' "$IPLAN" | grep -A8 '^repo=ip-repo-done-fyi$' \
|
||||
| grep -q '^command=claude --model sonnet --effort high --advisor opus$'
|
||||
| grep -q '^command=claude --model sonnet --effort high$'
|
||||
check "class=repo block derives its command from the repo's own route line" $?
|
||||
|
||||
# class=no-state and class=orphan-mailbox can never fabricate a command - ask
|
||||
|
|
@ -1766,8 +1771,8 @@ d6="$("$BOARD" --roots "$ROOT" --dispatch --repo no-such-repo --prompt-file "$DS
|
|||
[ "$rc" -eq 2 ]; check "dispatch: refuses a repo the scan does not know" $?
|
||||
|
||||
# Invalid traits must refuse, never degrade to a command without them: a
|
||||
# command missing --advisor reads exactly like a row that legitimately has no
|
||||
# advisor.
|
||||
# command built on three scored traits and one silently dropped one reads
|
||||
# exactly like a fully scored decision.
|
||||
d7="$("$BOARD" --roots "$ROOT" --dispatch --repo repo-a --prompt-file "$DSP" --target-pane no \
|
||||
--path bogus --verification strong --reversibility cheap --scope local --rationale t 2>&1)"; rc=$?
|
||||
[ "$rc" -eq 2 ]; check "dispatch: refuses trait values route.sh rejects, rather than emitting a partial command" $?
|
||||
|
|
@ -1789,18 +1794,27 @@ printf '%s\n' "$d8" | grep -q '^paste=cd '; check "dispatch: plan block carries
|
|||
printf '%s\n' "$d8" | grep -q "command=.*\"\$(cat $DSP)\""; check "dispatch: the command carries the prompt in argv, never bare model+effort" $?
|
||||
printf '%s\n' "$d8" | grep -q "^paste=cd $ROOT/repo-a && claude .*\"\$(cat $DSP)\""; check "dispatch: paste= is the complete one-string form, prompt included" $?
|
||||
|
||||
# The cost comes from route.sh's row table and nowhere else, so --advisor is a
|
||||
# property of the ROW. These two differ ONLY in reversibility: both land on
|
||||
# row 3 (Opus 5/high), and only the costly one carries the advisor. A dispatch
|
||||
# that took --model/--effort directly would have no source for that flag at
|
||||
# all, and forwarding it blindly is how a session ends up looking peer-reviewed
|
||||
# without being.
|
||||
# The cost comes from route.sh's row table and nowhere else. These two differ
|
||||
# ONLY in reversibility, and both land on row 3 (Opus 5/high) - so since the
|
||||
# advisor rule was struck (2026-09-12) they must produce the IDENTICAL command.
|
||||
# costly is the case that used to add --advisor opus, which makes it the exact
|
||||
# input a reintroduced rule would fire on: this is the order's named red test,
|
||||
# not a sampled one.
|
||||
d9="$("$BOARD" --roots "$ROOT" --dispatch --repo repo-a --prompt-file "$DSP" --target-pane no \
|
||||
--path known --verification strong --reversibility cheap --scope multi-file --rationale t 2>/dev/null)"
|
||||
printf '%s\n' "$d9" | grep -q '^command=claude --model opus --effort high "'; check "dispatch: model+effort come from the route row (row 3, no advisor at cheap)" $?
|
||||
printf '%s\n' "$d9" | grep -q '^command=claude --model opus --effort high "'; check "dispatch: model+effort come from the route row (row 3)" $?
|
||||
d10="$("$BOARD" --roots "$ROOT" --dispatch --repo repo-a --prompt-file "$DSP" --target-pane no \
|
||||
--path known --verification strong --reversibility costly --scope local --rationale t 2>/dev/null)"
|
||||
printf '%s\n' "$d10" | grep -q '^command=claude --model opus --effort high --advisor opus "'; check "dispatch: the advisor flag follows the ROW (costly -> row 3 with advisor)" $?
|
||||
printf '%s\n' "$d10" | grep -q '^command=claude --model opus --effort high "'; check "dispatch: reversibility=costly emits NO advisor (rule struck 2026-09-12)" $?
|
||||
if printf '%s\n' "$d10" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "dispatch: no advisor anywhere in the costly dispatch block" "$rc"
|
||||
if printf '%s\n' "$d10 claude --advisor opus" | grep -q -- '--advisor'; then rc=0; else rc=1; fi
|
||||
check "control: that grep does find a planted advisor" "$rc"
|
||||
# The paste line is assembled separately from command=, so it is pinned
|
||||
# separately: a flag reintroduced in only one of the two is the shape that
|
||||
# sends the operator and the driver down different paths.
|
||||
if printf '%s\n' "$d10" | grep '^paste=' | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "dispatch: the paste line carries no advisor either" "$rc"
|
||||
printf '%s\n' "$d8" | grep -q '^next-cost=Sonnet 5/high$'; check "dispatch: the row's next-cost is reported alongside the command" $?
|
||||
|
||||
# --no-go stops the Go MESSAGE, not the work: the startup command - prompt in
|
||||
|
|
@ -2133,7 +2147,7 @@ mkrepo "$RG_ROOT/repo-rationale-overrides"
|
|||
} > "$RG_ROOT/repo-rationale-overrides/STATE.md"
|
||||
|
||||
RG_PLAN="$("$BOARD" --roots "$RG_ROOT" --plan 2>/dev/null)"
|
||||
RG_CMD='^command=claude --model sonnet --effort high --advisor opus$'
|
||||
RG_CMD='^command=claude --model sonnet --effort high$'
|
||||
|
||||
printf '%s\n' "$RG_PLAN" | grep -A6 '^repo=repo-greedy-rationale$' | grep -q "$RG_CMD"
|
||||
check "route line whose rationale names all four traits still yields a command" $?
|
||||
|
|
@ -3252,6 +3266,187 @@ check "denominator: a missing scan root adds nothing to the counts" $?
|
|||
|
||||
/bin/rm -rf "$DEN_ROOT" "$DEN_COORD" 2>/dev/null
|
||||
|
||||
|
||||
# --- 33. --row <repo>: one machine-readable line per column -----------------
|
||||
# Order 20260912T202210Z-7588027378 (.claude, operator decision 2026-09-12).
|
||||
# The motivation is a measured misreading, not a convenience: on 11.09 the PM
|
||||
# read the FLY column off the table BY EYE and got it wrong. Every other
|
||||
# rendering that a program consumes emits `key=value` for exactly that reason -
|
||||
# the table's fixed-width columns are for a human, and a 34-character repo key
|
||||
# already shifts a row two characters right (a stated, unclosed gap). A repo
|
||||
# holding a value nobody can grep is a repo whose state is read by counting
|
||||
# spaces.
|
||||
#
|
||||
# It is a RENDERING of the scan every other view already ran, never a second
|
||||
# scan - the same rule --brief has carried since it shipped. Two numbers under
|
||||
# one name, computed twice, is the defect this file names most often.
|
||||
#
|
||||
# ONE DEPARTURE, stated rather than smuggled: `upushet=` is not a column the
|
||||
# scan computes, and it is measured here, once, for the named repo only. It is
|
||||
# in the order's field list, it is exactly the kind of fact that gets misread
|
||||
# ("nothing unpushed" vs "not measured"), and measuring it for one repo in a
|
||||
# one-repo rendering is not a scan. It never enters the table, the plan or the
|
||||
# briefing.
|
||||
ROW_ROOT="$(mktemp -d)"
|
||||
ROW_COORD="$(mktemp -d)"
|
||||
|
||||
mkrepo "$ROW_ROOT/row-repo"
|
||||
{
|
||||
echo "# STATE - row-repo"
|
||||
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
||||
echo "<!-- board: status=blocked; blocked-on=other-repo; next-cost=Opus 5/high -->"
|
||||
echo "<!-- route: path=known; verification=strong; reversibility=cheap; scope=local; rationale=x -->"
|
||||
echo "The whole next step, well past the 38 characters the table column cuts at."
|
||||
} > "$ROW_ROOT/row-repo/STATE.md"
|
||||
|
||||
# Mailbox fixture: 3 pending messages, 2 pending orders, 1 claimed order. The
|
||||
# three counts are deliberately three DIFFERENT integers, and that is the whole
|
||||
# design of this fixture rather than an arbitrary choice. Built first with
|
||||
# 2/1/1, it was mutation-tested by making `fly` read the ORDRE field - the
|
||||
# exact 2026-09-11 misreading this rendering exists to close - 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, in the
|
||||
# section written to prevent it. With 3/2/1 that mutation turns it red.
|
||||
mkdir -p "$ROW_COORD/row-repo/inbox" "$ROW_COORD/row-repo/orders/claimed"
|
||||
echo "m" > "$ROW_COORD/row-repo/inbox/20260901T101010Z-1-from-x.md"
|
||||
echo "m" > "$ROW_COORD/row-repo/inbox/20260901T101011Z-2-from-x.md"
|
||||
echo "m" > "$ROW_COORD/row-repo/inbox/20260901T101014Z-5-from-x.md"
|
||||
echo "o" > "$ROW_COORD/row-repo/orders/20260901T101012Z-3-from-x.md"
|
||||
echo "o" > "$ROW_COORD/row-repo/orders/20260901T101015Z-6-from-x.md"
|
||||
echo "o" > "$ROW_COORD/row-repo/orders/claimed/20260901T101013Z-4-from-x.md"
|
||||
|
||||
ROW_OUT="$(CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row row-repo 2>/dev/null)"; rc=$?
|
||||
[ "$rc" -eq 0 ]; check "row: a known repo exits 0" $?
|
||||
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^repo=row-repo$'; check "row: repo= is the board key" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q "^dir=$ROW_ROOT/row-repo\$"; check "row: dir= is the checkout path" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^status=blocked$'; check "row: status= is the bare token, not the blocked>target display" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^blocked-on=other-repo$';check "row: blocked-on= is its own field" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^next-cost=Opus 5/high$';check "row: next-cost= survives the space and the slash" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^inn=3$'; check "row: inn= counts pending messages" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^ordre=2$'; check "row: ordre= counts pending orders" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^fly=1$'; check "row: fly= counts claimed orders" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^siste='; check "row: siste= reports the last-commit reading" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^drt='; check "row: drt= reports the working-tree reading" $?
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^upushet='; check "row: upushet= is present" $?
|
||||
|
||||
# The three counts are the whole point: FLY was misread as ORDRE, so a rendering
|
||||
# that emitted the same digit under two names would reproduce the defect it
|
||||
# exists to close.
|
||||
[ "$(printf '%s\n' "$ROW_OUT" | grep -c '^\(inn\|ordre\|fly\)=')" -eq 3 ]
|
||||
check "row: inn, ordre and fly are three separate fields" $?
|
||||
|
||||
# NESTE uncut, same argument --brief has: the 38-char cut is the TABLE column's
|
||||
# property, and cutting here would make the cut string the only copy.
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^neste=.*38 characters the table column cuts at'
|
||||
check "row: neste= carries the whole line, not the table excerpt" $?
|
||||
|
||||
# `neste` is free prose and may contain anything the STATE.md author wrote, so
|
||||
# it is LAST - the same rule the RECORDS line itself obeys. A consumer reading
|
||||
# field N from the end depends on it.
|
||||
[ "$(printf '%s\n' "$ROW_OUT" | tail -1 | cut -d= -f1)" = "neste" ]
|
||||
check "row: neste= is the last line" $?
|
||||
|
||||
# One line per field, every line a field: a value that wrapped would be read as
|
||||
# a field name by anything grepping ^name=.
|
||||
ROW_BAD="$(printf '%s\n' "$ROW_OUT" | grep -v '^[a-z][a-z-]*=' || true)"
|
||||
[ -z "$ROW_BAD" ]; check "row: every line is a field=value line:${ROW_BAD:- clean}" $?
|
||||
|
||||
# ASCII, because the row is consumed by awk/sed/grep under bash 3.2 - the same
|
||||
# constraint every other emitted block in this file carries. Asserted on a
|
||||
# fixture whose own prose is ASCII, so this measures the renderer, not the
|
||||
# STATE.md it happened to read.
|
||||
if printf '%s' "$ROW_OUT" | LC_ALL=C grep -q '[^ -~]'; then rc=1; else rc=0; fi
|
||||
check "row: the output is ASCII" "$rc"
|
||||
if printf '%s' "$ROW_OUT" | LC_ALL=C grep -q '[^ -~]x'; then rc=1; else rc=0; fi
|
||||
check "control: that ASCII grep runs at all (no match on a clean string)" "$rc"
|
||||
|
||||
# A repo whose NESTE is NOT ASCII still emits one line per field. The renderer
|
||||
# cannot make the operator's Norwegian prose ASCII and must never try - the
|
||||
# field NAMES are the machine surface, the value is whatever was written.
|
||||
mkrepo "$ROW_ROOT/row-utf8"
|
||||
{
|
||||
echo "# STATE - row-utf8"
|
||||
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
||||
echo "<!-- board: status=planned; blocked-on=-; next-cost=Sonnet 5/high -->"
|
||||
printf 'M\xc3\x85LT i dag: ingenting gjenst\xc3\xa5r.\n'
|
||||
} > "$ROW_ROOT/row-utf8/STATE.md"
|
||||
ROW_U="$(CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row row-utf8 2>/dev/null)"
|
||||
ROW_UBAD="$(printf '%s\n' "$ROW_U" | grep -v '^[a-z][a-z-]*=' || true)"
|
||||
[ -z "$ROW_UBAD" ]; check "row: non-ASCII NESTE still yields one field per line" $?
|
||||
printf '%s\n' "$ROW_U" | grep -q '^neste=.*ingenting'
|
||||
check "row: non-ASCII NESTE is carried through, never mangled or dropped" $?
|
||||
|
||||
# An unknown repo must REFUSE. Emitting an empty block would be the same defect
|
||||
# as a bare `command=`: a consumer reading ^status= would get nothing back and
|
||||
# read it as a repo with no status, which is a real and different state ("?").
|
||||
ROW_ERR="$(CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row no-such-repo 2>&1)"; rc=$?
|
||||
[ "$rc" -eq 2 ]; check "row: an unknown repo exits 2" $?
|
||||
printf '%s\n' "$ROW_ERR" | grep -q 'no-such-repo'
|
||||
check "row: the refusal names the repo it could not find" $?
|
||||
ROW_ERR_OUT="$(CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row no-such-repo 2>/dev/null)"
|
||||
[ -z "$ROW_ERR_OUT" ]; check "row: a refusal writes nothing at all to stdout" $?
|
||||
|
||||
# A missing value is a usage error, not a silently empty row.
|
||||
CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row >/dev/null 2>&1
|
||||
[ $? -eq 2 ]; check "row: --row with no value exits 2" $?
|
||||
|
||||
# Read-only, asserted rather than assumed: this is the one property every
|
||||
# rendering in this file shares and the one a new rendering is most likely to
|
||||
# break.
|
||||
ROW_BEFORE="$(find "$ROW_ROOT" "$ROW_COORD" -type f | sort | while read -r f; do printf '%s %s\n' "$f" "$(stat -f %m "$f")"; done)"
|
||||
CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row row-repo >/dev/null 2>&1
|
||||
ROW_AFTER="$(find "$ROW_ROOT" "$ROW_COORD" -type f | sort | while read -r f; do printf '%s %s\n' "$f" "$(stat -f %m "$f")"; done)"
|
||||
[ "$ROW_BEFORE" = "$ROW_AFTER" ]; check "row: writes nothing - no file added, removed or touched" $?
|
||||
|
||||
# upushet: the three states must stay distinguishable. This fixture has no
|
||||
# upstream at all, so the honest answer is "not measured", never 0 - the same
|
||||
# rule DRT's ? and coord-count's exit 3 carry. A 0 here would say "nothing is
|
||||
# waiting to be pushed" about a repo that has never had a remote.
|
||||
printf '%s\n' "$ROW_OUT" | grep -q '^upushet=?$'
|
||||
check "row: a repo with no upstream reports upushet=?, never 0" $?
|
||||
|
||||
# Known-positive control, and the case the field exists for: a real upstream
|
||||
# with a real unpushed commit must report the count. Without this the ? above
|
||||
# would pass on a field that can only ever say ?.
|
||||
ROW_UP="$ROW_ROOT/row-upstream"
|
||||
mkrepo "$ROW_UP"
|
||||
{
|
||||
echo "# STATE - row-upstream"
|
||||
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
||||
echo "<!-- board: status=planned; blocked-on=-; next-cost=Sonnet 5/high -->"
|
||||
echo "Next step."
|
||||
} > "$ROW_UP/STATE.md"
|
||||
git -C "$ROW_UP" add -A >/dev/null 2>&1
|
||||
git -C "$ROW_UP" commit -qm "state" >/dev/null 2>&1
|
||||
ROW_BARE="$(mktemp -d)/origin.git"
|
||||
git init -q --bare "$ROW_BARE" >/dev/null 2>&1
|
||||
git -C "$ROW_UP" remote add origin "$ROW_BARE" >/dev/null 2>&1
|
||||
git -C "$ROW_UP" push -q -u origin HEAD >/dev/null 2>&1
|
||||
ROW_U0="$(CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row row-upstream 2>/dev/null)"
|
||||
printf '%s\n' "$ROW_U0" | grep -q '^upushet=0$'
|
||||
check "row: control - an upstream with everything pushed reports upushet=0" $?
|
||||
echo "more" > "$ROW_UP/extra.txt"
|
||||
git -C "$ROW_UP" add -A >/dev/null 2>&1
|
||||
git -C "$ROW_UP" commit -qm "unpushed work" >/dev/null 2>&1
|
||||
ROW_U1="$(CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" --row row-upstream 2>/dev/null)"
|
||||
printf '%s\n' "$ROW_U1" | grep -q '^upushet=1$'
|
||||
check "row: one unpushed commit reports upushet=1" $?
|
||||
|
||||
# The table is UNCHANGED by all of this. --row is a rendering, so a field that
|
||||
# only exists there must not leak into the view a human reads.
|
||||
ROW_TABLE="$(CLAUDE_COORD_DIR="$ROW_COORD" "$BOARD" --roots "$ROW_ROOT" 2>/dev/null)"
|
||||
if printf '%s' "$ROW_TABLE" | grep -qi 'upushet'; then rc=1; else rc=0; fi
|
||||
check "row: upushet does not leak into the table" "$rc"
|
||||
|
||||
# Structural, in the shape this file already uses for "no write path exists":
|
||||
# --row must not run its own repo discovery. A second scan is the defect the
|
||||
# rendering rule exists to prevent, and a behavioural test cannot see it.
|
||||
grep -q -- '--row' "$BOARD"; check "row: board.sh actually carries the --row flag" $?
|
||||
|
||||
/bin/rm -rf "$ROW_ROOT" "$ROW_COORD" 2>/dev/null
|
||||
|
||||
|
||||
echo ""
|
||||
echo "board-selftest: $PASS passed, $FAIL failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
|
|
|
|||
179
scripts/board.sh
179
scripts/board.sh
|
|
@ -80,8 +80,9 @@
|
|||
# the exact command to start a session there. The 38-char cut is the table
|
||||
# column's property, not the record's, so the briefing prints NESTE uncut. Each
|
||||
# command is derived by CALLING route.sh with that repo's own four traits -
|
||||
# next-cost alone cannot yield it, since the advisor flag is a property of the
|
||||
# ROW. A repo with no route line is told so rather than handed a guess.
|
||||
# next-cost alone cannot yield it, since it names the row rather than spelling
|
||||
# the command, and there must be exactly one spelling in circulation. A repo
|
||||
# with no route line is told so rather than handed a guess.
|
||||
#
|
||||
# --brief is still read-only: it writes nothing. The file write lives in
|
||||
# brief-nightly.sh, which renders to a temp file and renames it into place, and
|
||||
|
|
@ -200,14 +201,24 @@
|
|||
# --path P --verification V --reversibility R --scope S
|
||||
# --rationale "why"
|
||||
#
|
||||
# Model, effort and --advisor come from route.sh's row table, never from a
|
||||
# --model/--effort pair: the advisor flag is a property of the ROW, and a
|
||||
# dispatch that took the model directly would have no honest source for it.
|
||||
# Model and effort come from route.sh's row table, never from a --model/--effort
|
||||
# pair: the rubric has exactly one copy, and a dispatch that took the model
|
||||
# directly would be a second, unscored way to reach the same decision.
|
||||
# --target-pane is a MEASUREMENT (morning --probe-panes) and has no default -
|
||||
# see the dispatch() comment for what happens when it is guessed.
|
||||
#
|
||||
# --row <repo> is a SEVENTH rendering: the table's row for ONE repo, as
|
||||
# `field=value` lines a program can grep instead of columns a human counts
|
||||
# spaces in. It exists because the columns WERE misread - FLY read as ORDRE on
|
||||
# 2026-09-11 - and because every other rendering a program consumes is already
|
||||
# key=value. It takes no position: every field is a value the scan already
|
||||
# holds, under the name its column carries, with `neste` last and uncut.
|
||||
# One exception, stated rather than blended in: `upushet` is not a column, and
|
||||
# is measured once for the named repo only. See row_one() for what it can and
|
||||
# cannot claim.
|
||||
#
|
||||
# Usage: board.sh [--roots <dir>[,<dir>...]] [--plain]
|
||||
# [--brief|--plan|--inbox-plan|--dispatch|--voyage ...]
|
||||
# [--brief|--plan|--inbox-plan|--dispatch|--voyage|--row <repo>]
|
||||
# [--focus "<prose>"]
|
||||
# Env: CLAUDE_COORD_DIR overrides the mailbox root.
|
||||
# BOARD_ROOTS overrides the default scan roots.
|
||||
|
|
@ -224,6 +235,7 @@ INBOX_PLAN=0
|
|||
FOCUS=""
|
||||
DISPATCH=0
|
||||
VOYAGE=0
|
||||
ROW=0; ROW_REPO=""
|
||||
D_REPO=""; D_PROMPT=""; D_PANE=""; D_ORDER=""
|
||||
D_PATH=""; D_VER=""; D_REV=""; D_SCOPE=""; D_RAT=""
|
||||
|
||||
|
|
@ -238,10 +250,16 @@ while [ $# -gt 0 ]; do
|
|||
# bash 3.2: `shift 2` past the end of $# is a no-op -> would loop forever.
|
||||
--roots) [ $# -ge 2 ] || { echo "board: --roots requires a value" >&2; exit 2; }
|
||||
ROOTS="$2"; shift 2 ;;
|
||||
# Four renderings of one scan, so exactly one may be selected: last wins.
|
||||
--brief) BRIEF=1; PLAN=0; INBOX_PLAN=0; VOYAGE=0; shift ;;
|
||||
--plan) PLAN=1; BRIEF=0; INBOX_PLAN=0; VOYAGE=0; shift ;;
|
||||
--inbox-plan) INBOX_PLAN=1; BRIEF=0; PLAN=0; VOYAGE=0; shift ;;
|
||||
# Renderings of one scan, so exactly one may be selected: last wins.
|
||||
--brief) BRIEF=1; PLAN=0; INBOX_PLAN=0; VOYAGE=0; ROW=0; shift ;;
|
||||
--plan) PLAN=1; BRIEF=0; INBOX_PLAN=0; VOYAGE=0; ROW=0; shift ;;
|
||||
--inbox-plan) INBOX_PLAN=1; BRIEF=0; PLAN=0; VOYAGE=0; ROW=0; shift ;;
|
||||
# One repo, one field=value line per column. Same `shift 2` guard as every
|
||||
# other value flag, for the same bash 3.2 reason - and a MISSING value is a
|
||||
# usage error rather than an empty row, since an empty row reads exactly
|
||||
# like a repo whose every column is blank.
|
||||
--row) [ $# -ge 2 ] || { echo "board: --row requires a repo name" >&2; exit 2; }
|
||||
ROW=1; ROW_REPO="$2"; BRIEF=0; PLAN=0; INBOX_PLAN=0; VOYAGE=0; DISPATCH=0; shift 2 ;;
|
||||
# Raw operator prose, forwarded verbatim by the driver: it does not
|
||||
# tokenize, match or normalize, so every bit of that work is here. Same
|
||||
# `shift 2` guard as --roots, for the same bash 3.2 reason.
|
||||
|
|
@ -249,8 +267,8 @@ while [ $# -gt 0 ]; do
|
|||
FOCUS="$2"; shift 2 ;;
|
||||
# Dispatch rendering: one repo, one prompt file, one startup command. Same
|
||||
# `shift 2` guard as every other value flag, for the same bash 3.2 reason.
|
||||
--dispatch) DISPATCH=1; BRIEF=0; PLAN=0; INBOX_PLAN=0; VOYAGE=0; shift ;;
|
||||
--voyage) VOYAGE=1; BRIEF=0; PLAN=0; INBOX_PLAN=0; DISPATCH=0; shift ;;
|
||||
--dispatch) DISPATCH=1; BRIEF=0; PLAN=0; INBOX_PLAN=0; VOYAGE=0; ROW=0; shift ;;
|
||||
--voyage) VOYAGE=1; BRIEF=0; PLAN=0; INBOX_PLAN=0; DISPATCH=0; ROW=0; shift ;;
|
||||
--repo) [ $# -ge 2 ] || { echo "board: --repo requires a value" >&2; exit 2; }
|
||||
D_REPO="$2"; shift 2 ;;
|
||||
--prompt-file) [ $# -ge 2 ] || { echo "board: --prompt-file requires a value" >&2; exit 2; }
|
||||
|
|
@ -1079,9 +1097,9 @@ rows() {
|
|||
|
||||
# --- Briefing rendering (--brief) ------------------------------------------
|
||||
# The startup command for one repo, derived from that repo's OWN route line by
|
||||
# calling route.sh. Deriving it from next-cost instead would not work even in
|
||||
# principle: the advisor flag is a property of the ROW, and two rows can share
|
||||
# a model/effort pair while differing on it. A repo with no route line gets its
|
||||
# calling route.sh. Deriving it from next-cost instead would mean a second
|
||||
# writer for the same command string, spelled from a rubric NAME rather than
|
||||
# from the row table itself. A repo with no route line gets its
|
||||
# next-cost printed and is told where the command comes from - a guessed
|
||||
# command would read as authoritative while being a guess, which is worse than
|
||||
# no command at all.
|
||||
|
|
@ -1303,13 +1321,18 @@ fi
|
|||
# brief-nightly.sh already owns the briefing's write.
|
||||
#
|
||||
# THE COST COMES FROM route.sh's ROW TABLE, never from a --model/--effort pair
|
||||
# handed in. That is not tidiness: `--advisor opus` is a property of the ROW,
|
||||
# two rows share a model/effort pair while differing on it, and the CLI accepts
|
||||
# a wrong advisor silently (it drops it for a Fable main model without failing).
|
||||
# A dispatch that took the model directly would have no source for that flag,
|
||||
# and both available guesses are wrong in the same direction - a session that
|
||||
# looks peer-reviewed without being. Scoring the four traits is the caller's
|
||||
# judgement; turning them into a command is this lookup.
|
||||
# handed in. That is not tidiness: the rubric is a policy with exactly one copy,
|
||||
# and a dispatch accepting the model directly would be a second way to reach the
|
||||
# same decision - one that records no traits, no rationale and no next-cost, so
|
||||
# nothing afterwards can say whether the ROUTING was wrong or the SCORING was.
|
||||
# Scoring the four traits is the caller's judgement; turning them into a command
|
||||
# is this lookup.
|
||||
#
|
||||
# Until 2026-09-12 this paragraph rested on `--advisor opus` instead: the flag
|
||||
# was a property of the ROW, so a model/effort pair could not yield it. That
|
||||
# rule is struck (see route.sh), and the argument is rewritten rather than
|
||||
# quietly left standing on a premise the code no longer has - a comment
|
||||
# defending a mechanism that was removed is how the next session "restores" it.
|
||||
dispatch() {
|
||||
# --target-pane is REQUIRED and has no default, the same rule route.sh's
|
||||
# --last-effort carries: it is a MEASUREMENT of the world (does the target
|
||||
|
|
@ -1387,8 +1410,8 @@ dispatch() {
|
|||
[ -d "$d_dir" ] || { echo "board: the directory for $D_REPO does not exist: $d_dir" >&2; exit 2; }
|
||||
|
||||
# Rejected traits REFUSE. Degrading to a command without them is the worst
|
||||
# available outcome: a command missing --advisor is indistinguishable from a
|
||||
# row that legitimately has none.
|
||||
# available outcome: a command built on three scored traits and one silently
|
||||
# dropped one is indistinguishable from a fully scored decision.
|
||||
d_route="$(bash "$ROUTE" --path "$D_PATH" --verification "$D_VER" \
|
||||
--reversibility "$D_REV" --scope "$D_SCOPE" --rationale "$D_RAT" 2>&1)" || {
|
||||
echo "board: route.sh rejected the traits, so there is no command to give:" >&2
|
||||
|
|
@ -2097,6 +2120,114 @@ if [ "$VOYAGE" -eq 1 ]; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
# --- Row rendering (--row <repo>) -------------------------------------------
|
||||
# A SEVENTH rendering of the same scan, never a second scan, and read-only like
|
||||
# every other one. (Order 20260912T202210Z-7588027378 calls it the sixth; by
|
||||
# this file's own numbering --inbox-plan is the fourth and --dispatch the fifth,
|
||||
# so it is the seventh. The count is corrected here rather than carried wrong,
|
||||
# for the same reason every other number in this file is measured.)
|
||||
#
|
||||
# WHAT IT ANSWERS: the table's row for ONE repo, as `field=value` lines a
|
||||
# program can grep. The table is fixed-width and is for a human; reading a
|
||||
# column off it by eye is how FLY was misread as ORDRE on 2026-09-11, and a
|
||||
# 34-character repo key already shifts a row two characters right - a stated,
|
||||
# unclosed gap that makes counting spaces strictly worse than it looks. Every
|
||||
# other rendering a program consumes (--plan, --inbox-plan, --dispatch) is
|
||||
# key=value for exactly this reason; the default view was the one left out.
|
||||
#
|
||||
# It takes NO position and adds NO judgement: every field is a value $RECORDS
|
||||
# already holds, printed under the name its column carries. The three mailbox
|
||||
# counts are three separate fields because they are three separate facts - INN
|
||||
# is "others wait on you", ORDRE is "work waits on this repo", FLY is "someone
|
||||
# took an order" - and this rendering exists because they were confused.
|
||||
#
|
||||
# ONE FIELD IS NOT A RENDERING, and it is named rather than blended in:
|
||||
# `upushet` is not a column the scan computes. It is measured here, once, for
|
||||
# the named repo only, because the order asks for it and because "nothing is
|
||||
# waiting to be pushed" and "nobody looked" are exactly the pair this repo
|
||||
# refuses to render alike. One reading for one repo in a one-repo view is not a
|
||||
# scan; it never enters the table, the plan or the briefing.
|
||||
#
|
||||
# It reads the remote-TRACKING ref, not the remote. That is a real bound, not a
|
||||
# hedge: a stale ref under-reports after someone else pushed, and over-reports
|
||||
# after a push this checkout has not fetched. The honest reading of `upushet=N`
|
||||
# is "the local ref says N", and `git ls-remote` is still what a claim ABOUT
|
||||
# THE WORLD costs.
|
||||
row_unpushed() {
|
||||
ru_up="$(git -C "$1" rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null)" || return 1
|
||||
[ -n "$ru_up" ] || return 1
|
||||
ru_n="$(git -C "$1" rev-list --count "$ru_up..HEAD" 2>/dev/null)" || return 1
|
||||
case "$ru_n" in ''|*[!0-9]*) return 1 ;; esac
|
||||
printf '%s' "$ru_n"
|
||||
}
|
||||
|
||||
row_one() {
|
||||
r_rec="$(awk -F'|' -v n="$ROW_REPO" '$3==n {print; exit}' "$RECORDS")"
|
||||
# REFUSE rather than emit an empty block. A consumer reading ^status= off an
|
||||
# empty output gets nothing back and reads it as a repo with no board line -
|
||||
# which is a real, different state this very rendering spells as `?`.
|
||||
if [ -z "$r_rec" ]; then
|
||||
echo "board: no repo named '$ROW_REPO' in the scanned roots" >&2
|
||||
return 2
|
||||
fi
|
||||
r_disp="$(printf '%s' "$r_rec" | cut -d'|' -f4)"
|
||||
r_cost="$(printf '%s' "$r_rec" | cut -d'|' -f5)"
|
||||
r_inn="$(printf '%s' "$r_rec" | cut -d'|' -f6)"
|
||||
r_drt="$(printf '%s' "$r_rec" | cut -d'|' -f7)"
|
||||
r_alder="$(printf '%s' "$r_rec" | cut -d'|' -f8)"
|
||||
r_siste="$(printf '%s' "$r_rec" | cut -d'|' -f9)"
|
||||
r_dir="$(printf '%s' "$r_rec" | cut -d'|' -f10)"
|
||||
r_blocked="$(printf '%s' "$r_rec" | cut -d'|' -f11)"
|
||||
r_ordre="$(printf '%s' "$r_rec" | cut -d'|' -f12)"
|
||||
r_ordreage="$(printf '%s' "$r_rec" | cut -d'|' -f13)"
|
||||
r_fly="$(printf '%s' "$r_rec" | cut -d'|' -f14)"
|
||||
r_flyage="$(printf '%s' "$r_rec" | cut -d'|' -f15)"
|
||||
r_voy="$(printf '%s' "$r_rec" | cut -d'|' -f16)"
|
||||
# `neste` is free prose and may contain a literal '|', which is why it is the
|
||||
# last field of the record - so it is taken as "everything from 17 on", never
|
||||
# as field 17.
|
||||
r_neste="$(printf '%s' "$r_rec" | cut -d'|' -f17-)"
|
||||
|
||||
# The TOKEN, not the display string: the table renders a blocked repo as
|
||||
# `blocked>target` because one column has to carry both, and this rendering
|
||||
# has two fields and no such constraint. A consumer matching status=blocked
|
||||
# must not have to know about the arrow.
|
||||
case "$r_disp" in
|
||||
blocked\>*) r_status=blocked ;;
|
||||
*) r_status="$r_disp" ;;
|
||||
esac
|
||||
|
||||
r_unpushed="$(row_unpushed "$r_dir")" || r_unpushed="?"
|
||||
|
||||
echo "repo=$ROW_REPO"
|
||||
echo "dir=$r_dir"
|
||||
echo "status=$r_status"
|
||||
echo "blocked-on=$r_blocked"
|
||||
echo "next-cost=$r_cost"
|
||||
echo "inn=$r_inn"
|
||||
echo "ordre=$r_ordre"
|
||||
echo "ordre-alder=$r_ordreage"
|
||||
echo "fly=$r_fly"
|
||||
echo "fly-alder=$r_flyage"
|
||||
echo "voy=$r_voy"
|
||||
echo "drt=$r_drt"
|
||||
echo "alder=$r_alder"
|
||||
echo "siste=$r_siste"
|
||||
echo "upushet=$r_unpushed"
|
||||
# LAST, and for the same reason it is last in the record: it is free prose
|
||||
# lifted out of a STATE.md, so anything placed after it would be unreachable
|
||||
# for a consumer reading from the end. Printed WHOLE - the 38-character cut
|
||||
# belongs to the table column, and cutting here would make the cut string the
|
||||
# only copy, which is the defect --brief was built to stop.
|
||||
echo "neste=$r_neste"
|
||||
return 0
|
||||
}
|
||||
|
||||
if [ "$ROW" -eq 1 ]; then
|
||||
row_one
|
||||
exit $?
|
||||
fi
|
||||
|
||||
count() { awk -F'|' -v b="$1" '$1==b' "$RECORDS" | wc -l | tr -d ' '; }
|
||||
|
||||
echo "BOARD - tverr-repo oppmerksomhetstavle ($(awk 'END{print NR}' "$RECORDS") repo)"
|
||||
|
|
|
|||
|
|
@ -141,7 +141,8 @@ check "all 81 trait combinations emit one of the 4 calculator rows" $?
|
|||
# to show the same string back. Runs for all six rows.
|
||||
ROOT="$(mktemp -d)"
|
||||
MBOX="$(mktemp -d)"
|
||||
cleanup() { /bin/rm -rf "$ROOT" "$MBOX" 2>/dev/null; }
|
||||
RL_ROOT=""
|
||||
cleanup() { /bin/rm -rf "$ROOT" "$MBOX" ${RL_ROOT:+"$RL_ROOT"} 2>/dev/null; }
|
||||
trap cleanup EXIT
|
||||
|
||||
rt_bad=0
|
||||
|
|
@ -359,13 +360,14 @@ check "board + route + route-last stacked still yield prose in NESTE" $?
|
|||
# --- 9. Startup command and fallback --------------------------------------
|
||||
# Two spellings of ONE decision: the rubric name for the board line, the CLI
|
||||
# alias for the command the operator pastes. They must never disagree.
|
||||
# These assert the WHOLE string, advisor included, so section 14's rule cannot
|
||||
# be widened without a deliberate edit here - the exact-match is the tripwire.
|
||||
# These assert the WHOLE string, so no flag can be added back to the emitted
|
||||
# command without a deliberate edit here - the exact-match is the tripwire that
|
||||
# keeps section 14's absence rule from being widened around.
|
||||
cmd="$(field partial strong cheap local command)"
|
||||
[ "$cmd" = "claude --model opus --effort high" ]; check "command mirrors the row (opus/high)" $?
|
||||
|
||||
cmd="$(field known strong cheap local command)"
|
||||
[ "$cmd" = "claude --model sonnet --effort high --advisor opus" ]; check "command mirrors the row (sonnet/high)" $?
|
||||
[ "$cmd" = "claude --model sonnet --effort high" ]; check "command mirrors the row (sonnet/high)" $?
|
||||
|
||||
# The rubric requires ALWAYS naming one row cheaper as the quota fallback.
|
||||
fb="$(field partial strong cheap local fallback)"
|
||||
|
|
@ -377,7 +379,7 @@ fb="$(field known strong cheap local fallback)"
|
|||
# The fallback needs its own pasteable command or the operator translates by
|
||||
# hand at exactly the moment they are under quota pressure.
|
||||
fbc="$(field partial strong cheap local fallback-command)"
|
||||
[ "$fbc" = "claude --model sonnet --effort xhigh --advisor opus" ]; check "fallback ships its own command" $?
|
||||
[ "$fbc" = "claude --model sonnet --effort xhigh" ]; check "fallback ships its own command" $?
|
||||
|
||||
# --- 10. The command carries no 'cd' --------------------------------------
|
||||
# One repo per terminal tab: a startup command prefixed with cd is wrong by
|
||||
|
|
@ -393,9 +395,9 @@ check "no emitted command contains a cd prefix" "$rc"
|
|||
# Model aliases are whatever the INSTALLED claude accepts - never hardcoded
|
||||
# without a gate, because an alias that stops resolving turns every emitted
|
||||
# command into a paste that fails.
|
||||
# Capture the effort TOKEN only. Anything may legitimately follow it on the
|
||||
# command line (--advisor does, since section 14), and a match that swallowed
|
||||
# the tail would report a valid effort as invalid.
|
||||
# Capture the effort TOKEN only. The command ends at the effort today, but the
|
||||
# pattern stays tail-tolerant: a match that swallowed whatever a later flag
|
||||
# added would report a valid effort as invalid.
|
||||
efforts="$(printf '%s' "$out" | sed -n 's/^command=claude --model [a-z]* --effort \([a-z]*\).*/\1/p')"
|
||||
case "|low|medium|high|xhigh|max|" in *"|$efforts|"*) rc=0 ;; *) rc=1 ;; esac
|
||||
check "emitted effort is in the verified effort set" "$rc"
|
||||
|
|
@ -467,117 +469,129 @@ check "route.sh no longer claims effort is unobservable from inside" "$rc"
|
|||
grep -q 'CLAUDE_EFFORT' "$R"
|
||||
check "route.sh names the source the caller should measure from" $?
|
||||
|
||||
# --- 14. The advisor: capability gap first, stakes second -----------------
|
||||
# The advisor is a second, stronger model consulted mid-task, so a session
|
||||
# carrying one costs more than a session without. It must therefore fire where
|
||||
# there is a NEED and nowhere else, or it decays into the always-on
|
||||
# advisorModel setting it exists to replace. Two independent needs qualify,
|
||||
# and they turn out to be almost disjoint.
|
||||
# --- 14. The advisor is NOT the rubric's to emit --------------------------
|
||||
# Struck by operator decision 2026-09-12
|
||||
# (~/.claude/docs/2026-09-12-helhetlig-vurdering-arbeidssystemet.md, cut row 3).
|
||||
# The rule it replaces fired on two needs - Sonnet rows always, Opus rows at
|
||||
# costly|one-way stakes - and read well. What killed it was a measurement, not
|
||||
# a change of taste: of 54 dispatches the PM issued 08.-12.09, ZERO carried
|
||||
# --advisor opus, because the operator starts sessions by hand and pastes the
|
||||
# model and effort, not the whole line. A rule nothing honours is not a policy,
|
||||
# it is a claim about the world that the world disagrees with - and this repo's
|
||||
# own standing rule is that an emitted value must be evidence, never decoration.
|
||||
#
|
||||
# 1. THE MAIN MODEL IS SONNET (rows 1-2). Here opus is a genuine capability
|
||||
# lift, not a peer: opus judgement at sonnet cost. This is the load-bearing
|
||||
# half, because every fallback-command is one row cheaper and the cheapest
|
||||
# rows are Sonnet - so this is what makes the QUOTA FALLBACK safe to take.
|
||||
# Pinned below by the row-3-falls-back-to-row-2 case.
|
||||
# So the calculator emits no advisor at all, and the advisor becomes what it
|
||||
# already was in practice: an operator decision per session. That is a strictly
|
||||
# SAFER direction for the one thing the old rule protected - the quota fallback
|
||||
# is still one row cheaper, it just no longer implies a capability lift nobody
|
||||
# was taking.
|
||||
#
|
||||
# 2. A MISTAKE IS EXPENSIVE TO UNDO (reversibility costly|one-way). On an Opus
|
||||
# row the advisor is a peer review rather than a lift, which is worth
|
||||
# paying for when being wrong is not cheap to reverse.
|
||||
#
|
||||
# The two barely overlap: costly forces row 3 and one-way forces row 4, so a
|
||||
# Sonnet row ALWAYS has reversibility=cheap and trigger 2 can never reach it.
|
||||
# Rule 1 covers the Sonnet rows, rule 2 covers the Opus rows.
|
||||
#
|
||||
# What is deliberately NOT a trigger: verification=none on its own. The set it
|
||||
# would add beyond rule 2 is exactly {verification=none AND
|
||||
# reversibility=cheap} - mistakes that are cheap to reverse. It survives on the
|
||||
# Sonnet rows only via rule 1, which is about the model, not the trait.
|
||||
# Pinned as an ABSENCE over the whole trait space rather than on four sampled
|
||||
# rows, because the claim is that no path emits it - the same "no write path
|
||||
# exists" argument the channel split uses. An absence check is worthless
|
||||
# without a known-positive control, so the sweep's own grep is proven able to
|
||||
# find a planted advisor before it is trusted to report none.
|
||||
|
||||
adv="$(field known strong cheap local command)"
|
||||
printf '%s' "$adv" | grep -q -- '--advisor opus'
|
||||
check "row 1 (Sonnet/high) always carries an advisor" $?
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "row 1 (Sonnet/high) carries no advisor" "$rc"
|
||||
|
||||
adv="$(field known weak cheap local command)"
|
||||
printf '%s' "$adv" | grep -q -- '--advisor opus'
|
||||
check "row 2 (Sonnet/xhigh) always carries an advisor" $?
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "row 2 (Sonnet/xhigh) carries no advisor" "$rc"
|
||||
|
||||
adv="$(field known strong costly local command)"
|
||||
printf '%s' "$adv" | grep -q -- '--advisor opus'
|
||||
check "reversibility=costly carries an advisor onto an Opus row" $?
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "reversibility=costly no longer pulls an advisor onto an Opus row" "$rc"
|
||||
|
||||
adv="$(field known strong one-way local command)"
|
||||
printf '%s' "$adv" | grep -q -- '--advisor opus'
|
||||
check "reversibility=one-way carries an advisor onto an Opus row" $?
|
||||
|
||||
# The Opus rows reached WITHOUT expensive stakes are the whole no-advisor set.
|
||||
adv="$(field partial strong cheap local command)"
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "row 3 via path=partial at cheap stakes takes NO advisor" "$rc"
|
||||
|
||||
adv="$(field known strong cheap multi-file command)"
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "row 3 via scope=multi-file at cheap stakes takes NO advisor" "$rc"
|
||||
|
||||
adv="$(field known strong cheap cross-cutting command)"
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "row 4 via scope=cross-cutting at cheap stakes takes NO advisor" "$rc"
|
||||
|
||||
# Rows 5-6 (Fable) are unreachable through this calculator now - section 1
|
||||
# pins that directly, and section 3's closed vocabulary covers every command
|
||||
# this script can ever emit. There is nothing left here for "Fable takes no
|
||||
# advisor" to test: that fact still holds (gated against the installed claude
|
||||
# by the CLI check below), but row_advisor()'s branch for it can no longer be
|
||||
# exercised through the public interface.
|
||||
check "reversibility=one-way no longer pulls an advisor onto an Opus row" "$rc"
|
||||
|
||||
# The fallback is the half the old rule called load-bearing, so it is pinned
|
||||
# in its own right: dropping a row under quota pressure must not reintroduce
|
||||
# the flag by the back door.
|
||||
adv="$(field known strong one-way local fallback-command)"
|
||||
printf '%s' "$adv" | grep -q -- '--advisor opus'
|
||||
check "row 4 fallback to row 3 keeps the advisor at one-way stakes" $?
|
||||
|
||||
# THE POINT OF RULE 1, pinned. Row 3 at cheap stakes takes no advisor, but its
|
||||
# quota fallback is row 2 - a Sonnet row - which does. Dropping a row under
|
||||
# quota pressure must not silently drop the safety net with it.
|
||||
adv="$(field partial strong cheap local fallback-command)"
|
||||
printf '%s' "$adv" | grep -q -- '--advisor opus'
|
||||
check "an Opus row with no advisor still falls back to an advised Sonnet row" $?
|
||||
|
||||
adv="$(field known strong cheap cross-cutting fallback-command)"
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "row 4 falling back to row 3 at cheap stakes stays unadvised" "$rc"
|
||||
check "the row-4 fallback carries no advisor either" "$rc"
|
||||
|
||||
# Closed vocabulary, same reason as the row table: one spelling in circulation.
|
||||
# fable is not merely weaker here, it is REJECTED as an advisor outright.
|
||||
allout="$("$R" --path known --verification strong --reversibility one-way \
|
||||
--scope cross-cutting --rationale x 2>/dev/null)"
|
||||
bad="$(printf '%s' "$allout" | grep -o -- '--advisor [a-z0-9-]*' | grep -v -- '--advisor opus')"
|
||||
[ -z "$bad" ]; check "the only advisor value ever emitted is opus:${bad:- none other}" $?
|
||||
adv="$(field partial strong cheap local fallback-command)"
|
||||
if printf '%s' "$adv" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "an Opus row falling back to a Sonnet row carries no advisor" "$rc"
|
||||
|
||||
if command -v claude >/dev/null 2>&1; then
|
||||
# A real gate that spends NO tokens. Advisor validation runs BEFORE the
|
||||
# empty-prompt check, so `-p ""` reaches the validator and then exits: a
|
||||
# valid advisor fails on the missing prompt, an invalid one fails on itself.
|
||||
# --help cannot gate this - it short-circuits before option validation, so
|
||||
# even an unknown flag exits 0 and the gate would pass without testing.
|
||||
# Row 1-2's trigger is "sonnet main + opus advisor" specifically, so the
|
||||
# main model is pinned rather than left to the ambient default.
|
||||
ADVOUT="$(claude --model sonnet --advisor opus -p "" 2>&1 | head -3)"
|
||||
if printf '%s' "$ADVOUT" | grep -q 'unknown option'; then rc=1; else rc=0; fi
|
||||
check "installed claude accepts the --advisor flag at all" "$rc"
|
||||
# THE SWEEP: every trait combination the calculator accepts, every line of
|
||||
# output. 81 combinations, so a rule surviving on one unsampled corner cannot
|
||||
# hide. Accumulated into one string and grepped once - a per-combination check
|
||||
# would add 81 lines to the summary and drown the rest of the suite.
|
||||
sweep=""
|
||||
for s_path in known partial undetermined; do
|
||||
for s_ver in strong weak none; do
|
||||
for s_rev in cheap costly one-way; do
|
||||
for s_sc in local multi-file cross-cutting; do
|
||||
sweep="$sweep
|
||||
$("$R" --path "$s_path" --verification "$s_ver" --reversibility "$s_rev" \
|
||||
--scope "$s_sc" --rationale x 2>/dev/null)"
|
||||
done
|
||||
done
|
||||
done
|
||||
done
|
||||
if printf '%s' "$sweep" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "no advisor in any output over all 81 trait combinations" "$rc"
|
||||
|
||||
if printf '%s' "$ADVOUT" | grep -q 'cannot advise'; then rc=1; else rc=0; fi
|
||||
check "installed claude accepts opus advising a sonnet main" "$rc"
|
||||
# Known-positive control: the grep above reports an absence, so it must be
|
||||
# shown able to report a presence. Without this the sweep would pass just as
|
||||
# happily against an empty string.
|
||||
if printf '%s' "$sweep claude --advisor opus" | grep -q -- '--advisor'; then rc=0; else rc=1; fi
|
||||
check "control: the sweep's grep does find a planted advisor" "$rc"
|
||||
|
||||
# The help text is the rubric's published form, so the rule has to leave there
|
||||
# too - a doc line nothing tests is a doc line that drifts, and a struck rule
|
||||
# still written down is worse than one never removed: it reads as current.
|
||||
# The literal flag string is absent from route.sh ENTIRELY, including the
|
||||
# paragraph that records what was struck - that paragraph names "an opus
|
||||
# advisor flag" in words on purpose. A blunt grep cannot tell a description
|
||||
# from a specification (the same reason the cache assertion in coord-selftest
|
||||
# runs on an extracted path rather than the whole file), and keeping the
|
||||
# string out is cheaper than teaching the check to read prose. Do not
|
||||
# "restore the quote" here.
|
||||
HELPOUT="$("$R" --help 2>/dev/null)"
|
||||
if printf '%s' "$HELPOUT" | grep -q -- '--advisor opus'; then rc=1; else rc=0; fi
|
||||
check "--help no longer documents emitting --advisor opus" "$rc"
|
||||
|
||||
if printf '%s' "$HELPOUT" | grep -q 'THE ADVISOR is emitted'; then rc=1; else rc=0; fi
|
||||
check "--help no longer carries the advisor rule block" "$rc"
|
||||
|
||||
# Removing the rule silently would leave a reader guessing whether the advisor
|
||||
# is forbidden, forgotten, or someone else's. It is the third, and the help
|
||||
# says which.
|
||||
printf '%s' "$HELPOUT" | grep -q 'advisor is an operator decision per session'
|
||||
check "--help states whose decision the advisor is instead" $?
|
||||
|
||||
grep -q 'advisor is an operator decision per session' "$R"
|
||||
check "route.sh itself carries that sentence, not just its help output" $?
|
||||
|
||||
# --- 14b. Old route lines still parse ------------------------------------
|
||||
# Backward compatibility, pinned rather than assumed. Measured on the real
|
||||
# tree the day the rule was struck: 0 of 48 `<!-- route:` lines anywhere in
|
||||
# ~/repos carry an advisor field at all, so nothing in circulation depends on
|
||||
# it - but board.sh's route_cmd_for() reads these lines by trait NAME, and a
|
||||
# reader that broke on an unknown field would turn a STATE.md written last
|
||||
# month into "that repo has no route line", which is the misreading this whole
|
||||
# file exists to prevent.
|
||||
RL_ROOT="$(mktemp -d)"; mkdir -p "$RL_ROOT/repo-legacy"
|
||||
( cd "$RL_ROOT/repo-legacy" && git init -q . 2>/dev/null ) || true
|
||||
{
|
||||
printf '# STATE\n\n## NESTE - START HER\n'
|
||||
printf '<!-- board: status=planned; blocked-on=-; next-cost=Sonnet 5/high -->\n'
|
||||
printf '<!-- route: path=known; verification=strong; reversibility=cheap; scope=local; advisor=opus; rationale=legacy line written before the rule was struck -->\n'
|
||||
printf 'legacy next step\n'
|
||||
} > "$RL_ROOT/repo-legacy/STATE.md"
|
||||
RL_OUT="$("$BOARD" --roots "$RL_ROOT" --plan 2>/dev/null)"
|
||||
printf '%s' "$RL_OUT" | grep -q '^command=claude --model sonnet --effort high$'
|
||||
check "a route line carrying a legacy advisor= field still yields a command" $?
|
||||
if printf '%s' "$RL_OUT" | grep -q -- '--advisor'; then rc=1; else rc=0; fi
|
||||
check "and the command derived from it carries no advisor" "$rc"
|
||||
|
||||
# row_advisor() never emits '--advisor fable' - it depends on the opposite
|
||||
# direction: whether opus can advise a FABLE main model. That is the fact
|
||||
# rows 5-6's suppression rests on, so that is what must be gated. (Measured
|
||||
# at CC 2.1.226: fable is now itself accepted as an advisor for opus/sonnet
|
||||
# mains - a capability-ordering change worth flagging to the operator, but
|
||||
# not what this check exists to pin.)
|
||||
FABOUT="$(claude --model fable --advisor opus -p "" 2>&1 | head -3)"
|
||||
printf '%s' "$FABOUT" | grep -q 'cannot advise'
|
||||
check "installed claude still rejects opus advising a fable main" $?
|
||||
else
|
||||
echo " skip - claude not on PATH, advisor flag gate not run"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "route-selftest: $PASS passed, $FAIL failed"
|
||||
|
|
|
|||
|
|
@ -29,10 +29,7 @@
|
|||
# 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.
|
||||
# section 6 pins that half).
|
||||
#
|
||||
# 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.
|
||||
|
|
@ -66,32 +63,14 @@
|
|||
# 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 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
|
||||
# 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.
|
||||
# THE ADVISOR IS NOT EMITTED, and that is a decision rather than an omission:
|
||||
# the advisor is an operator decision per session, never the rubric's. Until
|
||||
# 2026-09-12 this calculator appended an opus advisor flag on a NEED - always on
|
||||
# the Sonnet rows, and on the Opus rows at costly|one-way stakes. It was struck
|
||||
# on a measurement: of 54 dispatches issued 08.-12.09 not one carried the flag,
|
||||
# because sessions are started by hand from the model and effort, not from the
|
||||
# whole line. A rule nothing honours is not policy, and an emitted value that
|
||||
# nobody acts on is decoration in a field whose only job is to be evidence.
|
||||
#
|
||||
# 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
|
||||
|
|
@ -288,41 +267,14 @@ row_base_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 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-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).
|
||||
#
|
||||
# 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")"; }
|
||||
# No advisor is appended here or anywhere else - the advisor is an operator
|
||||
# decision per session, not a property this rubric computes (struck
|
||||
# 2026-09-12, see the header). row_cmd() is therefore the row's base command
|
||||
# and nothing more; it stays a function rather than collapsing into
|
||||
# row_base_cmd() because the emitted command and the row table are two
|
||||
# separate things that happened to converge, and a later flag would attach
|
||||
# here, to one place, for both the winning row and its fallback.
|
||||
row_cmd() { row_base_cmd "$1"; }
|
||||
|
||||
FB=$((ROW - 1)); [ "$FB" -ge 1 ] || FB=1
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue