Files
scrabble-game/PLAN.md
T
2026-06-02 12:00:01 +02:00

153 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Scrabble Game — implementation plan
Living plan and **stage tracker**. Each stage is implemented in its own session;
the rules for starting and finishing a stage are in [`CLAUDE.md`](CLAUDE.md).
The architecture/decision record is [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md);
behaviour is [`docs/FUNCTIONAL.md`](docs/FUNCTIONAL.md). When a stage produces a
decision, bake it back here **and** into the affected docs/code in the same PR.
## Context
Greenfield multiplatform Scrabble. Players arrive from a platform (Telegram
first; later VK/MAX/iOS/Android) or standalone web (email / guest). Three
executables — `gateway`, `backend`, `ui` — plus per-platform side-services.
Deliberately simpler than the sibling `../galaxy-game` (idea donor, not a
template). The `../scrabble-solver` engine is embedded in-process as a library.
## Locked decisions (recap — full record in docs/ARCHITECTURE.md)
Stack: `go.work` monorepo, modules `scrabble/<name>`, Go 1.26.x, backend
gin+pgx+Postgres(schema `backend`)+goose+zap+OTel (deps added when first used).
Wire: Connect-RPC + FlatBuffers (client↔gateway), REST/JSON + `X-User-ID`
(gateway↔backend), gRPC server-stream for live events. Auth: platform-native,
thin opaque session token, no Ed25519/signing, likely no Redis. UI: pure
HTML5/CSS, plain Svelte + Vite, Capacitor for native. MVP surfaces: Telegram +
web (email + ephemeral guest) + link/merge. Variants: ru/en/Эрудит.
Legality: validate-at-submit. End: empty bag+rack / 6 scoreless / 24h timeout.
Hint: top-1. Word-check: unlimited + complaint. Robot: P(win)≈0.40, margin
targeting, [2,90]min skewed timing, sleep 00:0007:00 opp-tz, nudge logic.
Dictionary: pin per game. History: structured + GCG export, dictionary-
independent (see ARCHITECTURE §9.1).
## Stage tracker
| # | Stage | Status |
|---|-------|--------|
| 0 | Scaffolding (go.work, backend skeleton, docs, CI) | **done** |
| 1 | Backend foundation (config, server, Postgres+goose, sessions, accounts) | todo |
| 2 | Engine package over scrabble-solver | todo |
| 3 | Game domain (lifecycle, rules, hint, word-check, history+GCG, stats) | todo |
| 4 | Lobby & social (matchmaking, friends, block, chat, profile, nudge) | todo |
| 5 | Robot opponent | todo |
| 6 | Gateway edge (Connect/FB, platform auth, sessions, push bridge, admin) | todo |
| 7 | UI (plain Svelte + Vite, board, lobby, chat, i18n) | todo |
| 8 | Telegram integration (bot side-service, deep-link, push) | todo |
| 9 | Admin & dictionary ops (complaint review, version reload) | todo |
| 10 | Account linking & merge | todo |
| 11 | Polish (observability, perf with evidence, deploy) | todo |
Scaffolding is incremental: `go.work` lists only existing modules; each stage
adds the modules it needs.
## Stages
Each stage: read this plan + relevant docs, **interview the owner on the open
details below**, implement within scope, then update plan/docs/code and get CI
green before marking done.
### Stage 0 — Scaffolding *(done)*
Scope: `go.work` (Go 1.26.3, `use ./backend`); minimal runnable `backend`
(gin, zap, `/healthz`, `/readyz`, env config); docs skeleton; `PLAN.md`;
`CLAUDE.md`; `.gitea/workflows/go-unit.yaml`; README; `.gitignore`.
Acceptance: `go build ./backend/...` + `go vet` + gofmt clean +
`go test ./backend/...` green; CI green on push.
### Stage 1 — Backend foundation
Scope: config/server route groups (`/api/v1/{public,user,internal,admin}`,
probes), Postgres (pgx) + embedded goose migrations + schema `backend`,
telemetry (OTel) wiring, in-memory cache scaffolding, thin sessions + accounts +
platform identities.
Open details: Postgres version + DSN/`search_path` convention; jet vs
sqlc/sqlx (default jet); migration naming; exact session-token shape (opaque
random length, TTL, revocation); account/identity table shape; whether the
admin bootstrap lands here or in Stage 9.
### Stage 2 — Engine package
Scope: `backend/internal/engine` over scrabble-solver — versioned DAWG
load/registry, GenerateMoves/ValidatePlay/ScorePlay wrappers, bag/rack, the
**dictionary-independent** game-state model + decode helpers. Add
`replace scrabble-solver => ../scrabble-solver` to `go.work` here and solve the
CI sibling-checkout (clone `gitea.iliadenisov.ru/.../scrabble-solver`).
Open details: how CI obtains the solver (clone sibling vs publish/tag the
solver module); in-memory game-state representation; how blanks and exchanges
are modelled; Эрудит specifics to verify against the solver.
### Stage 3 — Game domain
Scope: create/join, turn order, submit play/pass/exchange/resign,
validate-at-submit, scoring, end-conditions, 24h timeout/auto-resign, hint,
word-check + complaint capture, structured history + GCG writer, stats on
finish.
Open details: GCG dialect details (blanks, exchanges, notation); exact stats
edge cases; turn-timeout scheduler mechanism (cron vs per-game timer);
complaint payload shape.
### Stage 4 — Lobby & social
Scope: matchmaking pool, friends, block, per-game chat, profile + email
confirm-code, nudge.
Open details: pool fairness/keying confirmation; deep-link format per platform;
chat length limit + retention; friend-request lifecycle; email-code provider
(SMTP relay choice).
### Stage 5 — Robot opponent
Scope: human-like player — balance ~0.40, margin targeting, skewed [2,90]min
timing + sleep + nudge logic, friend/DM blocking, name pool.
Open details: exact delay distribution + parameters; margin band; name pool
source; how the scheduler drives robot moves; metrics for tuning balance.
### Stage 6 — Gateway edge
Scope: Connect/gRPC-Web (h2c), Telegram initData validation → session →
`X-User-ID`, in-memory rate-limit, admin Basic-Auth passthrough, FlatBuffers
transcoding, in-app push stream bridging backend `push` gRPC stream, email +
ephemeral-guest paths.
Open details: FlatBuffers schema layout + message_type catalog; rate-limit
classes/limits; admin surface routing; session cache shape at the gateway.
### Stage 7 — UI
Scope: plain Svelte + Vite static; Connect-web + FlatBuffers client; lobby (my
games, profile tabs); board (HTML5/CSS grid, drag-n-drop, no assets); chat;
hint/word-check; in-app stream; i18n en/ru; in-memory session (+IndexedDB if
available); Capacitor-ready structure.
Open details: detailed game-board UX (deferred by the owner to this stage);
client routing; offline/refresh behaviour; design system / theming.
### Stage 8 — Telegram integration
Scope: bot side-service, deep-link invites, platform push (your-turn / nudge),
Mini App launch/auth; backend↔platform internal API.
Open details: bot framework/library; deep-link scheme; push message templates;
internal API contract; Mini App hosting/origin.
### Stage 9 — Admin & dictionary ops
Scope: admin endpoints (users, games, complaint review queue, dictionary
versions + reload), complaint→dictionary update pipeline.
Open details: whether a server-rendered console is wanted or JSON-only; the
dictionary rebuild/deploy pipeline; complaint resolution workflow.
### Stage 10 — Account linking & merge
Scope: link-via-confirm; merge-into-A (stats sum, transfer games/friends,
dedupe). High blast-radius — focused regression tests.
Open details: conflict resolution (active games on both, duplicate friends,
display-name collisions); irreversibility/audit; confirm-flow per platform.
### Stage 11 — Polish
Scope: observability dashboards, evidence-based performance work, prod
build/deploy.
Open details: deployment target/host; dashboards; load expectations.
## Refinements logged during implementation
- **Stage 0**: solver `replace` deferred to Stage 2 (nothing imports it yet;
adding the path now would break CI, which checks out only this repo). Docker /
compose deferred to a stage that has something to deploy. Trunk is `master`
(owner preference); `feature/*` + PR from Stage 1; the genesis commit lands on
`master` by necessity.