Files
platform/docs/chat-panes-next-session.md
T
pastilhasandClaude Opus 5 907ac46eec write down where the remote pane socket bug stands
A pane on a remote server reads fine and never connects its socket. Captured what has been
ruled out by direct test — the server accepts that exact key over wss with and without a
browser Origin, on the first try — so the next session does not re-derive any of it.

The remaining question is client-side lifecycle with several sockets mounted at once, and the
first move is instrumentation rather than theory: the console says a close arrived during
CONNECTING and does not say who called it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:25:41 +01:00

4.3 KiB

Multi-pane chat: where it stands, and the one open bug

Written 2026-08-10 at the end of a long session, so the next one starts from evidence instead of re-deriving it.

The open bug

A pane pointed at a REMOTE server shows Disconnected and never gets a reply. The pane opens, lists and reads that server's conversations fine (HTTP with the API key works). Sending produces nothing.

Console, repeatedly:

WebSocket connection to 'wss://officer.pastilhas.dev/api/chat/ws?token=ofk_…' failed:
WebSocket is closed before the connection is established.

The same message also appears for the LOCAL pane's ws://localhost:9010/..., yet the local pane works and shows connected. So the message alone is not the bug — something closes the remote one and it never comes back.

Ruled out, each by direct test — do not re-test these

  • The server. wss://officer.pastilhas.dev/api/chat/ws?token=<the same ofk_ key> opens on the first try from outside the browser. Tested twice: with no Origin header, and with Origin: http://localhost:9010 (what the browser sends). Both OPEN.
  • The key. Same key, works over HTTP for /chat/sessions, and opens the socket above.
  • Websocket auth not understanding API keys. upgradeWs in server.tsx uses resolveAuthToken, the same resolver as the HTTP doors, and comments say so explicitly. chat is an execution capability and the owner passes isWsProviderAllowed.
  • Officer being stale on alpha. Pulled and restarted; the failure persists.
  • Send being dropped. Fixed in 243bd04useChatWebSocket.send used to return silently when the socket was not OPEN. It now queues and flushes on open. That fix is real and worth keeping, but it did not resolve this: a socket that never opens never flushes.

Where to look next

src/workspaces/hooks/src/useChatWebSocket.ts, and specifically what happens with SEVERAL instances mounted at once — one per pane.

The handlers look individually correct: close returns early when socketRef.current !== socket, the effect depends on [url] alone, and isCleaningUpRef is reset on mount. What has NOT been established is how those interact across three simultaneous instances plus React's dev StrictMode double-invoke, which creates, closes and recreates every socket on mount.

Concrete things to try, cheapest first:

  1. Instrument before theorising. Log url, readyState and instance identity on every create, open, close and retry. The console message says a close arrived during CONNECTING; it does not say who called it. That is the whole question and it is one log line away.
  2. Check whether the effect re-runs. url is a string, so it should be stable — but chatSocketUrl(serverId, localStorage.getItem('BEARER_TOKEN')) is recomputed every render, and if anything makes serverId flip (say, a pane re-resolving its target) the URL changes and the socket is torn down mid-handshake, forever.
  3. Suspect the transcript resolver in ChatPane. It calls onTargetChange on success, which rewrites the tab state, which produces a new target object for every pane in that tab. If that ever loops, every pane's socket is rebuilt on every pass and none survives the handshake.
  4. Try one pane pointed at the remote server, alone. If it connects, the bug is about multiplicity rather than about the remote server, which would be the single most useful fact to have.

What works, so it is not re-litigated

  • Adding a second server by URL + ofk_ key, verified against /api/auth/me before storing.
  • Per-pane server chips; the list, the directory picker and the transcript all follow the pane's server.
  • Tabs, splitting to three panes, closing, renaming a tab, and the tab name winning the page title.
  • Reading a remote conversation end to end.

Setup facts worth not rediscovering

  • Andre's Mac key lives in agent memory. Alpha needs its own; the one in the console above is alpha's.
  • bun dev on the Mac serves a STALE bundle whenever a new file is added to a workspace package — the symptom is X is not a function for an export that plainly exists. Restart it; do not debug it.
  • The Mac is https://macbook.pastilhas.dev through NPM on alpha (100.64.0.1 → Mac 100.64.0.9:9010), and the cert is valid under strict TLS.