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:
Kjell Tore Guttormsen 2026-08-29 07:10:21 +02:00
commit 4369499e4b
3 changed files with 236 additions and 22 deletions

View file

@ -411,7 +411,7 @@ marketplace plugin. Three components, one boundary:
- **Board (`scripts/board.sh`):** cross-repo attention board. Reads STATE.md
next-step blocks + board lines, `git status`, and mailbox pending counts, and
prints one line per repo. Read-only by construction: it writes to no repo, no
STATE.md and no mailbox. Pinned by `board-selftest.sh` (300 checks).
STATE.md and no mailbox. Pinned by `board-selftest.sh` (314 checks).
**It lives here because the mailbox is one of its three inputs, and it carries
the same axis distinction the mailbox does.** A pending count means *others
@ -631,6 +631,55 @@ marketplace plugin. Three components, one boundary:
`coord-inbox` or `board`. The dependency runs one way: the driver consumes the
plan, the plan never knows a terminal exists.
**Order 20260826T115026Z-9982513971 (2026-08-26): board consumed
`coord-count.sh`'s STDOUT and threw away its EXIT STATUS, so the whole
three-status contract F5 had just built stopped at the producer.** Every call
site piped it (`bash coord-count.sh 2>/dev/null | awk ...`), and a pipeline
reports the LAST stage's status, so `exit 3` - "the mailbox root you named is
not there, not counted, not zero" - was discarded at each one. `HAVE_COUNT`
only ever tested that the SIBLING FILE exists, which is a strictly weaker
question. Measured on the shipped 0.30.0 script against a mailbox root that
does not exist: `coord-count.sh` 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. It was carved out of F5 deliberately
rather than swept in - that session scoped itself to `coord-count.sh` and said
widening in silence was not its call, and the operator agreed - which is why
this is its own order and its own commit.
**`COUNT_OK` is the measurement's verdict; `HAVE_COUNT` stays, and the two
messages stay DISTINCT.** "The sibling is missing" is repaired by restoring a
file, "the world you named is not there" by fixing `CLAUDE_COORD_DIR` - the
same reason `coord-count.sh`'s own 2 and 3 are different statuses, and a
reader handed one "unavailable" for both cannot tell which repair to make.
`$COUNT_WHY` carries the exact reason, including the status number, into all
five reporting sites, and any nonzero is caught - the rule is "the measurement
failed", not "3 specifically". A partial stdout captured before a failure is
DISCARDED: a half-count is the same defect one layer down, since it also looks
measured. Selftest section 27 pins the failure cases behind SIX known-positive
controls (an existing root still names its debtor; a successful measurement
warns about nothing; an existing but EMPTY root still claims zero debt) plus a
ground-truth assertion that `coord-count.sh` really does exit 3 on that input,
so a future coord-count that stops doing so fails the section rather than
silently disarming it. Mutation-verified: restoring `if true` in place of the
status test turns exactly those seven checks red and leaves all six controls
green.
**The three separate `coord-count.sh` invocations `--brief` used to make are
now one.** With a status to honour, three runs would mean three statuses to
reconcile; `coord-count.sh` writes nothing, so one reading is the same reading.
The table and `--plain` views still never invoke it at all - they read the
mailbox only through what `RECORDS` already counted, and `$COUNT_WHY` says so
in those words rather than claiming a measurement nobody attempted.
**Bounded gap, stated rather than closed:** the TABLE's own `INN`/`ORDRE`/`FLY`
columns count with `ls "$COORD/$name/..."`, which yields 0 for every repo when
the mailbox root is absent - the same "failed measurement renders as a
reassuring value" shape, one source over. It is NOT a `coord-count.sh`
consumer, so consuming exit 3 cannot reach it, and the order scoped itself to
exit 3 and warned by name against silent widening. Naming it here is the
point: the next session finds a decision, not an oversight.
It also cross-checks itself against `coord-count.sh`, and that is not
belt-and-braces. The repo scan and the mailbox are two different populations:
a mailbox can carry a name no scan will ever produce — a declared non-git
@ -1030,7 +1079,7 @@ obligations in another repo.
builtins only in hook and tests.
- TDD: no behavior change without a failing selftest check first.
`bash scripts/coord-selftest.sh` must exit 0 (242/242),
`bash scripts/board-selftest.sh` must exit 0 (300/300),
`bash scripts/board-selftest.sh` must exit 0 (314/314),
`bash scripts/route-selftest.sh` must exit 0 (69/69),
`bash scripts/orders-selftest.sh` must exit 0 (110/110) and
`bash scripts/state-line-guard-selftest.sh` must exit 0 (54/54).

View file

@ -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

View file

