feat(socket): reconnect host and candidates with stable identity (#1) #25

Merged
Reliodas merged 1 commit from feat/1-reconnect-participants into develop 2026-06-22 23:12:50 +00:00
Owner

Implements ticket #1 — stable participant identifiers and reconnection tokens for the host (MJ) and candidates.

Changes

  • Identity decoupled from socket.id: each participant (host included) gets a stable id + secret token (crypto.randomUUID). The socket id is now a live binding reassigned on every (re)connection. Round 2 structures keyed by player.id survive reconnects unchanged.
  • Grace delay (RECONNECT_GRACE_SECONDS = 30s): disconnection flags the participant offline and defers removal. A host drop keeps the room open so it can be resumed.
  • Reconnection: new room:resume { code, token } event; the client persists its identity in localStorage and replays it on every connect. Invalid/expired token → room:error ("Session expirée"), local session cleared.
  • State: adds myId, hostConnected, per-candidate connected; the client resolves "me" via myId instead of socket.id, with offline indicators.
  • Additive socket contract (no MAJOR bump).

Verification (local, all green)

  • Server node:test 82 pass / 1 skip (incl. 3 reconnection integration scenarios)
  • Server Jest grace-delay suite (fake timers) 9 pass
  • Server c8 coverage 81.3 / 78.1 / 87.3 / 81.3 — above floors
  • Client Vitest 39 pass (new useSocket.test.ts); coverage above floors and improved
  • Client production build OK
  • Playwright e2e 2 pass (incl. candidate reload journey)
  • Typechecks clean across shared/server/client

Docs updated: docs/Implémentation.md and docs/Regles.md.

Refs #1

Implements ticket #1 — stable participant identifiers and reconnection tokens for the host (MJ) and candidates. ## Changes - **Identity decoupled from `socket.id`**: each participant (host included) gets a stable `id` + secret `token` (`crypto.randomUUID`). The socket id is now a live binding reassigned on every (re)connection. Round 2 structures keyed by `player.id` survive reconnects unchanged. - **Grace delay** (`RECONNECT_GRACE_SECONDS = 30s`): disconnection flags the participant offline and defers removal. A host drop keeps the room open so it can be resumed. - **Reconnection**: new `room:resume { code, token }` event; the client persists its identity in `localStorage` and replays it on every `connect`. Invalid/expired token → `room:error` ("Session expirée"), local session cleared. - **State**: adds `myId`, `hostConnected`, per-candidate `connected`; the client resolves "me" via `myId` instead of `socket.id`, with offline indicators. - Additive socket contract (no MAJOR bump). ## Verification (local, all green) - Server `node:test` 82 pass / 1 skip (incl. 3 reconnection integration scenarios) - Server Jest grace-delay suite (fake timers) 9 pass - Server c8 coverage 81.3 / 78.1 / 87.3 / 81.3 — above floors - Client Vitest 39 pass (new `useSocket.test.ts`); coverage above floors and improved - Client production build OK - Playwright e2e 2 pass (incl. candidate reload journey) - Typechecks clean across shared/server/client Docs updated: `docs/Implémentation.md` and `docs/Regles.md`. Refs #1
feat(socket): reconnect host and candidates with stable identity
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
72ec6ef5cd
Decouple participant identity from the volatile socket id. Each candidate
and the host now receive a stable id plus a secret reconnection token; the
socket id becomes a live binding reassigned on every (re)connection.

Disconnection is no longer immediate: the participant is flagged disconnected
and removal is deferred by RECONNECT_GRACE_SECONDS. A host drop keeps the room
open so it can be resumed. The new room:resume event replays a stored token to
restore role, score and personal state; the client persists its identity in
localStorage and replays it on every connect. Invalid or expired tokens get a
room:error and clear the local session.

State now exposes myId, hostConnected and per-candidate connected; the client
resolves "me" via myId instead of socket.id, with offline indicators.

Covered by room.store unit tests, a Jest grace-delay suite with fake timers,
Socket.IO reconnection integration scenarios, a useSocket client test and a
Playwright reload journey. Docs updated.

Refs #1

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reliodas merged commit 7b1c060c19 into develop 2026-06-22 23:12:50 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: jeux/le-grand-concours#25
No description provided.