Skip to content

Let deliberate terminal use claim shared sizing - #890

Open
mariusvniekerk wants to merge 6 commits into
mainfrom
t3code/claim-terminal-size-owner
Open

Let deliberate terminal use claim shared sizing#890
mariusvniekerk wants to merge 6 commits into
mainfrom
t3code/claim-terminal-size-owner

Conversation

@mariusvniekerk

@mariusvniekerk mariusvniekerk commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

A workspace terminal can be open in several browsers—or on desktop and mobile—but tmux still has only one effective geometry. A background view can otherwise leave the session sized for a screen nobody is actively using, causing stale wrapping and row/column behavior in the view the user is actually looking at.

This change makes deliberate use the ownership signal. Typing, pasting, terminal pointer interaction, or deliberately resizing its pane transfers size ownership to that view. Passive browser reflow only updates fallback geometry, so merely opening or laying out another view does not steal control. If the owner goes away, an eligible active view takes over with its last known size.

sequenceDiagram
    autonumber
    participant V as View in use
    participant O as Ownership coordinator
    participant T as Terminal / tmux

    Note over V,O: Passive reflow updates this view's fallback size only<br/>Ownership does not change
    Note over V: Deliberate input or<br/>effective pane resize
    Note over V,O: Before the claim, another browser may own the size
    V->>O: claim_resize(current columns, rows)
    O->>O: Replace owner with this view
    O->>T: Apply winning geometry
    T-->>O: Resize settled
    O-->>V: Claim acknowledged
    opt Terminal input triggered the claim
        V->>T: Forward triggering input
    end
    Note over V,T: Latest deliberate action wins<br/>across desktop and mobile views
Loading

generated by a clanker

mariusvniekerk and others added 4 commits August 12, 2026 12:56
Passive geometry from another browser could leave the terminal sized for a screen the user was not actually using. Treat direct terminal interaction as the ownership signal, retain each attachment's preferred fallback dimensions, and keep local attachments ahead of Fleet viewers.

SSH Fleet viewers use independent proxy PTYs, so the winning size is mirrored across them to prevent stale clients from constraining the remote tmux session.

Validation: affected Go suites and race-focused ownership tests; 3,338 frontend unit tests; Chromium desktop/mobile tmux regression. Vite+ formatting is clean; the repository-wide lint/type check remains blocked by pre-existing errors across unrelated files.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <198982749+openai-codex@users.noreply.github.com>
A deliberate resize claim must settle tmux before the user input that motivated it reaches the PTY. Closing direct and HTTP Fleet streams on settlement failure prevents input from being interpreted against stale geometry, while generation-scoped acknowledgements keep failed and superseding claims retryable.

Raw SSH attaches cannot share that synchronous ownership boundary, so their documented guarantee is intentionally limited to asynchronous coordination among proxy PTYs on one hub rather than implying cross-hub or direct-viewer ownership.

Validation: affected Go suites and race-focused resize ownership tests. The short hook lane passed after an unrelated e2e-server timing test cleared on isolated rerun.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <198982749+openai-codex@users.noreply.github.com>
A terminal opened in another browser could keep resize ownership even while the user actively reshaped the pane they were viewing. Treat effective pointer and keyboard divider changes as short-lived geometry intent, and claim only when xterm actually crosses a row or column boundary.

Passive viewport reflow, zero-motion gestures, and clamped resize attempts remain non-claiming, so background browsers and mobile layouts cannot take ownership accidentally.

Validation: 3,345 frontend unit tests; 101 focused geometry tests; repeated real two-browser Chromium/tmux divider-drag regression; frontend format, lint policy, kit UI, Svelte, Effect, and context checks.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <198982749+openai-codex@users.noreply.github.com>
Owner reselection begins without an owner after the active viewer disconnects. Separating that state from member comparison keeps fallback sizing deterministic and makes the nil-safety invariant visible to static analysis before delivery.

Validation: full Fleet API package tests and nilaway.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <198982749+openai-codex@users.noreply.github.com>
@roborev-ci

roborev-ci Bot commented Aug 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (ea75f4e)

The resize-ownership change has three medium-severity correctness issues affecting PTY sizing and input ordering.

