@ucsandman/legcli 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +213 -0
  2. package/README.md +95 -65
  3. package/bin/leg.mjs +123 -14
  4. package/docs/DECISIONS.md +18 -0
  5. package/docs/DEMO.md +20 -14
  6. package/docs/DEVIATIONS.md +1 -0
  7. package/docs/ERRORS.md +68 -0
  8. package/docs/ROADMAP-v2.md +50 -5
  9. package/docs/VOCABULARY.md +27 -0
  10. package/docs/board-guide.md +529 -96
  11. package/docs/cli-contracts.md +241 -5
  12. package/docs/concepts.md +167 -19
  13. package/docs/configuration.md +65 -1
  14. package/docs/faq.md +21 -5
  15. package/docs/getting-started.md +15 -11
  16. package/docs/redesign-2026-09-17.md +477 -0
  17. package/docs/screenshots/background-1280.png +0 -0
  18. package/docs/screenshots/board-400px.png +0 -0
  19. package/docs/screenshots/board-details-open.png +0 -0
  20. package/docs/screenshots/board-drawer.png +0 -0
  21. package/docs/screenshots/board-handoff.png +0 -0
  22. package/docs/screenshots/board-running.png +0 -0
  23. package/docs/screenshots/capacity-drawer-1280.png +0 -0
  24. package/docs/screenshots/floor.png +0 -0
  25. package/docs/screenshots/new-card-dialog.png +0 -0
  26. package/docs/screenshots/settings-ladder-1280.png +0 -0
  27. package/docs/screenshots/terminals-1280.png +0 -0
  28. package/fixtures/limits/claude/claude-fable-limit.json +11 -0
  29. package/fixtures/limits/claude/claude-model-limit.json +1 -1
  30. package/fixtures/limits/claude/claude-session-limit.json +1 -1
  31. package/fixtures/limits/claude/claude-weekly-limit.json +1 -1
  32. package/fixtures/live/claude/resume-model-probe.json +20 -0
  33. package/fixtures/live/claude/usage-oauth.json +87 -0
  34. package/fixtures/verified.json +1 -1
  35. package/package.json +3 -2
  36. package/scripts/board-jump-probe.mjs +335 -0
  37. package/scripts/seed-fake-cards.mjs +59 -6
  38. package/scripts/seed-wes-board.mjs +81 -12
  39. package/src/accounts.mjs +6 -1
  40. package/src/attach.mjs +378 -93
  41. package/src/audit.mjs +1 -1
  42. package/src/board/board.css +203 -11
  43. package/src/board/board.js +664 -200
  44. package/src/board/entry.js +343 -0
  45. package/src/board/floor.html +51 -39
  46. package/src/board/floor.js +585 -73
  47. package/src/board/index.html +122 -45
  48. package/src/board/sessions.js +1569 -141
  49. package/src/board/strip.js +163 -0
  50. package/src/buckets.mjs +101 -0
  51. package/src/cards.mjs +9 -1
  52. package/src/chain.mjs +13 -0
  53. package/src/hook.mjs +7 -1
  54. package/src/ledger.mjs +10 -2
  55. package/src/models.mjs +265 -0
  56. package/src/orchestrator.mjs +13 -4
  57. package/src/preferences.mjs +278 -5
  58. package/src/scheduler.mjs +24 -1
  59. package/src/server.mjs +625 -78
  60. package/src/sessions.mjs +17 -1
  61. package/src/taps/claude-usage.mjs +107 -3
  62. package/src/taps/claude.mjs +144 -5
  63. package/src/taps/codex.mjs +23 -3
  64. package/src/usage-poll.mjs +260 -0
  65. package/src/usage.mjs +439 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,218 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0 (2026-09-18)
