Repository navigation
acpmux ls: compact by default (live named lanes, STUCK, UNKNOWN peers); never starts a daemon - #28046
Conversation
…ve sessions A busy host lists thousands of sessions, mostly closed, which no agent can read whole. `acpmux ls` with no filter now prints the live sessions, newest first, at most 40, and says how many it left out and which flag shows them (--all lists every session). --live, --name REGEX and --host HOST (a peer, or this machine / `local`) narrow both the text and the --json output; --live is pushed to the daemon (excludeStatus). `acpmux ls --json` with no flag is unchanged: the full list that lane-place, pr-owner and the reconcilers read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-8768a81516e2534dc01e
command.rs and run/standard.rs are over the god-file ceiling on base; moving ls out keeps both shrinking instead of growing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-8768a81516e2534dc01e
A closed session with no event log is a purged one the daemon drops at startup, so the fixture's closed rows vanished from the full listing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-8768a81516e2534dc01e
…s and counts Red on the base: ls starts an empty daemon on a home no daemon serves (hq#4897 failure 4: the second assertion of ls_with_no_daemon_starts_none, 'started a daemon', fails there), and the text list has no hidden count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7e86556247e832138230
Leo, 2026-10-30: "acpmux ls should probably not be overly... verbose? i guess" / "to keep everything efficient... tho idk". Part of HQ#5123 (the ergonomics row) and HQ#4897 failure 4 / HQ#5075. On top of #27813's --live/--host and its move to cli/run/ls.rs: the default text list is the live lanes someone named: NAME, HOST (only while some row is a peer's), STATE, TURNS, AGE, STUCK (why a lane needs someone: permission, capacity, harness down, failed turn, stalled Nm) and WARN (usageWarnings). Closed, reviewer, sub- and unnamed sessions are counted on the last line, never cut short (no 40-row cap); --all lists them (live first), a filter hides nothing, --wide adds AGENT, STATUS and LAST. A down peer's rows read unknown and an UNKNOWN line names each down peer. #27813's --name REGEX goes: #28012's positional PATTERN is the one name filter. ls never starts a daemon: with none it fails at once with no_daemon; while a live daemon holds the lock without serving (a handover) it waits for the socket. --json is unchanged. scripts/ls_bench.py times ls against one daemon and session set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7e86556247e832138230
…nect, Windows handover - A filter that matches nothing still prints the UNKNOWN line of each down peer (acpmux ls --host air-blue with air-blue down), and says "no sessions match these filters" without claiming --all undoes them. - Each connect attempt is bounded by the handover budget: a socket that accepts but never answers its hello ends in daemon_not_serving. - Windows: daemon.pid present counts as a held lock, so ls waits out a handover there too instead of saying no_daemon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7e86556247e832138230
A stale daemon.pid made every ls wait out the handover budget, and an orderly stop removes it before the lock is released. A shared try of the lock fails only while a daemon holds it; a daemon starting in that instant reports lockHeld and its starter tries again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7e86556247e832138230
|
Review (codex gpt-6.1-sol high, fast mode; Astra hangs through the router from this host): SHIP at 197bdde Three passes over base d85f7bd:
|
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base db5ea03; does not block |
The fleet rule lint (tests/test_ci_fleet_rule_lints.py) refuses new lines naming the personal workstation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7e86556247e832138230
|
Found 22 test failures on Blacksmith runners: Failures
...and 12 more test failures. View all on Blacksmith
|
…-ls-compact # Conflicts: # cmux-tui/crates/acpmux/src/cli/run/standard.rs
|
Review (codex gpt-6.1-sol high, fast mode): SHIP at c98743a That head's only new commit merges feat-cmux-next. It conflicted in Reds at c98743a, compared by name with the newest completed feat-cmux-next push runs (cmux-next 41018375309 at 54e0d5d; cmux-next guards 41019032868 at 695d1f5):
Base's own reds that this PR doesn't show: Crash safety, CmuxNextApp size cap, same-tree cmux-tui, generated files. |
--all and the filtered views listed live rows before closed ones, which broke newest-first for --limit, --since and --tag (the finding on #27813 at 077f42a). The compact view hides closed rows, so no view reorders now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7e86556247e832138230
|
Review (codex gpt-6.1-sol high, fast mode): SHIP at dab9345 New since the SHIP at c98743a: one commit, so that |
|
Merged feat-cmux-next (806dd9f, which has #28036's 84bca8e: the |
|
Review (the owner lane's independent review, at its land request): SHIP at 32abc17 Recorded by lane-gh-broker from this PR's owner lane, which asked to land this exact head after its independent review. next-autoland lands it once every red is the base's; a BLOCK at this head, a HOLD line or a hold label still stops it. |
|
Reds at 32abc17, by name against the newest completed feat-cmux-next push run (cmux-next 41022865781 at 9fa133b). |
|
Infra reds at 32abc17. Not this PR's, and no rerun requested:
Per failing test, no other red is this PR's (comment above): every |
|
Merge check by the cmux NEXT coordinator: 'PR checks / link' (job 123958335457) failed only because pr_status.py's PATCH of status comment 6516627773 got HTTP 403 'API rate limit exceeded for installation' (5 tries, 08:09-08:11Z), the same installation rate limit as 'warn' (6518486361). Neither is this PR's own red. The other reds match base by name (6518302732); Sol SHIP carried over a clean base merge (6517971479). Merging pinned to 32abc17. |
#28046 moved ls's flags into LsArgs (cli/run/ls.rs) and its renderer into cli/ls_table.rs. As agreed with that PR's owner, this PR's ls pieces move there: PATTERN and --name in LsArgs, the name clause in the retain, '|| named' in the filtered check (so 'ls review-*' shows reviewer rows) and in the daemon-limit guard, dotted fields through cli/select.rs (ls.rs's own parse_fields/project_fields/text_field copies dropped), and an example in ls_table::HELP. one_command, ls_selection, the select/fanout/ls/ls_table unit tests and clippy pass after the merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-54a4b8343b0a7acd8634
…s into the new ls table #28046 moved the ls text renderer into cli/ls_table.rs, with its own one-word WARN cell. standard.rs takes base's side (its old inline table and usage_warn_cell are gone); ls_table's wide WARN cell now names each rule with its number and the command that answers it (usage_warnings::with_command), and reads usageWarnings without indexing. Also brings base past 4154db0 (the CloudAppLinkOwnerAnswerTests compile red). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3b79c5cbd715a3cc84ce
|
Shipped in:
|
…rom #28046) table() read each row's cell and each column's width by index, and render() bumped a hidden count by index. They now use get / get_mut. A row shorter than the header used to panic in table(); now its missing cells are left out. A row longer than the header has its extra cells padded to no width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f
#28046 moved `acpmux ls` into cli/run/ls.rs and cli/ls_table.rs with its own --wide (AGENT, STATUS, LAST). The usage cells become seven columns of that wide table, before LAST, and ls.rs asks the daemon for usage on --wide (no --fields), --json, or --fields usageStats*. The compact view still sends no `usage` and is unchanged. change_kind keeps both sides' kinds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-fa1a02b216a82422dba8
…ts from #28046, #27685, #27809, #26984) - cli/run/ls.rs (72c0200, #28046): the ls request params and the filtered sessions write through JsonSet::set. - hub/cancel.rs (95243d1, #27685): forced_answer's `restarted` too. - hub/remote_rows.rs (e863077, #27809): mark_peer_stale reads status, stale and lastSeen with get. A missing field compares the way the Null that indexing returned did. - hub/restart_resume.rs (84705bd, #26984): BoundedJsonWriter copies at most `remaining` bytes with take, not a slice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f
…28301) #28301 moved every command's --help examples into help_examples/ tables and attaches them to the clap tree; an inline 'Examples:' after_help suppresses a command's table. Conflicts resolved: - run.rs: keep 'mod fanout' and #28301's 'pub(crate) mod launch'. - command.rs Send: base's attributes and session doc, with required_unless_present_any = [status, tag] for send --tag. - This PR's inline examples for 'send' and 'session info' move into help_examples/top.rs and session.rs, so the walk test covers them; ls keeps #28046's ls_table::HELP with this PR's pattern line. help_examples, select, fanout, run::ls, ls_table units; one_command, ls_selection, cli_help_reload, cli_ergonomics; clippy clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-54a4b8343b0a7acd8634
…g 703 > 693): rewrite the new hits without panics (#28134) * acpmux: restart tool previews cut strings without slicing (4 string_slice hits from #26984) The Rust panic ratchet is red on feat-cmux-next: acpmux string_slice 98 > baseline 94. The four new hits are in hub/restart_resume.rs, added by 84705bd (#26984): input_prefix and masked_scan cut at a char boundary with `[..end]`, mask_input_assignments with `[..assignment.end()]`, and safe_tool_text with `[..at]`. The two char-boundary cuts share one `char_prefix` helper that reads with `str::get`; the regex-end and rfind cuts use `get` too. Every index was already a char boundary, so behaviour is unchanged; the unreachable None arms keep the redacting side ("[secret]" alone, or an empty prefix). A new test cuts multibyte text at each spot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f * acpmux ls: the table pads without indexing (3 indexing_slicing hits from #28046) table() read each row's cell and each column's width by index, and render() bumped a hidden count by index. They now use get / get_mut. A row shorter than the header used to panic in table(); now its missing cells are left out. A row longer than the header has its extra cells padded to no width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f * acpmux: JSON reads and writes without indexing (6 indexing_slicing hits from #28046, #27685, #27809, #26984) - cli/run/ls.rs (72c0200, #28046): the ls request params and the filtered sessions write through JsonSet::set. - hub/cancel.rs (95243d1, #27685): forced_answer's `restarted` too. - hub/remote_rows.rs (e863077, #27809): mark_peer_stale reads status, stale and lastSeen with get. A missing field compares the way the Null that indexing returned did. - hub/restart_resume.rs (84705bd, #26984): BoundedJsonWriter copies at most `remaining` bytes with take, not a slice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f * acpmux: JSON and slice reads and writes without indexing (15 indexing_slicing hits that landed after 08fb305) Base gained acpmux indexing_slicing hits while this PR was open (703 > 693): - cli/orchestrate.rs (65b5de8, #28016): harnessFrom via JsonSet::set. - cli/resolve.rs (9d318f9, #28000): choice_commands finds the session argument with enumerate and checks the preceding argument with get. - hub/key_route.rs (9d318f9, #28000): name and status are read with get. - hub/message_events.rs, hub/mod.rs, prompt_provenance.rs, server/outbox.rs, server/peer_forward.rs (7050ae3, #27038): writes via set; outbox reads via pointer, where a missing value reads as Null, as indexing returned. - hub/paging.rs (e0f3350, #27614): the moot slot is read and cleared with get / get_mut. - server/requests/briefs.rs (4a0b8f0, #28172): peersFailed via set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f * panic ratchet: lower acpmux indexing_slicing 693 -> 688, unwrap_used 144 -> 143 (measured at 0c8a50c, run 41050016507) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f * acpmux: Stop reads its answer without indexing (2 indexing_slicing hits from #28154); keep the acpmux baseline at 693 hub/turns.rs (0a088aa, #28154): is_stopped_before_prompt and the stopped answer read with get, not index. A missing reason compares like Null did, and stopReason is always present in stopped_answer(). The acpmux baseline goes back to base's 693 / 144. acpmux hits landed three times while this PR was open, and a baseline pinned to today's exact count would turn base red the next time one lands before this PR merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f * acpmux: drop three JsonSet imports that super::* already brings in (clippy unused_imports) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-d9e7e9f7fb2b87a57d5f --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
acpmux ls never starts a daemon since #28046, so the bake smoke's acpmux-ls check fails on every image whose cmux-tui pin includes it (run 41313632714, pin a11123e: "no acpmux daemon ... listing starts none"). Start one with acpmux daemon start, which returns once it accepts clients, then list and shut it down as before. Agent-Session: https://cmux.com/deeplink/agent?id=aid-d014b1a44b10044c9b53 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Leo, 2026-10-30: "acpmux ls should probably not be overly... verbose? i guess" / "to keep everything efficient... tho idk", and "i feel like there's still a big mess of uncategorized stuff in the sidebar + various not as ergonomic/one-command-y as i'd liike set of commands for interacting with stuff" / "good intuitive stuff" / "and also docs!".
Part of HQ#5123 (the CLI ergonomics row) and HQ#4897 failure 4 / HQ#5075 (a listing that starts a daemon).
On a busy lane host,
acpmux lsprinted every session the daemon knew: about 370 rows there, 222 of them closed and about 200 reviewer boxes. A listing on a home with no daemon also started an empty one and printed "no sessions". Census probes read that as a host with nothing on it (HQ#4897 failure 4).Now:
usageWarnings). STUCK says in a few words why a lane needs someone:permission,capacity,harness down,failed turn,stalled 12m. Closed sessions, reviewer boxes, sub-sessions and sessions with only a generated name (codex-2) are hidden. The last line counts them:254 more hidden (222 closed, 32 reviewers): acpmux ls --all. On that host the list drops to 120 lines, and no row is ever cut off.--all(-a) lists the hidden rows, in the daemon's order (newest first), as every view does.--wide(-w) adds AGENT, STATUS and LAST (the last prompt). A filter (--status,--tag,--since,--limit,--live,--host) hides nothing.unknown(STUCKpeer offline), and one line per down peer saysUNKNOWN <peer>: peer offline for 3m; its sessions are last known, not live. Peer states come from_acpmux/status, read alongside the list. If that read fails, the list saysUNKNOWN peers: their link states could not be readand does not assume no peer is down.lsnever starts a daemon (text and--json). With none running it fails at once withno_daemon. While a live daemon holds the lock but isn't serving yet (a restart handover), it waits for the socket as before (daemon/running.rs, which reads the lock's pid without taking the lock or creating the file).--jsonis unchanged. I grepped leo-workspacetools/,machines/and cmuxscripts/: every script already readsacpmux --json lsorls --json, so no script parses the text table and nothing moves.Supersedes #27813, keeping its three commits and their authorship:
--live,--host, the move oflsintocli/run/ls.rs, and a closed fixture that keeps a log. Two changes to it: no 40-row cap (the list is never cut; the last line counts what's hidden), and no--name REGEX. Name filtering is #28012's positional glob (acpmux ls 'review-27*'), the one way to filter by name, with--nameas its alias there. Whichever of this and #28012 lands second carries the other's hunk intocli/run/ls.rs; a name pattern counts as a filter, so it hides nothing.Column contract agreed with #27899 (WARN: one word, rules named in
--wide) and #28015 (usage columns go in--wideafter STUCK and before WARN; only--wide/--jsonask the daemon for usage).Testing
cargo test -p acpmux --test ls_selection. On the base,ls_with_no_daemon_starts_nonefails at itsstarted a daemonassertion, andthe_default_text_list_is_compact_and_all_brings_the_rest_backfails on the missing hidden count. On the head, all 3 pass, including acpmux ls: --live, --name, --host; the plain view is the live sessions #27813's large-store test with the updated compact assertions (2700 live rows, never cut;300 more hidden (300 closed);--allkeeps newest-first order).cargo test -p acpmux --lib -- ls_table daemon::running cli::run::ls help_contract: 22 pass (the renderer, hide rules, STUCK reasons, UNKNOWN peers, WARN, the handover wait, the read-only lock probe, the help contract).lockHeld) but never fail one. The first two have unit tests; the Windows branch is not compiled here (no Windows target on this host).cargo clippy -p acpmux --all-targets: clean.scripts/cmux-next/prepush-gate.py: no new violations.Observed / Not run
acpmux ls --helpshows the examples.peerState/stalefields and itspeersstatus list.Performance
scripts/ls_bench.py(in this PR): the base (d85f7bd) and headacpmux lsclients against one head daemon in a temporary home with synthetic sessions (a mix of named lanes, reviewer boxes and generated names, as a busy lane host has), 30 runs each, interleaved, on a 32-vCPU Linux CI box (run, at de82911; the later Windows-only commit doesn't touch this path). Defaultlstext output:lsp50, 400 sessionslsp95, 400 sessionslsoutput, 400 sessionslsp50, 3000 sessionslsp95, 3000 sessionslsoutput, 3000 sessionsls --all --wide(every row and column, closest to the old output plus HOST/STUCK): p50 25.5 ms / 193.6 ms, p95 34.4 ms / 253.2 ms at 400 / 3000 sessions, so the extra_acpmux/statusread for peer states, made alongside the list, costs no more than the old renderer.--jsonis unchanged and makes no extra request.Changelog
Changed:
acpmux lslists only live, named lanes with a STUCK reason by default, and counts the rest (--alllists them,--wideadds every column); it never starts a daemon.Proof
CLI only, no UI. Sample output on a synthetic set of 400 sessions:
Agent session: agent session
🤖 Generated with Claude Code