Medium

  • frontend/src/lib/components/terminal/XtermTerminalPane.svelte:670 — Deliberate geometry changes emit claim_resize for every terminal endpoint, but workspace-level handlers in internal/terminal accept only resize. They silently discard the frame, leaving the PTY at its previous dimensions while subsequent identical measurements are suppressed. Restrict claim_resize to runtime-session endpoints, or make both workspace terminal bridges handle it as a resize and add full-stack coverage for the workspace route.

  • internal/workspace/localruntime/manager.go:2274 — A lower-priority attachment returns before checking whether the current PTY resize generation is unsettled. If a local owner sends an asynchronous resize and an HTTP Fleet viewer then types, the Fleet claim returns false, and its input is forwarded without waiting for tmux settlement. Preserve ownership priority, but return the pending generation for active non-owner claims so the caller refreshes tmux before forwarding input.

  • frontend/src/lib/components/terminal/XtermTerminalPane.svelte:1001 — Input forwarding ignores a failed claimTerminalResize(). During direct-session replay, resizeReady remains false until replay_ready, allowing keystrokes or pasted input to be sent without a resize claim or settlement; an incomplete retained terminal sequence can leave this window open indefinitely. Queue or suppress input until resize claiming is ready, then enqueue the claim before releasing buffered input.


Reviewers: 2 done | Synthesis: codex, 12s | Total: 6m35s

Owner reselection begins without an owner after the active viewer disconnects. Separating that state from member comparison keeps fallback sizing deterministic and makes the nil-safety invariant visible to static analysis before delivery.

Validation: full Fleet API package tests and nilaway.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <198982749+openai-codex@users.noreply.github.com>
fix: keep deliberate terminal resizing live

A pane drag could send a settled ownership claim at every terminal cell boundary. That serialized a resize path that already updates live and could make an active drag feel worse. Each affected terminal session now claims on its first cell change, then applies the rest of that gesture as ordinary owner resizes.

Workspace terminal endpoints also accept a claim as a resize. This prevents deliberate use from leaving a base or PTY-owner terminal at stale geometry. A real tmux-backed workspace test proves that failed claim settlement closes the connection before queued input reaches the shell.

A shared gesture remains visible to every affected terminal because ownership is per session, not a singleton. Lower-priority Fleet viewers do not wait on another owner's independent settlement. Reconnect replay also keeps forwarding input immediately; trapping keystrokes during replay is a worse failure mode than the short stale-geometry window.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <198982749+openai-codex@users.noreply.github.com>
@roborev-ci

roborev-ci Bot commented Aug 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (8c30b03)

Terminal resize settlement has two medium-severity gaps that can allow input to reach the PTY before geometry settles.

Medium

  • internal/workspace/localruntime/manager.go:2274 — A lower-priority attachment’s resize claim returns before checking for an unsettled resize from the current higher-priority owner. Fleet HTTP input can therefore reach tmux while a recent local resize remains pending. For every valid active claim, request settlement of any outstanding resize generation; only applying the claimant’s dimensions should remain priority-gated.

  • frontend/src/lib/components/terminal/XtermTerminalPane.svelte:1007 — Input is forwarded even when claimTerminalResize() returns false. Local runtime connections keep resizeReady=false until replay parsing finishes, so typing, paste, wheel, or binary input during that window can reach the PTY without claiming ownership or settling geometry. Delay forwarding until the resize claim can be enqueued, or explicitly resolve/cancel the replay boundary before claiming and forwarding deliberate input.


Reviewers: 2 done | Synthesis: codex, 11s | Total: 9m50s

@mariusvniekerk mariusvniekerk self-assigned this Aug 13, 2026
Resize ownership sends text control frames between binary terminal frames.
The focus observer combined both streams, so it could report lost input even
when tmux received the complete command. Limit this assertion to terminal
traffic so it measures the user-visible behavior.

Review decisions: a lower-priority Fleet viewer does not need to wait for
another owner's pending settlement. Reconnect replay also keeps input
immediate because queued or suppressed keystrokes are worse than a brief
stale-geometry window.

Review source: #890 (comment)

Generated with Codex
Co-authored-by: Codex <198982749+openai-codex@users.noreply.github.com>
@roborev-ci

roborev-ci Bot commented Aug 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (ed32765)

The change is generally sound, but one medium-severity resize-ownership issue remains.

Medium

  • Passive viewers can steal terminal resize ownershipfrontend/src/lib/components/terminal/XtermTerminalPane.svelte:1007

    onData includes automatic terminal-protocol replies, not only user input. When process output triggers a reply such as a cursor-position response, every visible viewer may send claim_resize, allowing a passive or unfocused browser to take ownership nondeterministically.

    Fix: Claim ownership only through trusted user-gesture paths such as pointer, keyboard, paste, and wheel events. Forward automatic onData and onBinary replies without claiming. Add a two-view full-stack test confirming that a terminal-generated query does not transfer ownership.


Reviewers: 2 done | Synthesis: codex, 10s | Total: 10m9s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant