Harness Client
Scope
Owns being the client of a running supervisor: attach, observe, answer.
It is not for driving work yourself — harness-loop owns single-track work
(basicly loop run), and the supervisor itself owns fan-out over lanes
(basicly loop supervise <root>). Reach for this skill when a supervisor is
already running, or may be, and you are a second session looking in.
Every command here is either a pure read or a write to one decision item, and none of them touches a worktree, a branch, or a gate. The one exception is the board's default action route, below: it can approve a checkpoint or kill a lane.
Attach: one command, no lock
basicly loop session <root-issue> # live status of the session
basicly loop session <root-issue> --json # same, machine-readable
It reads the tracker and the supervisor's lockfile and writes nothing, so it never contends with the supervisor and any number of clients may attach at once. It answers four things:
- supervisor — who holds the repo's singleton lock, and how old their
heartbeat is.
(none running)means the root is unsupervised; a stale heartbeat means the holder crashed. - lane — each in-flight lane, its worktree and branch, whether that worktree still exists, and what the lane last ran (agent, outcome, tokens).
- decisions — how many judgments the session is waiting on.
- grant — the autonomy level in force and the tokens spent against its budget.
A root nobody is supervising is a valid attach, not an error. Exit status is 0
either way — use basicly loop decisions when you want an exit code that
means "blocked on a human".
A wall display attaches the same way
For a screen in the room rather than a terminal:
basicly board serve # http://127.0.0.1:8787
basicly board serve --port 0 # take an ephemeral port and print it
It takes no lock and writes no file, but by default it registers one POST route
that runs the basicly command an operator submits — a closed table of three
(answer a decision, approve a checkpoint, kill a lane), the last two behind a
typed confirm code. Pass --no-actions for an unattended wall, where a POST is
405 and nobody can drive a lane from a screen anyone in the room can touch, and
leave --bind unset so it stays on the loopback.
While the supervisor's heartbeat is fresh it serves the snapshot that
supervisor already wrote and folds nothing; with no live holder it folds for
itself every --refresh seconds. Every panel carries the snapshot's age, so a
frozen screen reads as frozen rather than as calm.
GET /snapshot.json is the same harness-board/v1 document, byte for byte,
for any other consumer. Ctrl-C reports how many refreshes it managed.
Never take the lock from a live holder
The lock is a repo singleton because two supervisors would land to the same
base checkout concurrently. So while loop session reports a fresh heartbeat:
do not start a second loop supervise, do not run basicly loop advance on
one of its lanes, and never delete or edit the lockfile under
.basicly/usage/. Answer the decision the session is waiting on instead —
that is what unblocks it, and the supervisor picks the answer up on its next
pass.
A stale heartbeat is the one case where taking over is right, and the way to take over is to start a supervisor:
basicly loop supervise <root-issue> # claims a stale lock atomically
Deleting the file by hand instead races a contender that may already be mid-takeover.
Answering a pending decision
basicly loop decisions <root-issue> # the queue; exits 1 when any pend
basicly loop watch <root-issue> # stream newly pending items
basicly loop answer <decision-id> "<answer>" # record one answer
Present each item to the human as itself: the bead it blocks, its kind, the question, and the detail the lane recorded as evidence. Then record the human's answer verbatim — it becomes durable evidence on the bead and the lane's next dispatch is assembled from it, so a paraphrase becomes the instruction the agent follows.
IMPORTANT: never answer a decision on the human's behalf. Every queued item is there because the engine determined the fact is not derivable from recorded state, so inventing one lands a wrong answer carrying human attribution. If nobody is available, either leave it pending or delegate it explicitly:
basicly loop decide <decision-id> --root <root-issue>
That invokes the decider agent under a confined tool surface, bounded to the session's intake corpus. It abstains back to the human when the answer is not in the corpus — an abstention is the correct outcome, not something to retry around.
Attribute a non-human answerer so the trail stays readable:
basicly loop answer <decision-id> "<answer>" --by "<who-decided>"
Poll no tighter than the heartbeat
loop watch --interval defaults to the supervisor's heartbeat cadence, and
each pass walks the whole session tree in the tracker. Polling faster buys no
fresher state and competes with the supervisor for the tracker.
basicly loop watch <root-issue> --once # one pass for a script, then exit
What a client does not do
Answering is the client's only write. Approving a checkpoint, granting
autonomy, landing a lane, and closing a bead all stay with whoever drives the
loop, under the confirm-code and grant rules harness-loop describes — a
client that approves a checkpoint has approved it on the supervisor's behalf,
which is precisely the human judgment the checkpoint exists to require.