feat(board): ORDRE and FLY carry the age of the oldest order

Both columns were pure counts (ls | wc -l), so ORDRE 2 read identically
whether both orders arrived yesterday or both had sat for ten days. The
defect was never that orders sit there - that is a prioritisation
question owned by humans. It was that no surface showed the AGE ACROSS
REPOS: coord-order-inbox.sh does show one, but only inside the one repo
at SessionStart, so a repo nobody opens shows nobody anything.

Measured across the live mailbox 2026-08-29 with `find ~/.claude/coord
-path '*/orders/*.md' -maxdepth 3 -type f`: 30 pending orders over 22
repos, 5 of them ten days old. Re-measured here after this session
consumed two of them: 29 over 23 mailboxes, still 5 at ten days. The
residual delta is not attributable from this repo - the mailbox has
other writers - and is reported rather than reconciled away.

Rendered N:Md. This is not a new mechanism in the file: ALDER (STATE.md
mtime) and SISTE (last commit) already read a clock the same way.

The age is the OLDEST order, never the newest and never a mean - a repo
holding one fresh order and one ten-day-old order has a ten-day-old
problem, and the newest reading is exactly what would hide it. FLY
carries the identical reading, and there it matters at least as much:
nothing un-claims an order when the session that took it dies, so a
claim with no age is the 117-hour claim with its only counter-evidence
removed.

An empty queue prints a BARE 0, never "0:0d": "no orders" and "an order
that arrived today" are different facts, the same reason FLY exists
beside ORDRE. An unreadable mtime prints "?" for the WHOLE reading
rather than skipping the file, because an unmeasured file could be the
oldest one - the same token coord-count and DRT already use.

NO NEW SORT AXIS. The age is display-only; putting it in the ranking
would silently reorder a parser living in another repo. --plan's fly=
and every other emitted key=value block are untouched: the requirement
is that a human reading the board sees an order is old without opening
the repo, and the table is where that is read. Nothing here writes -
board.sh stays read-only, and no cron, hook or notifier was added.

TDD: board-selftest.sh section 28 written first and RED before board.sh
was touched. It builds a throwaway coord tree with touch -t mtimes and
asserts its OWN ground truth (the stale fixture really is 10 days old)
before anything depends on it. Known-positive control: a second repo
with two genuinely fresh orders, so the 10d assertion is proven able to
fail. Mutation-verified: flipping -lt to -gt in oldest_order_age turns
exactly the two oldest-specific checks red, all controls green.

Four pre-existing assertions in board-selftest and orders-selftest
matched the old bare-count cell and were updated to the N:Md form; the
claim each pins - INN, ORDRE and FLY are never summed - is unchanged.

Suites under real /bin/bash 3.2: board 325/325 (was 314), coord 242/242,
route 69/69, orders 110/110, guard 54/54. npm test 11/11.

No version bump, no tag, no catalog ref - as ordered.

Order: 20260829T045435Z-276822386-from-.claude

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-08-29 07:29:13 +02:00
commit ee5a86a40b
4 changed files with 238 additions and 20 deletions

View file

