Stage 7 (wip): docs bake + stage renumber (insert UI Stage 8, shift +1)
- PLAN.md: new Stage 8 (UI social/account/history); Telegram->9, Admin->10, Linking->11, Polish->12; tracker + Stage 7 refinements; split the Stage 6 'wired in Stage 7' note between 7 and 8 - ARCHITECTURE: promote ui to current (slice scope, board-replay, codegen, theming, mock) - FUNCTIONAL(+ru): client-app section with the Stage 7/8 split - README + ui/README + CLAUDE.md: UI build/run/test, codegen, pnpm notes - bumped Stage 8-11 refs (+1) across docs and code comments
This commit is contained in:
@@ -40,11 +40,12 @@ independent (see ARCHITECTURE §9.1).
|
||||
| 4 | Lobby & social (matchmaking, friends, block, chat, profile, nudge) | **done** |
|
||||
| 5 | Robot opponent | **done** |
|
||||
| 6 | Gateway edge (Connect/FB, platform auth, sessions, push bridge, admin) | **done** |
|
||||
| 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 |
|
||||
| 7 | UI — playable slice (Svelte+Vite, board, lobby, chat, hint/word-check, i18n) | todo |
|
||||
| 8 | UI — social/account/history (friends, blocks, invitations, profile edit, stats, history/GCG) | todo |
|
||||
| 9 | Telegram integration (bot side-service, deep-link, push) | todo |
|
||||
| 10 | Admin & dictionary ops (complaint review, version reload) | todo |
|
||||
| 11 | Account linking & merge | todo |
|
||||
| 12 | Polish (observability, perf with evidence, deploy) | todo |
|
||||
|
||||
Scaffolding is incremental: `go.work` lists only existing modules; each stage
|
||||
adds the modules it needs.
|
||||
@@ -70,7 +71,7 @@ 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.
|
||||
admin bootstrap lands here or in Stage 10.
|
||||
|
||||
### Stage 2 — Engine package
|
||||
Scope: `backend/internal/engine` over scrabble-solver — versioned DAWG
|
||||
@@ -120,25 +121,90 @@ 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
|
||||
#### Suggested layouts (lobby + game screen)
|
||||
|
||||
User note:
|
||||
> Detailed interview about UI/UX is **strongly** required.
|
||||
> Too much to discuss.
|
||||
|
||||
```text
|
||||
┌────────────────────┐
|
||||
│ Display_Name =│- Profile
|
||||
├────────────────────┤- Settings
|
||||
│ Invitations │- About
|
||||
│ - list │
|
||||
├────────────────────┤
|
||||
│ Active games │
|
||||
│ - list │
|
||||
├────────────────────┤
|
||||
│ Finished games │
|
||||
│ - list │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
├────────────────────┤
|
||||
│ ┌───┐ ┌───┐ ┌───┐│
|
||||
│ New │ Stats Tourn│
|
||||
│ └───┘ └───┘ └───┘│
|
||||
└────────────────────┘
|
||||
┌────────────────────┐
|
||||
Lobby│◄ ==│- History
|
||||
├────────────────────┤- Chat
|
||||
│You Ann Kaya Rick│- Check word
|
||||
│136 700 179 39│- Drop game
|
||||
├────────────────────┤
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ c │
|
||||
│ words │
|
||||
│ o │
|
||||
│ s │
|
||||
│ s │
|
||||
│ │
|
||||
│ │
|
||||
├──┬──┬──┬──┬──┬──┬──┤ ┌──┐
|
||||
│A │Q │Z │* │N │I │W │◄│ │MakeMove/Reset
|
||||
├──┴──┴──┴──┴──┴──┴──┤ └──┘
|
||||
│ ┌───┐ ┌───┐ ┌───┐ │
|
||||
│ Draw│ Skip│ Shfl│ │
|
||||
│ └───┘ └───┘ └───┘ │
|
||||
└────────────────────┘
|
||||
```
|
||||
|
||||
### Stage 8 — UI: social, account & history surfaces
|
||||
Scope: the UI surfaces deferred from Stage 7's playable slice, wiring the matching
|
||||
backend/gateway operations as each screen needs them (the Stage 6 vertical-slice
|
||||
pattern): friends (request/accept/decline/list), per-user blocks, friend-game
|
||||
invitations (create 2–4 player, accept/decline, invitations list), profile **editing**
|
||||
(`account.UpdateProfile` + the email confirm-code binding UI), the statistics screen,
|
||||
and the history viewer with GCG export/download.
|
||||
Open details: friends/invitations UX; stats presentation; history/GCG viewer + download
|
||||
mechanics; any new validation the profile-editing forms need.
|
||||
|
||||
### Stage 9 — 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
|
||||
### Stage 10 — 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
|
||||
### Stage 11 — 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
|
||||
### Stage 12 — Polish
|
||||
Scope: observability dashboards, evidence-based performance work, prod
|
||||
build/deploy.
|
||||
Open details: deployment target/host; dashboards; load expectations.
|
||||
@@ -164,9 +230,9 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
- HTTP surface: **service/store/cache layer only**. `/api/v1/{public,user,
|
||||
internal,admin}` groups + `X-User-ID` middleware are scaffolding (exposed via
|
||||
`Server` group accessors); the session/account REST handlers land with the
|
||||
gateway in **Stage 6**. Admin bootstrap deferred to **Stage 9**.
|
||||
gateway in **Stage 6**. Admin bootstrap deferred to **Stage 10**.
|
||||
- Telemetry: providers + request-timing middleware + otelsql; exporters
|
||||
`none` (default) / `stdout`; OTLP + dashboards deferred to **Stage 11**.
|
||||
`none` (default) / `stdout`; OTLP + dashboards deferred to **Stage 12**.
|
||||
- Tests/CI: integration tests behind the `integration` build tag in
|
||||
`backend/internal/inttest` + new `integration.yaml` (testcontainers, Ryuk
|
||||
off, serial), firing on push and PR. Backend now **hard-depends on Postgres
|
||||
@@ -247,7 +313,7 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
wins/losses; `max_word_points` = best single **move** score; ties draw,
|
||||
resign/timeout is a loss, guests get no stats.
|
||||
- **Complaint** (interview): full payload with `game_id`; word-check is scoped
|
||||
to the game's pinned `(variant, dict_version)`. Stage 9 owns the resolution
|
||||
to the game's pinned `(variant, dict_version)`. Stage 10 owns the resolution
|
||||
lifecycle, so the `status` column carries no value CHECK yet.
|
||||
- **GCG** (interview): standard Poslfit dialect (UTF-8, `#player`/`#lexicon`
|
||||
pragmas, `8G`/`H8` coordinates, lower-case blanks, `.` pass-throughs, `-TILES`
|
||||
@@ -295,7 +361,7 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
stored as a **SHA-256 hash**; a `Mailer` seam with an SMTP relay
|
||||
(`BACKEND_SMTP_*`) and a default **log mailer**. It binds an email to the
|
||||
current account; an email already confirmed by another account → `ErrEmailTaken`
|
||||
(**merge is Stage 10**); email-as-login is Stage 6 and reuses this mechanism.
|
||||
(**merge is Stage 11**); email-as-login is Stage 6 and reuses this mechanism.
|
||||
- **Multi-player drop-out** (interview; discharges the Stage 3 deferral): the
|
||||
engine's `Resign` now drops a seat and the rest **play on** while ≥ 2 are
|
||||
active, finishing (last-survivor wins) when one remains; `winner` excludes all
|
||||
@@ -362,9 +428,11 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
end-to-end — auth (`auth.telegram`/`auth.guest`/`auth.email.request`/
|
||||
`auth.email.login`), `profile.get`, `game.submit_play`/`game.state`,
|
||||
`lobby.enqueue`/`lobby.poll`, `chat.post`, all five push events, and the admin
|
||||
passthrough. The remaining domain operations (friends, blocks, invitations,
|
||||
hint, word-check, pass/exchange/resign, history/GCG, profile editing) reuse the
|
||||
identical transcode pattern and are wired in **Stage 7** as the UI needs them.
|
||||
passthrough. The remaining domain operations reuse the identical transcode
|
||||
pattern and are wired as the UI needs them: the play-loop ops (pass/exchange/
|
||||
resign, hint, evaluate, word-check/complaint, history, my-games list, chat
|
||||
list/nudge) in **Stage 7**; the social/account ops (friends, blocks,
|
||||
invitations, profile editing, stats, GCG export) in **Stage 8**.
|
||||
- **Wire contracts in a new shared `scrabble/pkg` module** (interview): the
|
||||
backend push proto (`pkg/proto/push/v1`) and the FlatBuffers edge payloads
|
||||
(`pkg/fbs`, one `scrabblefb` namespace) live here with **committed** generated
|
||||
@@ -402,7 +470,7 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
(the galaxy donor's crypto stack was dropped, per §3).
|
||||
- **Admin = gateway validates Basic-Auth** (interview): the gateway checks
|
||||
`GATEWAY_ADMIN_USER`/`_PASSWORD` and reverse-proxies to backend
|
||||
`/api/v1/admin/*`; the backend admin surface is a single `ping` until Stage 9.
|
||||
`/api/v1/admin/*`; the backend admin surface is a single `ping` until Stage 10.
|
||||
- **Rate-limit = 2 dimensions, 3 classes** (interview): public per-IP (30/min,
|
||||
burst 10), authenticated per-user (120/min, burst 40), admin per-IP (60/min,
|
||||
burst 20), plus an email-code per-IP sub-limit (5/10 min); token bucket
|
||||
@@ -416,6 +484,48 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
(unit) — integration stays `./backend/...` (the only module with tagged tests).
|
||||
The solver clone + `BACKEND_DICT_DIR` steps are unchanged.
|
||||
|
||||
- **Stage 7** (interview + implementation):
|
||||
- **Scope = playable slice** (interview): the *whole* UI shell plus the core play
|
||||
loop end-to-end; the social/account/history surfaces were split out into a new
|
||||
**Stage 8** and the later stages shifted +1 (Telegram→9, Admin→10, Linking→11,
|
||||
Polish→12). Stage 7 wires only the operations the slice needs (the Stage 6
|
||||
"as the UI needs them" pattern): the new gateway/transcode + backend-REST ops
|
||||
`games.list`, `game.{pass,exchange,resign,hint,evaluate,check_word,complaint,
|
||||
history}`, `chat.{list,nudge}`. The only new domain code is `game.ListForAccount`
|
||||
(the "my games" query) and seat **`display_name`** resolution (server DTO layer);
|
||||
`SeatView` gained a trailing `display_name`. Friends/blocks/invitations,
|
||||
profile-editing, stats and the history/GCG viewer are Stage 8.
|
||||
- **Stack** (interview): plain **Svelte 5 (runes) + TypeScript + Vite**, no
|
||||
SvelteKit; `@connectrpc/connect-web` + the `flatbuffers` runtime, with the edge
|
||||
TS bindings generated from the **same** `edge.proto` (`protoc-gen-es`) and
|
||||
`scrabble.fbs` (`flatc --ts`) and **committed** under `ui/src/gen/` (dev-time
|
||||
codegen, like `cmd/jetgen` / `pkg/Makefile`; CI builds the committed output).
|
||||
- **No board on the wire** (discovered): `StateView` carries no grid, so the client
|
||||
**replays the decoded move journal** (`game.history`, newly wired) onto an empty
|
||||
board; premium squares + tile values are a client-side map **ported from
|
||||
`scrabble-solver/rules/rules.go`** with a Vitest parity test.
|
||||
- **Board UX** (interview): full-width, borderless; tiles placed by **Pointer-Events
|
||||
drag or tap** (no HTML5 DnD — it has no touch support); a contextual **MakeMove**
|
||||
control (short tap → make/reset popup, ~1 s press-and-hold → commit); per-tile
|
||||
recall by tapping a pending tile; a **two-state zoom** (15↔9 cells) on touch only
|
||||
(auto-zoom-in on placement, double-tap / pinch manual); a blank-letter chooser.
|
||||
All board/tiles/effects are **pure HTML5/CSS + Unicode** — no image/font/SVG asset.
|
||||
- **Theming** (interview): own **CSS custom-property tokens**, light/dark via
|
||||
`prefers-color-scheme`, **Telegram-themeParams-ready** (a runtime hook can override
|
||||
the tokens; the SDK is wired in the Telegram stage). **Navigation** (interview):
|
||||
dependency-free **hash router**; session token in memory + **IndexedDB**, re-resolved
|
||||
on reload (reopen Subscribe, refetch the open game); stream reconnect on focus.
|
||||
**i18n** en/ru is a hand-rolled typed catalog (compile-time key parity + a test).
|
||||
- **Mock transport** (owner request): a build-flagged in-memory fake (`VITE_MOCK`,
|
||||
`pnpm start`) drives lobby → active game → board with no backend, tree-shaken out
|
||||
of production; it is the same fixture the Playwright smoke uses.
|
||||
- **Tests/CI** (interview): **Vitest** units (board replay, placement machine,
|
||||
premium parity, i18n parity, FlatBuffers codec) + a **Playwright** smoke against
|
||||
the mock; a new **`ui-test.yaml`** workflow (type-check, unit, build with a
|
||||
**bundle-size budget** — prod is ~67 KB gzip JS — and a chromium e2e). The Go
|
||||
workflows already cover the new backend/gateway/pkg code; a `game.ListForAccount`
|
||||
integration test and gateway transcode tests for the new ops were added.
|
||||
|
||||
## Deferred TODOs (cross-stage)
|
||||
|
||||
- **TODO-1 — publish & version the solver.** Once `scrabble-solver` is stable,
|
||||
@@ -433,7 +543,7 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
git submodule (the ~0.5–0.7 MB DAWGs are regenerated wholesale and bloat git
|
||||
history); pin by tag/hash for a reproducible startup set. A submodule/LFS pull
|
||||
is a **deploy-time** way to populate the directory, **not** the runtime
|
||||
dynamic-reload mechanism (Stage 9) — keep the `BACKEND_DICT_DIR` directory as
|
||||
dynamic-reload mechanism (Stage 10) — keep the `BACKEND_DICT_DIR` directory as
|
||||
the runtime contract: a new `.dawg` appears in it and is loaded with
|
||||
`dawg.Load`.
|
||||
- **TODO-3 — garbage-collect abandoned guest accounts.** Stage 6 makes a guest a
|
||||
|
||||
Reference in New Issue
Block a user