Skip to content

fix(transport): apply reconnect snapshot before notifying reconnect subscribers - #175

Merged
big-guy merged 1 commit into
ness-dev:mainfrom
blindpirate:fix/reconnect-snapshot-order
Jul 14, 2026
Merged

fix(transport): apply reconnect snapshot before notifying reconnect subscribers#175
big-guy merged 1 commit into
ness-dev:mainfrom
blindpirate:fix/reconnect-snapshot-order

Conversation

@blindpirate

@blindpirate blindpirate commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Disclaimer

I have 0 knowledge about TypeScript. I love Harness app but run into an issue frequently:

I run a harness server in remote server, and use ssh -L to forward the port to localhost. Then I use a backend with localhost:xxxx. If the SSH process is disconnected and reconnected, the remote session in Harness app becomes unresponsive - I have to restart new Claude/shell sessions to continue.

I asked Claude to fix it. After the fix, I built a local version and verified it perfectly fixed the issue. So I think it's valuable and would like it to be included in the next release.

Summary

  • Sequence the WebSocket reconnect handshake so the state snapshot is applied before reconnect subscribers fire their join-on-connect side effects.
  • Introduces finishOpenHandshake() which awaits sendSnapshotRequest()onSnapshot, then re-fetches the (newly minted) clientId and notifies reconnect listeners — instead of firing both as independent fire-and-forget chains.

Why

Previously the snapshot request and transport:getClientId raced. A delayed snapshot could land after terminal:join, overwriting the restored controller roster with the stale "old client disconnected" state. The result: PTY input was silently gated off until the user opened a new tab.

Test plan

  • I did a local build and verified it fixed my painpoint.
  • Added applies the reconnect snapshot before notifying reconnect subscribers test exercising a delayed snapshot across an initial connect and a reconnect, asserting strict snapshot → reconnect ordering on both.
  • npm run typecheck
  • npx electron-vite build
  • npx vitest run src/renderer/transport-websocket.test.ts (3 passed)

@frenchie4111

Copy link
Copy Markdown
Collaborator

@blindpirate thanks for the contribution! I'll review this sometime this week and get it into 2.12

@frenchie4111 frenchie4111 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed b47bc74 — fix is correct and the regression test targets the right ordering.

Three minor non-blocking observations:

  1. transport-websocket.ts:177-181 — if sendSnapshotRequest or request('transport:getClientId') rejects but the socket stays open, connectPromise holds a rejected promise until something closes the socket. Not reachable with the current server (those handlers either succeed or the socket dies), but a one-liner this.ws?.close() on rejection would be defense-in-depth.

  2. transport-websocket.ts:184-192 — the message listener is bound before finishOpenHandshake resolves, so state frames arriving in the gap get applied and then wiped by setSnapshot (no seq check). Theoretical today since the server sends snapres ahead of any catch-up events on that socket; worth a comment so a future server change doesn't reintroduce it.

  3. transport-websocket.ts:234-250 — snapshot and clientId are awaited serially though they're independent. Promise.all followed by sequenced callbacks would preserve ordering and halve handshake latency. Cosmetic.

Test at transport-websocket.test.ts:224-284 is correct but uses fixed 50/100/200ms sleeps — may flake on loaded CI. Promise-deferred sequencing would be sturdier, but not worth blocking on.

Comment left on behalf of @frenchie4111 by Claude via Harness.

…ubscribers

Sequence the snapshot fetch and clientId re-fetch into a single
finishOpenHandshake() so the state mirror is reconciled before
reconnect listeners re-fire join-on-connect side effects. Previously a
delayed snapshot could land after terminal:join and overwrite the
restored controller roster with stale "old client disconnected" state,
silently gating PTY input until a new tab was opened.
@big-guy
big-guy force-pushed the fix/reconnect-snapshot-order branch from b47bc74 to 89dc65b Compare July 14, 2026 19:20
@big-guy
big-guy merged commit b13b757 into ness-dev:main Jul 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants