--focus "<prose>" narrows the day plan to repos whose STATE.md DECLARES a matching topic marker. Measured on the real tree: 26 blocks to 6. The filter alone was never the feature. --plan documents that it takes one position (the order), hides nothing, and labels what it cannot rank rather than dropping it, so a silent cutoff would break a property the format had already written down. The same run prints the slugs the prose resolved to, how many blocks it removed, the repos that MENTION a resolved slug with no marker line - named, not counted - and how many STATE.md it searched. Each surviving block carries the declaration it survived on. Enumerated rather than counted because the decisive find behind this feature was invisible to every string measurement until the held-back population was listed. "nevner", never "dekker": this reports text found, and board.sh has no grounds for a claim about relevance. The slug vocabulary comes from the scanned STATE.md themselves, so the "no other file" invariant survives. Emitted as key=value, not '#' comments: the consumer's parser runs in paragraph mode and drops any block without tab=, so a comment would reach the operator on the terminal path and vanish on the driver path - the one case where the cutoff is applied unseen. board-selftest 89 -> 113. The narrowing check is comparative, since every presence-only assertion passes against a no-op filter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0186vKCzuUEN5WcJB82kddzF
829 lines
42 KiB
Bash
Executable file
829 lines
42 KiB
Bash
Executable file
#!/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 "<!-- board: status=in-progress; blocked-on=-; next-cost=sonnet/xhigh -->"
|
|
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 "<!-- board: status=deferred; blocked-on=-; next-cost=sonnet/high -->"
|
|
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 "<!-- board: status=aktiv; blocked-on=-; next-cost=opus/high -->"
|
|
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 "<!-- board: status=planned; blocked-on=-; next-cost=opus/xhigh -->"
|
|
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
|
|
# (<modell>/<effort>) 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 "<!-- board: status=in-progress; blocked-on=-; next-cost=Sonnet 5/xhigh -->"
|
|
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 "<!-- board: status=blocked; blocked-on=repo-a; next-cost=opus/high -->"
|
|
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 <root>/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 "<!-- board: status=done; blocked-on=-; next-cost=sonnet/high -->"
|
|
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 "<!-- board: status=in-progress; blocked-on=-; next-cost=fable/xhigh -->"
|
|
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 "<!-- board: status=in-progress; blocked-on=-; next-cost=Opus 5/high -->"
|
|
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 "<!-- board: status=in-progress; blocked-on=-; next-cost=Opus 5/high -->"
|
|
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 "<!-- board: status=in-progress; blocked-on=-; next-cost=Sonnet 5/high -->"
|
|
echo "<!-- route: path=known; verification=strong; reversibility=cheap; scope=local; rationale=x -->"
|
|
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 "<!-- board: status=planned; blocked-on=-; next-cost=Opus 5/high -->"
|
|
echo "<!-- route: path=knwon; verification=strong; reversibility=cheap; scope=local; rationale=x -->"
|
|
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 `<slug>: <status-token> <emdash> 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 "<!-- board: status=in-progress; blocked-on=-; next-cost=Opus 5/high -->"
|
|
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 "<!-- board: status=planned; blocked-on=-; next-cost=Sonnet 5/high -->"
|
|
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 "<!-- board: status=in-progress; blocked-on=-; next-cost=Opus 5/high -->"
|
|
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 "<!-- board: status=planned; blocked-on=-; next-cost=Sonnet 5/high -->"
|
|
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 "<!-- board: status=blocked; blocked-on=repo-a; next-cost=Opus 5/high -->"
|
|
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 "<!-- board: status=done; blocked-on=-; next-cost=Sonnet 5/high -->"
|
|
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
|