Client reconnect-via-resume on accidental drop (#93 follow-up — the seamless-resume half) #115
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/cellblock#115
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Background
Filed as the deferred half of cellblock#93's fix-shape fork. #93 (PR landing now) ships Option A:
ws.onclose/onerror→ accidental-drop detection → brief "connection lost" overlay →backToYard(). That removes the frozen-client symptom under every drop scenario (the empirically-confirmed operator gap). This tracker is Option B: attempt a seamless reconnect before falling back to that return-to-title.Operator ratified A-now + B-follow-up on #93 ("(A) per Shipwright's lean, if it won't mean too much wasted work; otherwise (B) straight"); the implementer wasted-work assessment came back LOW, so A shipped first.
Scope (the reconnect path — #93 AC3)
On an accidental drop while a resume token exists, attempt to re-establish the session before giving up:
handleDrop(accidental close, in net.ts), if a resume is viable, open a fresh WebSocket to the same/wsand let the existingonopen→{type:'resume', token}path run (that machinery already exists in net.ts).matchStart/state), the client re-syncs and play continues — no return-to-title.error, socket won't open, or the grace window elapses), fall back to Option A'sbackToYard()recovery.Verified substrate facts (from the #93 pre-flight)
server/reconnect.go:26 resumeGrace = 25 * time.Second). The server holds the match slot for 25s awaiting a resume — the reconnect attempt must complete inside that window. No server change needed; the resume path already exists.matchStart(net.ts,LS_TOKEN); the client already sends{type:'resume', token}on a fresh socket'sonopen.Cost / why deferred
Option B requires a real
net.tsrestructure: the socket is currently a singleconst ws, so reconnect means making it reassignable and extracting handler-binding into a re-bindable function, plus attempt-accounting (retry count / window). This is net-new structure beyond A — A's handler registration, thedeliberate/closeddiscrimination, the overlay, andbackToYard()as the terminal fallback all transfer cleanly into B.Acceptance criteria
backToYard().close()still skips reconnect entirely (the existingdeliberateflag).srv.close()followed by a re-accept +matchStartproves the seamless-resume path; asrv.close()with no re-accept proves the fallback.Cross-refs
srv.close()+ re-accept drives both paths)Anchor
2026-06-22 #93 pre-flight fork (Shipwright). Option A shipped; this is the deferred seamless-resume enrichment.