@ -554,7 +554,8 @@ UNBLOCKS="${TMPDIR:-/tmp}/board-unblocks.$$"
OWED="${TMPDIR:-/tmp}/board-owed.$$"
ALLMAIL="${TMPDIR:-/tmp}/board-allmail.$$"
DEADLETTERS="${TMPDIR:-/tmp}/board-deadletters.$$"
trap '/bin/rm -f "$RECORDS" "$UNBLOCKS" "$OWED" "$ALLMAIL" "$DEADLETTERS" 2>/dev/null' EXIT
COUNTRAW="${TMPDIR:-/tmp}/board-countraw.$$"
trap '/bin/rm -f "$RECORDS" "$UNBLOCKS" "$OWED" "$ALLMAIL" "$DEADLETTERS" "$COUNTRAW" 2>/dev/null' EXIT
# --- Owed lookup (name -> messages that actually owe a reply) --------------
# `inbox` (field 6 of RECORDS) is raw pending-file count and stays that way -
@ -581,11 +582,62 @@ trap '/bin/rm -f "$RECORDS" "$UNBLOCKS" "$OWED" "$ALLMAIL" "$DEADLETTERS" 2>/dev
HAVE_COUNT=1
[ -f "$SELFDIR/coord-count.sh" ] || HAVE_COUNT=0
# COUNT_OK is the measurement's own verdict, and it is a STRICTLY stronger
# question than HAVE_COUNT's "does the sibling exist" (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 - precisely so it could SAY the measurement failed instead
# of rendering a reassuring value. Every call site here used to pipe it straight
# into awk (`bash coord-count.sh 2>/dev/null | awk ...`), and a pipeline reports
# the LAST stage's status, so the producer's exit code was discarded at each
# one. Measured on 0.30.0 against a mailbox root that does not exist:
# coord-count.sh 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.
#
# The two causes stay DISTINGUISHABLE in $COUNT_WHY rather than collapsing into
# one "unavailable" flag, for the same reason coord-count's own 2 and 3 are
# different statuses: "the sibling is missing" is repaired by restoring a file,
# "the world you named is not there" by fixing CLAUDE_COORD_DIR, and a reader
# handed one message for both cannot tell which repair to make.
#
# Run ONCE into $COUNTRAW, for the three views that consume it. This also
# replaces the three separate invocations --brief used to make (OWED,
# DEADLETTERS, brief_orphans): with a status to honour, three runs would mean
# three statuses to reconcile, and coord-count.sh is read-only, so one reading
# is the same reading.
COUNT_OK=0
COUNT_WHY="coord-count.sh mangler"
: > "$COUNTRAW"
if [ "$HAVE_COUNT" -eq 1 ]; then
if [ "$BRIEF" -eq 1 ] || [ "$PLAN" -eq 1 ] || [ "$INBOX_PLAN" -eq 1 ]; then
bash "$SELFDIR/coord-count.sh" > "$COUNTRAW" 2>/dev/null
count_rc=$?
if [ "$count_rc" -eq 0 ]; then
COUNT_OK=1; COUNT_WHY=""
else
# Discard whatever reached stdout before the failure: a partial count is
# the same defect class one layer down - it looks measured.
: > "$COUNTRAW"
case "$count_rc" in
3) COUNT_WHY="coord-count.sh avsluttet 3 - postkasse-roten finnes ikke" ;;
2) COUNT_WHY="coord-count.sh avsluttet 2 - kall-feil mot coord-count.sh" ;;
*) COUNT_WHY="coord-count.sh avsluttet $count_rc - maalingen feilet" ;;
esac
fi
else
# The table and --plain views never read the mailbox beyond what RECORDS
# already counted, so no measurement is attempted and none is claimed.
COUNT_WHY="coord-count.sh ble ikke kjort for denne visningen"
fi
fi
: > "$OWED"
if { [ "$BRIEF" -eq 1 ] || [ "$PLAN" -eq 1 ]; } && [ "$HAVE_COUNT" -eq 1 ]; then
if { [ "$BRIEF" -eq 1 ] || [ "$PLAN" -eq 1 ]; } && [ "$COUNT_OK" -eq 1 ]; then
ow_tab="$(printf '\t')"
bash "$SELFDIR/coord-count.sh" 2>/dev/null \
| awk -F"$ow_tab" -v OFS='|' '{print $1, $3}' > "$OWED"
awk -F"$ow_tab" -v OFS='|' '{print $1, $3}' "$COUNTRAW" > "$OWED"
fi
# --- Dead-letter lookup (name -> age in days), --brief only ----------------
@ -596,10 +648,9 @@ fi
# 3-day threshold reach $DEADLETTERS, so brief_deadletters() below never has to
# re-parse the raw column or re-apply the threshold itself.
: > "$DEADLETTERS"
if [ "$BRIEF" -eq 1 ] && [ "$HAVE_COUNT" -eq 1 ]; then
if [ "$BRIEF" -eq 1 ] && [ "$COUNT_OK" -eq 1 ]; then
dl_tab="$(printf '\t')"
bash "$SELFDIR/coord-count.sh" 2>/dev/null \
| awk -F"$dl_tab" -v OFS='|' '$4 != "-" && $4+0 >= 3 {print $1, $4}' > "$DEADLETTERS"
awk -F"$dl_tab" -v OFS='|' '$4 != "-" && $4+0 >= 3 {print $1, $4}' "$COUNTRAW" > "$DEADLETTERS"
fi
# --- All-mail lookup (name -> pending, owed), --inbox-plan only ------------
@ -610,10 +661,9 @@ fi
# gated: an extra coord-count.sh subprocess on every table/plain invocation
# would tax a path that never reads it.
: > "$ALLMAIL"
if [ "$INBOX_PLAN" -eq 1 ] && [ "$HAVE_COUNT" -eq 1 ]; then
if [ "$INBOX_PLAN" -eq 1 ] && [ "$COUNT_OK" -eq 1 ]; then
am_tab="$(printf '\t')"
bash "$SELFDIR/coord-count.sh" 2>/dev/null \
| awk -F"$am_tab" -v OFS='|' '{print $1, $2, $3}' > "$ALLMAIL"
awk -F"$am_tab" -v OFS='|' '{print $1, $2, $3}' "$COUNTRAW" > "$ALLMAIL"
fi
# --- Chain-root credit -----------------------------------------------------
@ -746,14 +796,13 @@ brief_cmd() {
# coord-count.sh is the right source and the only safe one: it counts without
# delivering, where coord-inbox.sh would mark broadcasts seen just by looking.
brief_orphans() {
if [ "$HAVE_COUNT" -ne 1 ]; then
if [ "$COUNT_OK" -ne 1 ]; then
echo ""
echo "UTENFOR REPO-SKANNEN: kan ikke sjekke - coord-count.sh mangler."
echo "UTENFOR REPO-SKANNEN: kan ikke sjekke - $COUNT_WHY."
return 0
fi
bo_tab="$(printf '\t')"
bo_out="$(bash "$SELFDIR/coord-count.sh" 2>/dev/null \
| awk -F"$bo_tab" '$2+0>0 {print $1"'"$bo_tab"'"$2}' \
bo_out="$(awk -F"$bo_tab" '$2+0>0 {print $1"'"$bo_tab"'"$2}' "$COUNTRAW" \
| while IFS="$bo_tab" read -r bo_name bo_n; do
[ -n "$bo_name" ] || continue
awk -F'|' -v n="$bo_name" '$3==n {f=1} END{exit !f}' "$RECORDS" \
@ -775,9 +824,9 @@ brief_orphans() {
# claimed-repo in board-selftest.sh section 16 both do). The action half
# (report to sender / retract) is unapproved design and is not built here.
brief_deadletters() {
if [ "$HAVE_COUNT" -ne 1 ]; then
if [ "$COUNT_OK" -ne 1 ]; then
echo ""
echo "ALDRI LEST: kan ikke sjekke - coord-count.sh mangler."
echo "ALDRI LEST: kan ikke sjekke - $COUNT_WHY."
return 0
fi
[ -s "$DEADLETTERS" ] || return 0
@ -814,8 +863,8 @@ brief() {
# debt": $OWED is empty for the same reason it would be on a genuinely
# debt-free day, and continuing into the branch below actively mislabeled a
# reply-owing repo as FYI-only (inbox>0, owed==0 looks identical either way).
if [ "$HAVE_COUNT" -ne 1 ]; then
echo "ADVARSEL: coord-count.sh mangler - kan ikke avgjore hvem som skylder svar."
if [ "$COUNT_OK" -ne 1 ]; then
echo "ADVARSEL: $COUNT_WHY - kan ikke avgjore hvem som skylder svar."
echo "Gjeldstall er IKKE null, bare ikke beregnet. Se ${SELFDIR}/coord-count.sh."
brief_orphans
brief_deadletters
@ -1381,7 +1430,7 @@ plan() {
# intentionally left unchanged (a policy change to a format the operator
# decided has a second consumer elsewhere), but the driver must be able to
# see that the omission happened rather than read a shorter list as complete.
[ "$HAVE_COUNT" -eq 1 ] || printf 'advarsel=coord-count.sh mangler - gjeldsvekting (gruppe 2, inbox:N) er ikke beregnet\n'
[ "$COUNT_OK" -eq 1 ] || printf 'advarsel=%s - gjeldsvekting (gruppe 2, inbox:N) er ikke beregnet\n' "$COUNT_WHY"
# --- Free capacity: NAMED, never turned into a tab ------------------------
# The question the plan could not answer until now, in the operator's own
@ -1546,7 +1595,7 @@ inbox_plan() {
# measured effect was 1 block silently becoming 0, not an empty population
# rendered as such. key=value, not '#', for the same reason as plan()'s
# advarsel= line: the driver consumer drops comment lines by rule.
[ "$HAVE_COUNT" -eq 1 ] || printf 'advarsel=coord-count.sh mangler - populasjonen er ikke beregnet, ikke tom\n'
[ "$COUNT_OK" -eq 1 ] || printf 'advarsel=%s - populasjonen er ikke beregnet, ikke tom\n' "$COUNT_WHY"
ipn=0
while IFS='|' read -r owed pend name cls dir status cost neste; do