41a642ef974dd6ca6c78c20ce38c3c05ecb9e7fe
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
41a642ef97 |
R4: push enrichment — events carry a state delta, kill the last poll
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 37s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
Enrich the in-app live stream into a delta channel so the UI renders a move from the event without a follow-up game.state, and make the matchmaking poll a stream-down fallback. - pkg/fbs: trailing fields on opponent_moved (move+game+bag_len), your_turn (move_count), match_found (state), game_over (game), notify (account/invitation/state), MoveResult (rack+bag_len); regenerate Go + TS. - backend: notify owns the FB encoding (encode.go + payload.go input structs); game/lobby/social map their domain types in. emitMove builds the move delta; game.Service.InitialState feeds match_found/game_started the recipient's initial StateView; friends/invitations notify carry their account/invitation. The move-commit response (submit_play/pass/exchange/resign) returns the actor's refilled rack + bag size. - gateway: MoveResult transcode carries rack+bag_len. - ui: pure lib/gamedelta.ts reducer advances the per-game cache keyed on move_count (idempotent + gap-safe); app.svelte seeds the cache on match_found/game_started; Game.svelte applies the delta (commit/pass/exchange/resign drop their load()); NewGame polls only while app.streamAlive is false. - docs: ARCHITECTURE §10, FUNCTIONAL(+ru), backend/gateway/ui READMEs; PRERELEASE R4 marked done + Refinements. |
||
|
|
bf7dca0a09 |
Stage 17: fix the robot-nudge frequency + per-game push language
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
Two owner-reported defects from a live contour game. A. Frequency: the robot's proactive nudge fired hourly for 12h+ (a 12h idle threshold then the 1h cooldown, uncapped). Replaced with a lengthening, randomized schedule (proactiveNudgeGap): the first nudge ~60-90 min into the human's turn, each later gap growing toward 1-6h (uniform sample in [60min, ceil], ceil ramping 90min->6h over 12h of idle, measured from the previous nudge), so a long wait gets a handful of increasingly-spaced reminders instead of a stream. B. Language: out-of-app push routed by the recipient's GLOBAL service_language (last-login-wins), so after re-logging via the RU bot an English game's nudges came from the RU bot. Now a game push (your_turn, game_over, nudge, match_found) carries the game's own language (engine.Variant.Language) on push.Event, and the gateway routes by it (falling back to service_language for non-game pushes). The New-Game variant-gating guarantees the game's bot is one the player has started, so delivery is never blocked. Tests: proactiveNudgeGap unit + retimed TestRobotProactiveNudge; TestVariantLanguage; emit your_turn/game_over language; TestNudgeRoutedByGameLanguage integration. Docs: ARCHITECTURE (§7 nudge, §10/§13 routing), FUNCTIONAL (+ _ru), PLAN tracker. |
||
|
|
10412fee8e |
Stage 17 round 5 — backend/correctness bug fixes
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Failing after 12s
CI / ui (pull_request) Successful in 30s
CI / gate (pull_request) Failing after 1s
CI / deploy (pull_request) Has been skipped
- Resign on the opponent's turn: engine ResignSeat(seat) resigns a specific seat (not just toMove); game.Resign bypasses the turn check and forfeits the actor's seat. - Quick-match cancel was a UI no-op (only stopped polling): add the full path (REST /lobby/cancel -> gateway lobby.cancel -> client) and clear the matchmaker's pending result on Cancel, so a cancelled search is dequeued (no 'already queued', no later robot-substituted game). NewGame dequeues on cancel and on abandon. - Lobby win/loss: result.ts ranked by score, so a 0-0 resignation read as a win. The winner now takes rank 1 and the viewer is placed from rank 2 — matching the game-detail screen. - Friend request to a robot: robots no longer block requests; the request stays pending and expires (friendRequestTTL), mirroring a human who ignores it. - Nudge cooldown: ErrNudgeTooSoon now maps to a distinct nudge_too_soon code with a correct message; the chat nudge button disables during the hourly cooldown; the nudge note reads 'Waiting for your move!' (button keeps the Nudge action label). Tests: engine/service off-turn resign, matchmaker cancel-clears-result, friend-to-robot inttest, result.ts 0-0 resignation, nudge_too_soon mapping. |
||
|
|
635f2fd9fc |
Stage 17: backend defect fixes (nudge code, TG name, robot names/timing, multi-device push, move-duration metric + admin analytics)
- #3 nudge-on-own-turn: distinct result code nudge_own_turn + i18n (was reused 'not_your_turn') - #2 sanitize connector registration name to the editable format; Player/Игрок-XXXXX fallback - #5 variant-aware robot name pools (composed full/colloquial first + surname forms; ru gets <=20% latin) - #4 move-number-aware robot move timing (early 1-5min -> late 10-90min, skew k=4) - #7 emit move event to the actor too (multi-device sync); opponent_moved stays in-app only - #1 live game_move_duration{variant,phase} histogram + admin console per-user min/avg/max columns and an inline-SVG move-time-by-move-number chart (offline from the journal) - ProvisionRobot bypasses editor name validation (system names like 'Peter J.') |
||
|
|
d733ce3119 |
Stage 8: UI social/account/history surfaces
Wire the deferred Stage 7 surfaces end-to-end (UI -> gateway transcode -> backend REST -> existing domain services): friends (incl. one-time friend codes), per-user blocks, friend-game invitations, profile editing + email binding, the statistics screen, and the in-game history + GCG export. Friends gain two add paths (interview decision, a deliberate plan change): one-time 6-digit codes (friend_codes table, 12h TTL, single-use, rate-limited redeem); and play-gated requests (shared game required) where an explicit decline is permanent, an ignored request lapses after 30 days, and a code bypasses a decline. Migration 00006 widens friendships_status_chk and adds friend_codes. Lobby notification badge is poll + push: a new generic `notify` event drives it live; the client polls on open/focus. Language stays a single Settings control that writes through to the durable account's preferred_language. GCG export is finished-only (game.ErrGameActive) and shares/downloads the .gcg file. Tests: backend unit + inttest (friend gate/decline/code, ListInvitations, GetStats, GCG gate), gateway transcode round-trips + notify constructor, UI vitest (codecs, win-rate, share choice) + Playwright social specs. Docs: PLAN (Stage 8 done + refinements + TODO-5), ARCHITECTURE, FUNCTIONAL(+ru), UI_DESIGN, TESTING, module READMEs. |
||
|
|
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. |
||
|
|
85baabe4ba |
Stage 5: robot opponent (pool, seed-derived strategy, move driver, matchmaker substitution)
- internal/robot: durable kind='robot' account pool (migration 00004); every per-game and per-turn choice derived deterministically from the game seed (restart-stable FNV mix); a background move driver; margin targeting (band 1-30, closest-to-band); right-skewed [2,90]min delays (median ~10m); opponent-anchored sleep with +/-3h drift; daytime nudge reply + proactive 12h nudge; friend/chat blocked via profile toggles. - engine.Candidates (decoded ranked plays); game.Candidates + RobotTurns; social.LastNudgeAt. - matchmaker: 10s wait then robot substitution (reaper) + Poll delivery seam. - config (BACKEND_ROBOT_DRIVE_INTERVAL, BACKEND_LOBBY_ROBOT_WAIT, BACKEND_LOBBY_REAPER_INTERVAL); main wiring + boot-time pool provisioning. - metrics: robot account_stats (authoritative balance) + robot_games_finished_total OTel counter + per-finish log. - docs: PLAN, ARCHITECTURE, FUNCTIONAL(+ru), TESTING, README; account.go comment. - tests: robot strategy units, matchmaker reaper/Poll, engine.Candidates; inttest robot full-game / substitution / proactive-nudge. |
||
|
|
bfa8797f8c |
Stage 4: lobby & social (matchmaking, friends, blocks, chat+nudge, invitations, profile, email, multi-player drop-out)
Engine: multi-player drop-out-and-continue with a per-game tile disposition (remove default / return), resigned seats skipped and excluded from the win, leaver rack never revealed; 2-player behaviour unchanged. New domains (service/store, no HTTP yet): internal/social (friend request/accept graph, per-user blocks, per-game chat with nudge as a message kind, content filter via mvdan.cc/xurls/v2 + leet/separator normaliser + phone heuristic) and internal/lobby (in-memory variant-keyed matchmaking pool, friend-game invitations invite->accept with lazy 7-day expiry). account gains profile editing and the email confirm-code flow (Mailer seam: SMTP or log mailer). Migration 00003_social.sql + regenerated jet. main wires the new services into the server (accessors for the Stage 6 handlers); robot substitution stays in Stage 5, REST/stream/push in Stage 6/8. Docs (PLAN, ARCHITECTURE, FUNCTIONAL+ru, TESTING, README) updated. |