feat(board): PLAN column - where a destination is missing or untouched

A display-only table column and a `plan=` field in --row, read from the
filesystem: `-` = no file named exactly PLAN.md in the repo root; `Nd` =
whole days the plan's mtime lies behind the last commit (`0d` when as new
or newer); `?` = a plan with no commit to compare against. The content is
never read, and the legend says the column does not judge whether a
criterion can be falsified. Not a sort key, not in --plan, not in SUM.

The name is matched byte for byte through a glob plus a basename compare:
APFS is case-insensitive by default, so `[ -f PLAN.md ]` is true for a
`plan.md`. The glob needs no subprocess.

The checks were written red in a separate eval before the column existed
and pass unchanged; they now live in board-selftest section 34 and the
eval file is removed (two judges for one column would drift).
Mutation-verified: a case-blind test, sorting on the field, a plan= key
in --plan and a legend without IKKE each turn exactly one check red.

The new record field sits before `neste`, so every "last field onward"
index moved 17 -> 18, and F6's right-counted DRT read moved NF-3 -> NF-4.

board-selftest 428 -> 461 (badge 961). Suites: coord 257, board 461,
route 73, orders 116, guard 54; npm test 12/12, run after git add.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-17 16:17:49 +02:00
commit 87fb78334e
7 changed files with 327 additions and 260 deletions

View file

@ -467,7 +467,7 @@ marketplace plugin. Three components, one boundary:
- **Board (`scripts/board.sh`):** cross-repo attention board. Reads STATE.md
next-step blocks + board lines, `git status`, and mailbox pending counts, and
prints one line per repo. Read-only by construction: it writes to no repo, no
STATE.md and no mailbox. Pinned by `board-selftest.sh` (428 checks).
STATE.md and no mailbox. Pinned by `board-selftest.sh` (461 checks).
**It lives here because the mailbox is one of its three inputs, and it carries
the same axis distinction the mailbox does.** A pending count means *others
@ -1454,6 +1454,41 @@ marketplace plugin. Three components, one boundary:
digit. A fixture that cannot tell two columns apart is the defect wearing a
passing test, inside the section written to prevent it.
- **`PLAN` is a table column read from the FILESYSTEM, never from the file**
(operator decision 2026-09-17). `-` = no file named exactly `PLAN.md` in the
repo root; `Nd` = whole days its mtime lies behind the last commit; `?` = a
plan with no commit to compare against. It answers where a destination is
missing or untouched and nothing else - it does not judge whether a criterion
can be falsified, and the legend says so. Three alternatives were weighed
first and rejected on measurement, not taste: a free-text `goal=` field
(the case that motivated this HAD a goal in words; a field cannot tell words
from a falsifiable criterion), a mandatory acceptance flag on orders (every
order in that case was precisely specified - the gap was at version level),
and an "activity without a release" heuristic (a release silenced it on the
one known case, three days before the loop was diagnosed).
**The name is matched BYTE FOR BYTE through a glob (`"$d"/PLAN.m[d]` plus a
basename compare), never by `[ -f PLAN.md ]`.** APFS is case-insensitive by
default, so that test is true for `plan.md` and would report a plan that is
not there; a glob matches the names readdir returns, as stored, and costs no
subprocess. Selftest section 34 asserts the trap is ARMED on the machine
running it, so the byte-exact check is measuring something. Display only:
not a sort key, not in `--plan`, not in SUM - pinned by swapping two repos'
PLAN values between runs and requiring the same order.
**The checks were written RED first, in a separate file, and moved into
board-selftest once the column passed them unchanged.** Red checks inside
the suite would have turned main red and broken the README count check, so
"red for the right reason" could not have been told apart from anything
else. The separate file is gone - two judges for one column would drift.
Mutation-verified against four broken variants (case-blind test, sorting on
the field, a `plan=` key in `--plan`, a legend without IKKE), each turning
exactly one check red. Adding the field before `neste` exposed that
`--dispatch` had taken `neste` from field 14 onward since the record had 14
fields, gluing three columns onto the prose; fixed separately, and every
"last field onward" index now moves with the record (17 -> 18 here). F6
counts DRT from the right, so its `NF-3` became `NF-4` for the same reason.
- **Skills (`skills/coord-send/`, `skills/board/`, `skills/route/`, `skills/dispatch/`):** natural-language front
doors mapping user intent to engine invocations. No mailbox logic lives here
either. `board` additionally owns the *ranking* — which repo wins and why —
@ -1502,7 +1537,7 @@ obligations in another repo.
builtins only in hook and tests.
- TDD: no behavior change without a failing selftest check first.
`bash scripts/coord-selftest.sh` must exit 0 (257/257),
`bash scripts/board-selftest.sh` must exit 0 (428/428),
`bash scripts/board-selftest.sh` must exit 0 (461/461),
`bash scripts/route-selftest.sh` must exit 0 (73/73),
`bash scripts/orders-selftest.sh` must exit 0 (116/116) and
`bash scripts/state-line-guard-selftest.sh` must exit 0 (54/54).