TUI: create and interact with shared terminal tabs - #72
Conversation
Independent initial review: GPT-5.6 LunaPinned and verified unchanged head Risk: high. FOLLOW-UP - stale error remains after successful terminal return
When an attach/resize/output attempt sets Residual risk: no live Darwin PTY/Bubble Tea NEEDS ATTENTION |
Independent initial review: Grok 4.6Risk: high. Confirmed exact head No Residual risks, not blockers: no permanent real-TTY integration test remains; selected-project deletion can move the cursor by index but cannot silently attach; mouse offsets are coupled to the header and pinned by rendered-geometry tests; unread bytes after Ctrl-G can become dashboard keys. APPROVE |
Initial-wave dispositionAccepted the GPT-5.6 Luna |
Independent remediation review: Grok 4.6Confirmed exact head The inherited finding is fixed. A failed finish assigns the sanitized terminal error; a later nil-error finish clears No APPROVE |
Independent remediation review: GPT-5.6 LunaPinned and verified unchanged at The prior finding is fixed at Inspected the complete remediation delta and current Residual risk: no additional live interactive Darwin walkthrough was performed in this follow-up; native Darwin CI is green. APPROVE |
Closes nicodes/ormos-be#505
Risk: high. This adds raw-terminal ownership, local shell input/output and resize forwarding, shared-session attachment, mouse routing, and pairing-token persistence. A cleanup or identity defect could leave the operator TTY raw, consume dashboard input, or attach to the wrong shared shell.
Summary
created,idorderprojectID:sessionIDas the shared PTY keytea.Exec, then explicitly restore mouse cell-motion reportingTerminal Runner
ExecCommand.Run, after Bubble Tea releases its reader and restores cooked modeF_DUPFD_CLOEXEC; poll it every 10 ms so cancellation does not close or strand a reader on process stdinTerminalClient.Detachacross normal return, context cancellation, client completion, I/O failure, resize failure, and panicSIGWINCHdimensions when both are in the relay-valid 1..1000 rangeErrTerminalInputBackpressureevery 10 ms for at most 2 seconds while draining live outputVerification
Pinned toolchain:
go1.25.13; Bubble Tea:v1.3.10.-count=20-count=5mise exec -- go test ./... -count=1mise exec -- go test -race -count=1 ./...mise exec -- go vet ./...mise exec -- govulncheck ./...(0 reachable vulnerabilities; one required-module vulnerability is not called)test -z "$(gofmt -l .)"git diff --check-trimpathbuilds withCGO_ENABLED=0: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64github.com/nicodes/ormos/relayManual PTY Walkthrough
A one-off shared-tag test file, removed before this commit, used a real pseudo-terminal and the provisioned agent control plane without starting a second tunnel. It created one temporary persisted tab, attached through a real Bubble Tea program at 80x24, typed
ORMOS505-TYPE, resized the pseudo-terminal to 100x32 and observedstty sizereport32 100, detached with Ctrl-G, observedORMOS505-DASHBOARD-RESTORED, reattached the sameprojectID:sessionID, observed preserved shell state asORMOS505-REATTACH:shared, detached again, and passed in 1.650s with a 90-second test timeout. The exact temporary record was deleted; a before/after record-ID set comparison reported zero created and zero removed residue.An earlier
script(1)attempt did not deliver navigation keys as discrete Bubble Tea events and entered the system-name editor instead. The unintended value was corrected back to the exact captured pre-test value, verified through/system/info; before/after terminal-record sets were unchanged. No value or credential is included here.Deliberate Gate Failures
Each production mutation was applied alone and reverted before
b864476.session_idtosession;TestTerminalSessionHTTPWireShapefailed withPOST body ... want exact snake-case fieldsconflict exhaustion requests=7 random=7, want 8calls = 8, ambiguous/non-conflict failure was retriedTestTerminalRowsAndSanitizedLabelsfailed withterminal order = "a-tab", "z-tab"; want relay created,id order+ terminal; the row-construction gate failed with the exact flattened row listzz-afterinstead of the projects+ terminalrow:4000terminalsCmd; Init, event, mutation, twentieth tick, and terminal-completion subtests each reportedterminal refresh calls = 0, want 1terminal completion did not re-enable mouse cell motionrecord-ainstead ofproject-a:a-tabfailed create attached=truewhile still proving the error string was sanitizedwrites = ["ab\a"]; Ctrl-G or trailing bytes were forwardedterminal input backpressureterminal input remained busy for 2sF_DUPFD_CLOEXECwithF_DUPFD; it failed withpolling stdin duplicate can leak into a spawned PTY shellprocess stdin was closed: ... broken piperestores=0detach=0panic cleanup restored=false detach=0SIGWINCH; the second-resize condition was not reached within 500 msinitial resizes = nilnarrow view hid the terminal actionerror = <nil>Run = <nil> detach=1Two initially proposed assertions were false greens and were corrected before the evidence above: collision exhaustion had compared against the production constant, and deleted-tab fallback happened to land on
+ terminalby unchanged index. The final tests use a literal threshold of 8 and retain a later tab so each mutation changes the verdict.Remediation\n\nInitial review head:
b864476913ce876e36908d5aaa8a7ff244571b82. GPT-5.6 Luna identified one user-visibleFOLLOW-UP: a successful terminal return left a prior terminal error on the restored dashboard. Commitca4a85cclears that error only onterminalFinishedMsg{err:nil}and adds a failure-then-success regression.\n\nVerification after remediation:\n\n- focused regression:-count=20\n- focused race regression:-count=5\n- full tests and full race tests:-count=1\n-go vet ./..., formatting, andgit diff --check\n- deliberate omission of the success branch failed withsuccessful terminal return left stale error \"attach failed\"\nReview
Two independent reviewers must inspect the same unchanged green initial head. The repository has only the PR author as a collaborator, so GitHub cannot record self-approval; per the repository-owner decision on the preceding stack layers, published independent
APPROVEstatuses are the approval record.