Server: gravity-hold during countdown window — companion to #110 (AC2(b) completion) #111
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 (filed 2026-06-22 as Engineer-companion to cellblock#110)
Shipwright's pre-flight probe on #110 (10ef) source-verified BOTH client + server: the "Lockdown... BREAK!" countdown is CLIENT-LOCAL COSMETIC, not a real phase. Server NEVER emits 'countdown'; the server-side authoritative 20Hz gravity tick starts IMMEDIATELY at matchStart (solo.go:68 ticker; versus ready-ups THEN ticks at once).
#110's client-side fix (Option 1, Shipwright lane) gates client input during the 3s countdown overlay — closes the REPORTED harm (unfair input head-start). But pieces still auto-fall server-driven during overlay (~2-3 rows in 3s). For full correctness, server should hold gravity during the countdown window.
Severity: MED — server-side polish, AC2(b) completion
The reported harm (unfair input head-start) is closed by #110's client fix; this is the engine-room polish that completes AC2(b) ("game-tick does not advance during countdown") on the server-authoritative side.
Fix-direction (Engineer lane)
Option A — wire the existing vestigial 'countdown' phase scaffolding (Phase type already has 'countdown' value; client guards already reference
|| phase==='countdown'):Option B — server-side gravity-hold without phase emission:
Lean: Option A. Wires the scaffolding to its anticipated purpose; gives client REAL phase to gate on rather than local timer; retires the vestigial-scaffolding substrate-debt over time.
Acceptance criteria
Cross-refs
Anchor
2026-06-22 Shipwright pre-flight probe on #110 surfaced server-vs-client-authoritative-decomposition. Filed as Engineer-companion per Shipwright's recommendation; dispatch decision is operator's call or natural-flow after #110 client fix lands.
Pre-flight probe: Option A is mis-framed — recommend Option B
Source-verified on current main (probe-only, no code changed):
The server has NO phase concept. Zero
phase/countdownreferences inserver/*.go. NeitherStateMessage({Type, Tick, Winner, Players, GarbageQueue}) norMatchStartMessagecarries a phase field. Phase is entirely a client-local concept (client/src/state.ts:48).The
'countdown'phase value is vestigial, client-only, and never even assigned.state.ts:48defines it in thePhaseunion andmain.ts:953guards(phase === 'playing' || phase === 'countdown')— but the client never assignsphase = 'countdown'(grep: zero assignments). The countdown is a cosmetic overlay driven by a client-local timer (countdownStart, main.ts:953–1024), shown during the first 3s of the locally-set'playing'phase.⇒ Option A is not "wire existing scaffolding." There's no server-side phase to emit into and no wire field to carry it. A would require: (1) a NEW protocol field (phase on the wire), (2) a NEW server phase state-machine (countdown→playing + timing), AND (3) a CLIENT rewire to consume the server phase instead of its local timer (Shipwright's lane). That's a cross-slice, protocol-changing, multi-chamber change — beyond AC2(b) completion and beyond what "vestigial scaffolding" implies.
⇒ Option B is the substrate-fit minimal fix (server-only, no protocol change):
game.go run()) andrunSolo(solo.go).pausedgate (#12 reconnect) skipsgs.tick()+ input while frozen. A countdown-hold is the same freeze-for-a-window. I'll add a SEPARATE countdown gate (not overloadpaused, to avoid #12 reconnect-state confusion).Sub-choice within B (flagging for the AC): AC2(b) is literally "game-tick does not advance," but the
pausedmechanism freezes BOTH tick AND input. I recommend the countdown-hold freeze both (full server-authority) — gravity-only still lets a crafted client MOVE the piece during countdown (the #110 client input-lock is client-side; the server backstop is the authoritative completion — same untrusted-input principle as #87's solo guard). If you'd rather keep it strictly gravity-only (input is #110's job), I'll scope to that.When Option A WOULD be right (decision-tree, not conclusion): if a server-authoritative phase concept were a deliberate broader investment (server-synced overlay, server-driven phase transitions for future states), A's protocol work is justified as infrastructure — but that's a separate, larger, cross-slice item, not AC2(b) completion. Could be a follow-up (retire the vestigial client
'countdown'value + add a server phase) if that investment is wanted later.Proposing B + the freeze-scope sub-choice before branching, per the dispatch.