4
+
5
+ The first day on 0.12.0 with three real terminals found five things. Usage
6
+ polling moves out of the terminals into the board, so the endpoint stops
7
+ answering 429 and the timeline stops repeating it. Every rung of a ladder can
8
+ now name a model from a catalog Leg reads off the installed CLIs, and the new
9
+ card form is rebuilt around that. The board holds still under a reader with a
10
+ row expanded. The floor is a page you can start work from. And a page whose
11
+ files are newer than the process serving them says so.
12
+
13
+ - **One usage poll per login, in the board.** Every claude terminal used to
14
+ ask Claude's usage endpoint once a minute on its own; three terminals plus
15
+ Claude Code's own polling meant a 429 every other minute, and the terminal's
16
+ timeline logged `claude usage unavailable: usage endpoint 429: {...}` each
17
+ time the answer flipped. The board process now polls each login once per
18
+ `LEG_USAGE_POLL_MS` (60s), backs off to ten minutes on any refusal and snaps
19
+ back on the first good answer, and writes the two windows onto every active
20
+ session of that login. A refusal is recorded once on the usage record
21
+ (`error`, `error_since`, owner only) with one status line, `claude usage
22
+ unavailable since 9:03 AM: usage endpoint 429: rate_limit_error`, and one
23
+ `claude usage is back`; it never erases the measured buckets. Terminals
24
+ poll nothing, so a terminal started before this release keeps its old
25
+ minute-by-minute poll until it is restarted.
26
+ - **A model catalog, read from the CLIs you have.** `GET /api/models` lists
27
+ what each provider can run today: claude's aliases (fable, opus, sonnet,
28
+ haiku); codex's `models_cache.json` entries with `visibility: list` plus the
29
+ default from `config.toml` (gpt-6-astra, gpt-5.6-sol, gpt-5.6-terra,
30
+ gpt-5.6-luna, gpt-5.5 on the machine this shipped from); `agy models` and
31
+ `grok models`, each run at most once an hour behind the answer and cached
32
+ under `<LEG_HOME>/models/`. A rung's model is validated by shape for every
33
+ provider and by membership for claude only, so next month's codex model is
34
+ not refused and a saved ladder is never silently reset to the default.
35
+ - **The new card form, rebuilt.** Task first, then the repository as a picker
36
+ of known repos or a typed path, the branch to cut from, and **Who runs it**:
37
+ one row per rung with provider, model, permissions, ask before start and max
38
+ turns, reorderable, prefilled from your ladder, with **Save as my default
39
+ ladder** writing it back to Settings. Advanced holds the workflow, scripted
40
+ first agent, leases, merge method, test command and title. The dialog posts
41
+ its chain as one object per rung, so `claude/fable` then `claude/opus` is
42
+ finally two legs and not one. Two columns at 900px, one below; it scrolls on
43
+ a short window instead of hiding Create card below the fold.
44
+ - **The one-line entry row is never empty.** It derives its rungs from
45
+ `handoff_order` when a preferences file predates ladders, the ladder noun
46
+ opens both the rung and the model selects, and with only metered providers
47
+ and spending off it says so instead of "no agent is configured".
48
+ - **The board holds still.** With a row expanded, a text selection live, the
49
+ pointer on a row or focus in the list, the needs-you re-sort is computed but
50
+ not drawn until you come out (hover and focus release after 30s on their
51
+ own). The expansion is never detached from the document, so its selection,
52
+ scroll offsets and open confirm row survive every 3-second poll; the
53
+ region's top is anchored against rows above it changing height; the
54
+ timeline appends new lines above you without moving what you read, and a
55
+ status line repeated word for word within a minute folds into one with
56
+ `×N`. `scripts/board-jump-probe.mjs` is the regression harness: it printed a
57
+ 205px drift on 0.12.0 and 0px now.
58
+ - **The floor is a page you start work from.** The four login panels move
59
+ behind the same **Capacity and models** disclosure as on the board, under
60
+ the same one-line strip; the **Run in the background** entry sits under it
61
+ and posts exactly what the board's does; Running, Waiting on you, Queued,
62
+ Backlog and Done today are Background-style rows with counts in their
63
+ headings, a queued row says its position and what it waits for, and j/k and
64
+ Enter work as on the board. `More settings` carries the typed task to the
65
+ board's dialog through `/#new-card=`. The strip and the entry row live once,
66
+ in `src/board/strip.js` and `src/board/entry.js`, loaded by both pages.
67
+ - **A page newer than its process says so.** `/api/health` answers with the
68
+ process version; the page files carry theirs. When they differ the board and
69
+ the floor print `This board process runs leg 0.10.0 and the page files are
70
+ 0.13.0. Restart it to match: leg down && leg up`, which is the state a board
71
+ left running across an `npm i -g` lands in, and the state that hid every
72
+ 0.12.0 feature on the first morning.
73
+
74
+ ## 0.12.0 (2026-09-17)
75
+
76
+ The board is rebuilt around the two questions you actually open it to answer:
77
+ which terminal needs you, and which model is about to run out. Usage stops
78
+ being a region and becomes one strip plus a property of every row; a hand-off
79
+ can now change the model instead of the CLI; and background work comes back as
80
+ rows you can read at a glance instead of a ledger you have to dig through.
81
+
82
+ - **The board top: a capacity strip, and the login panels behind one
83
+ disclosure.** Under the verdict, one strip prints each login's *binding*
84
+ bucket rather than whichever window happened to be stored: `claude 63%
85
+ 6:01 PM`, `codex back Sat 10:11 PM`, `agy no figure`, `grok no reading`. The
86
+ four login panels are not rewritten, they move intact behind **Capacity and
87
+ models** with every gauge, notch and `aria-valuetext` they had, and the
88
+ claude panel head grows a model rail whose chips read `fable 63%` and
89
+ `opus 12%`. Every branch of `verdictLines()` is now asserted under
90
+ `VERDICT_CH` by `test/board-verdict.test.mjs`, so a 52px headline can no
91
+ longer wrap to three lines. Measured on the seeded board at 1280: the first
92
+ terminal row sits at 536px, where it used to sit at 1382px.
93
+ - **A terminal row says what it is doing, on which model.** The register reads
94
+ `waiting on you baton on main dirty 3 ahead 2 claude/fable`, and a quiet
95
+ agent carries `quiet 5m`. `ahead` is one `git rev-list --count` on the
96
+ existing poll; the model is the alias the leg resolved to, updated from the
97
+ transcript for claude so a silent fallback off Fable becomes visible, and
98
+ never a guessed default. Under the prompt the row prints its binding bucket,
99
+ `63% of the fable week`, and past the warn threshold the rung that would keep
100
+ the terminal.
101
+ - **A terminal waiting on a human says so, everywhere.** Leg now wires Claude
102
+ Code's `Notification` hook (`permission_prompt`, `idle_prompt`,
103
+ `agent_needs_input`). The row's status word becomes `waiting on you` and it
104
+ carries the question verbatim: `waiting on you: permission to run Bash(git
105
+ push origin HEAD), asked 2m ago`. That sorts the row first, counts it in the
106
+ region head (`4 running, 2 waiting on you, 4 share the claude login`) and in
107
+ the tab, which becomes `(2) Leg` with a dotted favicon. Codex, agy and grok
108
+ publish no such signal, so their rows say `quiet Nm` and never claim to be
109
+ waiting. `permission_prompt` fires after about six seconds and mostly when
110
+ you look away, so this is a reliable notice, not an instant one.
111
+ - **A hand-off can now change the model, not just the CLI.** Destinations are
112
+ rungs of `(agent, account, model)`. The ladder's default is `claude/fable`,
113
+ `claude/opus`, `claude/sonnet`, then each remaining installed agent, and a
114
+ terminal's expansion prints it: `now: claude / fable, then claude / opus,
115
+ then claude / sonnet, then codex, then agy` with `first eligible now:
116
+ claude / opus` under it. A claude downshift with a known session id starts
117
+ `claude --resume <id> --model <alias>` and skips the bundle, so the
118
+ conversation survives the switch; every other rung is primed from the bundle
119
+ as before. `leg ladder` prints and edits the same thing from a terminal
120
+ (`ls`, `set <n> <agent>[/<account>[/<model>]]`, `rm <n>`, `spend on|off`),
121
+ and `leg sessions handoff <id> --to claude/default/opus` names a rung.
122
+ - **The wall is attributed to what it actually walled.** A Fable limit walls
123
+ Fable, not the login: `walls{}` is keyed by model and `limited_until` is left
124
+ alone, so `claude/sonnet` keeps working. A session, weekly or spend limit is
125
+ account scoped, and a same-login model rung is then refused as a wasted
126
+ switch with the reason on the row. Wording Leg cannot parse walls the whole
127
+ login and prints the evidence it failed on rather than guessing.
128
+ - **Nothing spends money unless you said it could.** `may_spend` is off. An
129
+ automatic hand-off skips any rung that bills credits or a metered balance and
130
+ writes the reason to the ledger; the Settings sentence is *A rung that spends
131
+ usage credits or metered balance may be taken by an automatic hand-off*, and
132
+ today it adds *Usage credits are off, so there is nothing to spend through
133
+ the wall.* rather than offering a dead control. A `reserve` per login holds a
134
+ floor back from automatic hand-offs only: *An automatic hand-off skips a rung
135
+ past the floor; a hand-off you press yourself still takes it, and the picker
136
+ says so.* Climbing back is a radio with the rule printed under it: *Leg never
137
+ interrupts a running turn to climb.*
138
+ - **Cards are terminals you are not sitting at.** Live cards are rows in a
139
+ **Background** panel directly under Terminals, in the terminal row's
140
+ register with a measured work stat and the same button grid; a running card
141
+ says `no message until this leg ends, started 8:15 PM`, because `-p
142
+ --output-format json` is mute until the leg exits. Finished cards fall into
143
+ one ledger line, `10 finished cards, 5 done, 5 failed, last 8:15 PM`, so ten
144
+ done cards are one row and not ten. Starting one is a single field,
145
+ `Run in the background:`, over an inferred sentence whose nouns are buttons
146
+ (*in recruiting-tool on main, with claude/fable then claude/opus then
147
+ claude/sonnet then codex then agy, build only*); the old thirteen-field
148
+ dialog is still there as **More settings**.
149
+ - **Two verbs for leaving and coming back.** The End confirm row grows
150
+ **End, and keep going as a card**: it writes the bundle, hands the
151
+ terminal's own worktree to a card with its lineage, and says where the work
152
+ went. On a card, **Take over** pauses it and hands back the one command a
153
+ browser cannot run, `leg claude --resume-card <id>`, in an interactive
154
+ terminal primed from the card's bundle.
155
+ - **A time figure, or nothing, and never a time without its sample count.**
156
+ `burn()` reads a per-bucket history ring and prints a rate only with at least
157
+ three samples spanning ten minutes inside the current window; the estimate is
158
+ the endpoint slope, capped at the reset, and a flat or falling line prints
159
+ nothing at all. Where it can speak, the row and the verdict say *about 2h 40m
160
+ of Fable left, from 9 samples over 4h*. A reading that comes back unchanged
161
+ more than ten minutes after the last sample is still recorded, so a flat hour
162
+ is a measured zero rather than a starved gate.
163
+
164
+ Fixes:
165
+
166
+ - **Fixed: the only live Claude wall Leg ever captured was classified as no
167
+ progress.** The fixture pattern matched `You've hit your Fable limit` and
168
+ Anthropic now writes `You've reached your Fable limit`, so a real wall on the
169
+ headless path scored `{outcome: "no_progress", signal: "none"}` and the
170
+ terminal sat on a dead login. The pattern takes both wordings, and
171
+ `fixtures/limits/claude/claude-fable-limit.json` is a new `observed-live`
172
+ fixture produced from the captured response rather than from the docs.
173
+ - **Fixed: the board printed the wrong percentage for a claude login.** The tap
174
+ kept only `five_hour` and `seven_day` out of the usage response and threw the
175
+ rest away, so a login whose binding bucket was the Fable week was reported at
176
+ the account-wide figure. The whole `limits[]` array is now read into
177
+ `buckets[]`, and the strip, the verdict and the row all print the bucket that
178
+ will actually stop you.
179
+ - **Fixed: a seeded board's rows were live controls on real repositories.**
180
+ `scripts/seed-wes-board.mjs` named real paths, and a click on a seeded row
181
+ cut a worktree in a real checkout. The seed now names `C:\Projects-seed\...`,
182
+ realistic in shape and impossible in fact. `docs/ERRORS.md` carries the
183
+ entry.
184
+
185
+ Migration:
186
+
187
+ - `preferences.json` keeps `handoff_order` and now derives it from the ladder,
188
+ so `validHandoffOrder`, `requireHandoffOrder` and every older terminal keep
189
+ working unchanged. It gains `handoff_ladder` (rungs of `{agent, account,
190
+ model, when, cost}`), `may_spend` (default `false`), `climb_back` (default
191
+ `next-handoff`, or `never`), `reserve` (`{agent: percent}`),
192
+ `notify_terminal` (default `true`) and `notify_board` (default `false`). A
193
+ bare `handoff_order` expands into one `model: null, when: always` rung per
194
+ agent, so behaviour is unchanged until you edit a rung.
195
+ - Usage records under `$LEG_HOME/usage/` gain `buckets`, `walls`, `history`,
196
+ `extra_usage` and `facts`. An older Leg ignores them; a record without them
197
+ falls back to the two windows it has always had.
198
+ - Session records gain `model`, `waiting` and `ahead`, all nullable. On a
199
+ shared board all three are dropped from someone else's row (`waiting` carries
200
+ the verbatim question, `model` and `ahead` describe this machine's usage and
201
+ someone else's work). Your own terminal is never redacted, so you keep all
202
+ three on it whichever role you hold.
203
+ - `playwright` is a dev dependency now, so `scripts/board-shots.mjs` runs from
204
+ a fresh `npm install`. It is not shipped in the package.
205
+
206
+ Still assumed, and marked as such:
207
+
208
+ - Whether `codex resume <id> -m <model>` composes. The `resume` subcommand and
209
+ the `-m` flag are each verified from `codex --help`; putting them together is
210
+ not. A codex rung therefore ships primed from the bundle, and only the claude
211
+ rungs claim to keep the conversation.
212
+ - Whether codex, agy and grok leave an OSC 2 terminal title alone once the
213
+ child starts drawing. That is why naming the terminal tab is not in this
214
+ release: the browser tab badge needs no such assumption, and ships.
215
+
3
216
  ## 0.11.0 (2026-09-17)
