sig select: redirect a seatless multi-seat (CARTE) owner to ?seat=<owned[0]> so tray + overlay + reserve align — TDD
A CARTE owner (all 6 seats, both polarities) entering SIG_SELECT with no ?seat saw the sig overlay + reserve URL locked to the canonical PC seat (one polarity) while the TRAY followed `current_slot` = owned[0] (the lowest owned slot, often the OTHER polarity). A sig reserved from that view filed against the WRONG seat — the seat the owner thought he covered stayed empty — so that polarity never reached 3-ready, its 12s countdown ran to 0 but the server-side `_fire` bailed at `len(ready) < 3` and never advanced; the other polarity proceeded. Switching pos-circles via GATE VIEW (sets ?seat) re-aligned every surface and unstuck it. A 3-agent trace confirmed the mechanism + corrected my first guess: this is NOT a WS problem (the cursor group only drives the cosmetic flashing numeral; the SIG→SKY advance is a threading.Timer broadcasting to the room_<id> group every socket joins). The stall is the misfiled reservation → 3-ready COMPLETENESS failure, rooted in two seat resolvers disagreeing when seatless: the overlay / sig_confirm use `_canonical_user_seat` (PC-first) while the tray / reserve use `_viewer_current_slot` owned[0]. Fix: `room_view` redirects a seatless multi-seat owner (gate_slots.filter(gamer) .count() > 1) in SIG_SELECT to ?seat=<current_slot>, so EVERY surface (tray, overlay, reserve URL, WS cursor group) resolves to one seat via the already-correct ?seat path — the same realignment a GATE-VIEW switch does. SIG_SELECT-only (SKY_SELECT already keys off selected_seat); single-seat gamers / non-owners / anon fail the guard. Unaffected: the multi-gamer sig FTs (one seat each) + the WS-direct CarteCursorGroupTest. TDD: 6 ITs in CarteTrayFollowsSelectedSeatTest (redirect / ?seat-present no-redirect / overlay+tray agree post-redirect / single-seat / non-owner / SKY_SELECT no-redirect); the red `'PC' != 'BC'` was the divergence itself. 381 epic-view ITs green. [[project-sig-select-seat-switch-open-problems]] [[feedback-ws-cursor-group-must-match-acting-seat]] Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -733,6 +733,30 @@ def gatekeeper(request, room_id):
|
||||
def room_view(request, room_id):
|
||||
room = Room.objects.get(id=room_id)
|
||||
_expire_lapsed_seats(room)
|
||||
# CARTE seat alignment: a multi-seat owner arriving at SIG_SELECT with no
|
||||
# ?seat gets the sig overlay + reserve URL locked to the canonical PC seat,
|
||||
# while the TRAY follows owned[0] (often a different polarity). A sig reserved
|
||||
# from that view files against the WRONG seat → the seat the owner thought he
|
||||
# covered stays empty → that polarity never reaches 3-ready → its countdown
|
||||
# runs to 0 but _fire bails at `len(ready) < 3`, so it never advances. Redirect
|
||||
# to ?seat=<owned[0]> so every surface (tray, overlay, reserve URL, WS cursor
|
||||
# group) resolves to ONE seat via the proven ?seat path — the same realignment
|
||||
# a GATE-VIEW switch performs. SIG_SELECT-only: SKY_SELECT already keys its
|
||||
# overlay off selected_seat (~L639); single-seat gamers / non-owners fail the
|
||||
# owner test (gate-slot ownership, matching _viewer_current_slot). Staging bug
|
||||
# 2026-06-08, trace-confirmed (the misfiled-reservation chain).
|
||||
if (
|
||||
request.GET.get("seat") is None
|
||||
and room.table_status == Room.SIG_SELECT
|
||||
and request.user.is_authenticated
|
||||
and room.gate_slots.filter(gamer=request.user).count() > 1
|
||||
):
|
||||
current_slot = _viewer_current_slot(room, request.user, None)
|
||||
if current_slot is not None:
|
||||
return redirect(
|
||||
f"{reverse('epic:room', kwargs={'room_id': room.id})}"
|
||||
f"?seat={current_slot}"
|
||||
)
|
||||
ctx = _role_select_context(room, request.user, request.GET.get("seat"))
|
||||
ctx["room"] = room
|
||||
# `page-room` drives the navbar GATE VIEW swap (mirrors my-sea's
|
||||
|
||||
Reference in New Issue
Block a user