Skip to content

feat: standby rows — a quit leaves live rows one arrival from open, for a day - #273

Merged
olliegilbey merged 27 commits into
mainfrom
feat/standby-rows
Sep 23, 2026
Merged

olliegilbey merged 27 commits into
mainfrom
feat/standby-rows

Conversation

@olliegilbey

Copy link
Copy Markdown
Owner

What changed

Standby rows. After a session quit, the rows whose agent was live come back as standby (◐) for 24 hours. Arriving on one (Alt+Up/Down, a click, Alt+N) opens it at once. Dormant rows (○) still need Alt+Enter. Stacks on #271.

  1. Two stamp writers. The SessionEnd hook stamps a row when the reason is other (a quit or a tab close). The hook is lossy at a kill-session (QA run 33: one of five landed), so the launch (clear_session_order) also stamps every row still bound, dated from the store's last write, not the launch. A tab close is pruned by the bar and leaves the row dormant.
  2. The rank holds. A walk is not a commitment. An opened standby row keeps its frecency rank: the bind gives its tab the row's own ordinal, and the quit and the launch carry each tab's ordinal into its row. Alt+Enter on a dormant row, or on a row whose stamp has expired, still goes to the top.
  3. No hop. Opened rows hopped bottom, top, then their place in about 100 ms. The bar now joins a new tab to its row by the pane's launch command (clave spawn <uuid>, which zellij reports from the first frame), and sends no birth touch for a standby row's tab.
  4. Expiry on the bar's clock. The wire carries standby_since; the bar decides the 24 hours itself, because an idle fleet writes no snapshot for hours.
  5. Glyphs from one icon set. ●, ○ and ◐ are all Font Awesome (U+F111, U+F10C, U+F042), the same size in every Nerd Font measured. Base-font circles beside a Nerd Font icon matched only by luck. README icons regenerated.
  6. Swarm review fixes (11 lanes + verifier over this branch and feat!: remove the live-set restore; fix the frames-off width flap; the remote QA drive #271): the fixes above for the launch date and the bar clock, three tests that could not fail, ~150 lines of dead width diagnostics and two probe scripts deleted, stale docs, and the remote drive now refuses to start beside a running one.
  7. Docs. QA-DRIVE.md has a runbook for driving both machines at once and a section on adding a drive check. AGENTS.md and the testing docs: the agent kills the sandboxes it staged, by exact name.

Closes # (no issue was filed for standby rows)

Risk class(es) from the taxonomy (tick all that apply — they are cumulative):

  • Pure logic / model
  • Generated artifacts (config.kdl / layout.kdl / launch.kdl)
  • CLI surface (new subcommand or flag)
  • Cross-process / IPC (pipes, plugin shellouts, multi-writer store paths)
  • Install / environment (release mechanics, dev-install, PATH, doctor) → label needs-live-validation
  • Visual / UX (glyphs, colours, widths, fonts) → label host-untestable

Verified automatically

Command Result
cargo test --workspace 823 passed; 0 failed (clave-bar 314)
cargo build -p clave-bar --target wasm32-wasip1 Finished, no warnings
cargo clippy --workspace --all-targets -- -D warnings Finished, no warnings
bash scripts/qa/lib-selftest.sh 0 failures

Class-specific evidence:

  • Tests added, red-first? Yes. The hop tests failed with the hop's exact order ([Tab(1), Tab(2), Tab(3), Standby("u-s1")]); the host rank test failed first on the expired stamp.
  • just mutants main: 153 tested, 9 missed, all known equivalents (run_* → Ok(()), a const-assert loop in clave-types, RowHeight::mode_at from feat!: remove the live-set restore; fix the frames-off width flap; the remote QA drive #271). just mutants over the hop fix: 2 missed, both then covered by a test that fails under each (checked by hand).
  • Proptest: the ordinal total-order runner updated for a touch that mints nothing.
  • Cross-process: phase 6c checks every row the quit left live is on standby, one Alt+Down opens exactly one, its bind spends the stamp, its tab keeps the row's rank, and no birth touch reached it. The selftest proves each of those checks goes red.
  • Visual: glyph sizes measured with fonttools across four installed Nerd Fonts (FOOTGUNS).

QA drive, live

Run Machine Result
33 box, Mac 6c red: one of five rows on standby. SessionEnd is lossy at a kill; the launch became the second writer.
34, 35 box, Mac 0-7 green; standby 5/5 and the arrival leg on both.
36 Mac 0-7 green, 249 checks (before the hop fix). Walk of four standby rows peaked at 73 file handles of 256.
36 box Red once in phase 5 (the known rapid-burst intermittent), then 0-7 green, 248 checks.
37 box 0-7 green on the hop fix, 249 checks.
38 Mac 0-7 green on the hop fix, 250 checks. Maintainer walked the standby rows: the hop is gone, glyphs match.

Review lanes run

Lane Ran? Findings
Vendored fugu review (.claude/commands/fugu-review.md) no —
Independent adversarial reviewer (Opus subagent, blind) yes, as the swarm verifier 9 confirmed: 8 fixed, 1 partly (the shared box sandbox, below); 2 refuted.
CodeRabbit CLI (coderabbit review --committed --base main) no —
Other (Codex, PR bots) on the PR —

Findings DECLINED, with reasoning:

  • An AgentRecord::unbind helper for the three carry sites, a neutral quit: bool for the SessionEnd reason, a named_tabs helper, and evlog lines at the stamp sites: declined as churn, not defects.
  • The remote sandbox is still shared between worktrees on the box (Remote QA: each local worktree needs its own checkout and sandbox on the box #272). The drive now refuses to start beside a running one; real isolation needs a linked worktree per local worktree on the box, and a launch to prove it.

Could NOT be verified, and why

  • The 24-hour expiry has never run live; unit tests pin the edge on both the host and the bar.
  • A new tab's sidebar still flickers once as it loads: its first frame draws before it has read the store. Present on main for every new tab; not changed here.

Live steps for the maintainer

  1. After feat!: remove the live-set restore; fix the frames-off width flap; the remote QA drive #271, cut the release. Quit clave with agents running, relaunch. Expected: one tab for the most recent agent, the rows you left running show ◐, the rest ○.
  2. Alt+Down onto a ◐ row: it opens with no Alt+Enter, and stays in its place in the list.

Handoff and links

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: olliegilbey/clave/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: af0d0adc-51e8-45c0-8aa4-9a9d63678269


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Base automatically changed from fix/bar-separator-column to main September 23, 2026 10:44
… the wire

Plumbing only; nothing stamps it yet. A row whose agent ended with the
session is STANDBY for a day (maintainer ruling, 2026-09-22): arrival
opens it, where a dormant row waits for Alt+Enter. The host decides
expiry against its own clock, because the bar has none. The stamp
carries the tab the agent held, so a tab close that the agent's
SessionEnd beat to the store can still be pruned.

The record field is standby_stamp, not standby: the wire parity test
caught the same name meaning two types.
Quitting zellij fires SessionEnd for every running agent, and the hook
unbinds the tab. So the launch cannot read which rows were live. The
hook now stamps a standby record at that SessionEnd, with the time and
the tab. It stamps only when a tab was bound and the reason is `other`
or absent; /exit, /clear, resume and logout stay dormant.

The stamp clears on three events:
- a bind (the row is live again);
- a prune of the stamped tab (the human closed it, and SessionEnd
  reached the store before the prune);
- a launch, when the stamp is older than 24 hours. A launch also drops
  the stamped tab, because zellij reuses tab ids across sessions.

Tests: a_session_end_by_quit_stamps_standby_and_the_humans_own_end_does_not,
a_standby_stamp_clears_on_bind_on_its_tabs_prune_and_on_expiry (watched
red at the bind assertion first). Host lib 428 green; just gates green.
A row the last quit left live used to fall into the dormant block and
wait for Alt+Enter, like any other dormant row. Ollie wants those rows
to be easier to relaunch.

- Rows render live, then standby, then dormant. A stale standby row
  cannot open, so it stays dormant.
- The Alt+Up/Down ring now covers the live block and the standby rows.
  A walk, Alt+N or a click that lands on a standby row opens it at
  once. The cursor stays on that row, so a fast walk steps on to the
  next standby row before the opened tab takes focus.
- The glyph is the half-filled circle (U+25D0) in the dormant ink.
- The prune candidates and the witness settle include the stamped tab.
  When SessionEnd reaches the store before the prune, only the stamp
  still names the closed tab.

Tests, each watched red first: standby_rows_sit_between_the_live_block_
and_the_dormant_block, a_walk_opens_each_standby_row_on_arrival_and_
skips_the_dormant_block, a_click_or_alt_n_on_a_standby_row_opens_it,
only_the_executor_opens_a_standby_row (two models),
a_closed_tab_prunes_the_standby_stamp_it_names, and the glyph table.
Bar 308 green; just gates green.
The first cut stamped standby on reason `other` or no reason. A
standby row opens when the human arrives on it, so a false stamp
spawns an agent the human ended. A false dormant row costs one
Alt+Enter. So only `other` stamps now. The QA drive's phase 5b fires
SessionEnd by hand with no reason; that row now stays dormant, as the
drive expects.

Test: a_session_end_by_quit_stamps_standby_and_the_humans_own_end_does_not
now lists no reason among the endings that do not stamp. Watched red
first; host lib 428 green.
After the relaunch, 6c now also checks that every row bound before the
quit (less the eager row) carries a standby stamp. A missing row means
the quit's SessionEnd did not say `other`, or a bar pruned its tab while
the session went down. Both are open measurements; this run settles
them. After the beacon leg, one Alt+Down must bind exactly one more row,
that row must have been on standby, and its bind must spend the stamp.

The verdicts live in qa/lib.sh (standby_uuids, standby_expected_uuids,
arrival_checks). The selftest makes a lost standby row, a quit that
stamped nothing, an empty expectation and four wrong arrivals go red;
script_hygiene.rs requires the readers there.

Docs: standby row in UBIQUITOUS_LANGUAGE; FOOTGUNS "A launch cannot tell
which rows were live"; README glyph, key and restart bullet with a
generated status-standby icon; the QA-DRIVE 6c row.
`just mutants main` left two standby mutants alive: `<` to `<=` in
Standby::live_at, and `*` to `+` in STANDBY_SECS. Nothing tested the
exact 24-hour edge, and every test read the constant by name. The new
tests pin both; the live_at flip was checked by hand to fail.
…s matching itself

- `just mutants` and `just mutants-file` pass `--jobs ${MUTANTS_JOBS:-4}`.
  A serial run of 159 mutants took 23 minutes on a 12-core Mac.
- `remote-qa.sh qa-running` answered "running" with no drive alive: the
  ssh shell's own command line held the pgrep pattern. Brackets in the
  pattern stop the self-match. Measured on the devbox: it now answers
  "finished" with no drive process.
QA run 33 (2026-09-23) failed 6c on both machines: of five agents
running at a kill-session, one SessionEnd reached the store. The store
seq went 75 to 80 across the relaunch on the box and the Mac alike,
which leaves room for one hook write and no prune. So the other four
rows were still bound when the launch ran, and it cleared them as
dormant.

A row still bound at the launch was live at the quit. The launch now
stamps it before it clears the bind. /exit and a tab close unbind
before the quit, so they stay dormant. A stamp SessionEnd already
wrote keeps its own time.

Test: a_launch_puts_every_row_still_bound_on_standby, watched red.
Host lib 430 green; just gates green.
JetBrains Mono Nerd Font has no U+25D0, so the terminal drew the
standby glyph from a fallback font, visibly larger than the dormant
and live circles (Ollie's screenshot, 2026-09-23). nf-fa-adjust
(U+F042) is in the bar's font at 600 units against 620 for the other
two circles, and renders the same size. The README icon is
regenerated from the same table.
Run 33 measured one hook write of five running agents (store seq 75 to
80 on both machines). FOOTGUNS records it beside the standby entry, and
the QA ledger records runs 33 and 34.
The filled and hollow circles came from the base font, and the half
circle from the Nerd Font icon set. They matched in size only in
JetBrains Mono NF Mono (620 vs 600 units). Measured in the other Nerd
Fonts installed here: FiraCode NF Mono 1080 vs 1200, and the non-Mono
variants 620 vs 923 and 1080 vs 1800. The README asks only for a Nerd
Font, so another install would see mismatched circles.

The circles are now nf-fa-circle (U+F111), nf-fa-circle_o (U+F10C) and
nf-fa-adjust (U+F042). All three are identical in every font measured.
The glyph table test and the goldens pin the new code points. The README
icons are regenerated. FOOTGUNS records the rule: glyphs that must match
come from one set.
Ollie, 2026-09-23: an opened standby row went to the top of the live
block. It must go where its frecency puts it. A row with no score ranks
by its ordinal, and the new tab's birth touch minted a fresh top one.
A walk is not a commitment, so it must not do that.

- The bind of a standby row gives the tab the row's own ordinal. This
  overwrites a birth touch that landed first.
- A birth touch that lands after that bind keeps the rank. touch_in now
  returns None when it mints nothing.
- The quit's SessionEnd and the launch sweep carry the tab's ordinal
  into the row, the rule the prune already used (R2). Before this, a
  standby row came back ranked by an older prompt.

A woken dormant row (Alt+Enter) is still a commitment and still goes to
the top. Tests: both touch and bind arrival orders, the dormant contrast,
the SessionEnd carry, the launch carry. The QA drive's arrival leg now
checks that the tab kept the row's own rank, with a selftest case.
…inal

just mutants over the rank change (since ea8cc0b) left two survivors
in touch_in: '&&' to '||' in the keep guard, and the kept path's seq
bump. The new test mints for a tab whose ordinal belongs to a row
elsewhere, keeps for the tab's own agent at its own ordinal, and pins
the bump. Rerun: 11 caught, 1 unviable, 0 missed.
…aunch

The launch stamps every row still bound, because SessionEnd is lossy at
a kill (QA run 33: one of five landed). It stamped `now`, the launch
time, and the expiry pass after it used the same `now`. So a Monday
launch after a Friday quit gave those rows a fresh day of standby, and a
walk onto each spawned an agent. Found by the swarm review (correctness
lane, confirmed by the verifier).

The stamp now takes the store file's last write, read under the lock
before the launch writes: the last moment the old session is known to
have lived. It is a lower bound, so it errs toward dormant. Tests: the
existing launch test now pins the stamp to a back-dated mtime; a new test
back-dates the store past a day and sees the row come back dormant. Both
were red before the fix. The Standby docs name both writers.
The host decided `standby` once, when it wrote the snapshot, and pushes
only on change. An idle fleet writes nothing overnight, so a row past
its 24 hours still read standby in the morning, and the first walk onto
it spawned an agent. Found by the swarm review (correctness lane,
confirmed by the verifier). The comments said the bar had no clock; it
has one (`tick`, before every render and event).

The wire now carries `standby_since`, the time the row went down, in
place of the flag. One rule, `clave_types::standby_live`, serves the
host's launch pass and the bar's `is_standby`. Tests: the bar's new
clock test was red (the field did not exist); the standby row turns
dormant at exactly 24 hours by `tick`. The clears test now also pins
`standby_tab` on the snapshot, which nothing guarded (tests lane).
Swarm review, tests lane, confirmed by the verifier:
- The hook's "no tab bound" case passed no reason, so the reason check
  alone kept it green. It now passes `other`, the quit's reason. Removing
  the tab check turns it red (checked by hand).
- The standby fixture's uuid order matched its rank order, so deleting
  the standby block's sort kept every test green. The top-ranked row now
  has the later uuid. Deleting the sort turns three tests red (checked).
The devbox width flap is diagnosed and fixed (QA runs 30 to 35). Nine
of eleven swarm-review lanes found what it left behind:
- `width_deaf_reason`, a second copy of `width_effects`' gate ladder,
  with no test tying the two. It also logged `reason=owed` for every
  real ask, in the same render.
- `own_tab_tiled_pane_count`, and the `panes=` and `client=` fields on
  the swap-width and load lines. Nothing reads them.
- scripts/qa/width-probe.sh and width-probe-cli.sh. Nothing calls them.

The swap-width line keeps `tab=` and `active=`, because phase 6c counts
asks per tab. Tests, the wasm build and clippy are green; nothing that
remains referenced the deleted items.
…the new glyphs

Swarm review, staleness lane, confirmed by the verifier:
- "Every other row comes back dormant" was untrue once standby landed.
  The justfile, qa-drive.sh, lib.sh, setup.rs and QA-DRIVE's "What it
  asserts" now name the standby rows and the arrival leg.
- The glossary named SessionEnd as the only stamp writer; the launch
  stamps too (f1a0609).
- QA-DRIVE claimed a `dev scenario` refusal that left with the restore.
- A bar comment and the is_clave_binary doc named host callers that
  left with the restore.
- The manual checklist and LEDGER D10 named the old circle code points.
Swarm review, staleness lane: lib.sh, qa-drive.sh and the FOOTGUNS
entry said the quit's SessionEnd is the only writer. Since f1a0609 the
launch also stamps every row still bound, because SessionEnd is lossy
at a kill (QA run 33). Comment and prose only; the selftest is green.
Swarm review, guardrails lane: qa and drive started a second drive
beside a running one, which force-pushed the checkout under it,
restaged its sandbox and shared its log. Both verbs now refuse while
qa-running would say running. Checked: bash -n, qa-running on the box
(finished), script_hygiene green.

Not fixed here: two worktrees still share the box's one sandbox. A
plain clone is a main checkout, so it maps to clave-test whatever its
directory (sandbox.rs key_for). The fix is a linked worktree per local
worktree on the box, and it needs a launch to verify.
Ollie, 2026-09-23: "you are the one who kills." The old rule sent every
sandbox kill to him, and each 6c relaunch cost a round trip.
Ollie saw opened standby rows hop in about 100 ms: bottom, top, then their
own place. The new tab showed unranked before the store bound its agent;
its bar's birth touch then gave it a fresh top ordinal and the opener's
frecency; the bind then put the row's rank back.

The bar now joins a tab to its row by the pane's launch command,
`clave spawn <uuid>`, which zellij reports from the first frame (measured
in the sandbox's list-panes). An exited pane joins nothing. The bar sends
no birth touch for a standby row's tab, and none before its first
snapshot, when it cannot tell that tab from a newborn.

The host bind reset the rank for an expired stamp too; a row past its day
is dormant, and waking one is a commitment. It now resets only for a live
stamp.

Tests: three bar tests failed first (the hop's exact order was the
failure), the host rank test failed first on the expired case. The drive's
arrival leg now checks that no birth touch reached the opened tab; the
selftest proves that check goes red.
just mutants 7a0af62 left two survivors in spawned_agent's live-bind
guard. This test fails under both (checked by hand).
QA-DRIVE.md's agent protocol was a run history with the steps buried in
it, and still sent every kill to Ollie. It is now the loop that worked in
runs 33 to 38: commit before staging, stage the Mac and the box in one
message, the agent quits each sandbox at 6c and tears it down, one rerun
for the known burst intermittent, fd sampling for changes that open tabs,
then a ledger entry. A new section says how to add a drive check: a store
trace, a verdict in lib.sh, a selftest case that proves it goes red.

TESTING.md, CONTRIBUTING.md and the release runbook now carry the
2026-09-23 kill rule from AGENTS.md.
… launches

Both machines 0-7 green on the hop fix (box 249 checks at 73e883e, Mac 250
at f3c5929). The drive's 6c and teardown text still sent the quit to Ollie;
it now follows the 2026-09-23 kill rule in AGENTS.md.
@olliegilbey
olliegilbey merged commit dd40505 into main Sep 23, 2026
10 checks passed
@olliegilbey
olliegilbey deleted the feat/standby-rows branch September 23, 2026 11:17
@olliegilbey olliegilbey mentioned this pull request Sep 23, 2026
1 of 6 tasks
olliegilbey added a commit that referenced this pull request Sep 23, 2026
Workspace version, lockfile, README checkout line.

The cut carries standby rows (#273): after a quit, the rows whose agent
was live come back as standby for 24 hours, and an arrival opens one in
place with no hop. It also carries the live-set restore removal (#271,
breaking): a relaunch bakes one tab for the most recent row, and every
other row waits dormant. Plus the frames-off width flap fix and the
remote QA drive, both from #271.

The tree under the bump is the one QA run 39 drove green on both
machines (84d9110; git diff --stat 84d9110 dd40505 is empty).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant