feat(coord,board): flag mailboxes no session has ever read (WP1d detection half)

coord-count.sh gains a fourth TSV column: "-" when a mailbox has .origin
(a real session has read it via SessionStart), otherwise the age in whole
days of the oldest pending message. .origin is only written by
coord-inbox.sh's non---repo path, so its absence means the mailbox is
never reached by normal injection — a genuine dead letter, not merely
slow. board.sh's --brief surfaces mailboxes past the 3-day threshold as
a new "ALDRI LEST" section, mirroring the existing orphan-mailbox
listing. This is WP1d's detection half only (per .claude's coord
bestilling 2026-08-14); the action half (report-to-sender / retract) is
unapproved design, not built here.

coord-selftest.sh: 191 -> 197 checks. board-selftest.sh: 175 -> 178
checks (net +3; section 16 adds 3 new fixtures on top of the existing
175 baseline, some pre-existing counts shift with the trailing column).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194eV8b6BXNv6aKLovP8TP6
This commit is contained in:
Kjell Tore Guttormsen 2026-08-14 21:52:47 +02:00
commit 19c0c1010b
6 changed files with 211 additions and 18 deletions

View file

@ -1,7 +1,8 @@
#!/bin/bash
# coord-count.sh - count PENDING directed messages per mailbox WITHOUT
# delivering anything. Prints one "<mailbox>\t<pending>\t<debt>" line per mailbox
# that has unhandled mail, sorted by name; prints nothing when none do.
# delivering anything. Prints one "<mailbox>\t<pending>\t<debt>\t<origin_age>"
# line per mailbox that has unhandled mail, sorted by name; prints nothing when
# none do.
#
# TWO INTEGERS, NOT ONE. <pending> is every unhandled message; <debt> is the
# subset whose sender declared it expects a reply (frontmatter reply-expected,
@ -11,6 +12,15 @@
# numbers under one name with nothing to reconcile them - and a mailbox holding
# only notices would read as empty while its messages keep being re-injected.
#
# <origin_age> (WP1d, .claude 2026-08-14): "-" when the mailbox has a .origin
# file, otherwise the age in whole days of its OLDEST pending message.
# coord-inbox.sh writes .origin only from a REAL session's own SessionStart
# (REPO_PATH resolved via git rev-parse, never when --repo is passed
# explicitly), so a mailbox with no .origin has NEVER been reached by the
# normal per-repo injection - pending mail there is a dead letter, not merely
# slow. This script only reports the raw age; judging it against a threshold
# is board.sh's job, the same split as <pending> vs <debt> above.
#
# WHY THIS IS NOT coord-inbox.sh --repo <x>: reading IS delivery. The read path
# prints a broadcast and then records it as seen, so asking it "what is pending
# for x" would consume x's broadcast backlog as a side effect - once, silently,
@ -78,18 +88,46 @@ for d in "$COORD"/* "$COORD"/.[!.]*; do
[ -d "$d/inbox" ] || continue
# *.md is the message grammar; a stray file must not inflate a total the
# operator reads as "replies owed".
n=0; owed=0
# oldest_ts captures only the FIRST message whose filename matches the
# timestamp grammar. That is safe because the glob above is already
# name-sorted under LC_ALL=C (see the comment on it), and the grammar's
# timestamp prefix sorts identically to chronological order - so the first
# match encountered is the oldest, without a second pass or a full sort.
n=0; owed=0; oldest_ts=""
for m in "$d/inbox"/*.md; do
[ -e "$m" ] || continue
n=$((n + 1))
owes_reply "$m" && owed=$((owed + 1))
if [ -z "$oldest_ts" ]; then
mts="${m##*/}"
mts="${mts%%-*}"
case "$mts" in
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]Z)
oldest_ts="$mts" ;;
esac
fi
done
[ "$n" -gt 0 ] || continue
# "-" means either .origin exists (claimed, never a dead-letter candidate
# regardless of age) or the age could not be read (fail-safe, not
# fail-open - an unreadable age must never be treated as old, matching
# coord-sweep.sh's identical rule for the same filename grammar).
origin_age="-"
if [ ! -f "$d/.origin" ] && [ -n "$oldest_ts" ]; then
oldest_epoch="$(date -u -j -f '%Y%m%dT%H%M%SZ' "$oldest_ts" '+%s' 2>/dev/null)"
case "$oldest_epoch" in
[0-9]*)
now_epoch="$(date -u +%s)"
age_days=$(( (now_epoch - oldest_epoch) / 86400 ))
[ "$age_days" -ge 0 ] && origin_age="$age_days"
;;
esac
fi
# Absent, not zero: the question is "who has unhandled mail", and a list of
# zeroes answers a different one at every reader's expense. A mailbox holding
# only notices IS listed, with a debt of 0 - it has mail that will be
# re-injected until someone closes it, which is the thing worth knowing.
printf '%s\t%s\t%s\n' "$name" "$n" "$owed"
printf '%s\t%s\t%s\t%s\n' "$name" "$n" "$owed" "$origin_age"
done
exit 0