@ -1935,7 +1935,7 @@ printf '%s' "$ORD_OUT" | grep -q 'ORDRE'; check "table header carries an ORDRE c
# Matched on the rendered row, not by awk field index: KOST is "Sonnet 5/high"
# and contains a space, so a field number would be counting other columns and
# would keep passing if the layout shifted.
printf '%s' "$ORD_OUT" | grep -qE '^ord-both[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+3[[:space:]]+2[[:space:]]'
printf '%s' "$ORD_OUT" | grep -qE '^ord-both[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+3[[:space:]]+2:0d[[:space:]]'
check "INN 3 and ORDRE 2 are printed side by side, never summed" $?
printf '%s' "$ORD_OUT" | grep -qE '^ord-none[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+0[[:space:]]'
check "a repo with no mail and no orders reads 0 in both columns" $?
@ -2198,9 +2198,9 @@ mkorder fly-mixed claimed 20260104T000000Z-4-from-d
FLY_OUT="$("$BOARD" --roots "$FLY_ROOT" 2>/dev/null)"
printf '%s' "$FLY_OUT" | grep -q 'FLY'; check "table header carries a FLY column" $?
printf '%s' "$FLY_OUT" | grep -qE '^fly-mixed[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+2[[:space:]]+1[[:space:]]'
printf '%s' "$FLY_OUT" | grep -qE '^fly-mixed[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+2:0d[[:space:]]+1:0d[[:space:]]'
check "ORDRE 2 and FLY 1 print side by side, never summed into 3" $?
printf '%s' "$FLY_OUT" | grep -qE '^fly-inflight[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+0[[:space:]]+1[[:space:]]'
printf '%s' "$FLY_OUT" | grep -qE '^fly-inflight[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+0[[:space:]]+1:0d[[:space:]]'
check "an order in flight reads ORDRE 0 / FLY 1, not ORDRE 0 alone" $?
printf '%s' "$FLY_OUT" | grep -qE '^fly-empty[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+0[[:space:]]+0[[:space:]]'
check "known-positive: a repo with no orders at all still reads 0 in both" $?
@ -2562,6 +2562,117 @@ 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
# --- 28. ORDRE and FLY carry the AGE of the oldest order ---------------------
# Order 20260829T045435Z-276822386 (.claude, 2026-08-29). ORDRE and FLY were
# pure counts (`ls ... | wc -l`), so ORDRE 2 read identically whether both
# orders arrived yesterday or both had sat for ten days. Measured that day
# across the live mailbox with `find ~/.claude/coord -path '*/orders/*.md'
# -maxdepth 3 -type f`: 30 pending orders over 22 repos, 5 of them ten days
# old. coord-order-inbox.sh does show an age - but only inside the one repo, at
# SessionStart, so a repo nobody opens shows nobody anything.
#
# The age is the OLDEST pending order, never the newest and never a mean: a
# repo holding one fresh order and one ten-day-old order has a ten-day-old
# problem, and the newest reading is precisely what would hide it. This is not
# a new mechanism in the file - ALDER (STATE.md mtime) and SISTE (last commit)
# already read a clock the same way.
AGROOT="$(mktemp -d)"
AGCOORD="$(mktemp -d)"
AG_OLD="$(date -v-10d +%Y%m%d%H%M)"
mk_ag_repo() {
mkrepo "$AGROOT/$1"
{
echo "# STATE - $1"
echo ""
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
echo "<!-- board: status=in-progress; blocked-on=-; next-cost=Sonnet 5/high -->"
echo "Et steg."
} > "$AGROOT/$1/STATE.md"
}
mk_ag_order() { # $1=repo $2=subdir ("orders" or "orders/claimed") $3=file $4=touch-stamp or "-"
mkdir -p "$AGCOORD/$1/$2"
printf -- '---\nfrom: x\nto: %s\norder-id: %s\n---\nDo a thing.\n' "$1" "$3" \
> "$AGCOORD/$1/$2/$3.md"
[ "$4" = "-" ] || touch -t "$4" "$AGCOORD/$1/$2/$3.md"
}
# ag-old: one fresh order AND one ten days old. The whole point of the section.
mk_ag_repo ag-old
mk_ag_order ag-old orders 20260829T000000Z-fresh -
mk_ag_order ag-old orders 20260818T000000Z-stale "$AG_OLD"
# ag-fresh: KNOWN-POSITIVE CONTROL. Two orders, both fresh. If the assertion
# below could not tell these two repos apart it would pass on anything - this
# is the tree "where the age is wrong" that proves the check can fail.
mk_ag_repo ag-fresh
mk_ag_order ag-fresh orders 20260829T000001Z-a -
mk_ag_order ag-fresh orders 20260829T000002Z-b -
# ag-none: no orders at all. "0 days old" and "no orders" are two different
# facts and must not render the same - the same reason FLY exists next to ORDRE.
mk_ag_repo ag-none
# ag-fly: nothing pending, one order claimed ten days ago. FLY is the column
# that most needs this: nothing un-claims an order when the session dies, so a
# claim with no age is the 117-hour claim with its only counter-evidence removed.
mk_ag_repo ag-fly
mk_ag_order ag-fly orders/claimed 20260818T000001Z-inflight "$AG_OLD"
AGOUT="$(CLAUDE_COORD_DIR="$AGCOORD" "$BOARD" --roots "$AGROOT" 2>/dev/null)"
ag_row() { printf '%s' "$AGOUT" | grep "^$1 "; }
# Ground truth for the fixture itself: the stale file really is ~10 days old.
# Asserted before anything depends on it, so a broken `touch -t` fails here
# rather than silently turning the checks below into a test of nothing.
ag_gt="$(( ( $(date +%s) - $(stat -f %m "$AGCOORD/ag-old/orders/20260818T000000Z-stale.md") ) / 86400 ))"
[ "$ag_gt" -eq 10 ]
check "orderage: ground truth - the fixture's stale order really is 10 days old" $?
ag_cells() { ag_row "$1" | cut -c89- ; }
[ "$(ag_cells ag-old | awk '{print $1}')" = "2:10d" ]
check "orderage: ORDRE shows the OLDEST pending order's age, not the newest" $?
[ "$(ag_cells ag-old | awk '{print $1}')" = "2:0d" ]; [ $? -ne 0 ]
check "orderage: a fresh order alongside a stale one does not hide the stale one" $?
[ "$(ag_cells ag-fresh | awk '{print $1}')" = "2:0d" ]
check "orderage: control - two genuinely fresh orders read 0d" $?
[ "$(ag_cells ag-fresh | awk '{print $1}')" = "2:10d" ]; [ $? -ne 0 ]
check "orderage: control - the 10d assertion can FAIL, so it measures something" $?
# Absence must not borrow the shape of a measurement. Field-exact, not a
# substring search: the row also carries ALDER and SISTE, which legitimately
# print day counts of their own. Cut by COLUMN, not by whitespace field: the
# KOST cell ("Sonnet 5/high") contains a space, so counting tokens from either
# end lands on the wrong cell. 88 = 32 REPO + 34 STATUS + 14 KOST + 4 INN, plus
# the four single spaces between them; ORDRE and FLY are the next two tokens.
ag_none_ordre="$(ag_cells ag-none | awk '{print $1}')"
[ "$ag_none_ordre" = "0" ]
check "orderage: no pending orders renders a bare 0, never '0:0d'" $?
printf '%s' "$ag_none_ordre" | grep -q 'd'; [ $? -ne 0 ]
check "orderage: 'no orders' and '0 days old' do not render the same" $?
[ "$(ag_cells ag-fly | awk '{print $2}')" = "1:10d" ]
check "orderage: FLY carries its own age for a claimed order" $?
ag_fly_ordre="$(ag_cells ag-fly | awk '{print $1}')"
[ "$ag_fly_ordre" = "0" ]
check "orderage: a claimed order is still not counted in ORDRE" $?
# The legend is engine behaviour here, exactly as FLY's liveness denial is
# (section 24): a column whose form nothing pins is a column that drifts.
printf '%s' "$AGOUT" | grep -qi 'ELDSTE ordren'
check "orderage: the legend states the age is the OLDEST order, not the newest" $?
# No new sort axis (order requirement 3): the age is a DISPLAY value. Adding it
# to the sort would silently reorder a parser living in another repo, which
# board.sh:87-116 already forbids. ag-fresh and ag-old are both in-progress with
# identical STATE mtimes, so ordering stays alphabetical, not oldest-order-first.
ag_first="$(printf '%s' "$AGOUT" | grep -E '^ag-(old|fresh) ' | head -1 | awk '{print $1}')"
[ "$ag_first" = "ag-fresh" ]
check "orderage: order age is display-only - it does not reorder the board" $?
/bin/rm -rf "$AGROOT" "$AGCOORD" 2>/dev/null
echo ""
echo "board-selftest: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ] || exit 1