feat(board): --voyage reports the Voyage briefs in flight
board read STATE lines and knew nothing about a brief, so a programme running Voyage across several repos had no shared surface: nobody could answer which briefs were in flight, in what phase, and who was waiting on whom. Order 20260831T135934Z-696228720 fired the programme-level B1 row once /trekplan had been delivered in llm-ingestion-okf, so the fields were chosen against a run that had gone the whole way rather than guessed. Adds board.sh --voyage (a sixth rendering of the same scan, read-only) and a VOY column beside ORDRE and FLY, never summed with them and never in the sort. Detection is by PROPERTY, never by directory name: a directory holding brief.md or brief.md.draft under any of the three planning locations the convention recognises. It walks the FILESYSTEM, never the git index - llm-ingestion-okf gitignores .claude/projects/ (087be0b), so an index-based detector would report ZERO briefs in the one repo actually running one. The phase ladder measures ARTIFACTS, not sessions, and the legend says so: /trekexecute leaves a file behind only in its multi-session form, so a plan executed in one session leaves nothing and `plan` is the last thing the filesystem can prove. Nothing here claims a session is alive - the same refusal FLY carries. Three absences that must not borrow the shape of a measurement: brief_quality is read from the frontmatter block only and an absent field reads `-`, never `complete` (only 8 of ~40 briefs on the real tree carry it); research=- (never started) is distinct from research=0 (a directory holding nothing); and --voyage always prints its own denominator rather than rendering as an empty page. Blocking decisions are counted at DECLARATION SITES, not mentions: the pattern occurs on four lines of the real brief, of which one declares it, so a bare grep -c answers 4 where the honest answer is 1. The age is the NEWEST artifact, inverting the oldest-wins rule ORDRE and FLY carry - an order queue's problem is the oldest item still waiting, a project's problem is that its most recent activity is old. Mutation-verified three ways: newest->oldest turns exactly one check red, anchored pattern->bare string turns three red (the known-negative control among them), filesystem->git ls-files turns 23 red. Live-verified against the completed run: fase=plan, kvalitet=complete, blokkerende=1, gate=S4, research=5 - the only project of 51 across 14 repos with an open blocking decision. Cost measured rather than assumed: 0.31.0 takes 6.3s over the real 52-repo tree, with VOY 7.7-9.2s. The first cut called stat once per artifact and took 13.4s; the batched form is what makes the column affordable. CLAUDE.md's old "~3s" claim did not survive the measurement and is corrected. Bounded gap, stated rather than closed: whether a detected project is still "in flight" is not decided here. That needs a threshold, and a threshold would make the board decide that work is abandoned - the identical thing the order queue is already forbidden from doing. board-selftest.sh: 325 -> 360 checks. Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
0711b4209c
commit
85cd628c3e
5 changed files with 701 additions and 21 deletions
102
CLAUDE.md
102
CLAUDE.md
|
|
@ -411,7 +411,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` (325 checks).
|
||||
STATE.md and no mailbox. Pinned by `board-selftest.sh` (360 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
|
||||
|
|
@ -1074,6 +1074,98 @@ marketplace plugin. Three components, one boundary:
|
|||
way `--dispatch` requires `--target-pane` to be measured by the caller and
|
||||
passed in. Not built: not ordered, and keeping it out keeps the dependency
|
||||
running one way.
|
||||
- **`--voyage` is the SIXTH rendering, and it exists because `board` read STATE
|
||||
lines and knew nothing about a brief.** Order 20260831T135934Z-696228720
|
||||
(`.claude`, 2026-08-31) fired the programme-level B1 row once `/trekplan` had
|
||||
been delivered in `llm-ingestion-okf`, so the fields could be chosen against a
|
||||
run that had gone the whole way (`brief.md.draft` 30.08 -> `brief.md` -> four
|
||||
research topics + README 31.08 14:19-14:54 -> `plan.md` 15:30) rather than
|
||||
guessed. A programme with Voyage in several repos had no shared surface at
|
||||
all: nobody could answer which briefs were in flight, in what phase, and who
|
||||
was waiting on whom. Read-only holds untouched - every field is a read.
|
||||
|
||||
**Detection is by PROPERTY, never by directory name, and never through the git
|
||||
index.** The global CLAUDE.md says plans and briefs are defined by properties
|
||||
and recognises three locations (`.claude/projects/`, `docs/`,
|
||||
`features/<n>-<name>/`), so a directory counts iff it HOLDS `brief.md` or
|
||||
`brief.md.draft` under one of them. The index-based alternative was refused on
|
||||
a measurement the order supplied and this repo re-checked: `llm-ingestion-okf`
|
||||
gitignores `.claude/projects/` (commit 087be0b), so `git ls-files` would report
|
||||
ZERO briefs in the one repo actually running one - a broken query returning a
|
||||
positive-looking null, which is ansikt 4 pointed at our own tooling.
|
||||
Mutation-verified: swapping the filesystem test for `git ls-files` turns 23
|
||||
checks red, and the fixture asserts its own ground truth (`git check-ignore`
|
||||
really does ignore it) before anything depends on it. The known-negative
|
||||
control the location rule already passes is a real one - `maskinrommet`'s
|
||||
`serier/*/brief.md` are content briefs, not Voyage projects, and sit outside
|
||||
all three locations.
|
||||
|
||||
**The phase ladder measures ARTIFACTS, not sessions, and the legend says so in
|
||||
those words.** `brief-draft` -> `brief` -> `research` -> `plan` -> `execute` ->
|
||||
`review`, furthest artifact present wins. The bound is stated rather than
|
||||
hidden: `/trekexecute` leaves a file behind only in its MULTI-session form
|
||||
(`.session-state.local.json`), so a plan executed in one session leaves nothing
|
||||
and `plan` is the last thing the filesystem can prove. Nothing here claims a
|
||||
session is alive - the same refusal `FLY` carries, and pinned the same way, as
|
||||
a check on the legend TEXT.
|
||||
|
||||
**Three fields where an ABSENCE must not borrow the shape of a measurement.**
|
||||
`brief_quality` is read from the FRONTMATTER BLOCK ONLY (a body line is
|
||||
untrusted text and must not speak for the brief, the same rule
|
||||
`coord-count.sh` applies to `reply-expected`), and an absent field reads `-`,
|
||||
never `complete`: measured across the real tree 2026-08-31, only 8 of ~40
|
||||
briefs carry it at all. `research=-` (no directory, never started) and
|
||||
`research=0` (a directory holding nothing) are different facts - the completed
|
||||
run's `research/README.md` opened with "this directory held 0 files when the
|
||||
session started", and no board could show that. And `--voyage` always prints
|
||||
its own denominator; a tree with no projects says `ingen=0 ... funnet i N
|
||||
skannede repo` rather than rendering as an empty page.
|
||||
|
||||
**Blocking decisions are counted at DECLARATION SITES, not mentions.** The
|
||||
pattern `[BLOCKING DECISION, before S<n>]` occurs on FOUR lines of the real
|
||||
brief, of which ONE declares it and three refer to it in prose, so a bare
|
||||
`grep -c` answers 4 where the honest answer is 1. The bracket must open the
|
||||
line's content after list markers, headings and bold. Mutation-verified:
|
||||
replacing the anchored pattern with the bare string turns exactly three checks
|
||||
red, the known-negative control (a brief whose only occurrences are prose must
|
||||
count 0) among them. It reports what the brief DECLARES and infers nothing
|
||||
about resolution - board reports and refuses the inference.
|
||||
|
||||
**The age is the NEWEST artifact, which INVERTS the oldest-wins rule ORDRE and
|
||||
FLY carry, deliberately.** An order queue's problem is the oldest item still
|
||||
waiting; a project's problem is that its most recent activity is old
|
||||
("nothing has happened here for N days"). Both answer the same question about
|
||||
a different object. Mutation-verified: flipping `-gt` to `-lt` turns exactly
|
||||
the one oldest/newest check red with every control green. The repo-level `VOY`
|
||||
cell then takes the STALEST project's age, which is the oldest-wins rule
|
||||
applied one level up.
|
||||
|
||||
**`VOY` is display-only, never summed with `ORDRE`/`FLY`, and never in the
|
||||
sort** - the standing rule that a new ranking axis would silently reorder a
|
||||
parser living in another repo. It is not in `--plan`'s `key=value` blocks
|
||||
either: the requirement is that a human reading the board can see a brief is
|
||||
standing still, and the table is where that is read. The per-project detail
|
||||
lives in `--voyage` alone, because one cell cannot carry a phase when a repo
|
||||
holds seven briefs (`portfolio-optimiser` holds 20).
|
||||
|
||||
**The cost was measured rather than assumed, and this file's old "~3s" claim
|
||||
did not survive it.** Shipped 0.31.0 takes **6.3s** over the real 52-repo tree;
|
||||
with `VOY` it is **7.7-9.2s**. The first cut called `stat` once per artifact
|
||||
and took 13.4s - the batched form (`find ... -exec stat -f %m {} +`, one
|
||||
`find` and one `stat` per project) is what makes the column affordable beside
|
||||
the table. Live verification against the completed run reproduced every field
|
||||
the order named: `fase=plan`, `kvalitet=complete`, `blokkerende=1`,
|
||||
`blokkerende_gate=S4`, `research=5`, and it is the only project of 51 across
|
||||
14 repos with an open blocking decision.
|
||||
|
||||
**Bounded gap, stated rather than closed:** whether a detected project is
|
||||
still "in flight" is NOT decided here. Distinguishing a live brief from a
|
||||
finished one needs a threshold, and encoding one would make the board decide
|
||||
that work is abandoned - the identical thing the order queue is already
|
||||
forbidden from doing ("nothing here expires anything"). The board reports
|
||||
phase and age and leaves the judgement to the reader, which is why 51
|
||||
projects are listed rather than some filtered subset.
|
||||
|
||||
- **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 —
|
||||
|
|
@ -1122,7 +1214,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 (242/242),
|
||||
`bash scripts/board-selftest.sh` must exit 0 (325/325),
|
||||
`bash scripts/board-selftest.sh` must exit 0 (360/360),
|
||||
`bash scripts/route-selftest.sh` must exit 0 (69/69),
|
||||
`bash scripts/orders-selftest.sh` must exit 0 (110/110) and
|
||||
`bash scripts/state-line-guard-selftest.sh` must exit 0 (54/54).
|
||||
|
|
@ -1156,7 +1248,11 @@ obligations in another repo.
|
|||
- Hook smoke test: `node hooks/scripts/session-start.mjs` (expects JSON on stdout)
|
||||
- State-line-guard smoke test: `echo '{"tool_name":"Write","tool_input":{"file_path":"/tmp/STATE.md","content":"x\n"}}' | node hooks/scripts/pre-state-line-guard.mjs; echo $?`
|
||||
(expects exit 0, no output — a one-line STATE.md is under the limit)
|
||||
- Board smoke test: `bash scripts/board.sh` (read-only, ~3s over the real tree)
|
||||
- Board smoke test: `bash scripts/board.sh` (read-only, ~8s over the real
|
||||
52-repo tree - measured 2026-08-31; the older "~3s" figure predated both
|
||||
the current tree size and the VOY column)
|
||||
- Voyage smoke test: `bash scripts/board.sh --voyage` (read-only, writes
|
||||
nothing; ~22s, since it greps every detected brief)
|
||||
- Briefing smoke test: `bash scripts/board.sh --brief` (read-only, writes
|
||||
nothing). `brief-nightly.sh` DOES write — it overwrites `$CLAUDE_BRIEF_FILE`
|
||||
(default `~/.claude/briefing.md`), so point that at a scratch path when
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue