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:
parent
2377735554
commit
3beef2a603
12 changed files with 532 additions and 25 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "repo-mailbox",
|
||||
"version": "0.28.0",
|
||||
"version": "0.29.0",
|
||||
"description": "Local mailbox for coordination between Claude Code sessions in different repositories. Directed messages and broadcasts as plain Markdown files on your own disk, injected as context at session start. Local, private, no network.",
|
||||
"author": {
|
||||
"name": "Kjell Tore Guttormsen"
|
||||
|
|
|
|||
49
CHANGELOG.md
49
CHANGELOG.md
|
|
@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.29.0] - 2026-08-23
|
||||
|
||||
Answers a question the board could not answer before: which repos are finished
|
||||
and can take more work. Ordered as an investigation, not as a column - the
|
||||
argument for what was NOT built is in
|
||||
`docs/2026-08-23-free-capacity-investigation.md`.
|
||||
|
||||
### Added
|
||||
|
||||
- **`FLY` column: orders in flight (`orders/claimed/`).** `ORDRE` counted
|
||||
pending orders only, so a repo holding one order in flight and a repo holding
|
||||
no orders at all both printed `0` - the same digit for two opposite facts.
|
||||
Measured the day this was ordered: two panes stood open and idle for 45 hours
|
||||
holding finished orders, with full quota authorised, and no column reported
|
||||
it. FLY is the same queue in its other state, never summed with ORDRE and
|
||||
never a fourth axis.
|
||||
- **`--plan` names free capacity**: `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`. Such a repo has no
|
||||
next step to open a tab for and used to fall out of the plan silently. All
|
||||
four conditions are required - measured on the real tree, 4 of 17
|
||||
done/deferred repos were not free. Emitted as 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. The count prints at 0, so "none
|
||||
found" and "not computed" cannot render the same.
|
||||
- **`fly=N` on a tab block** whose repo already holds a claimed order. The
|
||||
driver types into live panes.
|
||||
|
||||
### Unchanged, deliberately
|
||||
|
||||
- **No process inspection.** `pgrep`/`ps`/`lsof` were considered and refused,
|
||||
and the selftest now asserts their absence structurally with a known-positive
|
||||
control. Every other column is a durable filesystem fact reproducible in a
|
||||
fixture tree; a process column measures one instant on one machine, needs
|
||||
`lsof` to map a process to a repo, and rests on a CPU-vs-elapsed THRESHOLD -
|
||||
which would make the board decide a session is dead, the same thing the order
|
||||
queue is already forbidden from doing.
|
||||
- **FLY does not mean a session is alive**, and the on-screen legend says so.
|
||||
Nothing un-claims an order when the session that claimed it dies: one order on
|
||||
the live mailbox had been claimed for 117 hours. Selftest section 24 pins the
|
||||
denial as a check on the legend text.
|
||||
|
||||
### Known gap, stated rather than closed
|
||||
|
||||
An open, idle pane in a `planned` repo with no claimed order and no queued work
|
||||
is still invisible, and cannot be seen from the filesystem at all. Closing it
|
||||
needs a terminal-side measurement supplied inward by the driver that already
|
||||
probes panes - the same direction `--dispatch --target-pane` already uses.
|
||||
|
||||
## [0.28.0] - 2026-08-20
|
||||
|
||||
Five fixes were committed and pushed after `v0.27.0` and none of them was
|
||||
|
|
|
|||
73
CLAUDE.md
73
CLAUDE.md
|
|
@ -347,7 +347,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` (259 checks).
|
||||
STATE.md and no mailbox. Pinned by `board-selftest.sh` (281 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
|
||||
|
|
@ -805,12 +805,77 @@ marketplace plugin. Three components, one boundary:
|
|||
- **Board's ORDRE column** counts PENDING orders per repo with the identical
|
||||
idiom as INN, and the two are never summed: INN is "others are waiting on
|
||||
YOU" (outgoing obligation), ORDRE is "authorized work is waiting on this
|
||||
REPO" (incoming). Claimed orders are excluded — the column answers what a
|
||||
session can pick up, and one in flight cannot be. Bounded gap, stated rather
|
||||
REPO" (incoming). Claimed orders are excluded from THIS column — it answers
|
||||
what a session can pick up, and one in flight cannot be; since 0.29.0 they are
|
||||
counted in FLY beside it (below). Bounded gap, stated rather
|
||||
than closed: an order addressed to a mailbox with no matching directory in
|
||||
the scanned roots is invisible here, exactly as mail to such a name is
|
||||
invisible in INN. There is deliberately no join built for it; `coord-count.sh`
|
||||
is the cross-check for the mail half only.
|
||||
|
||||
**Order 20260823T162951Z (2026-08-23): ORDRE alone printed the SAME DIGIT for
|
||||
two opposite facts, and the fix is a second column, not a fourth axis.** A
|
||||
repo holding one order in flight and a repo holding no orders at all both
|
||||
read `ORDRE 0` — "work is happening here" and "nothing is waiting here" were
|
||||
byte-identical rows. Measured the day the order was written: two panes stood
|
||||
open and idle for 45 hours holding finished orders, during a window with full
|
||||
quota authorised, and no column on the board reported it. **FLY** counts
|
||||
`orders/claimed/` — the same queue in its other state, which is why it is a
|
||||
second reading of a source the board already had rather than a new source,
|
||||
and why it is never summed with ORDRE. Verified live: one repo moved from
|
||||
`ORDRE 0` to `ORDRE 0 / FLY 1` beside another still reading `ORDRE 0 / FLY 0`
|
||||
and genuinely holding nothing.
|
||||
|
||||
**FLY never claims a session is ALIVE, and the legend denies it in those
|
||||
words.** A claim is a `mv` a session performed once; nothing un-claims it when
|
||||
that session dies, which is precisely why `coord-order-inbox.sh` already shows
|
||||
claimed orders with their in-flight age. Measured on the live mailbox the same
|
||||
day: one order had been claimed for **117 hours**. Rewording the column into
|
||||
"a session is running here" would be the process axis smuggled in as a file
|
||||
count. Selftest section 24 pins the denial as a check on the legend TEXT, so
|
||||
the wording is engine behavior and not prose.
|
||||
|
||||
**There is deliberately NO process inspection, and the selftest asserts it
|
||||
structurally** (no live `pgrep`/`pkill`/`lsof` in `board.sh`, with a
|
||||
known-positive control proving the grep can find a planted call). Four
|
||||
compounding reasons, argued in full in
|
||||
`docs/2026-08-23-free-capacity-investigation.md`: every other column is a
|
||||
durable filesystem fact reproducible in the fixture tree, while a process
|
||||
column measures one instant on one machine and could not be pinned here at
|
||||
all; mapping a process to a repo needs its cwd, hence `lsof`, against the
|
||||
zero-dependency convention; the ELAPSED-vs-CPU discriminator is a THRESHOLD,
|
||||
so encoding it makes the board decide a session is dead — the identical thing
|
||||
the order queue is already forbidden from doing ("nothing here expires
|
||||
anything, and building expiry would make the engine decide that a session is
|
||||
dead, which it cannot know"); and the file-based signal is no substitute
|
||||
either, which the 117-hour claim proves. The board reports the claim and
|
||||
refuses the inference.
|
||||
|
||||
- **`--plan` NAMES free capacity as lines, never as blocks (0.29.0).**
|
||||
`ledig_antall=N` plus one `ledig=<repo> (<status>)` line per repo with nothing
|
||||
OWED (not merely nothing pending — a notice is not an obligation), no pending
|
||||
order, nothing in flight, a clean tree, at `done` or `deferred`. Such a repo
|
||||
has no next step to open a tab for and used to simply fall OUT of the plan —
|
||||
correctly, but silently, which is where new capacity went unseen. All four
|
||||
conditions are load-bearing and `status=done` alone is not the same set:
|
||||
measured 2026-08-23, 13 of 17 done/deferred repos were free and 4 were not
|
||||
(two held a pending order, one owed a reply, one had an uncommitted tree).
|
||||
The LINE form is the design, not a formatting choice: `morning`'s `plan_parse`
|
||||
discards a block with no `tab=`, so a free repo emitted as a block would be
|
||||
visible to the operator and invisible to the driver, while a line is visible
|
||||
to both and can never be opened as a tab. The count prints at 0 too, so
|
||||
"none found" and "not computed" cannot render the same. A tab block whose repo
|
||||
holds a claimed order carries `fly=N` beside it — the driver types into live
|
||||
panes.
|
||||
|
||||
**The gap this leaves is stated rather than closed:** an open, idle pane in a
|
||||
`planned` repo with no claimed order and no queued work is still invisible,
|
||||
and cannot be seen from the filesystem at all — nothing there distinguishes
|
||||
it from a `planned` repo nobody has opened. Closing it needs a terminal-side
|
||||
measurement supplied INWARD by the driver that already probes panes, the same
|
||||
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.
|
||||
- **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 —
|
||||
|
|
@ -859,7 +924,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 (230/230),
|
||||
`bash scripts/board-selftest.sh` must exit 0 (259/259),
|
||||
`bash scripts/board-selftest.sh` must exit 0 (281/281),
|
||||
`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 (40/40).
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Session A in repo X leaves a message for repo Y; the next session in repo Y gets
|
|||
|
||||
*AI-generated: all code produced by Claude Code through dialog-driven development.*
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
|
|||
127
docs/2026-08-23-free-capacity-investigation.md
Normal file
127
docs/2026-08-23-free-capacity-investigation.md
Normal 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.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "repo-mailbox",
|
||||
"version": "0.28.0",
|
||||
"version": "0.29.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -2141,6 +2141,148 @@ check "--brief recovers the command too (route_cmd_for has one reader)" $?
|
|||
|
||||
/bin/rm -rf "$RG_ROOT" 2>/dev/null
|
||||
|
||||
|
||||
# --- 24. FLY (claimed orders) and --plan's free-capacity reading ------------
|
||||
# Order 20260823T162951Z (.claude, 2026-08-23): 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" vs "nothing is waiting here"). Measured on the live tree the day the
|
||||
# order was written: two tabs stood open and idle for 45 hours holding
|
||||
# finished orders while no column on the board reported it.
|
||||
#
|
||||
# FLY is NOT a fourth axis and never claims a session is alive. It counts
|
||||
# files in orders/claimed/ - the same order queue ORDRE already reads, in its
|
||||
# other state. Measured counter-example, live mailbox 2026-08-23: wiki-advise
|
||||
# held an order claimed for 117 hours. A claim is evidence that a session took
|
||||
# the order, never proof one is still working it, and the legend says so.
|
||||
FLY_ROOT="$(mktemp -d)"
|
||||
mkfly() { # name status
|
||||
mkrepo "$FLY_ROOT/$1"
|
||||
{
|
||||
echo "# STATE - $1"
|
||||
echo ""
|
||||
printf '## %s NESTE\n' "$HAND"
|
||||
echo "<!-- board: status=$2; blocked-on=-; next-cost=Sonnet 5/high -->"
|
||||
echo "<!-- route: path=known; verification=strong; reversibility=cheap; scope=local; rationale=x -->"
|
||||
echo "Next step for $1."
|
||||
} > "$FLY_ROOT/$1/STATE.md"
|
||||
# COMMITTED, deliberately: `mkrepo` alone leaves STATE.md untracked, so every
|
||||
# fixture repo would read DRT=1 - which silently satisfied the FLY column
|
||||
# assertions below before the column existed (the regex matched DRT's 1 where
|
||||
# it expected FLY's), and would make every repo permanently un-free under the
|
||||
# clean-tree condition. A dirty fixture is created explicitly where one is
|
||||
# wanted, never inherited.
|
||||
git -C "$FLY_ROOT/$1" add -A >/dev/null 2>&1
|
||||
git -C "$FLY_ROOT/$1" -c user.email=t@t -c user.name=t commit -qm init >/dev/null 2>&1
|
||||
}
|
||||
mkorder() { # mailbox state(pending|claimed) id
|
||||
mkdir -p "$CLAUDE_COORD_DIR/$1/orders/claimed"
|
||||
if [ "$2" = claimed ]; then f="$CLAUDE_COORD_DIR/$1/orders/claimed/$3.md"
|
||||
else f="$CLAUDE_COORD_DIR/$1/orders/$3.md"; fi
|
||||
printf -- '---\nfrom: d\nto: %s\norder-id: %s\nsubject: s\n---\nbody\n' "$1" "$3" > "$f"
|
||||
}
|
||||
|
||||
# THE defect, isolated: identical ORDRE, opposite meanings.
|
||||
mkfly fly-inflight planned; mkorder fly-inflight claimed 20260101T000000Z-1-from-d
|
||||
mkfly fly-empty planned
|
||||
# Both states at once, so the two counts are provably read from different dirs.
|
||||
mkfly fly-mixed planned
|
||||
mkorder fly-mixed pending 20260102T000000Z-2-from-d
|
||||
mkorder fly-mixed pending 20260103T000000Z-3-from-d
|
||||
mkorder fly-mixed claimed 20260104T000000Z-4-from-d
|
||||
|
||||
FLY_OUT="$("$BOARD" --roots "$FLY_ROOT" 2>/dev/null)"
|
||||
printf '%s' "$FLY_OUT" | grep -q 'FLY'; check "table header carries a FLY column" $?
|
||||
printf '%s' "$FLY_OUT" | grep -qE '^fly-mixed[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+2[[:space:]]+1[[:space:]]'
|
||||
check "ORDRE 2 and FLY 1 print side by side, never summed into 3" $?
|
||||
printf '%s' "$FLY_OUT" | grep -qE '^fly-inflight[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+0[[:space:]]+1[[:space:]]'
|
||||
check "an order in flight reads ORDRE 0 / FLY 1, not ORDRE 0 alone" $?
|
||||
printf '%s' "$FLY_OUT" | grep -qE '^fly-empty[[:space:]]+planned[[:space:]]+Sonnet 5/high[[:space:]]+0[[:space:]]+0[[:space:]]+0[[:space:]]'
|
||||
check "known-positive: a repo with no orders at all still reads 0 in both" $?
|
||||
printf '%s' "$FLY_OUT" | grep -q 'Next step for fly-mixed'
|
||||
check "NESTE still renders after the second field insertion" $?
|
||||
# The legend must refuse the liveness reading outright - a column that looked
|
||||
# like "a session is running here" would be the process axis smuggled in as a
|
||||
# file count, and wiki-advise's 117-hour claim is why that reading is false.
|
||||
printf '%s' "$FLY_OUT" | grep -qi 'FLY = '
|
||||
check "the legend defines FLY" $?
|
||||
printf '%s' "$FLY_OUT" | grep -qiE 'FLY.*(ikke|aldri).*(lever|kj[^ ]*r|aktiv)'
|
||||
check "the legend denies that FLY proves a session is alive" $?
|
||||
|
||||
# --- --plan: free capacity is NAMED, never turned into a tab ---------------
|
||||
# The operator's actual question ("which repos are finished and can take more
|
||||
# work") has an answer in fields the scan already reads, and it is not
|
||||
# status=done alone: measured on the real tree 2026-08-23, 4 of 17 done/
|
||||
# deferred repos were NOT free (2 held a pending order, 1 owed a reply, 1 had
|
||||
# an uncommitted tree). All four conditions are required.
|
||||
mkfly fly-free done
|
||||
mkfly fly-busy-order done; mkorder fly-busy-order pending 20260105T000000Z-5-from-d
|
||||
mkfly fly-busy-fly done; mkorder fly-busy-fly claimed 20260106T000000Z-6-from-d
|
||||
mkfly fly-busy-mail done
|
||||
mkdir -p "$CLAUDE_COORD_DIR/fly-busy-mail/inbox"
|
||||
printf -- '---\nfrom: x\nto: fly-busy-mail\nsubject: m\nreply-expected: yes\n---\nbody\n' \
|
||||
> "$CLAUDE_COORD_DIR/fly-busy-mail/inbox/20260107T000000Z-7-from-x.md"
|
||||
mkfly fly-busy-dirty done
|
||||
echo "uncommitted" > "$FLY_ROOT/fly-busy-dirty/scratch.txt"
|
||||
mkfly fly-free-def deferred
|
||||
|
||||
FLY_PLAN="$("$BOARD" --roots "$FLY_ROOT" --plan 2>/dev/null)"
|
||||
printf '%s\n' "$FLY_PLAN" | grep -qE '^ledig=fly-free \(done\)$'
|
||||
check "plan names a genuinely free repo with its status" $?
|
||||
printf '%s\n' "$FLY_PLAN" | grep -qE '^ledig=fly-free-def \(deferred\)$'
|
||||
check "plan names a deferred repo as free, labelled deferred rather than merged into done" $?
|
||||
printf '%s\n' "$FLY_PLAN" | grep -q '^ledig=fly-busy-order'; [ $? -ne 0 ]
|
||||
check "a done repo holding a pending order is not free" $?
|
||||
printf '%s\n' "$FLY_PLAN" | grep -q '^ledig=fly-busy-fly'; [ $? -ne 0 ]
|
||||
check "a done repo with an order in flight is not free" $?
|
||||
printf '%s\n' "$FLY_PLAN" | grep -q '^ledig=fly-busy-mail'; [ $? -ne 0 ]
|
||||
check "a done repo owing a reply is not free" $?
|
||||
printf '%s\n' "$FLY_PLAN" | grep -q '^ledig=fly-busy-dirty'; [ $? -ne 0 ]
|
||||
check "a done repo with an uncommitted tree is not free" $?
|
||||
# A count, so zero is an ANSWER and not an absence - the same rule fokus_utenfor
|
||||
# already follows with its "(ingen)" placeholder.
|
||||
printf '%s\n' "$FLY_PLAN" | grep -qE '^ledig_antall=2$'
|
||||
check "plan states how many free repos it found" $?
|
||||
# The plan's second consumer opens a tab per block; plan_parse discards a block
|
||||
# with no tab=, so a free repo emitted as a BLOCK would be invisible to the
|
||||
# driver. A single key=value line is visible to both and can never be opened.
|
||||
printf '%s\n' "$FLY_PLAN" | grep -A2 '^ledig=fly-free ' | grep -q '^tab='; [ $? -ne 0 ]
|
||||
check "a free repo is never emitted as a tab block" $?
|
||||
printf '%s\n' "$FLY_PLAN" | grep -q '^repo=fly-free$'; [ $? -ne 0 ]
|
||||
check "a free repo does not enter the tab list at all" $?
|
||||
# A repo the plan DOES open a tab for may already hold a claimed order: the
|
||||
# driver must be able to see that before typing into a live pane.
|
||||
printf '%s\n' "$FLY_PLAN" | grep -A6 '^repo=fly-inflight$' | grep -qE '^fly=1$'
|
||||
check "a planned repo with an order in flight carries fly=1 in its block" $?
|
||||
printf '%s\n' "$FLY_PLAN" | grep -A6 '^repo=fly-empty$' | grep -q '^fly='; [ $? -ne 0 ]
|
||||
check "known-positive: a planned repo with nothing in flight carries no fly= line" $?
|
||||
|
||||
# Zero free repos must still print the count line, or "none found" and "not
|
||||
# computed" become the same output - Verifiseringslovens ansikt 4.
|
||||
FLY_ROOT2="$(mktemp -d)"
|
||||
FLY_SAVE="$FLY_ROOT"; FLY_ROOT="$FLY_ROOT2"
|
||||
mkfly fly-only-planned planned
|
||||
FLY_ROOT="$FLY_SAVE"
|
||||
FLY_PLAN2="$("$BOARD" --roots "$FLY_ROOT2" --plan 2>/dev/null)"
|
||||
printf '%s\n' "$FLY_PLAN2" | grep -qE '^ledig_antall=0$'
|
||||
check "a tree with no free repo still states the count (zero is an answer)" $?
|
||||
printf '%s\n' "$FLY_PLAN2" | grep -q '^ledig='; [ $? -ne 0 ]
|
||||
check "a tree with no free repo emits no ledig= line" $?
|
||||
/bin/rm -rf "$FLY_ROOT2" 2>/dev/null
|
||||
|
||||
# board.sh must not have grown a process-inspection path. The three axes are
|
||||
# durable filesystem facts reproducible in this fixture tree; a pgrep/ps/lsof
|
||||
# column would measure the operator's machine at one instant, could not be
|
||||
# pinned here at all, and would make the engine decide a session is dead -
|
||||
# which CLAUDE.md already forbids the order queue from doing.
|
||||
grep -nE '(^|[^-[:alnum:]_])(pgrep|pkill|lsof)([^-[:alnum:]_]|$)' "$DIR/board.sh" | grep -qv '^[0-9]*:#'; [ $? -ne 0 ]
|
||||
check "board.sh inspects no processes (no live pgrep/pkill/lsof)" $?
|
||||
printf 'x\npgrep claude\n' > "$FLY_ROOT/known-positive.txt"
|
||||
grep -qE '(^|[^-[:alnum:]_])pgrep([^-[:alnum:]_]|$)' "$FLY_ROOT/known-positive.txt"
|
||||
check "known-positive: the process-inspection grep can find a planted pgrep call" $?
|
||||
|
||||
/bin/rm -rf "$FLY_ROOT" 2>/dev/null
|
||||
|
||||
echo ""
|
||||
echo "board-selftest: $PASS passed, $FAIL failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
|
|
|
|||
128
scripts/board.sh
128
scripts/board.sh
|
|
@ -9,6 +9,21 @@
|
|||
# STATE.md board line - optional machine-readable field (see below)
|
||||
# git status --porcelain - uncommitted risk
|
||||
# git log -1 --format=%ct - when anything last landed (the SISTE column)
|
||||
# ~/.claude/coord/<repo>/orders - PENDING orders (the ORDRE column)
|
||||
# ~/.claude/coord/<repo>/orders/claimed - orders IN FLIGHT (the FLY column).
|
||||
# Same queue, other state - not a fourth axis.
|
||||
# It exists because ORDRE alone printed the same
|
||||
# digit for two opposite facts: one order in
|
||||
# flight and no orders at all both read 0. FLY
|
||||
# never claims a session is ALIVE: nothing
|
||||
# un-claims an order when the session that took
|
||||
# it dies (measured 2026-08-23: one order held
|
||||
# claimed for 117 hours). There is deliberately
|
||||
# NO process inspection here - pgrep/ps/lsof
|
||||
# would measure the operator's machine at one
|
||||
# instant, could not be pinned in a fixture tree,
|
||||
# and would make this script decide a session is
|
||||
# dead, which it cannot know.
|
||||
# ~/.claude/coord/<repo>/inbox - UNHANDLED INBOUND: others addressed this
|
||||
# repo and it has not processed them. This is an
|
||||
# obligation the repo owes outward - NOT evidence
|
||||
|
|
@ -81,6 +96,19 @@
|
|||
# Within a group: the quantity that group is about (released, then owed), then a
|
||||
# Sonnet next-cost (cheap to move under quota pressure), then oldest plan first.
|
||||
#
|
||||
# --plan also NAMES free capacity, as `ledig_antall=N` plus one `ledig=<repo>
|
||||
# (<status>)` line per repo, and never as a block. A repo with nothing owed,
|
||||
# nothing queued, nothing in flight and a clean tree, at status done or
|
||||
# deferred, has no next step to open a tab for - it used to just fall out of
|
||||
# the plan, which is where new capacity went unseen. All four conditions are
|
||||
# required: status=done alone is not one of them (measured 2026-08-23, 4 of 17
|
||||
# done/deferred repos were not free). It is a LINE and not a block on purpose:
|
||||
# plan_parse discards a block with no `tab=`, so a block would be visible to
|
||||
# the operator and invisible to the driver, while a line is visible to both and
|
||||
# can never be opened as a tab. The count prints even at 0, so "none found" and
|
||||
# "not computed" cannot render the same. A tab block whose repo holds a claimed
|
||||
# order additionally carries `fly=N` - the driver types into live panes.
|
||||
#
|
||||
# 0.19.0 shipped a weighted score here instead, and 0.20.0 replaced it. The score
|
||||
# expressed one thing this cannot - "owes one message AND releases two others" as
|
||||
# a single quantity - and the operator accepted losing that. What a score could
|
||||
|
|
@ -309,7 +337,7 @@ done
|
|||
[ -n "$(printf '%s' "$REPOS" | tr -d '[:space:]')" ] || exit 0
|
||||
|
||||
# --- Collect one record per repo -------------------------------------------
|
||||
# Record: bucket|sortkey|name|status|cost|inbox|dirty|age|last|dir|blockedon|orders|neste
|
||||
# Record: bucket|sortkey|name|status|cost|inbox|dirty|age|last|dir|blockedon|orders|claimed|neste
|
||||
RECORDS=""
|
||||
MALFORMED=""
|
||||
|
||||
|
|
@ -344,6 +372,30 @@ printf '%s\n' "$REPOS" | while IFS= read -r d; do
|
|||
[ -n "$orders" ] || orders=0
|
||||
fi
|
||||
|
||||
# CLAIMED orders, counted separately and never added to the one above. Same
|
||||
# queue, other state - not a fourth axis, which is why it is a second reading
|
||||
# of orders/ rather than a new source. It exists because ORDRE alone printed
|
||||
# the SAME DIGIT for two opposite facts: a repo with one order in flight and a
|
||||
# repo with no orders at all both read 0, so "work is happening here" and
|
||||
# "nothing is waiting here" were indistinguishable (order 20260823T162951Z,
|
||||
# measured on the live tree: two tabs idle 45 hours holding finished orders,
|
||||
# and no column reported it).
|
||||
#
|
||||
# What this count 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 keeps showing claimed orders with their in-flight age. Measured
|
||||
# on the live mailbox the same day: wiki-advise held an order claimed for 117
|
||||
# hours. FLY is evidence that someone took the order, never proof anyone is
|
||||
# still working it - the 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.
|
||||
claimed=0
|
||||
if [ -d "$COORD/$name/orders/claimed" ]; then
|
||||
claimed="$(ls "$COORD/$name/orders/claimed"/*.md 2>/dev/null | wc -l | tr -d ' ')"
|
||||
[ -n "$claimed" ] || claimed=0
|
||||
fi
|
||||
|
||||
# Read for EVERY repo, not just the STATE-less ones: a repo whose plan file
|
||||
# is fresh can still have been silent for a year, and that is precisely the
|
||||
# repo no other column reports. A repo with no commits at all has no reading
|
||||
|
|
@ -359,8 +411,8 @@ printf '%s\n' "$REPOS" | while IFS= read -r d; do
|
|||
# No plan file, so no plan age: ALDER is blank rather than quietly showing
|
||||
# the commit age under a heading that means something else everywhere else
|
||||
# in the table. The sort key keeps using it - order is unchanged.
|
||||
printf '5|%06d|%s|-|-|%s|%s|-|%s|%s|-|%s|(ingen STATE.md)\n' \
|
||||
"$lastd" "$name" "$inbox" "$dirty" "$lastcol" "$d" "$orders"
|
||||
printf '5|%06d|%s|-|-|%s|%s|-|%s|%s|-|%s|%s|(ingen STATE.md)\n' \
|
||||
"$lastd" "$name" "$inbox" "$dirty" "$lastcol" "$d" "$orders" "$claimed"
|
||||
continue
|
||||
fi
|
||||
|
||||
|
|
@ -449,9 +501,9 @@ printf '%s\n' "$REPOS" | while IFS= read -r d; do
|
|||
# `orders` sits between blockedon and neste, never after it: `neste` is free
|
||||
# prose lifted out of a STATE.md and may contain a literal '|', so it has to
|
||||
# stay the last field on the line.
|
||||
printf '%s|%06d|%s|%s|%s|%s|%s|%sd|%s|%s|%s|%s|%s\n' \
|
||||
printf '%s|%06d|%s|%s|%s|%s|%s|%sd|%s|%s|%s|%s|%s|%s\n' \
|
||||
"$bucket" "$age" "$name" "$disp" "$cost" "$inbox" "$dirty" "$age" "$lastcol" "$d" \
|
||||
"${blockedon:--}" "$orders" "$neste"
|
||||
"${blockedon:--}" "$orders" "$claimed" "$neste"
|
||||
done > "${TMPDIR:-/tmp}/board.$$"
|
||||
|
||||
RECORDS="${TMPDIR:-/tmp}/board.$$"
|
||||
|
|
@ -558,15 +610,15 @@ awk -F'|' '$4 ~ /^blocked/ && $11 != "-" && $11 != "" {print $11}' "$RECORDS" \
|
|||
|
||||
hdr() {
|
||||
printf '\n%s\n' "$1"
|
||||
printf '%-32s %-34s %-14s %4s %6s %4s %6s %6s %s\n' \
|
||||
"REPO" "STATUS" "KOST" "INN" "ORDRE" "DRT" "ALDER" "SISTE" "NESTE"
|
||||
printf '%-32s %-34s %-14s %4s %6s %4s %4s %6s %6s %s\n' \
|
||||
"REPO" "STATUS" "KOST" "INN" "ORDRE" "FLY" "DRT" "ALDER" "SISTE" "NESTE"
|
||||
}
|
||||
|
||||
rows() {
|
||||
awk -F'|' -v b="$1" '$1==b' "$RECORDS" | sort -t'|' -k2,2n | \
|
||||
while IFS='|' read -r bucket sortkey name status cost inbox dirty age last dir blockedon orders neste; do
|
||||
printf '%-32s %-34s %-14s %4s %6s %4s %6s %6s %s\n' \
|
||||
"$name" "$status" "$cost" "$inbox" "$orders" "$dirty" "$age" "$last" "$(trunc "$neste" "$NESTE_WIDTH")"
|
||||
while IFS='|' read -r bucket sortkey name status cost inbox dirty age last dir blockedon orders claimed neste; do
|
||||
printf '%-32s %-34s %-14s %4s %6s %4s %4s %6s %6s %s\n' \
|
||||
"$name" "$status" "$cost" "$inbox" "$orders" "$claimed" "$dirty" "$age" "$last" "$(trunc "$neste" "$NESTE_WIDTH")"
|
||||
done
|
||||
}
|
||||
|
||||
|
|
@ -757,7 +809,7 @@ brief() {
|
|||
FILENAME==OWF { ow[$1] = $2 + 0; next }
|
||||
{ name = $3; owed = (name in ow) ? ow[name] : 0; if (owed > 0) print owed, $0 }
|
||||
' "$OWED" "$RECORDS" | sort -t'|' -k1,1nr | \
|
||||
while IFS='|' read -r owed bucket sortkey name status cost inbox dirty age last dir blockedon orders neste; do
|
||||
while IFS='|' read -r owed bucket sortkey name status cost inbox dirty age last dir blockedon orders claimed neste; do
|
||||
printf ' %-32s INN %-4s %s\n' "$name" "$owed" "$status"
|
||||
# Wrapped, not cut - the whole line is the point, but a 500-character one
|
||||
# is unreadable in a file nobody watched being written. Locale is set for
|
||||
|
|
@ -877,7 +929,7 @@ dispatch() {
|
|||
[ -n "$d_rec" ] || { echo "board: no repo named '$D_REPO' in the scanned roots ($ROOTS)" >&2; exit 2; }
|
||||
d_status="$(printf '%s' "$d_rec" | cut -d'|' -f4)"
|
||||
d_dir="$(printf '%s' "$d_rec" | cut -d'|' -f10)"
|
||||
d_neste="$(printf '%s' "$d_rec" | cut -d'|' -f13-)"
|
||||
d_neste="$(printf '%s' "$d_rec" | cut -d'|' -f14-)"
|
||||
[ -d "$d_dir" ] || { echo "board: the directory for $D_REPO does not exist: $d_dir" >&2; exit 2; }
|
||||
|
||||
# Rejected traits REFUSE. Degrading to a command without them is the worst
|
||||
|
|
@ -1287,6 +1339,40 @@ plan() {
|
|||
# decided has a second consumer elsewhere), but the driver must be able to
|
||||
# see that the omission happened rather than read a shorter list as complete.
|
||||
[ "$HAVE_COUNT" -eq 1 ] || printf 'advarsel=coord-count.sh mangler - gjeldsvekting (gruppe 2, inbox:N) er ikke beregnet\n'
|
||||
|
||||
# --- Free capacity: NAMED, never turned into a tab ------------------------
|
||||
# The question the plan could not answer until now, in the operator's own
|
||||
# words: "which repos are finished and can take more work". Until 0.29.0 such
|
||||
# a repo simply fell OUT of the plan - correctly, since it has no next step to
|
||||
# open a tab for, but silently, which is where new capacity was going unseen.
|
||||
#
|
||||
# It is a LOOKUP over fields the scan already read, exactly like every group
|
||||
# term above - no new source, no new axis. All four conditions are required,
|
||||
# and status=done alone is not one of them: measured on the real tree
|
||||
# 2026-08-23, 4 of 17 done/deferred repos were NOT free (two held a pending
|
||||
# order, one owed a reply, one had an uncommitted tree). Debt is OWED, not raw
|
||||
# pending, for the same reason group 2 is - a notice is not an obligation.
|
||||
#
|
||||
# Emitted as single key=value LINES, never as blocks, and that is the whole
|
||||
# design: `plan_parse` discards a block with no `tab=`, so a free repo written
|
||||
# as a block would 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 is printed even when it is 0, so "none found" and "not computed"
|
||||
# cannot render as the same output.
|
||||
lf="${TMPDIR:-/tmp}/board-ledig.$$"
|
||||
awk -F'|' -v OWF="$OWED" '
|
||||
FILENAME==OWF { ow[$1] = $2 + 0; next }
|
||||
{
|
||||
name = $3; status = $4; dirty = $7 + 0; orders = $12 + 0; claimed = $13 + 0
|
||||
owed = (name in ow) ? ow[name] : 0
|
||||
if (status != "done" && status != "deferred") next
|
||||
if (owed > 0 || orders > 0 || claimed > 0 || dirty > 0) next
|
||||
print name " (" status ")"
|
||||
}
|
||||
' "$OWED" "$RECORDS" | sort > "$lf"
|
||||
printf 'ledig_antall=%s\n' "$(awk 'END{print NR+0}' "$lf")"
|
||||
sed 's/^/ledig=/' "$lf"
|
||||
/bin/rm -f "$lf" 2>/dev/null
|
||||
if [ -n "$FOCUS" ]; then
|
||||
fp_state="$(awk -F'|' '{print $10}' "$RECORDS" | while read -r fp_sd; do
|
||||
[ -n "$fp_sd" ] && [ -f "$fp_sd/STATE.md" ] && echo x
|
||||
|
|
@ -1322,7 +1408,7 @@ plan() {
|
|||
echo ""
|
||||
|
||||
pn=0
|
||||
while IFS='|' read -r why bucket sortkey name status cost inbox dirty age last dir blockedon orders neste; do
|
||||
while IFS='|' read -r why bucket sortkey name status cost inbox dirty age last dir blockedon orders claimed neste; do
|
||||
[ -n "$name" ] || continue
|
||||
pn=$((pn + 1))
|
||||
printf 'tab=%s\n' "$pn"
|
||||
|
|
@ -1336,6 +1422,12 @@ plan() {
|
|||
fp_ev="$(focus_evidence "$dir")" && printf 'fokus_treff=%s\n' "$fp_ev"
|
||||
fi
|
||||
printf 'status=%s\n' "$status"
|
||||
# Emitted only when there IS one, like fokus_treff= above: a `fly=0` on
|
||||
# every block would be noise, and its absence already says nothing is in
|
||||
# flight. A tab whose repo holds a claimed order may already have a session
|
||||
# in it, and the driver types into live panes - it has to be able to see
|
||||
# this before it does.
|
||||
[ "${claimed:-0}" -gt 0 ] 2>/dev/null && printf 'fly=%s\n' "$claimed"
|
||||
printf 'neste=%s\n' "$neste"
|
||||
plan_cmd "$dir" "$cost"
|
||||
echo ""
|
||||
|
|
@ -1376,7 +1468,7 @@ inbox_plan() {
|
|||
FILENAME==RCF {
|
||||
name = $3
|
||||
rc_bucket[name] = $1; rc_status[name] = $4; rc_cost[name] = $5
|
||||
rc_dir[name] = $10; rc_neste[name] = $13
|
||||
rc_dir[name] = $10; rc_neste[name] = $14
|
||||
next
|
||||
}
|
||||
{
|
||||
|
|
@ -1451,6 +1543,14 @@ echo "INN = uhaandtert innboks (andre venter paa DEG). DRT = ukommiterte filer."
|
|||
# legend states the distinction where they are read - they measure opposite
|
||||
# directions and are never summed.
|
||||
echo "ORDRE = ventende ordrer (arbeid venter paa REPOET). INN og ORDRE summeres ALDRI."
|
||||
# FLY is the SAME queue as ORDRE in its other state, never a fourth axis and
|
||||
# never a liveness reading. The denial is part of the legend, not a footnote:
|
||||
# nothing un-claims an order when the session that claimed it dies (measured
|
||||
# 2026-08-23, wiki-advise: one order claimed 117 hours), so "an order is
|
||||
# claimed here" is the only claim this number can carry.
|
||||
echo "FLY = ordrer i arbeid (claimed). Beviser aldri at en okt lever eller er aktiv."
|
||||
echo " Beviser bare at noen TOK ordren; ingenting frigir den om okten dor."
|
||||
echo " Summeres ALDRI med ORDRE - samme ko, annen tilstand."
|
||||
echo "ALDER = dager siden STATE.md endret. SISTE = dager siden siste commit."
|
||||
|
||||
[ "$(count 1)" -gt 0 ] && { hdr "BLOKKERT (venter paa ekstern avhengighet)"; rows 1; }
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ description: >-
|
|||
user names no repo and no tool — choosing *between* repos is this skill. Not for
|
||||
"where were we" inside the current repo: that is this repo's own STATE.md,
|
||||
already injected at session start.
|
||||
version: "0.28.0"
|
||||
version: "0.29.0"
|
||||
---
|
||||
|
||||
# board — which repo deserves the next session
|
||||
|
|
@ -64,11 +64,23 @@ rule exists to stop. The run costs about three seconds.
|
|||
| `STATUS` | `planned` / `in-progress` / `blocked` / `deferred` / `done`, or `blocked>X` naming the repo it waits on. `?` means the STATE.md has no board line. |
|
||||
| `KOST` | Model/effort for the next step, from the rubric row table in `route.sh` (the `route` skill writes it; this one only reads it). |
|
||||
| `INN` | Unhandled inbox: **other repos are waiting on THIS one**. An obligation it owes outward. |
|
||||
| `ORDRE` | Pending orders: **authorized work is waiting on this repo**, unclaimed and pickable. |
|
||||
| `FLY` | Orders in flight (claimed). Someone TOOK the order — never proof a session is still alive. |
|
||||
| `DRT` | Uncommitted files. |
|
||||
| `ALDER` | Days since STATE.md last changed — the age of the *plan*. `-` where the repo has none. |
|
||||
| `SISTE` | Days since the last commit — the age of the *work*. `-` where the repo has no commits yet. |
|
||||
| `NESTE` | First line of the STATE.md next-step block, truncated. |
|
||||
|
||||
**`ORDRE` and `FLY` are never summed, and `FLY` is never read as "busy."** They
|
||||
are the same queue in two states. Before `FLY` existed, a repo with one order in
|
||||
flight and a repo with no orders at all both printed `ORDRE 0` — the same digit
|
||||
for two opposite facts, which is how two tabs sat idle for 45 hours holding
|
||||
finished orders with nothing on the board reporting it. What `FLY` still cannot
|
||||
tell you is whether a session is *running*: nothing un-claims an order when the
|
||||
session that claimed it dies (one order on the live mailbox had been claimed for
|
||||
117 hours). Say "an order is claimed here", never "a session is working here".
|
||||
The board inspects no processes and will not start.
|
||||
|
||||
**`INN` never means "this repo is waiting on someone."** It means the opposite:
|
||||
messages arrived and were not handled. The mailbox format carries no reply-to or
|
||||
thread field, so outbound waiting is not derivable from it at all — `blocked>X` is
|
||||
|
|
@ -178,6 +190,18 @@ Two things to say out loud when you hand it over:
|
|||
command to paste. Name those repos rather than letting the operator discover it
|
||||
per tab. Fixing them is the `route` skill's job, in *that* repo — never a side
|
||||
quest here.
|
||||
- **`ledig_antall=N` and the `ledig=` lines are free capacity, not tabs.** They
|
||||
name the repos that can take NEW work: nothing owed, nothing queued, nothing in
|
||||
flight, clean tree, at `done` or `deferred`. They carry no `tab=` and no
|
||||
command on purpose — there is no next step to start, so the operator decides
|
||||
what to send there. Read them out when the ask is about capacity ("hvem kan ta
|
||||
mer arbeid", "hvor har jeg ledig kapasitet") and whenever the plan is short.
|
||||
`status=done` alone is **not** the same set: on the real tree 4 of 17
|
||||
done/deferred repos were not free. Never derive this list yourself from the
|
||||
table — the engine joins four fields you would have to join by hand.
|
||||
- **`fly=N` on a tab block means that repo already holds a claimed order.** Say
|
||||
so before the operator opens the pane. It is not proof a session is live, and
|
||||
it is not a reason to drop the tab — it is a reason to look first.
|
||||
|
||||
### When the day has a subject ("fokusdag")
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ description: >-
|
|||
covers retiring a broadcast that has become wrong or obsolete: "retract that
|
||||
broadcast", "that announcement is outdated, pull it", "trekk tilbake kringkastingen",
|
||||
"den broadcasten er utdatert".
|
||||
version: "0.28.0"
|
||||
version: "0.29.0"
|
||||
---
|
||||
|
||||
# coord-send — natural-language front door for inter-repo messages
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ description: >-
|
|||
skill. Not for choosing WHICH repo deserves the next session (that is `board`),
|
||||
not for scoring model and effort alone (that is `route`), and not for sending a
|
||||
message to another repo (that is `coord-send`).
|
||||
version: "0.28.0"
|
||||
version: "0.29.0"
|
||||
---
|
||||
|
||||
# dispatch — hand a session a task it can actually start on
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ description: >-
|
|||
the operator names no model and no tool — choosing the model for the next
|
||||
session IS this skill. Not for choosing which REPO gets the next session:
|
||||
that is the `board` skill.
|
||||
version: "0.28.0"
|
||||
version: "0.29.0"
|
||||
---
|
||||
|
||||
# route — what the next session should run with
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue