From c439c67485b377cd8d22f43d2a6539fb5ffdae24 Mon Sep 17 00:00:00 2001 From: polymath-orchestrator Date: Tue, 7 Jul 2026 16:58:07 +0000 Subject: [PATCH 1/3] test: reproduce tmux geometry policy drift (#65) --- .../adapters/runtime/tmux/tmux_test.go | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/backend/internal/adapters/runtime/tmux/tmux_test.go b/backend/internal/adapters/runtime/tmux/tmux_test.go index a6ae8bf68e..02aa87bca3 100644 --- a/backend/internal/adapters/runtime/tmux/tmux_test.go +++ b/backend/internal/adapters/runtime/tmux/tmux_test.go @@ -156,9 +156,10 @@ func TestCreateRejectsInvalidEnvKeys(t *testing.T) { // -- Create tests -- func TestCreateIssuesNewSessionAndStatusOff(t *testing.T) { - // new-session, set-option status, set-option mouse, has-session (exit 0 = alive) + // new-session, set-option status, set-option mouse, window-size latest, + // has-session (exit 0 = alive) r, fr := newTestRuntime(0) - fr.outputs = [][]byte{nil, nil, nil, nil} + fr.outputs = [][]byte{nil, nil, nil, nil, nil} h, err := r.Create(context.Background(), ports.RuntimeConfig{ SessionID: "sess-1", @@ -172,9 +173,10 @@ func TestCreateIssuesNewSessionAndStatusOff(t *testing.T) { if h.ID != "sess-1" { t.Fatalf("handle ID = %q, want sess-1", h.ID) } - // Expect 4 calls: new-session, set-option status, set-option mouse, has-session. - if len(fr.calls) != 4 { - t.Fatalf("calls = %d, want 4", len(fr.calls)) + // Expect 5 calls: new-session, set-option status, set-option mouse, + // window-size latest, has-session. + if len(fr.calls) != 5 { + t.Fatalf("calls = %d, want 5", len(fr.calls)) } // Call 0: new-session @@ -204,10 +206,16 @@ func TestCreateIssuesNewSessionAndStatusOff(t *testing.T) { t.Fatalf("call[2] = %#v, want %#v", got, want) } - // Call 3: has-session (IsAlive, uses exact-match target =sess-1). - if got, want := fr.calls[3].args, hasSessionArgs("sess-1"); !reflect.DeepEqual(got, want) { + // Call 3: window-size latest so the most recent browser/Electron attach, + // not the smallest stale client, drives the shared tmux window geometry. + if got, want := fr.calls[3].args, []string{"set-window-option", "-t", "sess-1", "window-size", "latest"}; !reflect.DeepEqual(got, want) { t.Fatalf("call[3] = %#v, want %#v", got, want) } + + // Call 4: has-session (IsAlive, uses exact-match target =sess-1). + if got, want := fr.calls[4].args, hasSessionArgs("sess-1"); !reflect.DeepEqual(got, want) { + t.Fatalf("call[4] = %#v, want %#v", got, want) + } } func TestCreateLaunchCommandContainsKeepAliveShell(t *testing.T) { From 44dae175cf24b73b8329a4b3f91a1e4c9f46edc7 Mon Sep 17 00:00:00 2001 From: polymath-orchestrator Date: Tue, 7 Jul 2026 17:00:09 +0000 Subject: [PATCH 2/3] fix: pin tmux window geometry policy (#65) --- .../adapters/runtime/tmux/commands.go | 7 +++ .../internal/adapters/runtime/tmux/tmux.go | 9 ++++ .../runtime/tmux/tmux_integration_test.go | 48 +++++++++++++++++++ .../adapters/runtime/tmux/tmux_test.go | 5 +- 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/backend/internal/adapters/runtime/tmux/commands.go b/backend/internal/adapters/runtime/tmux/commands.go index 1a55d3633a..e87e9baadb 100644 --- a/backend/internal/adapters/runtime/tmux/commands.go +++ b/backend/internal/adapters/runtime/tmux/commands.go @@ -30,6 +30,13 @@ func setMouseOnArgs(id string) []string { return []string{"set-option", "-t", id, "mouse", "on"} } +// setWindowSizeLatestArgs makes the newest attached client drive the session's +// shared window geometry. This avoids a stale or smaller client pinning a +// browser/Electron terminal to the wrong grid. +func setWindowSizeLatestArgs(id string) []string { + return []string{"set-window-option", "-t", id, "window-size", "latest"} +} + // killSessionArgs builds args for `tmux kill-session -t =`. The `=` prefix // requests exact-name matching so a session "foo" does not accidentally match // "foobar" (tmux otherwise does unique-prefix matching). diff --git a/backend/internal/adapters/runtime/tmux/tmux.go b/backend/internal/adapters/runtime/tmux/tmux.go index 892add6b90..87574eca41 100644 --- a/backend/internal/adapters/runtime/tmux/tmux.go +++ b/backend/internal/adapters/runtime/tmux/tmux.go @@ -136,6 +136,15 @@ func (r *Runtime) Create(ctx context.Context, cfg ports.RuntimeConfig) (ports.Ru return ports.RuntimeHandle{}, fmt.Errorf("tmux runtime: set mouse %s: %w", id, err) } + // Pin geometry policy for ao-owned sessions instead of inheriting the host + // tmux default. Browser and Electron attaches resize their PTY before tmux + // starts; "latest" ensures the most recent live client, not the smallest + // stale/mirrored client, drives the shared window size. + if _, err := r.run(ctx, setWindowSizeLatestArgs(id)...); err != nil { + _ = r.Destroy(context.Background(), ports.RuntimeHandle{ID: id}) + return ports.RuntimeHandle{}, fmt.Errorf("tmux runtime: set window size %s: %w", id, err) + } + handle := ports.RuntimeHandle{ID: id} alive, err := r.IsAlive(ctx, handle) if err != nil { diff --git a/backend/internal/adapters/runtime/tmux/tmux_integration_test.go b/backend/internal/adapters/runtime/tmux/tmux_integration_test.go index 1f491f8d56..ff500d165a 100644 --- a/backend/internal/adapters/runtime/tmux/tmux_integration_test.go +++ b/backend/internal/adapters/runtime/tmux/tmux_integration_test.go @@ -2,7 +2,10 @@ package tmux import ( "context" + "fmt" + "os" "os/exec" + "path/filepath" "strings" "testing" "time" @@ -123,6 +126,51 @@ func TestRuntimeIntegrationExactSessionParsing(t *testing.T) { } } +func TestRuntimeIntegrationPinsWindowSizeLatest(t *testing.T) { + tmuxPath, err := exec.LookPath("tmux") + if err != nil { + t.Skip("tmux unavailable") + } + + tmp := t.TempDir() + confPath := filepath.Join(tmp, "tmux.conf") + if err := os.WriteFile(confPath, []byte("set-window-option -g window-size smallest\n"), 0o644); err != nil { + t.Fatalf("write tmux config: %v", err) + } + socket := "ao65-" + strings.ReplaceAll(t.Name(), "/", "-") + wrapperPath := filepath.Join(tmp, "tmux-wrapper") + wrapper := fmt.Sprintf("#!/bin/sh\nexec %s -L %s -f %s \"$@\"\n", tmuxPath, socket, confPath) + if err := os.WriteFile(wrapperPath, []byte(wrapper), 0o755); err != nil { + t.Fatalf("write tmux wrapper: %v", err) + } + + ctx := context.Background() + id := strings.ReplaceAll(t.Name(), "/", "_") + r := New(Options{Binary: wrapperPath, Timeout: 5 * time.Second}) + + t.Cleanup(func() { + _ = r.Destroy(context.Background(), ports.RuntimeHandle{ID: id}) + _ = exec.Command(tmuxPath, "-L", socket, "-f", confPath, "kill-server").Run() + }) + + h, err := r.Create(ctx, ports.RuntimeConfig{ + SessionID: domain.SessionID(id), + WorkspacePath: tmp, + Argv: []string{"sh", "-c", "echo ready"}, + }) + if err != nil { + t.Fatalf("Create: %v", err) + } + + out, err := exec.Command(tmuxPath, "-L", socket, "-f", confPath, "show-options", "-Awv", "-t", h.ID, "window-size").CombinedOutput() + if err != nil { + t.Fatalf("show window-size: %v: %s", err, out) + } + if got := strings.TrimSpace(string(out)); got != "latest" { + t.Fatalf("window-size = %q, want latest", got) + } +} + // waitForOutput polls GetOutput until out contains want or the deadline passes. func waitForOutput(t *testing.T, r *Runtime, h ports.RuntimeHandle, want string, deadline time.Duration) string { t.Helper() diff --git a/backend/internal/adapters/runtime/tmux/tmux_test.go b/backend/internal/adapters/runtime/tmux/tmux_test.go index 02aa87bca3..c830a96e5a 100644 --- a/backend/internal/adapters/runtime/tmux/tmux_test.go +++ b/backend/internal/adapters/runtime/tmux/tmux_test.go @@ -81,6 +81,9 @@ func TestCommandBuilders(t *testing.T) { if got, want := setMouseOnArgs("sess-1"), []string{"set-option", "-t", "sess-1", "mouse", "on"}; !reflect.DeepEqual(got, want) { t.Fatalf("setMouseOnArgs = %#v, want %#v", got, want) } + if got, want := setWindowSizeLatestArgs("sess-1"), []string{"set-window-option", "-t", "sess-1", "window-size", "latest"}; !reflect.DeepEqual(got, want) { + t.Fatalf("setWindowSizeLatestArgs = %#v, want %#v", got, want) + } // kill-session and has-session use exact-match prefix =. if got, want := killSessionArgs("sess-1"), []string{"kill-session", "-t", "=sess-1"}; !reflect.DeepEqual(got, want) { t.Fatalf("killSessionArgs = %#v, want %#v", got, want) @@ -208,7 +211,7 @@ func TestCreateIssuesNewSessionAndStatusOff(t *testing.T) { // Call 3: window-size latest so the most recent browser/Electron attach, // not the smallest stale client, drives the shared tmux window geometry. - if got, want := fr.calls[3].args, []string{"set-window-option", "-t", "sess-1", "window-size", "latest"}; !reflect.DeepEqual(got, want) { + if got, want := fr.calls[3].args, setWindowSizeLatestArgs("sess-1"); !reflect.DeepEqual(got, want) { t.Fatalf("call[3] = %#v, want %#v", got, want) } From cb0935187b9be560b053428fe6a071b3f2785085 Mon Sep 17 00:00:00 2001 From: polymath-orchestrator Date: Tue, 7 Jul 2026 17:04:14 +0000 Subject: [PATCH 3/3] test: quote tmux integration wrapper args (#65) --- backend/internal/adapters/runtime/tmux/tmux_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/adapters/runtime/tmux/tmux_integration_test.go b/backend/internal/adapters/runtime/tmux/tmux_integration_test.go index ff500d165a..a3495fdbfa 100644 --- a/backend/internal/adapters/runtime/tmux/tmux_integration_test.go +++ b/backend/internal/adapters/runtime/tmux/tmux_integration_test.go @@ -139,7 +139,7 @@ func TestRuntimeIntegrationPinsWindowSizeLatest(t *testing.T) { } socket := "ao65-" + strings.ReplaceAll(t.Name(), "/", "-") wrapperPath := filepath.Join(tmp, "tmux-wrapper") - wrapper := fmt.Sprintf("#!/bin/sh\nexec %s -L %s -f %s \"$@\"\n", tmuxPath, socket, confPath) + wrapper := fmt.Sprintf("#!/bin/sh\nexec %s -L %s -f %s \"$@\"\n", shellQuote(tmuxPath), shellQuote(socket), shellQuote(confPath)) if err := os.WriteFile(wrapperPath, []byte(wrapper), 0o755); err != nil { t.Fatalf("write tmux wrapper: %v", err) }