#!/bin/bash # board-selftest.sh - prove board.sh end-to-end against a throwaway repo tree # and a throwaway mailbox (never touches ~/repos or ~/.claude/coord). Re-run # after any edit to board.sh. ASCII only, bash 3.2 safe. # # Multibyte STATE.md content (em-dash, the NESTE pointing-hand) is generated # with printf octal escapes so this script's own source stays pure ASCII - a # literal em-dash in shell source has crashed bash 3.2 under `set -u` before. set -u export LC_ALL=C DIR="$(cd "$(dirname "$0")" && pwd)" BOARD="$DIR/board.sh" ROOT="$(mktemp -d)" CLAUDE_COORD_DIR="$(mktemp -d)" export CLAUDE_COORD_DIR cleanup() { /bin/rm -rf "$ROOT" "$CLAUDE_COORD_DIR" 2>/dev/null; } trap cleanup EXIT PASS=0; FAIL=0 check() { if [ "$2" -eq 0 ]; then PASS=$((PASS+1)); echo " ok - $1"; else FAIL=$((FAIL+1)); echo " FAIL - $1"; fi; } # Multibyte building blocks (octal escapes keep this source ASCII). EMDASH="$(printf '\342\200\224')" HAND="$(printf '\360\237\221\211')" OSLASH="$(printf '\303\270')" mkrepo() { mkdir -p "$1" && git -C "$1" init -q 2>/dev/null; } echo "board-selftest (root: $ROOT, mailbox: $CLAUDE_COORD_DIR)" # --- 0. Empty root: no repos, still exits cleanly. --- out0="$("$BOARD" --roots "$ROOT" 2>/dev/null)"; rc=$? [ "$rc" -eq 0 ]; check "empty root exits 0" $? # --- Fixture tree --------------------------------------------------------- # repo-a: full board line, in-progress, cheap next step, unhandled inbox. mkrepo "$ROOT/repo-a" { echo "# STATE - repo-a" echo "" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" printf '**Lukk to MAJOR fra reviewen** %s deretter S3.3 concurrent fan-out.\n' "$EMDASH" } > "$ROOT/repo-a/STATE.md" # repo-b: STATE + NESTE prose but NO board line (the un-backfilled majority). mkrepo "$ROOT/repo-b" { echo "# STATE - repo-b" echo "" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" printf '> Kj%sr forskningstema 1 headless %s hard gate for motorens arkitektur.\n' "$OSLASH" "$EMDASH" } > "$ROOT/repo-b/STATE.md" # repo-c: no STATE.md at all (dormant class). mkrepo "$ROOT/repo-c" # repo-d: dirty working tree. mkrepo "$ROOT/repo-d" { echo "# STATE - repo-d" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Parkert med vilje." } > "$ROOT/repo-d/STATE.md" # Commit STATE.md first, so the dirty count proves board counts UNCOMMITTED # files only (2 junk files) rather than every file in a fresh tree. git -C "$ROOT/repo-d" add STATE.md >/dev/null 2>&1 git -C "$ROOT/repo-d" -c user.email=t@t -c user.name=t commit -qm init >/dev/null 2>&1 : > "$ROOT/repo-d/junk1.txt" : > "$ROOT/repo-d/junk2.txt" # repo-e: malformed status token (must be flagged, never silently accepted). mkrepo "$ROOT/repo-e" { echo "# STATE - repo-e" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Ugyldig token." } > "$ROOT/repo-e/STATE.md" # repo-g: prose containing a 'board:'-lookalike ABOVE the real board line. # An unanchored substring grep would match 'dashboard:' first and mis-parse the # whole repo; the board line is defined as living under the NESTE heading. mkrepo "$ROOT/repo-g" { echo "# STATE - repo-g" echo "Vi bygde et dashboard: status=done ble diskutert i forrige runde." printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Ekte neste steg her." } > "$ROOT/repo-g/STATE.md" # repo-h: next-cost written the way the model rubric actually names the models, # with a space and capitals ("Sonnet 5/xhigh"). This is spec-conformant # (/) and must parse - a lowercase-only pattern silently drops # it to "?" and hides the cost column exactly where it is needed. mkrepo "$ROOT/repo-h" { echo "# STATE - repo-h" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Neste steg her." } > "$ROOT/repo-h/STATE.md" # polyrepo/: NOT a git repo itself, but holds git repos one level down. mkdir -p "$ROOT/polyrepo" mkrepo "$ROOT/polyrepo/plug-x" { echo "# STATE - plug-x" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" printf 'Venter p%s amendment-pakken.\n' "$EMDASH" } > "$ROOT/polyrepo/plug-x/STATE.md" # repo-wt + wt-feature: a git WORKTREE (and a submodule) has .git as a FILE, # not a directory. A `git worktree add /feature-x` yields a sibling # directory that can CARRY its own STATE.md. Testing discovery for a .git # DIRECTORY only drops it silently - the same silent-loss class as the V6 # marker fix. mkrepo "$ROOT/repo-wt" { echo "# STATE - repo-wt" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" printf 'Hovedtreet %s ferdig.\n' "$EMDASH" } > "$ROOT/repo-wt/STATE.md" git -C "$ROOT/repo-wt" add STATE.md >/dev/null 2>&1 git -C "$ROOT/repo-wt" -c user.email=t@t -c user.name=t commit -qm init >/dev/null 2>&1 git -C "$ROOT/repo-wt" -c user.email=t@t -c user.name=t \ worktree add -q -b feature-x "$ROOT/wt-feature" >/dev/null 2>&1 # Distinct board line, so the check proves board read the STATE INSIDE the # worktree rather than matching the source repo's row by accident. { echo "# STATE - wt-feature" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" printf 'Arbeid i worktree %s eget neste steg.\n' "$EMDASH" } > "$ROOT/wt-feature/STATE.md" # repo-old: fresh STATE.md, last COMMIT 400 days old. This is the case the # fixture tree was missing entirely: with age read off STATE.md's mtime, a repo # whose work stopped long ago looks exactly as alive as one touched this # morning, and no column contradicts it. mkrepo "$ROOT/repo-old" { echo "# STATE - repo-old" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Ser levende ut, men ingen commit paa lenge." } > "$ROOT/repo-old/STATE.md" OLD_EPOCH=$(( $(date +%s) - 400 * 86400 )) git -C "$ROOT/repo-old" add STATE.md >/dev/null 2>&1 # %ct is the COMMITTER date. `--date=` sets the AUTHOR date only and would # leave %ct at now, so the fixture would be backdated in name only. GIT_AUTHOR_DATE="@$OLD_EPOCH +0000" GIT_COMMITTER_DATE="@$OLD_EPOCH +0000" \ git -C "$ROOT/repo-old" -c user.email=t@t -c user.name=t commit -qm init >/dev/null 2>&1 # repo-stale: the mirror image - STATE.md untouched since a fixed past date, # last commit today. The pair pins that the two ages are independent readings # and that neither of them silently became the sort key. mkrepo "$ROOT/repo-stale" { echo "# STATE - repo-stale" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Gammel STATE, fersk commit." } > "$ROOT/repo-stale/STATE.md" git -C "$ROOT/repo-stale" add STATE.md >/dev/null 2>&1 git -C "$ROOT/repo-stale" -c user.email=t@t -c user.name=t commit -qm init >/dev/null 2>&1 touch -t 202601010000 "$ROOT/repo-stale/STATE.md" # plain-dir/: no git repo anywhere under it - must be ignored entirely. mkdir -p "$ROOT/plain-dir/sub" echo "hei" > "$ROOT/plain-dir/sub/file.txt" # Coord fixture: 3 unhandled for repo-a, 1 archived (must not be counted). mkdir -p "$CLAUDE_COORD_DIR/repo-a/inbox" "$CLAUDE_COORD_DIR/repo-a/archive" for n in 1 2 3; do echo "msg" > "$CLAUDE_COORD_DIR/repo-a/inbox/2026-msg$n-from-x.md"; done echo "old" > "$CLAUDE_COORD_DIR/repo-a/archive/2026-old-from-x.md" # repo-owes: unhandled inbox AND a route line, so the briefing can derive the # EXACT startup command - advisor flag included - from the repo's own four # traits instead of guessing from next-cost alone. Its NESTE runs deliberately # past the table's 38-character column: carrying that line whole is the reason # the briefing exists at all. mkrepo "$ROOT/repo-owes" { echo "# STATE - repo-owes" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "" echo "Svar org-ops om badge-formatet, deretter bump katalogens ref og verifiser check-versions." } > "$ROOT/repo-owes/STATE.md" mkdir -p "$CLAUDE_COORD_DIR/repo-owes/inbox" echo "msg" > "$CLAUDE_COORD_DIR/repo-owes/inbox/2026-msg1-from-y.md" # repo-typo: a route line that EXISTS but carries a value outside route.sh's # closed set. This is the case that actually occurs as route lines are written # by hand across repos, and it is distinct from having no route line at all: # route.sh exits 2, the command comes back empty, and the briefing must say so # rather than print a bare "$ " that reads as a runnable command. mkrepo "$ROOT/repo-typo" { echo "# STATE - repo-typo" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "" echo "Neste steg for repo-typo." } > "$ROOT/repo-typo/STATE.md" mkdir -p "$CLAUDE_COORD_DIR/repo-typo/inbox" echo "msg" > "$CLAUDE_COORD_DIR/repo-typo/inbox/2026-msg1-from-y.md" # --- Focus fixtures -------------------------------------------------------- # The topic marker grammar is the register's, not this repo's: a STATE.md line # of the form `: prose`. board.sh only ever READS # it, and reads it from STATE.md alone - it must never start opening # register.md to learn the vocabulary, so the slug set is whatever the scanned # STATE.md files declare. # # repo-focus-plain declares the slug in the bare form. mkrepo "$ROOT/repo-focus-plain" { echo "# STATE - repo-focus-plain" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Neste steg for repo-focus-plain." echo "" printf -- '- demo-topic: in-progress %s wired at the boundary, ingen apen trad\n' "$EMDASH" } > "$ROOT/repo-focus-plain/STATE.md" # repo-focus-bold: the SAME declaration wrapped in bold and backticks. This is # the class the register's own grep loses, and it was the most consequential # find in the measurement that produced the decision - a heavy consumer made # invisible by two asterisks. A reader that only accepts the bare form # reproduces that defect. mkrepo "$ROOT/repo-focus-bold" { echo "# STATE - repo-focus-bold" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Neste steg for repo-focus-bold." echo "" printf -- '- **`demo-topic`**: planned %s pin bumpet, ikke startet\n' "$EMDASH" } > "$ROOT/repo-focus-bold/STATE.md" # repo-focus-mentions: names the slug in prose and declares NOTHING. This is # the held-back class the report exists for. It is in-progress with a live # next step, so the cutoff genuinely removes a repo that would otherwise be a # tab - which is what makes silence about it a lie rather than a rounding. mkrepo "$ROOT/repo-focus-mentions" { echo "# STATE - repo-focus-mentions" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Vi pinner demo-topic i byggefila, men har aldri skrevet en markorlinje." } > "$ROOT/repo-focus-mentions/STATE.md" # repo-focus-other: declares a DIFFERENT slug sharing one hyphen-part, so # "topic" resolves to two slugs and "demo" to one. Ambiguity must widen the # answer and say so, never silently pick a winner. mkrepo "$ROOT/repo-focus-other" { echo "# STATE - repo-focus-other" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" echo "Neste steg for repo-focus-other." echo "" printf -- '- other-topic: planned %s ikke startet\n' "$EMDASH" } > "$ROOT/repo-focus-other/STATE.md" OUT="$("$BOARD" --roots "$ROOT" 2>/dev/null)" # --- 1. Discovery --------------------------------------------------------- printf '%s' "$OUT" | grep -q 'repo-a'; check "discovers top-level git repo" $? printf '%s' "$OUT" | grep -q 'plug-x'; check "discovers nested polyrepo git repo (depth 2)" $? printf '%s' "$OUT" | grep -q 'repo-c'; check "lists git repo without STATE.md" $? printf '%s' "$OUT" | grep -q 'plain-dir'; [ $? -ne 0 ]; check "ignores non-git directory tree" $? printf '%s' "$OUT" | grep -q 'polyrepo'; [ $? -ne 0 ]; check "polyrepo container itself is not listed as a repo" $? # Guard the fixture itself: if git ever stops writing a .git FILE for # worktrees, the next two checks would pass for the wrong reason. [ -f "$ROOT/wt-feature/.git" ]; check "fixture: worktree .git is a FILE, not a dir" $? printf '%s' "$OUT" | grep -q 'wt-feature'; check "discovers git worktree (.git is a file)" $? printf '%s' "$OUT" | grep -qE 'wt-feature.*fable/xhigh' check "reads STATE.md from inside the worktree, not the source repo" $? # --- 2. Board line parsing (the B field) ---------------------------------- printf '%s' "$OUT" | grep -q 'in-progress'; check "parses status token from board line" $? printf '%s' "$OUT" | grep -q 'sonnet/xhigh'; check "parses next-cost from board line" $? printf '%s' "$OUT" | grep -qE 'repo-h.*Sonnet 5/xhigh' check "next-cost accepts spaces and capitals (rubric model names)" $? printf '%s' "$OUT" | grep -qE 'plug-x.*repo-a' check "blocked repo names its blocker on its own row" $? printf '%s' "$OUT" | grep -q 'deferred'; check "distinguishes deferred from blocked" $? # --- 3. Malformed input is flagged, not swallowed ------------------------- printf '%s' "$OUT" | grep -qi 'malformed\|ugyldig\|invalid'; check "malformed status token is flagged" $? # A 'dashboard:' lookalike earlier in the file must not win over the real line. printf '%s' "$OUT" | grep -qE 'repo-g.*planned.*opus/xhigh' check "board-line parse ignores 'board:' lookalikes in prose" $? # --- 4. Heuristic fallback when board line is absent ----------------------- printf '%s' "$OUT" | grep -q 'repo-b'; check "repo without board line still listed" $? printf '%s' "$OUT" | grep -qi 'forskningstema\|headless'; check "shows NESTE excerpt when board line absent" $? # Markdown noise must be stripped: a blockquoted NESTE line renders as prose. printf '%s' "$OUT" | grep -qE 'repo-b.*[[:space:]]>'; [ $? -ne 0 ] check "blockquote marker stripped from NESTE excerpt" $? # --- 5. Git + coord signals ------------------------------------------------ printf '%s' "$OUT" | grep -qE 'repo-d.*[^0-9]2([^0-9]|$)'; check "reports dirty file count" $? printf '%s' "$OUT" | grep -qE 'repo-a.*[^0-9]3([^0-9]|$)'; check "reports unhandled coord inbox count (archive excluded)" $? # --- 6. Bucketing ---------------------------------------------------------- printf '%s' "$OUT" | grep -qi 'blocked'; check "blocked repo surfaced" $? printf '%s' "$OUT" | grep -qiE 'dormant|sovende|uten STATE'; check "STATE-less repos bucketed separately" $? # --- 7. Robustness --------------------------------------------------------- # Multibyte prose must not crash the reader nor emit split-character garbage. [ -n "$OUT" ]; check "produces output over multibyte STATE prose" $? printf '%s' "$OUT" | iconv -f UTF-8 -t UTF-8 >/dev/null 2>&1 check "output is valid UTF-8 (no split multibyte truncation)" $? # A STATE.md with a NESTE heading and nothing after it must not hang or crash. mkrepo "$ROOT/repo-f" { echo "# STATE - repo-f"; printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"; } > "$ROOT/repo-f/STATE.md" "$BOARD" --roots "$ROOT" >/dev/null 2>&1; check "empty NESTE block does not crash" $? "$BOARD" --help >/dev/null 2>&1; check "--help exits 0" $? # Unreadable root is a no-op, not a crash. "$BOARD" --roots "$ROOT/does-not-exist" >/dev/null 2>&1; check "missing root is a clean no-op" $? # --- 8. The documented next-cost form IS the convention -------------------- # The cost field is free text on purpose: the parser reads to ';' or '-->' so # the rubric's spacing survives, and the fixtures above deliberately vary the # spelling to pin that leniency. But leniency on the read side means the ONLY # thing holding the field uniform is what --help prescribes, and --help once # prescribed a versionless `sonnet/xhigh` while the global rubric spelled # `Sonnet 5 / xhigh`. Two specs for one field: measured across the real tree, # not one board line used ours, and eight distinct strings were in circulation. # Pinned here because cleaning the DATA cannot fix that - a spec that # contradicts the rubric regenerates the divergence every session. HELP="$("$BOARD" --help 2>/dev/null)" printf '%s' "$HELP" | grep -q 'next-cost=Sonnet 5/xhigh' check "--help prescribes the canonical next-cost form" $? # The versionless form is what the rubric does NOT use; no example may show it. if printf '%s' "$HELP" | grep -qE '(sonnet|opus|fable)/(high|xhigh)'; then rc=1; else rc=0; fi check "--help shows no versionless model example" "$rc" # --- 9. SISTE: last commit, read for EVERY repo ---------------------------- # One column, one meaning. ALDER answers "when was the plan last touched" and # is blank where there is no plan; SISTE answers "when did anything last land" # and is read for every repo. Before this, a single ALDER column carried both # readings depending on which branch a repo fell into, so a repo with a STATE.md # could go a year without a commit and show nothing at all. [ "$(git -C "$ROOT/repo-old" log -1 --format=%ct 2>/dev/null)" = "$OLD_EPOCH" ] check "fixture: repo-old's COMMITTER date is actually backdated" $? printf '%s' "$OUT" | grep -q 'SISTE'; check "table has a SISTE column" $? printf '%s\n' "$OUT" | grep -qE '^repo-old .*[[:space:]]0d[[:space:]]+40[01]d[[:space:]]' check "fresh STATE.md + old commit: ALDER stays 0d, SISTE shows the 400 days" $? printf '%s\n' "$OUT" | grep -qE '^repo-stale .*[[:space:]][1-9][0-9]*d[[:space:]]+0d[[:space:]]' check "old STATE.md + fresh commit: the two ages read independently" $? # A repo with no commits has no last commit to report. A day count there would # be a fabricated reading, and the pre-existing -1d was exactly that. # The -n guard is load-bearing: an absent row would leave crow empty, and an # empty string trivially contains no day count - the check would pass by # finding nothing rather than by reading the row. crow="$(printf '%s\n' "$OUT" | grep '^repo-c ')" if [ -n "$crow" ] && ! printf '%s' "$crow" | grep -qE '[0-9]+d'; then rc=0; else rc=1; fi check "repo with no commits reports no day count in either age column" "$rc" # The four ranking rules stay four: SISTE is evidence the operator reads, never # an input to the order. If it had become the sort key, repo-old (400d) would # fall behind repo-stale (0d) instead of ahead of it. n_old="$(printf '%s\n' "$OUT" | grep -n '^repo-old ' | cut -d: -f1)" n_stale="$(printf '%s\n' "$OUT" | grep -n '^repo-stale ' | cut -d: -f1)" [ -n "$n_old" ] && [ -n "$n_stale" ] && [ "$n_old" -lt "$n_stale" ] check "SISTE is evidence, not a ranking input (order still follows ALDER)" $? # --- 8. Briefing rendering (--brief) -------------------------------------- # A second rendering of the SAME scan, never a second scan: the briefing # answers "who is waiting on me and what does answering cost", where the table # answers "what is the state of every repo". BRIEF="$("$BOARD" --roots "$ROOT" --brief 2>/dev/null)" printf '%s' "$BRIEF" | grep -q 'repo-owes' check "brief includes a repo with an unhandled inbox" $? printf '%s' "$BRIEF" | grep -q 'repo-b'; [ $? -ne 0 ] check "brief excludes a repo that owes nothing" $? # The table cuts NESTE at 38 characters. The briefing carries the whole line, # so the tail past the cut is the thing being proved here. printf '%s' "$BRIEF" | grep -q 'check-versions' check "brief prints the full NESTE line, not the 38-char table excerpt" $? # Derived by CALLING route.sh with the repo's own four traits - deliberately # not spelled out in board.sh - so the rubric keeps exactly one copy. Row 1 # carries the advisor, which is what makes the quota fallback safe to take. printf '%s' "$BRIEF" | grep -q 'claude --model sonnet --effort high --advisor opus' check "brief derives the exact startup command from the repo's route line" $? # The command must start its OWN line. `fold` copies its input's missing # trailing newline, which ran the command onto the tail of the NESTE prose and # produced a briefing whose commands could not be copied - caught by eye # against the real tree, which is exactly what a check is for. printf '%s' "$BRIEF" | grep -qE '^ \$ claude --model' check "startup command starts on its own line, not appended to NESTE prose" $? # A route line that parses to a value route.sh rejects must degrade the same # way a missing one does. Silence here would print a bare '$ ' - the shape of a # runnable command, carrying nothing - which is worse than an absent command # because it looks copied-and-pasteable. printf '%s' "$BRIEF" | grep -A4 'repo-typo' | grep -qi 'route' check "unparseable route line degrades to a marker, not to an empty command" $? printf '%s' "$BRIEF" | grep -A4 'repo-typo' | grep -qE '^ \$ claude'; [ $? -ne 0 ] check "unparseable route line never emits a command line at all" $? # repo-a owes 3 messages and has no route line. A fabricated command would be # worse than none: it would read as authoritative while being a guess. printf '%s' "$BRIEF" | grep -A4 'repo-a' | grep -qi 'route' check "repo owing mail but lacking a route line is marked, never guessed at" $? # Read-only by construction, and the briefing must not erode it. Reading IS # delivering in this engine, so a survey that consumed a backlog would be # precisely the defect the whole design forbids. snap() { find "$1" -type f -exec stat -f '%N %m %z' {} \; 2>/dev/null | sort; } b4="$(snap "$CLAUDE_COORD_DIR")" "$BOARD" --roots "$ROOT" --brief >/dev/null 2>&1 [ "$b4" = "$(snap "$CLAUDE_COORD_DIR")" ] check "brief writes nothing to the mailbox (reading is delivering)" $? # A mailbox can carry a name the repo scan will NEVER produce: a declared # non-git surface (CLAUDE_COORD_REPO, e.g. ~/repos itself) or a checkout # outside the scan roots. board.sh discovers git repos, so such a mailbox is # invisible to every column it prints - and the briefing exists to answer "who # is waiting on you", where dropping one silently is the loss-wearing-the- # shape-of-normal class this engine keeps naming. Measured against the real # mailbox before this check existed: 11 repos / 21 messages in the briefing # against 12 mailboxes / 22 pending in coord-count. mkdir -p "$CLAUDE_COORD_DIR/orphan-surface/inbox" echo "msg" > "$CLAUDE_COORD_DIR/orphan-surface/inbox/2026-msg1-from-z.md" BRIEF2="$("$BOARD" --roots "$ROOT" --brief 2>/dev/null)" printf '%s' "$BRIEF2" | grep -q 'orphan-surface' check "brief surfaces a pending mailbox that has no repo in the scan tree" $? # It must be reported as its own class, not silently folded in among repos the # scan actually found - there is no STATE.md behind it and so no next step. printf '%s' "$BRIEF2" | grep -qi 'utenfor repo-skannen\|uten repo' check "orphan mailbox is reported as its own class, not as a scanned repo" $? # --- 9. brief-nightly.sh: the only writer, and it writes atomically ------- # board.sh stays read-only, so the file write lives in a separate wrapper. # Unattended overwrite must never expose a half-written briefing: the wrapper # renders to a temp file in the same directory and renames it into place. NIGHTLY="$DIR/brief-nightly.sh" OUTFILE="$ROOT/briefing.md" CLAUDE_BRIEF_FILE="$OUTFILE" BOARD_ROOTS="$ROOT" bash "$NIGHTLY" >/dev/null 2>&1 check "brief-nightly exits 0" $? [ -f "$OUTFILE" ] && grep -q 'repo-owes' "$OUTFILE" check "brief-nightly writes the briefing to CLAUDE_BRIEF_FILE" $? ls "$OUTFILE".tmp.* >/dev/null 2>&1; [ $? -ne 0 ] check "brief-nightly leaves no temp file behind" $? # An EMPTY render is a failed render, and must leave the previous briefing # standing rather than truncating it to nothing - the failure mode a plain # `> file` redirect has, and the reason the temp-then-rename exists. A repo # tree where nobody owes anything is NOT this case: that renders a valid, # non-empty briefing saying so, and is written normally. echo "FORRIGE BRIEFING" > "$OUTFILE" CLAUDE_BRIEF_FILE="$OUTFILE" BOARD_ROOTS="$ROOT/does-not-exist" \ bash "$NIGHTLY" >/dev/null 2>&1 grep -q 'FORRIGE BRIEFING' "$OUTFILE" check "empty render never overwrites the previous briefing" $? # --- 10. Day-plan rendering (--plan) --------------------------------------- # A THIRD rendering of the same scan, on exactly the argument --brief was built # on. The table answers "what is the state of every repo"; the briefing answers # "who is waiting on me"; the plan answers "which repos do I open a tab for # today, in what order, with which command". It has TWO consumers - the operator # pasting commands, and a separate driver repo consuming the plan - which is why # it renders key=value blocks rather than prose: prose would make the FORMAT an # API that no test in this repo could hold stable. # # repo-blocked-owes: blocked AND owing mail. The exclusion of `blocked` is about # a repo's OWN next step, which by definition cannot be moved; answering a # message is a different axis, and is often precisely what unblocks it. Without # this fixture the debt-first rule and the status exclusion never meet, and # whichever one was written second would silently win. mkrepo "$ROOT/repo-blocked-owes" { echo "# STATE - repo-blocked-owes" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" printf 'Venter p%s repo-a, men skylder to svar.\n' "$EMDASH" } > "$ROOT/repo-blocked-owes/STATE.md" mkdir -p "$CLAUDE_COORD_DIR/repo-blocked-owes/inbox" for n in 1 2; do echo "msg" > "$CLAUDE_COORD_DIR/repo-blocked-owes/inbox/2026-msg$n-from-w.md" done # repo-done-owes: done AND owing mail. Same rule as repo-blocked-owes, but this # is the shape that actually occurs - measured on the real tree at 0.16.0, two # of the 26 planned repos were `done` with an unhandled inbox, and none were # blocked-with-debt. A repo whose own work is finished can still owe an answer. mkrepo "$ROOT/repo-done-owes" { echo "# STATE - repo-done-owes" printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH" echo "" printf 'Arbeidet er ferdig %s men innboksen er ikke gjort opp.\n' "$EMDASH" } > "$ROOT/repo-done-owes/STATE.md" mkdir -p "$CLAUDE_COORD_DIR/repo-done-owes/inbox" echo "msg" > "$CLAUDE_COORD_DIR/repo-done-owes/inbox/2026-msg1-from-w.md" PLAN="$("$BOARD" --roots "$ROOT" --plan 2>/dev/null)"; rc=$? check "--plan exits 0" "$rc" printf '%s' "$PLAN" | grep -q '^# PLAN ' check "plan carries a header naming what it is" $? # Machine-readable by construction: the driver repo reads these keys. printf '%s' "$PLAN" | grep -q '^tab=1$' check "plan numbers tabs starting at 1" $? # Debt outranks everything, most-owed first. repo-a owes 3, repo-blocked-owes 2, # repo-owes and repo-typo 1 each. [ "$(printf '%s\n' "$PLAN" | grep -A1 '^tab=1$' | grep '^repo=' | cut -d= -f2)" = "repo-a" ] check "the repo owing the most messages is tab 1" $? n_a="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-a$' | cut -d: -f1)" n_bo="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-blocked-owes$' | cut -d: -f1)" n_ow="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-owes$' | cut -d: -f1)" [ -n "$n_a" ] && [ -n "$n_bo" ] && [ -n "$n_ow" ] \ && [ "$n_a" -lt "$n_bo" ] && [ "$n_bo" -lt "$n_ow" ] check "debt group is ordered by message count, descending" $? # The status exclusion is about a repo's own next step, never about its debt. printf '%s' "$PLAN" | grep -q '^repo=repo-blocked-owes$' check "a BLOCKED repo that owes mail is still planned (debt is the other axis)" $? printf '%s\n' "$PLAN" | grep -A4 '^repo=repo-blocked-owes$' | grep -q '^why=inbox:2$' check "a blocked repo in the plan says debt, not status, is why it is there" $? # Debt before live work: repo-h is in-progress and owes nothing. n_h="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-h$' | cut -d: -f1)" [ -n "$n_h" ] && [ "$n_ow" -lt "$n_h" ] check "every repo owing mail is planned before any that owes none" $? # repo-a is BOTH in-progress and owing. It must appear exactly once - a repo # listed twice is two tabs for one repo, which is the plan failing at its job. [ "$(printf '%s\n' "$PLAN" | grep -c '^repo=repo-a$')" -eq 1 ] check "a repo qualifying under two rules is planned exactly once" $? printf '%s\n' "$PLAN" | grep -A3 '^repo=repo-a$' | grep -q '^why=inbox:3$' check "why= names the rule that put the repo in the plan" $? # in-progress outranks planned among the repos owing nothing. n_g="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-g$' | cut -d: -f1)" [ -n "$n_g" ] && [ "$n_h" -lt "$n_g" ] check "live work (in-progress) is planned before merely planned work" $? # Excluded classes. A tab for a done or deferred repo is noise; a tab for a # blocked repo that owes nothing cannot be moved at all. printf '%s' "$PLAN" | grep -q '^repo=repo-wt$'; [ $? -ne 0 ] check "plan excludes a done repo that owes nothing" $? # ...but the exclusion is conditional on owing nothing, and the header says so. # Read the other way, the plan's own header would call these entries a bug. printf '%s' "$PLAN" | grep -q '^repo=repo-done-owes$' check "a DONE repo that owes mail is still planned" $? printf '%s\n' "$PLAN" | grep -A4 '^repo=repo-done-owes$' | grep -q '^status=done$' check "a done repo in the plan still reports its real status" $? printf '%s\n' "$PLAN" | grep -m1 '^# Utelatt' | grep -q 'skylder svar' check "the header states the exclusions are conditional on owing nothing" $? printf '%s' "$PLAN" | grep -q '^repo=repo-d$'; [ $? -ne 0 ] check "plan excludes a deferred repo" $? printf '%s' "$PLAN" | grep -q '^repo=plug-x$'; [ $? -ne 0 ] check "plan excludes a blocked repo that owes nothing" $? printf '%s' "$PLAN" | grep -q '^repo=repo-c$'; [ $? -ne 0 ] check "plan excludes a repo with no STATE.md and no debt" $? # A repo with no board line has no declared next step, but dropping it SILENTLY # is what the table's own MERK line exists to prevent. It is planned last and # labelled, never omitted. printf '%s' "$PLAN" | grep -q '^repo=repo-b$' check "a repo lacking a board line is planned, not silently dropped" $? printf '%s\n' "$PLAN" | grep -A3 '^repo=repo-b$' | grep -q '^why=uavklart$' check "a repo with unknown status is labelled uavklart, not guessed into a rule" $? n_b="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-b$' | cut -d: -f1)" [ -n "$n_b" ] && [ "$n_g" -lt "$n_b" ] check "uavklart repos rank below every repo that declared a status" $? # The command comes from route.sh, same single copy of the rubric the briefing # uses - advisor flag included, since that is a property of the ROW. printf '%s' "$PLAN" | grep -q '^command=claude --model sonnet --effort high --advisor opus$' check "plan derives the exact startup command from the repo's route line" $? # Both no-command causes must degrade to a marker. A bare `command=` would be # the shape of a runnable command carrying nothing - worse than none, because a # driver reading `^command=` would type an empty line into a live pane. printf '%s' "$PLAN" | grep -q '^command=$'; [ $? -ne 0 ] check "plan never emits an empty command= line" $? printf '%s\n' "$PLAN" | grep -A6 '^repo=repo-typo$' | grep -q '^command_missing=' check "unparseable route line degrades to command_missing=, not to a guess" $? printf '%s\n' "$PLAN" | grep -A6 '^repo=repo-a$' | grep -q '^command_missing=' check "repo owing mail but lacking a route line is marked, never guessed at" $? # Same argument as the briefing: the 38-char cut is the TABLE column's property. printf '%s' "$PLAN" | grep -q '^neste=.*check-versions' check "plan prints the full NESTE line, not the 38-char table excerpt" $? # paste= is the operator's line and command= is the driver's. Both are kept # because they answer different questions: a driver cd's the pane itself and # would choke on a compound line, while the operator needs ONE thing to copy. # Assembling it by hand from two fields is where a tab lands in the wrong repo. printf '%s' "$PLAN" | grep -q "^paste=cd $ROOT/repo-owes && claude --model sonnet --effort high --advisor opus\$" check "paste= is the whole line: cd into the repo, then the startup command" $? printf '%s\n' "$PLAN" | grep -A7 '^repo=repo-typo$' | grep -q '^paste='; [ $? -ne 0 ] check "a repo with no usable route line gets no paste line either" $? # The driver cds into this, so a relative or missing path lands a session in the # wrong repo - constraint 2 of the Ghostty measurement, in field form. pdir="$(printf '%s\n' "$PLAN" | grep -m1 '^dir=' | cut -d= -f2-)" [ -n "$pdir" ] && [ -d "$pdir" ] && [ "${pdir#/}" != "$pdir" ] check "dir= is an absolute path that exists" $? # Every block must be complete: a driver that reads a block missing dir= or # command= has no safe default, and guessing one types into a live pane. n_tab="$(printf '%s\n' "$PLAN" | grep -c '^tab=')" n_rep="$(printf '%s\n' "$PLAN" | grep -c '^repo=')" n_dir="$(printf '%s\n' "$PLAN" | grep -c '^dir=')" n_why="$(printf '%s\n' "$PLAN" | grep -c '^why=')" n_sta="$(printf '%s\n' "$PLAN" | grep -c '^status=')" n_nes="$(printf '%s\n' "$PLAN" | grep -c '^neste=')" n_cmd="$(printf '%s\n' "$PLAN" | grep -c '^command=')" n_cms="$(printf '%s\n' "$PLAN" | grep -c '^command_missing=')" [ "$n_tab" -gt 0 ] && [ "$n_rep" -eq "$n_tab" ] && [ "$n_dir" -eq "$n_tab" ] \ && [ "$n_why" -eq "$n_tab" ] && [ "$n_sta" -eq "$n_tab" ] \ && [ "$n_nes" -eq "$n_tab" ] && [ $(( n_cmd + n_cms )) -eq "$n_tab" ] check "every planned block carries every field exactly once" $? # paste= is one per command=, never one per block: a repo with no command has # nothing to paste, and `paste=cd X && ` would run the cd and then a bare # newline - putting the operator in the right repo with no session started. n_pst="$(printf '%s\n' "$PLAN" | grep -c '^paste=')" [ "$n_pst" -eq "$n_cmd" ] check "paste= appears exactly where command= does, never beside command_missing" $? # Numbering must be dense: a driver opening tab 4 of 3 is a real failure mode. seq_ok=0 printf '%s\n' "$PLAN" | grep '^tab=' | cut -d= -f2 > "$ROOT/tabs.txt" # An empty tab list satisfies "every number is its index" vacuously, which is # the check passing by finding nothing rather than by reading the numbering. [ -s "$ROOT/tabs.txt" ] || seq_ok=1 i=0; while read -r t; do i=$((i+1)); [ "$t" = "$i" ] || seq_ok=1; done < "$ROOT/tabs.txt" check "tab numbers are dense and consecutive" "$seq_ok" # Read-only, same as every other rendering. Reading IS delivering in this # engine, so a plan that consumed a backlog would be the core defect. p4="$(snap "$CLAUDE_COORD_DIR")" "$BOARD" --roots "$ROOT" --plan >/dev/null 2>&1 [ "$p4" = "$(snap "$CLAUDE_COORD_DIR")" ] check "plan writes nothing to the mailbox (reading is delivering)" $? # The scan and the mailbox are two populations. An orphan mailbox has no repo to # cd into, so it cannot BE a tab - but dropping it makes the plan claim a # completeness it knows it lacks. It is reported as commentary. printf '%s' "$PLAN" | grep -q 'orphan-surface' check "plan surfaces a pending mailbox with no repo in the scan tree" $? printf '%s' "$PLAN" | grep -q '^repo=orphan-surface$'; [ $? -ne 0 ] check "orphan mailbox is commentary, never a tab block (there is no dir to cd to)" $? # Comment lines are '#'-prefixed so a consumer can drop them with one rule. printf '%s\n' "$PLAN" | grep -vE '^#|^$|^[a-z_]+=' | grep -q .; [ $? -ne 0 ] check "every plan line is a comment, a blank, or a key=value pair" $? printf '%s' "$PLAN" | iconv -f UTF-8 -t UTF-8 >/dev/null 2>&1 check "plan output is valid UTF-8" $? "$BOARD" --help 2>/dev/null | grep -q '\-\-plan' check "--help documents --plan" $? # --- 11. Focused day plan (--focus) --------------------------------------- # --focus is the first CUTOFF the plan has ever had, and every check here # exists because a cutoff is exactly what the plan format promised it was not. # The filter alone is half the feature; the other half is the run saying what # it held back, and they ship together or not at all. FPLAIN="$("$BOARD" --roots "$ROOT" --plan 2>/dev/null)" FDEMO="$("$BOARD" --roots "$ROOT" --plan --focus demo 2>/dev/null)" n_all="$(printf '%s\n' "$FPLAIN" | grep -c '^repo=')" n_focus="$(printf '%s\n' "$FDEMO" | grep -c '^repo=')" # The operator's instruction, as a test: prove it NARROWS. Every other check # in this section passes trivially against a no-op filter, which is precisely # the state the argument was in before this feature - swallowed by the unknown # argument fallback, full plan returned, no crash. "It did not crash" is not # evidence of a filter. [ "$n_focus" -lt "$n_all" ] && [ "$n_focus" -gt 0 ] check "focused plan is strictly smaller than the full plan, and not empty" $? printf '%s\n' "$FDEMO" | grep -q '^repo=repo-focus-plain$' check "a repo declaring the slug survives the focus" $? printf '%s\n' "$FDEMO" | grep -q '^repo=repo-focus-bold$' check "a bold/backtick declaration counts as declared (the register's grep loses it)" $? printf '%s\n' "$FPLAIN" | grep -q '^repo=repo-a$' printf '%s\n' "$FDEMO" | grep -q '^repo=repo-a$'; [ $? -ne 0 ] check "an off-topic repo is in the full plan and gone from the focused one" $? printf '%s\n' "$FDEMO" | grep -q '^repo=repo-focus-other$'; [ $? -ne 0 ] check "a repo declaring a DIFFERENT slug is not swept in" $? # The precondition. A repo that mentions the slug without declaring it is # neither kept nor dropped silently: it is named. Enumerating this class - not # reasoning about it - is what found the heaviest consumer in the real tree. printf '%s\n' "$FDEMO" | grep -q '^fokus_utenfor=' check "the run reports the repos it held back" $? printf '%s\n' "$FDEMO" | grep '^fokus_utenfor=' | grep -q 'repo-focus-mentions' check "the held-back report NAMES the repo, not just a count" $? printf '%s\n' "$FDEMO" | grep -q '^repo=repo-focus-mentions$'; [ $? -ne 0 ] check "a mention without a marker line does not become a tab" $? # Wording is load-bearing: board.sh reads text, and has no grounds whatever # for a claim about relevance. It says what it FOUND. printf '%s\n' "$FDEMO" | grep '^fokus_utenfor=' | grep -q 'nevner' check "the held-back line says 'nevner' (a fact about text found)" $? printf '%s\n' "$FDEMO" | grep '^fokus_utenfor=' | grep -qi 'dekker'; [ $? -ne 0 ] check "the held-back line never claims a repo 'dekker' the topic" $? # It states its own reach: 27 STATE.md, not "the tree". One repo in the real # measurement has its strongest evidence in a README the scan never opens. printf '%s\n' "$FDEMO" | grep -q '^fokus_rekkevidde=.*STATE.md' check "the run states its reach (N STATE.md searched, live-counted)" $? n_state="$(printf '%s\n' "$FDEMO" | sed -n 's/^fokus_rekkevidde=sokt i \([0-9]*\) STATE.md.*/\1/p')" # Both depths, because that is what board SCANS. The register's own roll-up # greps ~/repos/*/STATE.md and so cannot see a polyrepo child; the reach line # must state the population board actually searched, not the register's. real_state="$(ls "$ROOT"/*/STATE.md "$ROOT"/*/*/STATE.md 2>/dev/null | wc -l | tr -d ' ')" [ -n "$n_state" ] && [ "$n_state" = "$real_state" ] check "the reach count is measured, never a constant" $? printf '%s\n' "$FDEMO" | grep -q '^fokus=demo-topic$' check "the run names the slug the prose resolved to" $? # Per-block evidence: a focused run must say why each surviving block survived. # This is the need that closed topics= - relocated from all 27 blocks every # day into the focused run, where it is actually evidence. n_blocks="$(printf '%s\n' "$FDEMO" | grep -c '^repo=')" n_ev="$(printf '%s\n' "$FDEMO" | grep -c '^fokus_treff=')" [ "$n_ev" -eq "$n_blocks" ] && ev_ok=0 || ev_ok=1 check "every surviving block carries its own evidence (fokus_treff=)" "$ev_ok" printf '%s\n' "$FDEMO" | grep '^fokus_treff=' | grep -q 'demo-topic' check "the per-block evidence names the slug and its declared status" $? # Ambiguity widens and is named. "topic" is a hyphen-part of two declared # slugs; picking one silently would be the cutoff lying about its own size. FBOTH="$("$BOARD" --roots "$ROOT" --plan --focus topic 2>/dev/null)" printf '%s\n' "$FBOTH" | grep '^fokus=' | grep -q 'demo-topic' printf '%s\n' "$FBOTH" | grep '^fokus=' | grep -q 'other-topic' check "ambiguous prose resolves to the union, with every slug named" $? printf '%s\n' "$FBOTH" | grep -q '^repo=repo-focus-other$' check "the union actually includes the second slug's repo" $? # No match: the full plan, plus a line saying the focus was not applied. An # empty plan would be the worst outcome of all - the driver forwards operator # prose verbatim, so a typo would silently produce a morning with no tabs. FNONE="$("$BOARD" --roots "$ROOT" --plan --focus kaffe 2>/dev/null)" n_none="$(printf '%s\n' "$FNONE" | grep -c '^repo=')" [ "$n_none" -eq "$n_all" ] check "prose matching no declared slug returns the FULL plan, never an empty one" $? printf '%s\n' "$FNONE" | grep -q '^fokus_ikke_brukt=' check "a focus that was not applied says so" $? # Format invariant: --focus adds fields, it does not change the shape two # declared consumers parse. printf '%s\n' "$FDEMO" | grep -vE '^#|^$|^[a-z_]+=' | grep -q .; [ $? -ne 0 ] check "focused plan is still comments, blanks and key=value only" $? printf '%s' "$FDEMO" | iconv -f UTF-8 -t UTF-8 >/dev/null 2>&1 check "focused plan output is valid UTF-8" $? # The read-only invariant holds for the new path too: reading is delivering, # and a plan that consumed a backlog would be the core defect of the engine. before_inbox="$(ls "$CLAUDE_COORD_DIR/repo-a/inbox" 2>/dev/null | wc -l | tr -d ' ')" "$BOARD" --roots "$ROOT" --plan --focus demo >/dev/null 2>&1 after_inbox="$(ls "$CLAUDE_COORD_DIR/repo-a/inbox" 2>/dev/null | wc -l | tr -d ' ')" [ "$before_inbox" = "$after_inbox" ] check "--focus writes nothing to the mailbox" $? # bash 3.2: `shift 2` past the end loops forever. A flag whose value is missing # must not hang the driver that forwards it - and it refuses loudly, exactly as # --roots does, rather than degrading into "no focus" and printing a full plan # that looks like a focused one. "$BOARD" --roots "$ROOT" --plan --focus >/dev/null 2>&1 [ $? -eq 2 ] check "--focus with no value exits 2, like every other flag missing its value" $? "$BOARD" --help 2>/dev/null | grep -q '\-\-focus' check "--help documents --focus" $? echo "" echo "board-selftest: $PASS passed, $FAIL failed" [ "$FAIL" -eq 0 ] || exit 1 exit 0