fix(board): consume coord-count's exit status, not just its stdout
F5 gave coord-count.sh a three-status contract (0 counted, 2 usage error, 3 mailbox root absent) so it could SAY the measurement failed instead of rendering a reassuring value. board.sh threw that signal away: every call site piped it into awk, and a pipeline reports the LAST stage's status, so the producer's exit code was discarded at each one. HAVE_COUNT only ever tested that the sibling FILE exists - a strictly weaker question. Measured on 0.30.0 with a mailbox root that does not exist: coord-count printed "not counted, not zero" and exited 3, while --brief answered "Ingen repo skylder noen et svar i dag." and --plan emitted no advarsel= key at all. A gate that can fail whose consumer does not listen is a gate that does not fall. COUNT_OK is now the measurement's verdict, and COUNT_WHY carries the exact reason - including the status number - into all five reporting sites. The two causes stay distinguishable: "the sibling is missing" and "the world you named is not there" are different repairs. Any nonzero is caught, not 3 specifically, and a partial stdout captured before a failure is discarded because a half-count also looks measured. --brief's three separate coord-count invocations collapse to one: with a status to honour, three runs would mean three statuses to reconcile. board.sh stays read-only. The table and --plain views still never invoke coord-count at all, and COUNT_WHY says so rather than claiming a measurement nobody attempted. Bounded gap, stated rather than closed: the table's own INN/ORDRE/FLY columns count with ls and read 0 for every repo when the mailbox root is absent. Same defect shape, different source - not a coord-count consumer, so exit 3 cannot reach it, and the order warned by name against silent widening. TDD: board-selftest.sh section 27 written first and RED (7 failures) before board.sh was touched, behind six known-positive controls and a ground-truth assertion that coord-count really does exit 3 on that input. Mutation-verified: restoring `if true` in place of the status test turns exactly those seven red and leaves all six controls green. Suites under real /bin/bash 3.2: board 314/314 (was 300), coord 242/242, route 69/69, orders 110/110, guard 54/54. No version bump: 0.30.0 shipped two days ago, and this adds no flag, no exit code and no env var - it repairs an existing consumer. Recommend it rides the next collection rather than minting a release of its own; the operator decides, and the catalog owns the tag. Order: 20260826T115026Z-9982513971-from-.claude Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3e4a3bc62a
commit
4369499e4b
3 changed files with 236 additions and 22 deletions
|
|
@ -2446,6 +2446,122 @@ check "F10: the bad root is still named on stderr in the mixed case" $?
|
|||
/bin/rm -rf "$F10ROOT" "$F10COORD" "$F10EMPTY" "$f10a_err" "$f10b_err" \
|
||||
"$f10c_err" "$f10d_err" 2>/dev/null
|
||||
|
||||
# --- 27. board must CONSUME coord-count.sh's exit 3, not just its stdout ----
|
||||
# Order 20260826T115026Z-9982513971 (.claude, operator decision 2026-08-26).
|
||||
# F5 gave coord-count.sh a three-status contract - 0 counted, 2 usage error,
|
||||
# 3 mailbox root absent - so it can now SAY that the measurement failed instead
|
||||
# of rendering a reassuring value. board.sh threw that signal away: every call
|
||||
# site piped it into awk (`bash coord-count.sh 2>/dev/null | awk ...`), which
|
||||
# discards the producer's status, and HAVE_COUNT only ever tested that the
|
||||
# SIBLING FILE exists. A gate that can fail whose consumer does not listen is a
|
||||
# gate that does not fall.
|
||||
#
|
||||
# Measured on the shipped 0.30.0 script before this section was written, with a
|
||||
# mailbox root that does not exist: coord-count.sh printed "mailbox root does
|
||||
# not exist ... (not counted, not zero)" and exited 3, while board.sh --brief
|
||||
# answered "Ingen repo skylder noen et svar i dag." and --plan emitted no
|
||||
# advarsel= key at all. A failed measurement rendered as a confident zero.
|
||||
#
|
||||
# The two causes must stay DISTINGUISHABLE, for the same reason coord-count's
|
||||
# own 2 and 3 do: "the sibling is missing" and "the world you named is not
|
||||
# there" are different repairs, and section 17 pins the first one.
|
||||
F3ROOT="$(mktemp -d)"
|
||||
mkrepo "$F3ROOT/f3-debtor"
|
||||
{
|
||||
echo "# STATE - f3-debtor"
|
||||
echo ""
|
||||
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
||||
echo "<!-- board: status=in-progress; blocked-on=-; next-cost=Sonnet 5/high -->"
|
||||
echo "Noe a gjore."
|
||||
} > "$F3ROOT/f3-debtor/STATE.md"
|
||||
|
||||
# --- Known-positive control 1: a mailbox root that EXISTS and holds a debt.
|
||||
# Without this, a guard that warned on every invocation would pass the defect
|
||||
# checks below while proving nothing.
|
||||
F3GOOD="$(mktemp -d)"
|
||||
mkdir -p "$F3GOOD/f3-debtor/inbox"
|
||||
cat > "$F3GOOD/f3-debtor/inbox/20260101T000000Z-1-from-somebody.md" <<'EOF'
|
||||
---
|
||||
from: somebody
|
||||
to: f3-debtor
|
||||
subject: needs an answer
|
||||
date: 2026-01-01T00:00:00Z
|
||||
reply-expected: yes
|
||||
---
|
||||
Please respond.
|
||||
EOF
|
||||
|
||||
F3GB="$(CLAUDE_COORD_DIR="$F3GOOD" "$BOARD" --roots "$F3ROOT" --brief 2>/dev/null)"
|
||||
printf '%s' "$F3GB" | grep -q 'f3-debtor'
|
||||
check "exit3: control - an existing mailbox root still names the debtor in --brief" $?
|
||||
printf '%s' "$F3GB" | grep -qi 'coord-count'; [ $? -ne 0 ]
|
||||
check "exit3: control - a successful measurement warns about nothing" $?
|
||||
|
||||
F3GP="$(CLAUDE_COORD_DIR="$F3GOOD" "$BOARD" --roots "$F3ROOT" --plan 2>/dev/null)"
|
||||
printf '%s' "$F3GP" | grep -q '^advarsel='; [ $? -ne 0 ]
|
||||
check "exit3: control - a successful measurement emits no advarsel= key" $?
|
||||
|
||||
# --- Known-positive control 2: an EXISTING but empty mailbox root. A genuine
|
||||
# zero must stay a zero - this is the reading the defect case is meant to stop
|
||||
# being indistinguishable from, so it has to keep working.
|
||||
F3EMPTY="$(mktemp -d)"
|
||||
F3EB="$(CLAUDE_COORD_DIR="$F3EMPTY" "$BOARD" --roots "$F3ROOT" --brief 2>/dev/null)"
|
||||
printf '%s' "$F3EB" | grep -q '^Ingen repo skylder noen et svar i dag\.$'
|
||||
check "exit3: control - an existing but EMPTY mailbox root still claims zero debt" $?
|
||||
printf '%s' "$F3EB" | grep -qi 'coord-count'; [ $? -ne 0 ]
|
||||
check "exit3: control - an empty mailbox root is not reported as a failure" $?
|
||||
|
||||
# --- Ground truth for the defect case, asserted before anything depends on it:
|
||||
# coord-count.sh really does exit 3 on this input. If a future coord-count
|
||||
# stops doing that, this section must go red rather than silently disarm.
|
||||
F3MISS="$F3ROOT/no-such-mailbox-root"
|
||||
CLAUDE_COORD_DIR="$F3MISS" /bin/bash "$DIR/coord-count.sh" >/dev/null 2>&1
|
||||
[ "$?" -eq 3 ]
|
||||
check "exit3: ground truth - coord-count.sh exits 3 for an absent mailbox root" $?
|
||||
|
||||
# --- The defect.
|
||||
F3B="$(CLAUDE_COORD_DIR="$F3MISS" "$BOARD" --roots "$F3ROOT" --brief 2>/dev/null)"
|
||||
printf '%s' "$F3B" | grep -q '^Ingen repo skylder noen et svar i dag\.$'; [ $? -ne 0 ]
|
||||
check "exit3: --brief does not claim zero debt from a measurement that failed" $?
|
||||
printf '%s' "$F3B" | grep -qi 'coord-count.sh avsluttet 3'
|
||||
check "exit3: --brief names the failed measurement and its status" $?
|
||||
printf '%s' "$F3B" | grep -qi 'coord-count.sh mangler'; [ $? -ne 0 ]
|
||||
check "exit3: a failed measurement is NOT reported as a missing sibling" $?
|
||||
|
||||
F3P="$(CLAUDE_COORD_DIR="$F3MISS" "$BOARD" --roots "$F3ROOT" --plan 2>/dev/null)"
|
||||
printf '%s' "$F3P" | grep -q '^advarsel=coord-count.sh avsluttet 3'
|
||||
check "exit3: --plan carries a machine-readable advarsel= key, not a silent omission" $?
|
||||
|
||||
F3IP="$(CLAUDE_COORD_DIR="$F3MISS" "$BOARD" --roots "$F3ROOT" --inbox-plan 2>/dev/null)"
|
||||
printf '%s' "$F3IP" | grep -q '^advarsel=coord-count.sh avsluttet 3'
|
||||
check "exit3: --inbox-plan carries a machine-readable advarsel= key" $?
|
||||
|
||||
# The two --brief subsections that ALSO rest on coord-count.sh must not render
|
||||
# their absence as an answer either: "no orphan mailboxes" and "no dead letters"
|
||||
# are claims, and neither was measured here.
|
||||
printf '%s' "$F3B" | grep -qi 'UTENFOR REPO-SKANNEN'
|
||||
check "exit3: --brief's orphan-mailbox section says it could not check" $?
|
||||
printf '%s' "$F3B" | grep -qi 'ALDRI LEST'
|
||||
check "exit3: --brief's dead-letter section says it could not check" $?
|
||||
|
||||
# A nonzero that is NOT 3 must still be caught: the rule is "the measurement
|
||||
# failed", not "3 specifically". Simulated with a stub sibling in a scratch
|
||||
# copy, the same technique section 17 uses for the missing-sibling case.
|
||||
F3SCRATCH="$(mktemp -d)"
|
||||
cp "$DIR/board.sh" "$F3SCRATCH/board.sh"
|
||||
cp "$DIR/route.sh" "$F3SCRATCH/route.sh"
|
||||
cat > "$F3SCRATCH/coord-count.sh" <<'EOF'
|
||||
#!/bin/bash
|
||||
echo "stub: something went wrong" >&2
|
||||
exit 2
|
||||
EOF
|
||||
chmod +x "$F3SCRATCH/board.sh" "$F3SCRATCH/route.sh" "$F3SCRATCH/coord-count.sh"
|
||||
F3SP="$(CLAUDE_COORD_DIR="$F3GOOD" "$F3SCRATCH/board.sh" --roots "$F3ROOT" --plan 2>/dev/null)"
|
||||
printf '%s' "$F3SP" | grep -q '^advarsel=coord-count.sh avsluttet 2'
|
||||
check "exit3: a nonzero that is not 3 is caught too, and names its own status" $?
|
||||
|
||||
/bin/rm -rf "$F3ROOT" "$F3GOOD" "$F3EMPTY" "$F3SCRATCH" 2>/dev/null
|
||||
|
||||
echo ""
|
||||
echo "board-selftest: $PASS passed, $FAIL failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue