Repository navigation
Description
id(web): collab reconnect cap is defeated by the onopen reset — accept-then-drop server loops forever, never surfaces "error"
Evidence (verified this run, main 0cbb91b)
pkgs/id/web/src/collab.ts:
scheduleReconnect()caps attempts:if (reconnectAttempts >= 10)→ statuserror, stop (line ~150); attempts increment per schedule (line ~162).socket.onopenunconditionally resets the counter:reconnectAttempts = 0;(line ~442).
So the cap only counts consecutive sockets that never open. If the server accepts the WebSocket handshake and then drops the connection (abnormal close 1006, no Init ever sent — e.g. a server process that accepts the upgrade then panics/restarts, or a proxy that accepts then resets), every cycle resets the counter to 0 and the client reconnects forever at the capped 5s backoff, never reaching the error status and never giving the user a failure state.
Reproduced this run with a targeted vitest probe (fake WebSocket, fake timers, real initCollab; probe removed after verification):
- 15 cycles of open → close(1006) produced 16 sockets, every cycle logged
Reconnecting in ~250-300ms (attempt 1/10), and the status callback sequence never containederror(last statusconnecting).
Why it matters
The 10-attempt cap exists to give up and surface an error state to the user. In the accept-then-drop failure mode — the most common real one for a local/LAN server that is up at TCP level but broken above it — that protection never fires: the tab retries indefinitely, the UI sits at "connecting" forever, and there is no signal that the session is dead.
Suggested fix
Only reset reconnectAttempts once a session is actually established — i.e. when the Init message is handled (the point where the server has proved it can serve this document) — not on bare socket open. A long-lived stable session that later drops should still restart from attempt 1, which the Init-based reset preserves.
Acceptance criteria
- With a server that accepts the handshake then drops abnormally (no Init), the client stops after 10 reconnect attempts and the status callback receives
error; no further sockets are created. - After a successful Init, a later abnormal drop restarts backoff from attempt 1 (stable-session behavior unchanged).
- New regression tests for
initCollabreconnect behavior (there is currently nocollab.test.ts) covering the cap under accept-then-drop cycles. - Existing web unit tests and typecheck pass.
Activity
developing-today commentedon Oct 27, 2026
CODE-464
dezren39 commentedon Oct 27, 2026
🤖 Agent note · filed
new-issue-creator-code-b)initCollab(15 open→1006 cycles → 16 sockets, always "attempt 1/10", nevererror).pkgs/id/web/src/collab.ts(cap ~:150, increment ~:162, reset :442)dezren39 commentedon Oct 27, 2026
🤖 Agent note · picked up
new-issue-creator-code-b)onopento Init handling inpkgs/id/web/src/collab.tsand addcollab.test.tsreconnect regression tests.creator-b/1368-collab-reconnect-capdezren39 commentedon Oct 27, 2026
🤖 Agent note · check-in (fleet monitor)
28 remaining items
dezren39 commentedon Nov 2, 2026
🤖 Agent note · targeted verification demo (no code changed)
repo-worker-code)reproduced= the reviewed fix holds on its own head: checked out PR Fix #1368: cap collab retries until Init #3734 headb1b28d1d47a7a04560e817be924df4e2b9f243bbinto an isolated read-only laptop worktree and ran only the touched-surface test.bun run test -- src/collab.test.tsinpkgs/id/web— 1 file passed, 6 tests passed (vitest 5.0.5). No full suite, no CI-trigger or app-code change, no mutation of PR Fix #1368: cap collab retries until Init #3734.runtime-gap/high-risk(verification_class: "runtime-gap/high-risk"); instruction PR dezren39/hatch-personal#14.dezren39 commentedon Nov 3, 2026
agent-note:v1
state: triaged (coverage pass)
Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as small_fix at priority 50 (priority/normal). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
Found via: coverage pass 2026-11-03
dezren39 commentedon Nov 4, 2026
🤖 Agent note · picked up
repo-worker-code, lanecode-o)dezren39 commentedon Nov 4, 2026
🤖 Agent note · done
repo-worker-code, lanecode-o)Closes #1368.pkgs/id/web/src/collab.test.ts, both sides appending tests). Resolved on the laptop in worktree~/git/fleet/7698-1368keeping both test sets, restored one closing brace lost in the resolution, pushed merge commit 35ba3bc + fix 2ce1e90.bun run test -- src/collab.test.tson the laptop — 23 tests passed. Codex review on head 2ce1e90 completed with no findings. The pre-existing web typecheck failure atsrc/image-upload.test.ts:423was already documented on PR Fix #1368: cap collab retries until Init #3734 as reproduced on clean main.