Skipping lines that START with the comment opener is not the same as skipping a comment. Every continuation line of a wrapped comment under the NESTE heading still looked like prose, so the excerpt became comment internals instead of the next step. Both the board line and the route line wrap easily; measured, 1 of 28 repos was affected, with the symptom held down only by the convention of keeping those comments one-line. An open comment is now tracked to its closer, and a NESTE block that is nothing but a comment reports an empty block rather than promoting the comment's own text. Two limits are the format, not leftovers: an HTML comment body may not contain the closer at all, so a rationale quoting it still ends its own comment early. The fixture is the real shape - one-line, wrapped, one-line, prose - because a lone wrapped comment passes even with a flag that never resets, and the continuation marker sits at the start of its line because the table's 38-char cut would otherwise truncate it away and the check would pass against the broken code too. board-selftest 138 -> 142. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K262DRWBJzEpWoMsBjB8J
1150 lines
59 KiB
Bash
Executable file
1150 lines
59 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-boldstatus: the bold is on the STATUS token, not the slug. Found in
|
|
# the real tree AFTER the first release, which is the point: accepting bold on
|
|
# one half of the line and not the other is not a rule, it is an accident of
|
|
# which example was in front of us. The register's own grep loses this form too,
|
|
# so the repo writing it looks silent while having declared.
|
|
mkrepo "$ROOT/repo-focus-boldstatus"
|
|
{
|
|
echo "# STATE - repo-focus-boldstatus"
|
|
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-boldstatus."
|
|
echo ""
|
|
echo "demo-topic: **done** (1.8.1). akse-B naadd, ingen apen trad"
|
|
} > "$ROOT/repo-focus-boldstatus/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" $?
|
|
|
|
# The NESTE marker must be anchored to the heading form, not any line
|
|
# containing the word. A prose line mentioning "NESTE" earlier in the file
|
|
# must not be mistaken for the marker - that would make the excerpt the line
|
|
# AFTER the false match instead of the real next step under the real heading.
|
|
mkrepo "$ROOT/repo-neste-lookalike"
|
|
{
|
|
echo "# STATE - repo-neste-lookalike"
|
|
echo "## Fakta"
|
|
echo "Noe forklaring om NESTE-formen som ble diskutert i gaar."
|
|
echo "Dette er en tilfeldig linje som IKKE er det ekte neste steget."
|
|
echo ""
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=in-progress; blocked-on=-; next-cost=sonnet/xhigh -->"
|
|
echo "Det ekte neste steget er aa fikse boardskriptet."
|
|
} > "$ROOT/repo-neste-lookalike/STATE.md"
|
|
NESTE_OUT="$("$BOARD" --roots "$ROOT" 2>/dev/null)"
|
|
printf '%s' "$NESTE_OUT" | grep -qE 'repo-neste-lookalike.*ekte neste steget'
|
|
check "NESTE marker anchored to heading, not any content line mentioning it" $?
|
|
printf '%s' "$NESTE_OUT" | grep -qE 'repo-neste-lookalike.*tilfeldig linje'
|
|
[ $? -ne 0 ]; check "content line mentioning NESTE is not mistaken for the marker" $?
|
|
|
|
# A MULTI-LINE HTML comment under NESTE must be skipped THROUGH TO ITS CLOSER,
|
|
# not just on its opening line. Skipping only lines that START with the opener
|
|
# leaves every continuation line looking like prose, so the excerpt becomes
|
|
# comment internals - measured on the real tree, this repo was 1 of 28 affected
|
|
# and held the symptom down by keeping its comments one-line.
|
|
# The fixture is the REAL shape deliberately: one-line, multi-line, one-line,
|
|
# prose. A lone multi-line comment would prove enter-and-exit but not that the
|
|
# open-comment flag RESETS - a flag stuck on would still pass that weaker
|
|
# fixture by falling through to the same prose.
|
|
# NOTE: an HTML comment body cannot contain '-->' at all (that is the format,
|
|
# not a board.sh limit), so a rationale that quotes the closer still ends the
|
|
# comment early. That constraint survives this fix by construction.
|
|
mkrepo "$ROOT/repo-multiline-comment"
|
|
{
|
|
echo "# STATE - repo-multiline-comment"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=planned; blocked-on=-; next-cost=Opus 5/high -->"
|
|
# The marker sits at the START of the continuation line on purpose: the table
|
|
# cuts the excerpt at 38 characters, so a marker further in would be truncated
|
|
# away and the check would pass against the BROKEN code too.
|
|
echo "<!-- route: path=known; verification=strong; reversibility=cheap;"
|
|
echo " FORTSETTELSESLINJE som ikke er noe neste steg;"
|
|
echo " scope=local; rationale=brer seg over flere linjer. -->"
|
|
echo "<!-- route-last: model=Opus 5; effort=high; completed=yes -->"
|
|
echo "Det ekte neste steget staar under kommentarblokken."
|
|
} > "$ROOT/repo-multiline-comment/STATE.md"
|
|
ML_OUT="$("$BOARD" --roots "$ROOT" 2>/dev/null)"
|
|
printf '%s' "$ML_OUT" | grep -qE 'repo-multiline-comment.*ekte neste steget'
|
|
check "multi-line HTML comment skipped through to its closer" $?
|
|
printf '%s' "$ML_OUT" | grep -q 'FORTSETTELSESLINJE'
|
|
[ $? -ne 0 ]; check "comment continuation line never becomes the NESTE excerpt" $?
|
|
# The board line still parses: it is read by its own anchored grep, so the
|
|
# excerpt fix must not disturb it.
|
|
printf '%s' "$ML_OUT" | grep -qE 'repo-multiline-comment.*planned'
|
|
check "board line still parsed alongside a wrapped route comment" $?
|
|
|
|
# A NESTE block that is NOTHING BUT a multi-line comment has no next step, and
|
|
# must say so rather than promote comment internals to the excerpt.
|
|
mkrepo "$ROOT/repo-comment-only"
|
|
{
|
|
echo "# STATE - repo-comment-only"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- route: path=known; verification=strong;"
|
|
echo " scope=local; rationale=BARE EN KOMMENTAR her. -->"
|
|
} > "$ROOT/repo-comment-only/STATE.md"
|
|
CO_OUT="$("$BOARD" --roots "$ROOT" 2>/dev/null)"
|
|
printf '%s' "$CO_OUT" | grep -qE 'repo-comment-only.*tom NESTE-blokk'
|
|
check "NESTE block of only a multi-line comment reports an empty block" $?
|
|
|
|
"$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. Stated as
|
|
# group 2 over group 4, NOT as "before any that owes none" - since 0.20.0 the
|
|
# chain-root group sits above debt, so a repo owing nothing CAN outrank one that
|
|
# does. Section 12 pins that half on the fixture that shows it cleanly.
|
|
n_h="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-h$' | cut -d: -f1)"
|
|
[ -n "$n_h" ] && [ "$n_ow" -lt "$n_h" ]
|
|
check "a repo owing mail is planned before live work that owes and releases nothing" $?
|
|
|
|
# Group 2 over group 3: repo-g is planned and owes nothing.
|
|
n_g="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-g$' | cut -d: -f1)"
|
|
[ -n "$n_g" ] && [ "$n_ow" -lt "$n_g" ]
|
|
check "a repo owing mail is planned before a repo that merely has a plan" $?
|
|
|
|
# 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" $?
|
|
|
|
# repo-a owes 3 messages AND is the blocker plug-x and repo-blocked-owes both
|
|
# name. Under the group form why= names the GROUP that put the repo in the plan,
|
|
# and the chain-root group is the first one tested - so it reports what a session
|
|
# there would release rather than what it owes. Between 0.19.0 and 0.20.0 the
|
|
# same assertion held for a different reason (the dominant term of a score);
|
|
# the reason is written down because the check passes either way and only the
|
|
# reason says which form is running.
|
|
printf '%s\n' "$PLAN" | grep -A3 '^repo=repo-a$' | grep -q '^why=unblocks:2$'
|
|
check "why= names the group that planned the repo, releasing over owing" $?
|
|
|
|
# planned outranks in-progress among the repos that neither owe nor release.
|
|
# INVERTED at 0.20.0 by operator decision: a decided-but-unstarted next step is
|
|
# the step that converts a decision into motion, and live work is already moving.
|
|
# This is a policy call, not a preference - it is recorded here and in board.sh
|
|
# so a later session cannot read it as a sort that drifted.
|
|
[ -n "$n_g" ] && [ "$n_g" -lt "$n_h" ]
|
|
check "planned work is planned before live in-progress work (operator inversion)" $?
|
|
|
|
# Group 4 over group 5: repo-e declares an out-of-set status token and lands in
|
|
# the undeclared group, which is planned LAST and never dropped.
|
|
n_e="$(printf '%s\n' "$PLAN" | grep -n '^repo=repo-e$' | cut -d: -f1)"
|
|
[ -n "$n_e" ] && [ "$n_h" -lt "$n_e" ]
|
|
check "a repo with no declared status is planned after live work, not dropped" $?
|
|
|
|
# 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)" $?
|
|
|
|
# Bold around the STATUS rather than the slug. Accepting one and not the other
|
|
# is not a rule, just whichever example was in front of us first.
|
|
printf '%s\n' "$FDEMO" | grep -q '^repo=repo-focus-boldstatus$'
|
|
check "bold on the status token counts as declared, same as bold on the slug" $?
|
|
|
|
printf '%s\n' "$FDEMO" | grep '^fokus_treff=' | grep -q 'demo-topic: done'
|
|
check "the evidence reports the status unwrapped, never '**done**'" $?
|
|
|
|
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" $?
|
|
|
|
# Zero held back is a real answer and must LOOK like one. A trailing ": " with
|
|
# nothing after it is the same defect class the format already legislated
|
|
# against for command=: the shape of a value carrying nothing. Reached via a
|
|
# slug that nobody mentions without declaring.
|
|
FOTHER="$("$BOARD" --roots "$ROOT" --plan --focus other 2>/dev/null)"
|
|
printf '%s\n' "$FOTHER" | grep '^fokus_utenfor=' | grep -qE ':[[:space:]]*$'; [ $? -ne 0 ]
|
|
check "an empty held-back set renders a value, never a dangling colon" $?
|
|
|
|
# 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" $?
|
|
|
|
# --- 12. Chain-root credit in --plan --------------------------------------
|
|
# Ordered by what a repo OWES, the plan could never say what OPENING it would
|
|
# RELEASE. blocked-on was scanned and then used for one display string only.
|
|
#
|
|
# The credit goes to the ROOT of the chain and to nothing else, and that is the
|
|
# whole rule: opening a blocked repo moves nothing, because its own next step is
|
|
# by definition waiting on someone. Opening the root moves everything behind it.
|
|
# So the walk is transitive - crediting only the direct blocker would leave a
|
|
# two-hop chain with its root uncredited, which is the shape the real tree
|
|
# actually had when this was ordered.
|
|
#
|
|
# Three fixtures, one chain: leaf -> mid -> root.
|
|
mkrepo "$ROOT/chain-root"
|
|
{
|
|
echo "# STATE - chain-root"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
# Deliberately `planned`, not `in-progress`: the root must earn its position
|
|
# from the chain-root group alone, never from the status group, so the ranking
|
|
# checks below fail for the right reason instead of passing on fixture order.
|
|
echo "<!-- board: status=planned; blocked-on=-; next-cost=Opus 5/high -->"
|
|
printf 'Roten %s ingen gjeld, men holder to repo oppe.\n' "$EMDASH"
|
|
} > "$ROOT/chain-root/STATE.md"
|
|
|
|
mkrepo "$ROOT/chain-mid"
|
|
{
|
|
echo "# STATE - chain-mid"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=blocked; blocked-on=chain-root; next-cost=Opus 5/high -->"
|
|
printf 'Venter p%s chain-root.\n' "$EMDASH"
|
|
} > "$ROOT/chain-mid/STATE.md"
|
|
|
|
mkrepo "$ROOT/chain-leaf"
|
|
{
|
|
echo "# STATE - chain-leaf"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=blocked; blocked-on=chain-mid; next-cost=Opus 5/high -->"
|
|
printf 'Venter p%s chain-mid, som venter p%s roten.\n' "$EMDASH" "$EMDASH"
|
|
} > "$ROOT/chain-leaf/STATE.md"
|
|
|
|
# A cycle is not hypothetical: two repos can each declare they wait on the other
|
|
# after a miscommunication, and nobody would notice until the board hung. The
|
|
# walk must terminate and credit NOBODY - a cycle has no root by definition.
|
|
mkrepo "$ROOT/cyc-a"
|
|
{
|
|
echo "# STATE - cyc-a"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=blocked; blocked-on=cyc-b; next-cost=Opus 5/high -->"
|
|
echo "Venter paa cyc-b."
|
|
} > "$ROOT/cyc-a/STATE.md"
|
|
mkrepo "$ROOT/cyc-b"
|
|
{
|
|
echo "# STATE - cyc-b"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=blocked; blocked-on=cyc-a; next-cost=Opus 5/high -->"
|
|
echo "Venter paa cyc-a."
|
|
} > "$ROOT/cyc-b/STATE.md"
|
|
|
|
# blocked-on names a repo the scan never produced. Nothing validates that field,
|
|
# so a typo is the expected case, not the exotic one - and it must credit
|
|
# nothing rather than credit a name that has no directory to open.
|
|
mkrepo "$ROOT/dangle"
|
|
{
|
|
echo "# STATE - dangle"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=blocked; blocked-on=no-such-repo; next-cost=Opus 5/high -->"
|
|
echo "Venter paa noe som ikke finnes."
|
|
} > "$ROOT/dangle/STATE.md"
|
|
|
|
# THE DISCRIMINATING PAIR for the 0.20.0 group form. Every other ordering check
|
|
# in this section passes under BOTH the 0.19.0 score and the group form, because
|
|
# chain-root releases two repos and 40*2 beats any debt in this tree - it would
|
|
# have gone green while the operator's decision quietly failed to ship.
|
|
#
|
|
# These two are built so the forms DISAGREE. solo-root releases exactly one repo
|
|
# and owes nothing (score: 40 + 5 planned = 45). heavy-debtor owes four (score:
|
|
# 60 + 10 in-progress = 70). The score ranks the debtor first; the group form
|
|
# ranks the root first, because releasing is group 1 and debt is group 2.
|
|
mkrepo "$ROOT/solo-root"
|
|
{
|
|
echo "# STATE - solo-root"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=planned; blocked-on=-; next-cost=Opus 5/high -->"
|
|
printf 'Losner ett repo, skylder ingenting.\n'
|
|
} > "$ROOT/solo-root/STATE.md"
|
|
|
|
mkrepo "$ROOT/solo-leaf"
|
|
{
|
|
echo "# STATE - solo-leaf"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=blocked; blocked-on=solo-root; next-cost=Opus 5/high -->"
|
|
printf 'Venter p%s solo-root.\n' "$EMDASH"
|
|
} > "$ROOT/solo-leaf/STATE.md"
|
|
|
|
mkrepo "$ROOT/heavy-debtor"
|
|
{
|
|
echo "# STATE - heavy-debtor"
|
|
printf '## %s NESTE %s START HER\n' "$HAND" "$EMDASH"
|
|
echo "<!-- board: status=in-progress; blocked-on=-; next-cost=Opus 5/high -->"
|
|
printf 'Skylder fire svar, losner ingen.\n'
|
|
} > "$ROOT/heavy-debtor/STATE.md"
|
|
mkdir -p "$CLAUDE_COORD_DIR/heavy-debtor/inbox"
|
|
for n in 1 2 3 4; do
|
|
echo "msg" > "$CLAUDE_COORD_DIR/heavy-debtor/inbox/2026-msg$n-from-v.md"
|
|
done
|
|
|
|
P2="$("$BOARD" --roots "$ROOT" --plan 2>/dev/null)"; rc=$?
|
|
check "--plan still exits 0 with chain, cycle and dangling fixtures present" "$rc"
|
|
|
|
printf '%s' "$P2" | grep -q '^repo=chain-root$'
|
|
check "the root of a blocked chain is planned even though it owes no mail" $?
|
|
|
|
printf '%s\n' "$P2" | grep -A3 '^repo=chain-root$' | grep -q '^why=unblocks:2$'
|
|
check "the root is credited with the WHOLE subtree it holds up, not one edge" $?
|
|
|
|
# The exclusion these fixtures must not break: a blocked repo with no debt still
|
|
# gets no tab. The credit goes to the root precisely so the blocked repos do not
|
|
# need one - crediting them instead would open two tabs that cannot move.
|
|
printf '%s' "$P2" | grep -q '^repo=chain-mid$'; [ $? -ne 0 ]
|
|
check "a blocked middle link with no debt is still not planned" $?
|
|
printf '%s' "$P2" | grep -q '^repo=chain-leaf$'; [ $? -ne 0 ]
|
|
check "a blocked leaf with no debt is still not planned" $?
|
|
|
|
# Unblocking outranks work that neither owes nor releases anything. This is the
|
|
# point of the change: repo-h is in-progress and idle, chain-root is in-progress
|
|
# and holds two repos up.
|
|
n2_cr="$(printf '%s\n' "$P2" | grep -n '^repo=chain-root$' | cut -d: -f1)"
|
|
n2_h="$(printf '%s\n' "$P2" | grep -n '^repo=repo-h$' | cut -d: -f1)"
|
|
[ -n "$n2_cr" ] && [ -n "$n2_h" ] && [ "$n2_cr" -lt "$n2_h" ]
|
|
check "a chain root ranks above live work that releases nothing" $?
|
|
|
|
# THE decisive check for the 0.20.0 group form - the discriminating pair built
|
|
# above. solo-root releases ONE and owes nothing; heavy-debtor owes FOUR. This is
|
|
# the only ordering assertion in the file that the 0.19.0 score would fail, so it
|
|
# is the one that proves which form is actually running. Debt is still neither
|
|
# capped nor excluded here - it is one group lower, not down-weighted.
|
|
n2_sr="$(printf '%s\n' "$P2" | grep -n '^repo=solo-root$' | cut -d: -f1)"
|
|
n2_hd="$(printf '%s\n' "$P2" | grep -n '^repo=heavy-debtor$' | cut -d: -f1)"
|
|
[ -n "$n2_sr" ] && [ -n "$n2_hd" ] && [ "$n2_sr" -lt "$n2_hd" ]
|
|
check "releasing ONE repo outranks owing FOUR (group form, not the score)" $?
|
|
|
|
# The other half of the same rule: the debtor is still IN the plan, and still
|
|
# says debt is why. A group form that ranked debt lower by dropping it would be
|
|
# the excluded-debt defect wearing the operator's decision as a disguise.
|
|
printf '%s\n' "$P2" | grep -A3 '^repo=heavy-debtor$' | grep -q '^why=inbox:4$'
|
|
check "a repo outranked by a chain root still keeps its tab and its why=" $?
|
|
|
|
# A cycle terminates and credits nobody. Both halves matter: hanging is the
|
|
# failure everyone imagines, but silently inventing a root is the one that would
|
|
# ship - it looks like a working plan.
|
|
printf '%s' "$P2" | grep -q '^repo=cyc-a$'; [ $? -ne 0 ]
|
|
check "a cyclic blocked-on chain credits nobody (cyc-a not planned)" $?
|
|
printf '%s' "$P2" | grep -q '^repo=cyc-b$'; [ $? -ne 0 ]
|
|
check "a cyclic blocked-on chain credits nobody (cyc-b not planned)" $?
|
|
printf '%s' "$P2" | grep -q '^why=unblocks:0$'; [ $? -ne 0 ]
|
|
check "no repo is ever credited with an empty unblock count" $?
|
|
|
|
# A dangling blocked-on must not conjure a tab for a name with no directory.
|
|
printf '%s' "$P2" | grep -q '^repo=no-such-repo$'; [ $? -ne 0 ]
|
|
check "a dangling blocked-on target is never planned into existence" $?
|
|
printf '%s' "$P2" | grep -q '^repo=dangle$'; [ $? -ne 0 ]
|
|
check "the repo carrying a dangling blocked-on is itself still excluded" $?
|
|
|
|
# The format is the API. Changing the ranking form must not move, rename or drop
|
|
# a single field - the driver parses these keys positionally-independently, but
|
|
# it parses THESE keys.
|
|
n2_tab="$(printf '%s\n' "$P2" | grep -c '^tab=')"
|
|
n2_rep="$(printf '%s\n' "$P2" | grep -c '^repo=')"
|
|
n2_dir="$(printf '%s\n' "$P2" | grep -c '^dir=')"
|
|
n2_why="$(printf '%s\n' "$P2" | grep -c '^why=')"
|
|
n2_sta="$(printf '%s\n' "$P2" | grep -c '^status=')"
|
|
n2_nes="$(printf '%s\n' "$P2" | grep -c '^neste=')"
|
|
n2_cmd="$(printf '%s\n' "$P2" | grep -c '^command=')"
|
|
n2_cms="$(printf '%s\n' "$P2" | grep -c '^command_missing=')"
|
|
[ "$n2_tab" -gt 0 ] && [ "$n2_rep" -eq "$n2_tab" ] && [ "$n2_dir" -eq "$n2_tab" ] \
|
|
&& [ "$n2_why" -eq "$n2_tab" ] && [ "$n2_sta" -eq "$n2_tab" ] \
|
|
&& [ "$n2_nes" -eq "$n2_tab" ] && [ $(( n2_cmd + n2_cms )) -eq "$n2_tab" ]
|
|
check "ranked plan still carries every documented field exactly once per block" $?
|
|
|
|
printf '%s' "$P2" | grep -q '^command=$'; [ $? -ne 0 ]
|
|
check "ranked plan never emits an empty command= line" $?
|
|
|
|
# Debt still puts a repo in the plan and still names itself when it is the
|
|
# dominant reason. Rejecting the proposed cap on debt was a deliberate call:
|
|
# owing a reply is the other axis, and answering is often what unblocks a chain.
|
|
printf '%s\n' "$P2" | grep -A3 '^repo=repo-blocked-owes$' | grep -q '^why=inbox:2$'
|
|
check "debt still names itself in why= when it is the dominant reason" $?
|
|
|
|
# --brief is a second RENDERING of the same scan and must survive the change.
|
|
"$BOARD" --roots "$ROOT" --brief >/dev/null 2>&1
|
|
check "--brief still exits 0 after the ranking change" $?
|
|
|
|
# The plan must still print the scan whole. There is no cutoff by design, so a
|
|
# consumer's own tab cap is the only thing that ever hides a repo.
|
|
[ "$n2_tab" -ge "$n_tab" ]
|
|
check "the grouped plan hides nothing an earlier ordering showed" $?
|
|
|
|
echo ""
|
|
echo "board-selftest: $PASS passed, $FAIL failed"
|
|
[ "$FAIL" -eq 0 ] || exit 1
|
|
exit 0
|