fix(infra): a failed measurement must never render as a reassuring value

Tier 3, all five the same defect class (Verifiseringsloven ansikt 4): a
broken or uninstrumented query returning a positive-looking null, consumed
as a fact about the world.

F5  coord-count.sh: a mailbox root that does not exist was byte-identical
    to one where nobody has pending mail - zero lines, exit 0, silent
    stderr. Now exit 3 + a named stderr line; an existing-but-empty root
    stays a silent, clean 0. 3 rather than 2 because 2 already means "you
    called me wrong" and this means "the world you named is not there".
F14 coord-count.sh: the header promised exit 0 unconditionally while
    --exclude with no value already exited 2. Contract restated as
    0/2/3 and pinned as a check on the help TEXT.
F6  board.sh: `git status | wc -l` yields 0 lines whether the tree is
    clean or git refused to answer, so a failure printed DRT=0. Now "?",
    and BOTH awk consumers handle it - --plan's free-capacity test
    compares the field as a string against "0" (a "?" coerces to 0 in
    arithmetic and would certify an unmeasured tree as free), and the SUM
    roll-up names what it could not add.
F10 board.sh: a scan root that does not exist was skipped in silence and
    the empty scan exited 0. Bad roots are now named on stderr; exit 3
    only when NO root was scanned. A mix still exits 0 and prints the
    board. Replaces an assertion that encoded this defect as a pass.
F13 pre-state-line-guard.mjs: MAX_LINES is overridable via
    CLAUDE_STATE_MAX_LINES so the boundary is testable without hardcoding
    120 twice. An unusable value denies by name rather than falling back
    to the default - a limit that silently did not take effect is the
    same defect one layer up.

Every design choice mutation-tested; every negative check carries a
known-positive control. Section 11's first cut was vacuously green (wrong
basename + unexported fixture path) - recorded in CLAUDE.md rather than
quietly fixed, and the section now asserts its own ground truth.

Denominator measured, not estimated: coord-inbox.sh:57 and
coord-order-inbox.sh:60/64 carry the same `|| exit 0` shape and are
deliberately left alone (injection path, prose output, must never fail a
SessionStart) - stated in CLAUDE.md as a bounded gap.

Suites: coord 230->242, board 281->300, guard 40->54, route 69, orders
110, npm 11/11. Verified under system bash 3.2, not just Homebrew 5.3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-08-26 12:15:05 +02:00
commit d8fdeaa991
7 changed files with 606 additions and 16 deletions

View file