4
217
 
5
218
  - **grok is a card adapter, not just a terminal.** `--chain grok` works. Its
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  *Claude hits the five-hour wall. The terminal reads `handing off to codex`, and codex carries on there. Nothing is retyped. ([the full 53-second run](https://legcli.com/#handoff))*
13
13
 
14
- ![The Leg board at 1280px: a headline reading "All 4 terminals are on claude, and claude has 5% left", under it the staleness of the reading; a lit claude panel with its 7 day gauge at 95 percent past the reserve notch and its 5 hour gauge at 38; half panels for codex, at the wall, and agy, which publishes no figure; four terminal rows with their prompts and buttons; and counts for finished terminals, what landed and background tasks](https://legcli.com/img/docs/terminals-1280.png)
14
+ ![The Leg board at 1280px: a headline naming the terminal that has waited on you longest; under it a capacity strip with claude at 63 percent of its fable week, codex back on Saturday, agy with no figure and grok with no reading, and a button that opens the login panels; then four terminal rows, each with its status, repo and branch, uncommitted and unpushed counts, agent and model, the prompt, the one thing worth knowing and four buttons; then a Background panel of live cards and a one-line field for starting another; then counts for finished terminals, what landed, conversations and finished cards](https://legcli.com/img/docs/terminals-1280.png)
15
15
 
16
16
  You keep using your coding agents exactly as you do today, in any terminal,
17
17
  from your own config directory: Leg adds its hooks in a separate per-session
@@ -21,11 +21,16 @@ files and regions). `leg claude --model opus` is
21
21
  `claude --model opus` with four things running alongside it:
22
22
 
23
23
  1. **A board.** Opened once in your browser, reused after that. Every Leg
24
- session in every terminal is a card on it: agent, account, repo and branch,
25
- the task, the files it is touching, its 5h and 7d usage, what has landed on
26
- trunk. Two sessions editing the same file in one repo are flagged on both
27
- cards, and a second session in a checkout that already has one gets its
28
- own worktree and a **Land** button instead of writing over the first.
24
+ session in every terminal is a row on it, and the row's register reads
25
+ status, repo on branch, uncommitted and unpushed counts, agent and model,
26
+ and how long it has been quiet: `waiting on you baton on main dirty 3
27
+ ahead 2 claude/fable`. Under that come the prompt, the one thing worth
28
+ knowing, the files it is touching and the usage bucket that will stop it.
29
+ Capacity is one strip at the top rather than a region, with the login
30
+ panels and their gauges behind **Capacity and models**. Two sessions
31
+ editing the same file in one repo are flagged on both rows, and a second
32
+ session in a checkout that already has one gets its own worktree and a
33
+ **Land** button instead of writing over the first.
29
34
  2. **Usage tracking** per agent and account, from what each CLI already
30
35
  exposes: Claude Code's usage endpoint and its `StopFailure` hook, Codex's
31
36
  read-only app-server rate-limit read, agy's log, and Grok's billing proxy endpoint.
@@ -33,12 +38,18 @@ files and regions). `leg claude --model opus` is
33
38
  refreshed as the session goes, so the work is always ready to hand off.
34
39
  4. **The handoff itself.** Near the limit you get a warning. At the limit Leg
35
40
  saves the bundle, stops the agent, and starts the next option in the same
36
- terminal from that bundle: another login of the same agent if you added
37
- one, otherwise the next agent in the order shown on the terminal card.
38
- The default is claude -> codex -> agy (with grok supported in handoff order),
39
- and Settings changes the default for new terminals. Nothing is retyped.
40
- When every option is out, it tells you which resets first and when, waits
41
- for that reset with a countdown, and starts that agent from the bundle.
41
+ terminal from that bundle. The options are a ladder of rungs, and a rung is
42
+ an agent, a login and a model: the default is `claude/fable`,
43
+ `claude/opus`, `claude/sonnet`, then each remaining installed agent. A
44
+ Fable limit walls Fable and not the login, so the first move is usually to
45
+ another model on the same subscription, and for claude that move is
46
+ `claude --resume <id> --model opus`, which keeps the conversation instead
47
+ of replaying the bundle. Only when every claude rung is out does it move to
48
+ another CLI. Settings holds the ladder for new terminals, `leg ladder`
49
+ edits it from a shell, and **Details** on a row edits the copy that
50
+ terminal is running. Nothing is retyped. When every rung is out, Leg tells
51
+ you which resets first and when, waits for that reset with a countdown, and
52
+ starts that agent from the bundle.
42
53
 
43
54
  Subscription logins only: Leg strips `ANTHROPIC_API_KEY`,
44
55
  `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_CUSTOM_HEADERS`,
@@ -183,21 +194,23 @@ presenting it as current.
183
194
  both are stamped with the commit and the live terminals they describe.
184
195
  `claude "<prompt>"`, `codex "<prompt>"` and `agy -i "<prompt>"` all open the
185
196
  normal interactive session with that first turn.
186
- 5. **Order.** Other accounts of the same agent come first, then every other
187
- agent in the saved order, each tried once. The order is a priority list, not
188
- a rotation: put agy at the bottom and agy is the last option from a Claude
189
- terminal and from a Codex terminal alike. The board shows the exact
190
- sequence with the agent running now skipped, plus the preferred option and
191
- the first option eligible from current install and limit state. Use **Change
192
- order** on a terminal card to change that terminal, or Settings to set the
193
- default copied by new terminals. An option whose CLI is missing or whose
194
- wall has not reset is skipped.
197
+ 5. **The ladder.** A rung is an agent, a login and a model, and Leg walks from
198
+ rung 1 every time. Other models on the same login come first, then other
199
+ accounts of the same agent, then every other agent in the saved order, each
200
+ tried once. It is a priority list, not a rotation: put agy at the bottom and
201
+ agy is the last rung from a Claude terminal and from a Codex terminal alike.
202
+ The board prints the exact sequence with the rung running now marked, plus
203
+ the first rung eligible from current install and limit state. Use **Change
204
+ the ladder** in a terminal's expansion to change that terminal, `leg ladder`
205
+ to do it from a shell, or Settings to set the default copied by new
206
+ terminals. A rung whose CLI is missing, whose wall has not reset, or that
207
+ shares the window that is already out, is skipped with its reason.
195
208
  6. **All out.** The terminal prints each option with its reset time, soonest
196
209
  first, then stays open with a countdown to the first reset and starts that
197
- agent from the bundle when it arrives. The card says `waiting for <agent>
198
- at <time>`. Ctrl-C (or End on the card) quits with exit 3 instead.
210
+ agent from the bundle when it arrives. The row says `waiting for <agent>
211
+ at <time>`. Ctrl-C (or End on the row) quits with exit 3 instead.
199
212
 
200
- You can force a handoff any time: the **Hand off now** button on the card, or
213
+ You can force a handoff any time: the **Hand off now** button on the row, or
201
214
  `leg sessions handoff <id>`. Verified on this machine: `leg claude` opened
202
215
  the real Claude Code TUI with Leg's hooks firing into the session log, the
203
216
  usage poll recorded 36 % of the 5h window and 74 % of the 7d window, the
@@ -216,10 +229,12 @@ row flipped to observed-live (`node scripts/live-limits.mjs`). The same
216
229
  capture is wired for codex `usage_limit_exceeded` and agy `RESOURCE_EXHAUSTED`;
217
230
  no payload for either has been kept yet.
218
231
 
219
- Terminals started by this version can change order while they run. An older
220
- terminal stays on the order it started with; its card says a restart is needed
221
- and can save the desired default for the next launch. A normal agent exit ends
222
- the terminal. It does not trigger a handoff.
232
+ Terminals started by this version can change their ladder while they run:
233
+ Settings holds the default for new terminals, and **Details** on a row edits
234
+ the copy that terminal is running. An older terminal stays on the order it
235
+ started with; its row says a restart is needed and can save the desired
236
+ default for the next launch. A normal agent exit ends the terminal. It does
237
+ not trigger a handoff.
223
238
 
224
239
  ### The working environment travels too (optional)
225
240
 
@@ -350,22 +365,28 @@ only: the trail names repositories and people.
350
365
 
351
366
  `leg <agent>` opens it; `leg open` reopens it; `leg down` stops it.
352
367
 
353
- - **Instrument head**: one row per login, sticky at the top of the board and of
354
- the floor. Each row carries the 5h and 7d rails, the percentage, when that
355
- window resets and how long that is, a burn-rate sentence under the 5h rail,
356
- where and when Leg read the number, and one word for the state: `under 60`,
357
- `over 60`, `over 85`, `stale <n>m`, `at the wall` or `no reading`. A login
358
- at its wall keeps both rails and gains `at the wall`, `back <day time>` and
359
- `in <duration>` beside them.
360
- - **Terminals**: one full-width panel per session, the ones that need an answer
361
- first. Agent and session tail, the status word, the first prompt as a button,
362
- exactly one sentence (the highest-ranked thing true about the terminal), an
363
- `also:` disclosure naming the rest, the files as comma-separated text,
364
- `repo@branch`, `own worktree, from main` when the session cut its own
365
- worktree, and an elapsed clock. Two live sessions in one repo touching the
366
- same file print `codex (codex-99ab) is changing src/server.mjs in another
367
- checkout; whoever lands second rebases` on both panels, and a panel that needs
368
- you rises one step and says `waiting on you` in place of its status word.
368
+ - **Verdict and capacity strip**: the largest sentence on the page, then one
369
+ strip with a token per login carrying the binding bucket, a short track, the
370
+ percentage and one state word or clock: `claude 63% 6:01 PM`, `codex back
371
+ Sat 10:11 PM`, `agy no figure`, `grok no reading`. **Capacity and models**
372
+ opens a drawer holding the full login panels: the 5h and 7d rails, the reset
373
+ and how long that is, where and when Leg read the number, `at the wall` with
374
+ its `back <day time>`, and on the claude panel a rail of model chips
375
+ (`fable 63%`, `opus 12%`, a walled one saying `out until 9:14 PM`). The
376
+ sticky instrument head is still what `/floor` puts at the top.
377
+ - **Terminals**: one full-width row per session, the ones that need an answer
378
+ first. The register reads the status word, `repo on branch`, `dirty <n>` and
379
+ `ahead <n>`, `agent/model`, `quiet <n>m`, and `own worktree, from main` when
380
+ the session cut its own worktree; then the first prompt as a
381
+ button, exactly one sentence (the highest-ranked thing true about the
382
+ terminal), an `also:` disclosure naming the rest, the files as
383
+ comma-separated text, the binding bucket as `63% of the fable week`, and an
384
+ elapsed clock with the session tail. Two live sessions in one repo touching
385
+ the same file print `codex (codex-99ab) is changing src/server.mjs in
386
+ another checkout; whoever lands second rebases` on both rows. A Claude row
387
+ parked at a permission or idle prompt rises to the top, says `waiting on
388
+ you` in place of its status word, prints the question verbatim, and puts a
389
+ count in the browser tab (`(2) Leg`, with a dot on the favicon).
369
390
  After a Land the sentence is `landed on <base>, <7-char sha>, <n> files,
370
391
  +<added>/-<removed>`, or `Land was attempted at <time> onto <base> and
371
392
  bounced: <first line of the reason>. The branch still holds every commit;
@@ -376,21 +397,24 @@ only: the trail names repositories and people.
376
397
  - **Buttons**, in a fixed order that never reflows: Land, Hand off now,
377
398
  Details, End. Once a session has ended, Remove and Remove record take End's
378
399
  place. Details opens an expansion in flow under the panel.
379
- **Hand off now** takes the next option in the order. To name the destination
380
- instead, open Details and use **Hand off now to**, which lists every
381
- destination with the reason a greyed one cannot be picked (`at its usage
382
- limit, back 11:40`, `not installed on this machine`). `leg sessions handoff
383
- <id> --to codex` is the same choice from a terminal. If the destination you
384
- picked walls between the click and the hand-off, the work still continues
385
- down the order and the terminal says which one took it instead.
400
+ **Hand off now** takes the first open rung of the ladder. To name the
401
+ destination instead, open Details and use **Hand off now to**, which lists
402
+ every rung with its model, whether it keeps the conversation, and the reason
403
+ a greyed one cannot be picked (`at its usage limit, back 11:40`, `shares the
404
+ window that is out, buys nothing`, `not installed on this machine`).
405
+ `leg sessions handoff <id> --to claude/default/opus` is the same choice from
406
+ a terminal. If the rung you picked walls between the click and the hand-off,
407
+ the work still continues down the ladder and the terminal says which one
408
+ took it instead.
386
409
  - Below it, **Conversations**: every coding-agent conversation on this machine
387
410
  as a count that opens, the ones Leg started and the ones Claude Code, Codex,
388
411
  Grok, Antigravity and Copilot keep in their own stores. Filter by agent,
389
412
  search, repository, or only what Leg started; a row opens its last messages
390
413
  in place, with the `leg history continue` command to copy where the agent
391
414
  can resume by id. The same drawer lists every checkout Leg can see. Then
392
- optional **Background tasks** an agent runs in a separate worktree without
393
- joining the terminal conversation (see below), then **Settings**.
415
+ **Settings**. Background tasks are not down here: a live card is a row in
416
+ the **Background** panel directly under Terminals (see below), and only the
417
+ finished ones collapse into a ledger line beside these counts.
394
418
 
395
419
  The board reads `~/.leg/sessions/*/session.json` over server-sent events; a
396
420
  session whose runner process is gone is marked `lost`, never shown as live.
@@ -547,17 +571,23 @@ terminal this is when the board is shared), `LEG_TLS_CERT` / `LEG_TLS_KEY`
547
571
 
548
572
  Version 0.1 was the other way round: you dropped a task card on the board and
549
573
  Leg ran the agents headless in a git worktree, one per card, with a fallback
550
- chain, path leases, a scheduler and a merge queue. All of that still works and
551
- lives below the terminals lane, but it is no longer the way in.
552
-
553
- The New background card form starts with a repo, task, and real first agent.
554
- **Run now** queues it; turning that off saves a draft in Backlog. The default
555
- **Build only** workflow stops with its changes in the card's worktree and does
556
- not merge them. The Advanced **Build, test, and merge** and **Factory**
557
- workflows include an automatic land station; their labels say so before you
558
- choose them. Fallback agents, permissions, approval gates, turn caps, leases,
559
- trunk, merge method, tests, title, and scripted test/demo adapters are also
560
- under Advanced options.
574
+ chain, path leases, a scheduler and a merge queue. All of that still runs, and
575
+ a card is now a terminal you are not sitting at: same register, same one
576
+ sentence, same ladder, same bundle. Live cards are rows in the **Background**
577
+ panel directly under Terminals; finished ones fall into one ledger line that
578
+ opens.
579
+
580
+ Starting one is a single field. `Run in the background:` takes the task, and
581
+ the sentence under it is inferred with its nouns as buttons: the repo of the
582
+ terminal you were last in, the saved ladder, and the `build` workflow, which
583
+ stops with its changes in the card's worktree and does not merge them.
584
+ **More settings** is the full form, with the **Build, test, and merge** and
585
+ **Factory** workflows that include an automatic land station, plus fallback
586
+ agents, permissions, approval gates, turn caps, leases, trunk, merge method,
587
+ tests, title, and scripted test and demo adapters. **End, and keep going as a
588
+ card** on a terminal's End row makes a card out of the work in front of you;
589
+ **Take over** on a card gives you the command that turns it back into a
590
+ terminal.
561
591
 
562
592
  - `leg up` boots the board with the scheduler and merge queue and streams
563
593
  redacted logs; `leg card add --repo <path> --task "<t>" --chain claude,codex --queue`
@@ -684,7 +714,7 @@ Leg is commercial software under the [Leg License Agreement](LICENSE).
684
714
  It ships as readable JavaScript so you can see what it does on your machine,
685
715
  and you may modify it for your own use, but not redistribute it or work
686
716
  around the license check. Versions 0.2.0 and 0.3.0 were published under MIT
687
- and remain available. The version in this source tree is 0.8.0; see
717
+ and remain available. The version in this source tree is 0.13.0; see
688
718
  [npm](https://www.npmjs.com/package/legcli) for published versions and
689
719
  [CHANGELOG.md](CHANGELOG.md) for release notes.
690
720