test(dispatch): background form (claude --bg) with T4 guards - RED

Section 19b pins what the dispatch skill must say about the background
form the PM has used for 15+ starts since 2026-09-22 and that the skill
never mentioned: --model directly after claude (the budget hook counts
only that position), AskUserQuestion/SendMessage/ListAgents disallowed
(hardening T4), INGEN push, git checkout -B, both order verbs by full
path, and a description that triggers on it. Every check reads the
section only. 12 of 15 red against b278dbd; the two controls are green
(the model-first predicate catches a planted wrong order; --target-pane
still refuses a third value).

Order 20260923T070445Z-9974217048-from-.claude.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 09:14:00 +02:00
commit e579d825aa
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q

View file

@ -1919,6 +1919,56 @@ check "dispatch SKILL.md's ORDER assignment carries no fallback" "$rc"
grep -q -- '--order-id' "$DSKILL"
check "dispatch SKILL.md dispatches on --order-id" $?
# --- 19b. The dispatch skill's BACKGROUND form -----------------------------
# Order 20260923T070445Z-9974217048 (.claude; operator decision 2026-09-22):
# the PM starts sessions itself again with `claude --bg`, and hardening T4
# says every such child is started with AskUserQuestion, SendMessage and
# ListAgents disallowed and told that the operator pushes. The form had been
# used for 15+ starts and was written down nowhere in the skill. Every check
# reads the background SECTION only, never the whole file, so prose elsewhere
# (the far-end section's bare verb names, for one) cannot satisfy or trip it.
dsk_bg="$(awk '/^## The background form/{f=1; print; next} f && /^## /{exit} f' "$DSKILL")"
[ -n "$dsk_bg" ]; check "dispatch SKILL.md has a '## The background form' section" $?
dsk_bgl="$(printf '%s\n' "$dsk_bg" | grep -E 'claude .*--bg( |$)')"
[ -n "$dsk_bgl" ]; check "background section carries a 'claude ... --bg' command line" $?
# --model DIRECTLY after claude: pre-dispatch-budget.sh counts a start only in
# that position (measured 2026-09-22 - a valued flag before --model breaks it).
# The predicate is a function so its known-positive control runs the SAME code.
dsk_model_first() { printf '%s\n' "$1" | grep -E 'claude .*--bg( |$)' | grep -vqE 'claude --model [^ ]+ '; }
if [ -n "$dsk_bgl" ] && ! dsk_model_first "$dsk_bgl"; then rc=0; else rc=1; fi
check "every 'claude --bg' line has --model directly after 'claude'" "$rc"
dsk_model_first 'cd /x && claude --bg --name x --model sonnet --effort high "go"'
check "control: the --model-first predicate catches a planted '--bg --name x --model' line" $?
dsk_dis="$(printf '%s\n' "$dsk_bgl" | sed -n 's/.*--disallowedTools \([^ ]*\).*/\1/p' | head -1)"
for t in AskUserQuestion SendMessage ListAgents; do
case ",$dsk_dis," in *",$t,"*) rc=0 ;; *) rc=1 ;; esac
check "background command disallows $t (T4)" "$rc"
done
printf '%s\n' "$dsk_bg" | grep -q 'INGEN push'
check "background starter says INGEN push (the operator pushes)" $?
printf '%s\n' "$dsk_bg" | grep -q 'git checkout -B '
check "background starter pins the branch with 'git checkout -B'" $?
# The verbs are not on PATH (ORDRE 65): a bg child executes the starter
# literally, so both must be called by full path, and no line may start with
# the bare verb as a command.
printf '%s\n' "$dsk_bg" | grep -q 'bash [^ ]*/scripts/coord-order-claim\.sh '
check "background starter claims by FULL path to coord-order-claim.sh" $?
printf '%s\n' "$dsk_bg" | grep -q 'bash [^ ]*/scripts/coord-order-done\.sh '
check "background starter closes by FULL path to coord-order-done.sh" $?
if printf '%s\n' "$dsk_bg" | grep -qE '^[[:space:]]*coord-order-(claim|done) '; then rc=1; else rc=0; fi
check "background section has no bare coord-order-claim/-done command line" "$rc"
# The description is what makes a skill trigger; a form it does not mention is
# a form nobody reaches through the front door.
dsk_fm="$(awk 'NR==1 && /^---$/{f=1; next} f && /^---$/{exit} f' "$DSKILL")"
printf '%s\n' "$dsk_fm" | grep -q -- 'claude --bg'
check "dispatch SKILL.md description names 'claude --bg'" $?
printf '%s\n' "$dsk_fm" | grep -qi 'bakgrunn'
check "dispatch SKILL.md description carries a Norwegian background trigger" $?
# Known-negative: the background form is the SKILL's, not a third board.sh
# rendering - --target-pane stays exactly yes|no.
"$BOARD" --roots "$ROOT" --dispatch --repo repo-a --prompt-file "$DSP" --target-pane bg $DSPTRAITS >/dev/null 2>&1; rc=$?
[ "$rc" -eq 2 ]; check "control: --target-pane still refuses 'bg' (board.sh gained no third form)" $?
# --- 20. The ORDRE column ---------------------------------------------------
# Two mailbox-derived columns, never summed and never merged: INN is "others
# are waiting on YOU", ORDRE is "authorized work is waiting on this REPO".