@ -313,8 +313,20 @@ $dotentry"
OLD_IFS="$IFS"; IFS=','
set -- $ROOTS
IFS="$OLD_IFS"
# F10: `[ -d "$root" ] || continue` skipped a bad root in silence, and a scan
# that then found nothing exited 0 printing nothing - so a typo in --roots, a
# moved home directory and a genuinely empty tree were one single output.
# Verifiseringsloven ansikt 4: the nevner was never reported, so "no repos"
# could not be told apart from "nothing was looked at". Every bad root is now
# NAMED on stderr; the exit status only changes when NO root was scanned at all
# (below), because a mix still produces a real board that must be printed.
ROOTS_OK=0
for root in "$@"; do
[ -d "$root" ] || continue
if [ ! -d "$root" ]; then
echo "board: scan root does not exist: $root (contributed nothing - not scanned, not empty)" >&2
continue
fi
ROOTS_OK=$((ROOTS_OK + 1))
add_dot_repos "$root"
for entry in "$root"/*; do
[ -d "$entry" ] || continue
@ -334,6 +346,17 @@ $child"
done
done
# Status 3 rather than 2, matching coord-count.sh's F5 fix: 2 already means
# "you called me wrong" everywhere in this script (a flag missing its value, an
# unknown --repo), and this is "the world you named is not there". stdout stays
# empty on purpose - 3 is not an empty board, it is the absence of a scan.
if [ "$ROOTS_OK" -eq 0 ]; then
echo "board: no scan root exists (roots: $ROOTS) - nothing was scanned, and that is NOT an empty board" >&2
exit 3
fi
# Roots existed and were scanned; finding no repo in them is a real, measured
# answer and stays a silent, clean 0.
[ -n "$(printf '%s' "$REPOS" | tr -d '[:space:]')" ] || exit 0
# --- Collect one record per repo -------------------------------------------
@ -346,8 +369,28 @@ printf '%s\n' "$REPOS" | while IFS= read -r d; do
name="$(basename "$d")"
state="$d/STATE.md"
dirty="$(git -C "$d" status --porcelain 2>/dev/null | wc -l | tr -d ' ')"
[ -n "$dirty" ] || dirty=0
# F6: `git status | wc -l` yields 0 lines whether the tree is CLEAN or git
# refused to answer at all, so a failure used to print DRT=0 and every reader
# took it as "nothing uncommitted here" - Verifiseringsloven ansikt 4 on our
# own tooling. It is reachable, not theoretical: discovery tests .git with -e
# so worktrees are found, and a worktree whose parent checkout was deleted
# exits 128 here (measured). The exit status is now the discriminator, and an
# unmeasured tree reads "?" - the same token coord-count.sh already uses for
# an age it could not compute, and for the same fail-safe reason: not
# measured must never render as the reassuring value.
#
# Every shell consumer of this field prints it with %s and is unaffected. The
# two awk consumers are NOT, and both are handled where they read it: the
# free-capacity test (a "?" is not free) and the SUM roll-up (a "?" is named,
# never silently added as 0).
dirty_out="$(git -C "$d" status --porcelain 2>/dev/null)"; dirty_rc=$?
if [ "$dirty_rc" -ne 0 ]; then
dirty="?"
elif [ -z "$dirty_out" ]; then
dirty=0
else
dirty="$(printf '%s\n' "$dirty_out" | wc -l | tr -d ' ')"
fi
inbox=0
if [ -d "$COORD/$name/inbox" ]; then
@ -1363,10 +1406,15 @@ plan() {
awk -F'|' -v OWF="$OWED" '
FILENAME==OWF { ow[$1] = $2 + 0; next }
{
name = $3; status = $4; dirty = $7 + 0; orders = $12 + 0; claimed = $13 + 0
name = $3; status = $4; dirty = $7; orders = $12 + 0; claimed = $13 + 0
owed = (name in ow) ? ow[name] : 0
if (status != "done" && status != "deferred") next
if (owed > 0 || orders > 0 || claimed > 0 || dirty > 0) next
# dirty is compared as a STRING against exactly "0", never as $7 + 0: a
# "?" (git could not answer, see F6 above) coerces to 0 in arithmetic and
# would certify an UNMEASURED tree as free capacity - the precise shape
# of the defect this whole column change exists to close. Only a tree
# measured clean is clean.
if (owed > 0 || orders > 0 || claimed > 0 || dirty != "0") next
print name " (" status ")"
}
' "$OWED" "$RECORDS" | sort > "$lf"
@ -1564,9 +1612,15 @@ tot_in="$(awk -F'|' '{s+=$6} END{print s+0}' "$RECORDS")"
tot_dirty="$(awk -F'|' '{s+=$7} END{print s+0}' "$RECORDS")"
n_mal="$(grep -c 'MALFORMED' "$RECORDS" 2>/dev/null | tr -d ' ')"
n_nofield="$(awk -F'|' '$4=="?"' "$RECORDS" | wc -l | tr -d ' ')"
# tot_dirty above sums field 7, where a "?" contributes 0 - so the total is
# short by an unknown amount whenever git failed anywhere. Labelled rather than
# silently dropped, the same rule --plan already applies to repos with no board
# line: the number stays honest about what it did NOT include.
n_unmeas="$(awk -F'|' '$7=="?"' "$RECORDS" | wc -l | tr -d ' ')"
printf '\nSUM: %s uhaandterte innboks-meldinger, %s ukommiterte filer.\n' "$tot_in" "$tot_dirty"
[ "${n_mal:-0}" -gt 0 ] && printf 'ADVARSEL: %s repo har MALFORMED status-token (utenfor det lukkede settet).\n' "$n_mal"
[ "${n_nofield:-0}" -gt 0 ] && printf 'MERK: %s repo mangler board-linje - status/kost er ukjent (?), NESTE-utdrag brukes.\n' "$n_nofield"
[ "${n_unmeas:-0}" -gt 0 ] && printf 'MERK: %s repo har umaalbart arbeidstre - git svarte ikke, DRT er ? og IKKE med i summen over.\n' "$n_unmeas"
exit 0