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:
parent
44fb34ab72
commit
19c0c1010b
6 changed files with 211 additions and 18 deletions
|
|
@ -16,7 +16,7 @@ marketplace plugin. Three components, one boundary:
|
|||
`coord-done.sh` archives, `coord-count.sh` counts without delivering,
|
||||
`coord-sweep.sh` closes the aged FYI backlog machine-wide.
|
||||
Everything is pinned by `coord-selftest.sh`
|
||||
(191 checks, throwaway mailbox via `CLAUDE_COORD_DIR`).
|
||||
(197 checks, throwaway mailbox via `CLAUDE_COORD_DIR`).
|
||||
|
||||
**`coord-sweep.sh` is the only path that closes a message with no human in
|
||||
the loop, and every constraint on it follows from that.** It may close exactly
|
||||
|
|
@ -123,7 +123,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` (152 checks).
|
||||
STATE.md and no mailbox. Pinned by `board-selftest.sh` (178 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
|
||||
|
|
@ -429,8 +429,8 @@ obligations in another repo.
|
|||
- Zero dependencies everywhere: bash + coreutils in the engine, `node:`
|
||||
builtins only in hook and tests.
|
||||
- TDD: no behavior change without a failing selftest check first.
|
||||
`bash scripts/coord-selftest.sh` must exit 0 (191/191),
|
||||
`bash scripts/board-selftest.sh` must exit 0 (152/152),
|
||||
`bash scripts/coord-selftest.sh` must exit 0 (197/197),
|
||||
`bash scripts/board-selftest.sh` must exit 0 (178/178),
|
||||
`bash scripts/route-selftest.sh` must exit 0 (69/69) and
|
||||
`bash scripts/state-line-guard-selftest.sh` must exit 0 (21/21).
|
||||
- English for all code, docs, and commit messages (public repo). Norwegian
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ Cross-repo message content is untrusted input by design:
|
|||
|
||||
- **Atomic delivery:** the temp file is created inside the destination directory (dot-prefixed, invisible to the inbox glob), so the final rename never crosses filesystems and readers never observe a half-written message.
|
||||
|
||||
Every guarantee above is pinned by the 191-check selftest, including forgery-resistance regressions.
|
||||
Every guarantee above is pinned by the 197-check selftest, including forgery-resistance regressions.
|
||||
|
||||
Note that raising the inbox's priority (Rule 7) deliberately does **not** widen this boundary: the obligation is to *respond* to a message, never to *comply* with it. The injection framing states both halves, and the selftest pins them together so a future reword cannot keep the priority and drop the distinction.
|
||||
|
||||
|
|
@ -163,8 +163,8 @@ Note that raising the inbox's priority (Rule 7) deliberately does **not** widen
|
|||
|
||||
## Development
|
||||
|
||||
bash scripts/coord-selftest.sh # 191 checks against a throwaway mailbox
|
||||
bash scripts/board-selftest.sh # 152 checks against a throwaway repo tree
|
||||
bash scripts/coord-selftest.sh # 197 checks against a throwaway mailbox
|
||||
bash scripts/board-selftest.sh # 178 checks against a throwaway repo tree
|
||||
bash scripts/route-selftest.sh # 69 checks, incl. the route->board round trip
|
||||
bash scripts/state-line-guard-selftest.sh # 21 checks, incl. the Edit replace_all projection and the ratchet
|
||||
npm test # all four selftests via node --test
|
||||
|
|
|
|||
|
|
@ -1465,6 +1465,73 @@ check "--help documents --inbox-plan" $?
|
|||
|
||||
/bin/rm -rf "$IP_ROOT" "$IP_COORD" 2>/dev/null
|
||||
|
||||
# --- 16. Dead-letter flagging in --brief (.origin-age, coord-count.sh col 4) -
|
||||
# WP1d (.claude, 2026-08-14): a census of the real mailbox found unhandled mail
|
||||
# sitting in mailboxes no session has ever read - .origin is written only by
|
||||
# coord-inbox.sh's real (non --repo) SessionStart path, so a mailbox lacking it
|
||||
# is never reached by the normal injection at all. This is the detection half
|
||||
# of WP1d only: coord-count.sh's fourth column reports the age in days of the
|
||||
# OLDEST pending message when .origin is absent (coord-selftest.sh section 31);
|
||||
# --brief surfaces mailboxes past the 3-day threshold as their own class,
|
||||
# mirroring how it already surfaces orphan mailboxes with no matching repo.
|
||||
# The action half (report to sender / retract) is unapproved design and is
|
||||
# deliberately not built here.
|
||||
DL_ROOT="$(mktemp -d)"
|
||||
DL_COORD="$(mktemp -d)"
|
||||
|
||||
mkrepo "$DL_ROOT/dead-letter-repo"
|
||||
{
|
||||
echo "# STATE - dead-letter-repo"
|
||||
echo ""
|
||||
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
||||
echo "<!-- board: status=planned; blocked-on=-; next-cost=sonnet/high -->"
|
||||
echo "Noe a gjore."
|
||||
} > "$DL_ROOT/dead-letter-repo/STATE.md"
|
||||
mkdir -p "$DL_COORD/dead-letter-repo/inbox"
|
||||
cat > "$DL_COORD/dead-letter-repo/inbox/$(date -u -v-10d +%Y%m%dT%H%M%SZ)-1-from-somebody.md" <<'EOF'
|
||||
---
|
||||
from: somebody
|
||||
to: dead-letter-repo
|
||||
subject: rotting notice
|
||||
date: 2026-08-01T00:00:00Z
|
||||
reply-expected: no
|
||||
---
|
||||
Nobody has ever read this mailbox.
|
||||
EOF
|
||||
|
||||
# A mailbox WITH .origin must never be flagged, no matter how old its mail is -
|
||||
# .origin means a real session claims and reads it regularly.
|
||||
mkrepo "$DL_ROOT/claimed-repo"
|
||||
{
|
||||
echo "# STATE - claimed-repo"
|
||||
echo ""
|
||||
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
||||
echo "<!-- board: status=planned; blocked-on=-; next-cost=sonnet/high -->"
|
||||
echo "Noe annet."
|
||||
} > "$DL_ROOT/claimed-repo/STATE.md"
|
||||
mkdir -p "$DL_COORD/claimed-repo/inbox"
|
||||
printf '%s\n' "/tmp/claimed-repo" > "$DL_COORD/claimed-repo/.origin"
|
||||
cat > "$DL_COORD/claimed-repo/inbox/$(date -u -v-10d +%Y%m%dT%H%M%SZ)-1-from-somebody.md" <<'EOF'
|
||||
---
|
||||
from: somebody
|
||||
to: claimed-repo
|
||||
subject: old but claimed
|
||||
date: 2026-08-01T00:00:00Z
|
||||
reply-expected: no
|
||||
---
|
||||
This mailbox is claimed - never flagged.
|
||||
EOF
|
||||
|
||||
DLBRIEF="$(CLAUDE_COORD_DIR="$DL_COORD" "$BOARD" --roots "$DL_ROOT" --brief 2>/dev/null)"
|
||||
printf '%s' "$DLBRIEF" | grep -qi 'dead-letter-repo'
|
||||
check "brief flags a mailbox with no .origin and mail older than the threshold" $?
|
||||
printf '%s' "$DLBRIEF" | grep -qi 'aldri lest\|never read\|\.origin'
|
||||
check "the flag names why the mailbox is flagged, not just its name" $?
|
||||
printf '%s' "$DLBRIEF" | grep -A3 -i 'aldri lest' | grep -qi 'claimed-repo'; [ $? -ne 0 ]
|
||||
check "a mailbox WITH .origin is never flagged, regardless of message age" $?
|
||||
|
||||
/bin/rm -rf "$DL_ROOT" "$DL_COORD" 2>/dev/null
|
||||
|
||||
echo ""
|
||||
echo "board-selftest: $PASS passed, $FAIL failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
|
|
|
|||
|
|
@ -357,7 +357,8 @@ RECORDS="${TMPDIR:-/tmp}/board.$$"
|
|||
UNBLOCKS="${TMPDIR:-/tmp}/board-unblocks.$$"
|
||||
OWED="${TMPDIR:-/tmp}/board-owed.$$"
|
||||
ALLMAIL="${TMPDIR:-/tmp}/board-allmail.$$"
|
||||
trap '/bin/rm -f "$RECORDS" "$UNBLOCKS" "$OWED" "$ALLMAIL" 2>/dev/null' EXIT
|
||||
DEADLETTERS="${TMPDIR:-/tmp}/board-deadletters.$$"
|
||||
trap '/bin/rm -f "$RECORDS" "$UNBLOCKS" "$OWED" "$ALLMAIL" "$DEADLETTERS" 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 -
|
||||
|
|
@ -383,6 +384,20 @@ if { [ "$BRIEF" -eq 1 ] || [ "$PLAN" -eq 1 ]; } && [ -f "$SELFDIR/coord-count.sh
|
|||
| awk -F"$ow_tab" -v OFS='|' '{print $1, $3}' > "$OWED"
|
||||
fi
|
||||
|
||||
# --- Dead-letter lookup (name -> age in days), --brief only ----------------
|
||||
# coord-count.sh's fourth column (WP1d, .claude 2026-08-14) reports the age in
|
||||
# days of the oldest pending message in a mailbox with no .origin - a mailbox
|
||||
# no session has EVER read via the normal SessionStart path. "-" (has .origin,
|
||||
# or age unreadable) is filtered out here; only mailboxes actually past the
|
||||
# 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 ] && [ -f "$SELFDIR/coord-count.sh" ]; 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"
|
||||
fi
|
||||
|
||||
# --- All-mail lookup (name -> pending, owed), --inbox-plan only ------------
|
||||
# --inbox-plan's whole population is "every name coord-count.sh reports", so
|
||||
# unlike $OWED (which only needs the debt column) this keeps BOTH integers -
|
||||
|
|
@ -516,6 +531,22 @@ brief_orphans() {
|
|||
echo "(CLAUDE_COORD_REPO) eller et checkout utenfor scan-roten."
|
||||
}
|
||||
|
||||
# WP1d detection half (.claude, 2026-08-14): $DEADLETTERS already carries only
|
||||
# the mailboxes past the 3-day threshold, pre-filtered - this function only
|
||||
# renders. A mailbox here has NO .origin, meaning no session has ever read it
|
||||
# via the normal per-repo injection; the message sits there regardless of
|
||||
# whether it also happens to have a repo in the scan tree (dead-letter-repo,
|
||||
# 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() {
|
||||
[ -s "$DEADLETTERS" ] || return 0
|
||||
echo ""
|
||||
echo "ALDRI LEST - postkasser uten .origin med post eldre enn 3 dogn:"
|
||||
awk -F'|' '{printf " %-32s %s dogn\n", $1, $2}' "$DEADLETTERS"
|
||||
echo "Ingen sesjon har noensinne kjort coord-inbox for disse - posten blir"
|
||||
echo "aldri injisert av den vanlige SessionStart-veien."
|
||||
}
|
||||
|
||||
brief() {
|
||||
# OWED, not raw pending (field 6): this briefing is titled "repo som skylder
|
||||
# et svar" (repos that owe a reply), and a pending message that declared
|
||||
|
|
@ -554,6 +585,7 @@ brief() {
|
|||
printf '%s\n' "$fyi_only" | sed 's/^/ /'
|
||||
fi
|
||||
brief_orphans
|
||||
brief_deadletters
|
||||
echo ""
|
||||
echo "MERK: INN teller hva ANDRE venter paa fra deg. Hva et repo venter PAA"
|
||||
echo "staar kun i dets egen board-linje (blocked-on) - postkassen har ikke"
|
||||
|
|
@ -581,6 +613,7 @@ brief() {
|
|||
|
||||
printf '%s repo skylder svar, %s meldinger totalt.\n' "$n_owe" "$tot_msg"
|
||||
brief_orphans
|
||||
brief_deadletters
|
||||
echo ""
|
||||
echo "MERK: INN teller hva ANDRE venter paa fra deg. Hva et repo venter PAA"
|
||||
echo "staar kun i dets egen board-linje (blocked-on) - postkassen har ikke"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -437,8 +437,8 @@ grep -Fxq "$bigbc" "$SEENF" 2>/dev/null; check "seen: a read that completed does
|
|||
TAB="$(printf '\t')"
|
||||
cnt="$("$COUNT" 2>/dev/null)"; rc=$?
|
||||
[ "$rc" -eq 0 ]; check "count: exits 0" $?
|
||||
printf '%s\n' "$cnt" | grep -q "^count-a${TAB}2${TAB}2$"; check "count: reports a mailbox with its pending total and its debt" $?
|
||||
printf '%s\n' "$cnt" | grep -q "^count-b${TAB}1${TAB}1$"; check "count: reports every mailbox that has pending mail" $?
|
||||
printf '%s\n' "$cnt" | grep -q "^count-a${TAB}2${TAB}2${TAB}0$"; check "count: reports a mailbox with its pending total and its debt" $?
|
||||
printf '%s\n' "$cnt" | grep -q "^count-b${TAB}1${TAB}1${TAB}0$"; check "count: reports every mailbox that has pending mail" $?
|
||||
|
||||
# Drained mailboxes are absent, not zero: the caller asks "who is owed a reply",
|
||||
# and a list of zeroes answers a different question at every reader's expense.
|
||||
|
|
@ -602,9 +602,12 @@ reply-expected: no
|
|||
FORGE-BODY
|
||||
FORGE
|
||||
rc1="$(CLAUDE_COORD_DIR="$RDIR" "$COUNT" 2>/dev/null)"
|
||||
printf '%s\n' "$rc1" | grep -q "^ry${TAB}2${TAB}2$"
|
||||
# ry's two fixtures are hand-dated 2026-01-01 (not "now"), so the age column is
|
||||
# whatever that works out to be at test time, not 0 - only pending/debt/format
|
||||
# are pinned here; origin-age has its own dedicated section (31).
|
||||
printf '%s\n' "$rc1" | grep -qE "^ry${TAB}2${TAB}2${TAB}[0-9]+\$"
|
||||
check "reply-expected: a message without the field counts as debt" $?
|
||||
printf '%s\n' "$rc1" | grep -q "^rx${TAB}2${TAB}1$"
|
||||
printf '%s\n' "$rc1" | grep -q "^rx${TAB}2${TAB}1${TAB}0$"
|
||||
check "count: the second column is pending, the third is debt" $?
|
||||
|
||||
# Pending and debt are different numbers, and a mailbox holding only notices is
|
||||
|
|
@ -614,7 +617,7 @@ check "count: the second column is pending, the third is debt" $?
|
|||
# put two different numbers under one name with no way to reconcile them.
|
||||
mkdir -p "$RDIR/rz"
|
||||
CLAUDE_COORD_DIR="$RDIR" "$SEND" --to rz --from rsender --fyi --subject "n2" --message "ONLY-FYI" >/dev/null
|
||||
printf '%s\n' "$(CLAUDE_COORD_DIR="$RDIR" "$COUNT" 2>/dev/null)" | grep -q "^rz${TAB}1${TAB}0$"
|
||||
printf '%s\n' "$(CLAUDE_COORD_DIR="$RDIR" "$COUNT" 2>/dev/null)" | grep -q "^rz${TAB}1${TAB}0${TAB}0$"
|
||||
check "count: a mailbox holding only notices is listed with zero debt" $?
|
||||
|
||||
# The reader is told which terminal state the sender expects - per message, in a
|
||||
|
|
@ -843,7 +846,7 @@ CLAUDE_COORD_DIR="$DDIR" "$SEND" --to "../evil" --from d1 --subject x --message
|
|||
[ $? -eq 2 ]; check "send: path-traversal through a dot prefix still rejected" $?
|
||||
|
||||
dout="$(CLAUDE_COORD_DIR="$DDIR" "$COUNT" 2>/dev/null)"
|
||||
printf '%s\n' "$dout" | grep -q "^\.dotrepo${TAB}1${TAB}1$"
|
||||
printf '%s\n' "$dout" | grep -q "^\.dotrepo${TAB}1${TAB}1${TAB}0$"
|
||||
check "count: sees a dot-prefixed mailbox instead of skipping it" $?
|
||||
|
||||
# sweep uses the same enumeration as coord-count.sh, so an aged FYI inside a
|
||||
|
|
@ -859,6 +862,58 @@ CLAUDE_COORD_DIR="$DDIR" "$DONE" --repo .dotrepo --all >/dev/null
|
|||
[ -z "$(ls "$DDIR/.dotrepo/inbox"/*.md 2>/dev/null)" ]; check "done: drains a dot-prefixed repo's inbox directly (unaffected by the bug)" $?
|
||||
/bin/rm -rf "$DDIR" 2>/dev/null
|
||||
|
||||
# 31. .origin-age flagging (WP1d, .claude 2026-08-14): coord-inbox.sh only
|
||||
# writes .origin from a REAL session's own SessionStart (REPO_PATH resolved via
|
||||
# git rev-parse, never when --repo is passed explicitly - section 23). A
|
||||
# mailbox lacking .origin has therefore NEVER been read by any session's normal
|
||||
# injection; pending mail sitting there is a genuine dead letter, not merely
|
||||
# slow. coord-count.sh's fourth column reports this per mailbox: "-" when
|
||||
# .origin exists (not a candidate, regardless of message age), otherwise the
|
||||
# age in whole days of the OLDEST pending message - the worst case, since that
|
||||
# is how long the problem has existed. board.sh flags anything >= 3 at that
|
||||
# threshold; coord-count.sh only ever reports the raw age, never judges it.
|
||||
OADIR="$(mktemp -d)"
|
||||
|
||||
# (a) .origin present: never gets an age, no matter how old the mail is.
|
||||
CLAUDE_COORD_DIR="$OADIR" "$SEND" --to oa-claimed --from oas --subject "c1" --message "CLAIMED" >/dev/null
|
||||
printf '%s\n' "/tmp/oa-claimed" > "$OADIR/oa-claimed/.origin"
|
||||
age_it "$OADIR" oa-claimed CLAIMED "$(date -u -v-10d +%Y%m%dT%H%M%SZ)"
|
||||
printf '%s\n' "$(CLAUDE_COORD_DIR="$OADIR" "$COUNT" 2>/dev/null)" | grep -q "^oa-claimed${TAB}1${TAB}1${TAB}-$"
|
||||
check "origin-age: a mailbox with .origin reports '-' regardless of message age" $?
|
||||
|
||||
# (b) No .origin, message just sent: age is 0, not yet flagged.
|
||||
CLAUDE_COORD_DIR="$OADIR" "$SEND" --to oa-fresh --from oas --subject "f1" --message "FRESH" >/dev/null
|
||||
printf '%s\n' "$(CLAUDE_COORD_DIR="$OADIR" "$COUNT" 2>/dev/null)" | grep -q "^oa-fresh${TAB}1${TAB}1${TAB}0$"
|
||||
check "origin-age: a fresh message in an unclaimed mailbox reports age 0" $?
|
||||
|
||||
# (c) No .origin, message 10 days old: past the 3-day threshold.
|
||||
CLAUDE_COORD_DIR="$OADIR" "$SEND" --to oa-old --from oas --subject "o1" --message "STALE" >/dev/null
|
||||
age_it "$OADIR" oa-old STALE "$(date -u -v-10d +%Y%m%dT%H%M%SZ)"
|
||||
oa_stale="$(CLAUDE_COORD_DIR="$OADIR" "$COUNT" 2>/dev/null | awk -F"$TAB" '$1=="oa-old"{print $4}')"
|
||||
[ -n "$oa_stale" ] && [ "$oa_stale" -ge 9 ] 2>/dev/null
|
||||
check "origin-age: an unclaimed mailbox with old mail reports its age in days, past the threshold" $?
|
||||
|
||||
# (d) Oldest message wins when a mailbox has several: the reported age is the
|
||||
# worst case (how long this has been a problem), not the most recent arrival.
|
||||
CLAUDE_COORD_DIR="$OADIR" "$SEND" --to oa-multi --from oas --subject "m1" --message "MULTI-OLD" >/dev/null
|
||||
CLAUDE_COORD_DIR="$OADIR" "$SEND" --to oa-multi --from oas --subject "m2" --message "MULTI-NEW" >/dev/null
|
||||
age_it "$OADIR" oa-multi MULTI-OLD "$(date -u -v-10d +%Y%m%dT%H%M%SZ)"
|
||||
oa_multi="$(CLAUDE_COORD_DIR="$OADIR" "$COUNT" 2>/dev/null | awk -F"$TAB" '$1=="oa-multi"{print $4}')"
|
||||
[ -n "$oa_multi" ] && [ "$oa_multi" -ge 9 ] 2>/dev/null
|
||||
check "origin-age: reports the OLDEST pending message's age, not the newest" $?
|
||||
|
||||
# (e) A message whose filename does not match the timestamp grammar (pre-0.x
|
||||
# or hand-crafted) must never crash the count and must never be misread as
|
||||
# ancient - fail-safe, not fail-open, matching coord-sweep.sh's identical rule.
|
||||
mkdir -p "$OADIR/oa-garbage/inbox"
|
||||
echo "not from the grammar" > "$OADIR/oa-garbage/inbox/not-a-timestamp-from-x.md"
|
||||
oa_g_out="$(CLAUDE_COORD_DIR="$OADIR" "$COUNT" 2>/dev/null)"; oa_g_rc=$?
|
||||
[ "$oa_g_rc" -eq 0 ]; check "origin-age: a filename outside the timestamp grammar never crashes the count" $?
|
||||
printf '%s\n' "$oa_g_out" | grep -q "^oa-garbage${TAB}1${TAB}1${TAB}-$"
|
||||
check "origin-age: an unreadable timestamp reports '-', never a fabricated age" $?
|
||||
|
||||
/bin/rm -rf "$OADIR" 2>/dev/null
|
||||
|
||||
echo "----"
|
||||
echo "PASS=$PASS FAIL=$FAIL"
|
||||
[ "$FAIL" -eq 0 ]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue