feat(board): FLY column and free-capacity lines, no process inspection

ORDRE counted pending orders only, so a repo with one order in flight and a
repo with no orders at all both printed 0 - the same digit for two opposite
facts. Measured 2026-08-23: two panes stood open and idle for 45 hours holding
finished orders, with full quota authorised, and no column on the board
reported it.

FLY counts orders/claimed/ - the same queue in its other state, never summed
with ORDRE and never a fourth axis. It does NOT mean a session is alive:
nothing un-claims an order when the claiming session dies, and one order on
the live mailbox had been claimed for 117 hours. The legend denies the
liveness reading in those words, pinned as a check on the legend text.

--plan now names free capacity as ledig_antall=N plus one ledig=<repo>
(<status>) line per repo with nothing owed, no pending order, nothing in
flight, a clean tree, at done or deferred. All four conditions are required:
measured on the real tree, 4 of 17 done/deferred repos were not free. Lines,
never blocks - the plan's second consumer discards a block with no tab=, so a
block would be visible to the operator and invisible to the driver.

Process inspection was considered and refused; the selftest asserts the
absence of pgrep/pkill/lsof structurally with a known-positive control. The
full argument, and the gap left open, are in
docs/2026-08-23-free-capacity-investigation.md.

board-selftest: 259 -> 281 checks, all green. npm test 11/11.

Order 20260823T162951Z-941745020-from-.claude

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hfbfr8ERC63kuAfWeHYHQb
This commit is contained in:
Kjell Tore Guttormsen 2026-08-23 19:07:00 +02:00
commit 3beef2a603
12 changed files with 532 additions and 25 deletions

View file

@ -0,0 +1,127 @@
# Which repos are free? The investigation, and where it stopped
Ordered 2026-08-23. The operator's words, translated: *"we need more precision
about you knowing which repos are finished and can take more work."* What was
ordered was an investigation and a design, not a named column — the sending repo
supplied the problem and the measurements and left the shape of the answer here.
Repos are unnamed throughout. This is a public mirror, and which repo was idle
for how long is not a fact this file needs to carry to make its argument.
## The incident
A session read the board's `ORDRE` column, ran `pgrep -fl claude`, and told the
operator that four sessions were working. The operator looked at their screen:
one was. Measured afterwards, two of the four processes had accumulated ~38
minutes of CPU across **45 hours** of wall clock. They were open panes sitting at
a prompt, holding finished orders, during a window in which full quota had been
authorised against a deadline four days out.
Nothing on the board reported it, and that is the part this file is about.
## Three blind spots, one of which was ours
1. **`ORDRE` counted pending orders only.** So a repo with one order in flight
and a repo with no orders at all both printed `0`. The same digit for two
opposite facts — "work is happening here" and "nothing is waiting here" —
with no way to tell them apart. This one is a defect in this repo's own
rendering, and it is fixed.
2. **`STATUS` describes the plan, not the capacity.** `done` does mean "no open
step", which is close to what the operator wanted; `planned` and
`in-progress` say nothing about whether anyone is actually sitting there. One
of the idle repos was `planned` for the full 45 hours, entirely correctly.
3. **A process proves existence, not activity.** `pgrep` finds a session that
finished everything and went quiet. This one is not ours to fix — see below.
## What was changed
### `FLY`: the order queue's other state
A second count over `orders/claimed/`, printed in its own column beside `ORDRE`
and never summed with it. Same queue, other state — **not a fourth axis**, which
is why it is a second reading of a source the board already had rather than a new
source.
Verified live on the day it shipped: one repo went from `ORDRE 0` (reading as
"nothing here") to `ORDRE 0 / FLY 1`, next to another repo still reading
`ORDRE 0 / FLY 0` and genuinely holding nothing. Those two rows had been
byte-identical the day before.
**What `FLY` does not mean, and must never be reworded into: that a session is
alive.** A claim is a `mv` a session performed once. Nothing un-claims it when
that session dies — which is exactly why the order queue's read path already
shows claimed orders with their in-flight age. Measured on the live mailbox the
same day: one order had been sitting claimed for **117 hours**. `FLY` is evidence
that someone took the order. It is not evidence that anyone is still working it,
and the on-screen legend says so in those words, because a column that read as "a
session is running here" would be the process axis smuggled in as a file count.
### `--plan` names free capacity
`ledig_antall=N`, then one `ledig=<repo> (<status>)` line per free repo. Free
means all four at once: nothing **owed** (not merely nothing pending — a notice
is not an obligation), no pending order, nothing in flight, clean tree, at
`done` or `deferred`.
All four conditions are load-bearing. Measured on the real tree the same day: of
17 `done`/`deferred` repos, **13 were free and 4 were not** — two held a pending
order, one owed a reply, one had an uncommitted tree. `status=done` alone would
have named the wrong set roughly a quarter of the time.
It is emitted as **lines, never as blocks**, and that is the whole design rather
than a formatting preference. The plan's second consumer opens one pane per
block and discards any block without a `tab=` key; a free repo written as a block
would therefore be visible to the operator and invisible to the driver. A line is
visible to both and can never be opened as a tab by accident. The count prints
even when it is zero, so "none found" and "not computed" cannot render as the
same output.
A tab block whose repo already holds a claimed order additionally carries
`fly=N`. The driver types into live panes; it should be able to see that first.
## What was NOT changed, and the argument for it
**The board still inspects no processes.** No `pgrep`, no `ps`, no `lsof`, and a
structural check in the selftest now says so with a known-positive control
proving the grep can find a planted call. This was the order's open design
question, and the answer is no, for four reasons that compound:
1. **Every other column is a durable filesystem fact.** They survive a reboot and
they are reproducible in a throwaway fixture tree under `CLAUDE_COORD_DIR`. A
process column measures the operator's machine at one instant. There is no
fixture for it, so it would ship as an unmeasured assumption wearing a passing
test — this repo's own named defect class.
2. **Mapping a process to a repo needs its working directory**, which on macOS
means `lsof`. That is a new external dependency against a stated zero-
dependency convention, for a number that would still not mean what a reader
would take it to mean.
3. **The discriminator is a threshold, and a threshold is a verdict.** "38
minutes of CPU across 45 hours is idle" is a judgement about a session's
liveness. Encoding it makes the board decide that a session is dead. The order
queue is already forbidden from doing exactly this: nothing there expires
anything, because building expiry would require the engine to know something
it cannot. A process column is that same rule broken on a different surface.
4. **The file-based signal is not a substitute either**, and pretending otherwise
would just move the error. The 117-hour claim is the proof. So the board
reports the claim and refuses the inference, which is what "prints evidence
and takes no position" has always meant here.
The gap this leaves is real and is stated rather than closed: **a pane that is
open and idle with no claimed order and no queued work is still invisible to the
board.** Such a repo now shows up in `ledig=` if it is `done`/`deferred` and
clean, which covers the common case; a `planned` repo with an idle pane does not,
and cannot, because nothing in the filesystem distinguishes it from a `planned`
repo nobody has opened. Closing that needs a terminal-side measurement, and that
belongs to whatever drives the terminal, not to the mailbox.
## What this does not answer
The order asked whether the board should say anything about open tabs *at all*.
It now says nothing about them, deliberately. If the operator later wants pane
occupancy on the board, the honest construction is for the terminal driver —
which already probes panes and already consumes `--plan` — to supply that fact
inward, the same way `--dispatch` requires `--target-pane` to be measured by the
caller and passed in rather than looked up here. That direction keeps the
dependency running one way and keeps this repo unable to break on a terminal
API. It was not built, because it was not ordered and the operator has not asked
for it.