Probe: browser-restart resume on mobile + grace-window UX during opponent wait #123
Labels
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/cellblock#123
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?
Behavior (operator playtest 2026-06-23 post round-22)
Operator tested mobile-versus scenario:
Likely explanations (NEEDS PROBE)
For mobile-side resume failure:
For desktop-side ~30s freeze:
Severity: LOW-MED — probe-first
The desktop-freeze portion is likely expected-grace-window-behavior (not a bug); the mobile-resume failure is the substantive question.
Acceptance criteria (refine per probe)
Cross-refs
Anchor
2026-06-23 operator playtest cross-device resume cycle.
Probe findings — root cause: resume token in
sessionStorage(cleared on browser-restart)Probe complete (static-substrate-grounded; recommend verify-on-live on the fix PR).
Root cause (CONFIRMED, client-lane): the resume token is stored in
sessionStorage, notlocalStorage—client/src/net.ts:108(read),:157(write),:183/:217/:258(clear). The const is misleadingly namedLS_TOKEN.sessionStoragesurvives a page-reload within the same tab but is cleared when the browser/tab is fully closed. So the operator's exact scenario — close mobile browser entirely → reopen → navigate back — finds no token atws.onopen(net.ts:108) and falls through to a freshsendJoin()(net.ts:113) instead of{type:'resume'}. That's the "resume failed → fresh session" observed.sessionStorageby design.Fix options:
sessionStorage→localStorage(client-lane, my substrate): enables browser-restart resume within the 25s grace. Trade-offs: (i) cross-tab collision — two CELLBLOCK tabs would share one token slot, but this self-heals: the server rejects a stale/foreign token (Resume→ "unknown/expired" / "seat still active", reconnect.go:188/202) and the client clears it + falls back to join (net.ts:214-218); (ii) a stale token from a long-past match is attempted once on next open, then cleared on server-reject. Net: low-risk, directly fixes the operator scenario.Residual regardless of (A): the 25s server grace (reconnect.go:26) means even with
localStorage, resume only works if the browser reopens within ~25s of the drop — a narrow window on mobile. Beyond that, no resume is possible without extending grace (server-side, Engineer-adjacent, with its own cost of holding matches longer). NB: this window is further squeezed by the server-side drop-detection latency documented in #122 — the grace timer only starts once the server detects the drop, which can lag well past 25s.Lean: (A) localStorage — low-cost, self-healing, directly addresses the scenario; pair with honest UX copy when resume fails ("session expired — starting fresh"). Surfacing as a fork rather than unilaterally building because it touches the session-token substrate; the grace-extension question is deferred to Engineer.
#82 note (gated decision): the server already guards double-attach (
Resumerejects a seat that isn'tdisconnected, reconnect.go:200-203), which is relevant to whether the #82 idempotency-token-guard is moot.Cross-ref: #122 findings — the survivor-side "30s freeze" traces to a separate server-side detection-latency finding.