f9faebfa9199588d3afc1f5c48f9aee645070ab9
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f9faebfa91 |
fix(hint): stop the hint count going stale across games
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 51s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m20s
The in-game hint badge re-fetched on entry and, for a game where it was the player's turn, showed a too-high count that "reset" (e.g. back to 11) — the wallet hint spent in another game was not reflected. Root cause: the server sends one hints_remaining = per-game allowance + global wallet, and the client cached that combined number per game. The wallet is global, so spending a wallet hint in one game left every other game's cached count stale (a my-turn game holds the stalest value: the opponent-moved delta preserves the old number, whereas a game you just moved in re-cached a fresh one). The backend allowance-then-wallet spend order was already correct. Fix: split the two. StateView/HintResult gain a trailing wallet_balance field (the global wallet alone); the client derives the per-game allowance as hints_remaining - wallet_balance (stable, cacheable) and reads the wallet live from the profile, refreshing it from every state/hint response. The badge is allowance + live wallet, so a wallet hint anywhere updates every game at once. - wire: scrabble.fbs StateView/HintResult + pkg/wire.BuildStateView (the single encoder for both the gateway transcode and the backend's event StateView), gateway encode + resp structs, regen. - backend: game StateView/HintResult + service (GameState/Hint) + eventwire + notify PlayerState/encode + server DTOs. - ui: lib/hints.ts (pure hintsLeft), Game.svelte (badge + syncWallet on load/hint, carry wallet_balance through applyMoveResult), codec/model, mock. - docs: ARCHITECTURE §Hint. Tests: hints.ts unit (incl. the staleness case), TestHintPolicy extended (wallet_balance + allowance-first), gateway state/hint round-trips. |
||
|
|
90eaf4964b |
Stage 13: alphabet on the wire (UI alphabet-agnostic, TODO-4)
Tests · Go / test (push) Successful in 10s
Tests · Integration / integration (push) Successful in 12s
Tests · UI / test (push) Successful in 19s
Tests · Go / test (pull_request) Successful in 9s
Tests · Integration / integration (pull_request) Successful in 12s
Tests · UI / test (pull_request) Successful in 19s
Live play now exchanges per-variant alphabet indices instead of concrete letters (rack out; submit-play, evaluate, exchange, word-check in). The client caches each variant's (index, letter, value) table behind StateRequest.include_alphabet and renders the rack and blank chooser from it, dropping the hardcoded value/alphabet tables. History, the durable journal and GCG stay decoded concrete characters (ARCHITECTURE §9.1, unchanged). - pkg/fbs: new AlphabetEntry + PlayTile; StateView.rack -> [ubyte] + alphabet; StateRequest.include_alphabet; SubmitPlay/Eval tiles -> [PlayTile]; Exchange tiles + CheckWord word -> [ubyte] (committed Go + TS regenerated). - engine: AlphabetTable + a cached per-variant codec (LetterForIndex/EncodeRack/ DecodeTiles/DecodeWord) + BlankIndex sentinel; Go parity test. - backend server edge maps index<->letter (new thin game.Service.GameVariant); game.Service domain methods, engine.Game and the robot keep one letter-based play path. The gateway forwards indices verbatim (no alphabet table). - ui: lib/alphabet.ts in-memory cache; codec encodes/decodes indices; premiums.ts is geometry-only; the mock seeds a fixture table; the UI normalises display to upper case (codec + cache), leaving placement/board/checkword unchanged. Parity moved to the Go engine.AlphabetTable test; premiums.ts loses its value tables. Discharges TODO-4. |
||
|
|
408da3f201 |
Stage 6: gateway edge (Connect/FlatBuffers over h2c, platform/email/guest auth, sessions, rate-limit, admin passthrough, live push bridge)
New public ingress and the first network edge. Framework + a vertical slice of operations end-to-end; remaining ops reuse the same transcode pattern in Stage 7. Contracts (new module scrabble/pkg): - push.proto (backend->gateway gRPC server-stream) + scrabble.fbs (FlatBuffers edge payloads), committed generated Go; buf/flatc Makefiles (dev-time codegen). Backend: - REST handlers on the /api/v1 groups: internal session endpoints (telegram/guest/email login -> mint, resolve, revoke) and the user slice (profile, submit_play, state, lobby enqueue/poll, chat). - internal/notify in-process Publisher hub + internal/pushgrpc gRPC server (BACKEND_GRPC_ADDR) streaming your_turn/opponent_moved/chat/nudge/match_found; emission in game.commit, social, matchmaker. - migration 00005 accounts.is_guest; guests are durable rows excluded from stats; ProvisionGuest; email-as-login (RequestLoginCode/LoginWithCode). Gateway (new module scrabble/gateway): - Connect Gateway service over h2c (Execute + Subscribe), FlatBuffers<->JSON transcode registry, Telegram initData HMAC validator (seam), session cache, token-bucket rate limiter (3 classes), push fan-out hub, backend REST + push gRPC client, admin Basic-Auth reverse proxy. go.work: use ./pkg, ./gateway + replace scrabble/pkg. CI: gateway/**, pkg/** path filters; unit build/vet/test span all three modules. Docs (PLAN, ARCHITECTURE, FUNCTIONAL+ru, TESTING, READMEs) updated; gateway/pkg unit tests + guest/email-login integration tests. |