From 9e032bdc3eb8c0e54d55f6de8a156a0610700ad1 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Tue, 6 Oct 2026 11:03:09 +0200 Subject: [PATCH] feat(split): Pane B reconnects as soon as the server is back When SSE comes back after a server restart, handleInit's reconnect branch already re-opens the primary pane's socket; it now also calls the split pane tile's reconnectNow(), so Pane B no longer waits out its backoff (up to 10 s between tries) after every deploy. Live: Pane B was back 4.6 s after the server process respawned, i.e. as soon as it listened. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/web/public/app.js | 4 ++++ test/terminal-tile-input.test.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/web/public/app.js b/src/web/public/app.js index 903228ae..f8771a7a 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -4581,6 +4581,10 @@ class CodemanApp { // a give-up) nothing else would re-establish it from here. if (this._wsSessionId !== restoreId) this._connectWs(restoreId); void this._onSessionNeedsRefresh({ id: restoreId }); + // The split pane's second terminal reconnects on its own backoff (up to + // 10 s between tries); the server is back now, so skip the wait. A no-op + // while its socket is open, stopped for good, or destroyed. + this._splitPane?.reconnectNow?.(); } else { this.activeSessionId = null; // `auto`: the app is restoring a session on load, not a human opening diff --git a/test/terminal-tile-input.test.ts b/test/terminal-tile-input.test.ts index 9dcbe55e..200f2dd8 100644 --- a/test/terminal-tile-input.test.ts +++ b/test/terminal-tile-input.test.ts @@ -676,3 +676,16 @@ describe('TerminalTile claims the keyboard for the app-level shortcuts', () => { expect(term.focusListeners).toEqual([]); }); }); + +describe('the server coming back kicks Pane B', () => { + it("handleInit's reconnect branch asks the split pane's tile to reconnect without waiting out its backoff", () => { + // handleInit needs a whole app to run, so the wiring is pinned by source; + // reconnectNow() itself is exercised above. + const appSource = readFileSync(resolve(import.meta.dirname, '../src/web/public/app.js'), 'utf8'); + const start = appSource.indexOf('if (keepTerminal && restoreId === previousActiveId'); + const branch = appSource.slice(start, appSource.indexOf('} else {', start)); + + expect(start).toBeGreaterThan(-1); + expect(branch).toContain('this._splitPane?.reconnectNow?.();'); + }); +});