Skip to content

feat(herd): the mosh bar under a plain attach too - #356

Merged
ralyodio merged 1 commit into
mainfrom
herd-bar-attach
Aug 10, 2026
Merged

feat(herd): the mosh bar under a plain attach too#356
ralyodio merged 1 commit into
mainfrom
herd-bar-attach

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

moshcode attach <name> had the same shape of problem the workspace did: the "detach with Ctrl-b d" line printed before handover is gone with the agent's first repaint, and nothing replaces it. An attached session now grows the same one-line prompt along the bottom for as long as you are there.

   I-AM-API
────────────────────────────────────────────────────────────────
mosh ▸ ps · start claude · show <n> · kill <n> · detach · help
[api] 0:sh*                                    "api" 00:05 10-Aug-26

It comes off again when you detach, so a member is a plain member when nobody is looking at it.

The teardown is not tidiness

kill ends a member by killing its pane. A session still holding a bar would outlive the member it was named for and keep answering to its name on the roster — an empty room with a nameplate. So:

  • detaching removes the bar it added;
  • killSession takes the room too when the bar is all that is left of it;
  • sweepBars clears what a crashed client could not, skipping any session with a client attached — that bar is someone else's way out, not litter.

The jump key had to stop naming a pane

It was bound to the workspace's bar by pane id, which is the wrong answer from inside an attached member and gets wronger every time a bar is rebuilt. Now:

bind-key -T root F12   select-pane -t "{bottom-right}"

In the workspace and under an attach alike, the bar is the bottom row — so one binding finds it in both, and no rebuild can stale it.

The bar also asks where it is rather than assuming, so show means the right thing in each place: swap the content pane in the workspace, switch the client under an attach — giving that member a bar before you land in it, so you never arrive somewhere with no way back out.

What only a real client showed

tmux scales panes proportionally on resize. A bar built against an 80x24 window came back three rows tall the moment a client attached at 100x30:

while attached:
   api      h=29 w=100
   mosh-bar h=3  w=100      ← should be 1

Nothing outside the pane can predict when that happens, but the pane gets a resize event for it, so the bar keeps itself one row. The test stretches the window and fails without the fix (bar stretched on resize: api:34).

Verification

End to end, with a real second tmux client running the real moshcode attach:

while attached:                    after detach:
   api      h=27 w=100                api h=29
   mosh-bar h=1  w=100                bar present -> 0
   bar present -> 1                   member alive -> 1
                                      content kept -> 1

1520 tests pass, 0 fail. New coverage: the sweep rule (attached sessions spared, workspace spared, bar-only windows left alone), ensureBar idempotence, removeBar refusing to empty a window, the live on/off round trip asserting the member gets its height back, the orphan guard, and the resize regression.

🤖 Generated with Claude Code

`moshcode attach <name>` had the same shape of problem the workspace did:
once the agent has the keyboard, the "detach with Ctrl-b d" line printed
before handover is gone with its first repaint, and there is nothing on
screen to replace it. So an attached session now grows the same one-line
prompt along the bottom for as long as you are there.

It is taken away again on the way out, so a member is a plain member when
nobody is looking at it. That teardown is not tidiness: `kill` ends a
member by killing its pane, and a session still holding a bar would
outlive the member it was named for and keep answering to its name on the
roster. `killSession` now also takes the room when the bar is all that is
left of it, and `sweepBars` clears what a crashed client could not —
skipping any session with a client attached, because that bar is someone
else's way out.

The jump key had to stop naming a pane. It was bound to the workspace's
bar by id, which is the wrong answer from inside an attached member and
becomes wronger every time a bar is rebuilt. It is now
`select-pane -t {bottom-right}`: in the workspace and under an attach
alike, the bar is the bottom row, so one binding finds it in both.

The bar asks where it is rather than assuming, and `show` means the right
thing in each place — swap the content pane in the workspace, switch the
client under an attach, giving that member a bar before you land in it so
you never arrive somewhere with no way back out.

One thing that only a real client showed: tmux scales panes
proportionally on resize, so a bar built against an 80x24 window came
back THREE rows tall the moment a client attached at 100x30. Nothing
outside the pane can predict when that happens, but the pane gets a
resize event for it, so the bar now keeps itself one row. There is a test
that stretches the window and fails without the fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

93 finding(s)

HIGH/CRITICAL: 2 | MEDIUM: 43 | LOW: 48

Severity Rule Location
HIGH manifest-typosquat apps/pwa/package.json:19
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
MEDIUM tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:61
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:75
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:101
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:265
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:269
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:314
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:499
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:675
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:677
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:736
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:782
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:852
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:955
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1063
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1199
MEDIUM js-unescaped-html-sink apps/pwa/src/routes/moshpit.mjs:1419
MEDIUM js-dynamic-code-execution apps/pwa/test/apikey-mask.test.mjs:129
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:111
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:131
MEDIUM sql-template-interpolation apps/pwa/test/moshpit-terms.test.mjs:192
MEDIUM sql-string-concatenation src/cli-schema.mjs:127
MEDIUM tls-verification-disabled src/dns.mjs:741
MEDIUM sql-template-interpolation src/dns.mjs:2549
MEDIUM sql-template-interpolation src/selfupdate.mjs:166
MEDIUM sql-template-interpolation src/selfupdate.mjs:170
MEDIUM sql-template-interpolation src/selfupdate.mjs:208
MEDIUM sql-template-interpolation src/selfupdate.mjs:209
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:93
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:310
MEDIUM insecure-temp-file test/plugins.test.mjs:152
MEDIUM insecure-temp-file test/pty.test.mjs:28
MEDIUM insecure-temp-file test/pty.test.mjs:31
MEDIUM insecure-temp-file test/pty.test.mjs:40
MEDIUM insecure-temp-file test/pty.test.mjs:42
MEDIUM insecure-temp-file test/pty.test.mjs:47
MEDIUM insecure-temp-file test/pty.test.mjs:48
MEDIUM insecure-temp-file test/pty.test.mjs:49
MEDIUM insecure-temp-file test/tabs.test.mjs:8
MEDIUM insecure-temp-file test/tabs.test.mjs:13
MEDIUM insecure-temp-file test/tabs.test.mjs:14
MEDIUM insecure-temp-file test/tabs.test.mjs:22
MEDIUM insecure-temp-file test/trust.test.mjs:240
LOW secret-generic-credential apps/pwa/test/apikey-bearer-scheme.test.mjs:30
LOW secret-generic-credential apps/pwa/test/apikey-mask.test.mjs:38
LOW secret-generic-credential apps/pwa/test/apikey-reveal.test.mjs:35
LOW secret-generic-credential apps/pwa/test/approvals-context.test.mjs:28
LOW secret-generic-credential apps/pwa/test/approvals-credits.test.mjs:28

…and 43 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 2bcae39 into main Aug 10, 2026
4 checks passed
@ralyodio
ralyodio deleted the herd-bar-attach branch August 10, 2026 00:19
@ralyodio ralyodio mentioned this pull request Aug 10, 2026
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