New scrabble/loadtest module (the pre-release stress harness): seeds 1000 guest +
10000 durable accounts with pre-created sessions directly in Postgres (token hash
matches backend/internal/session), drives virtual players through the edge protocol
(real 2-4p games assembled via invitations, mid-ranked legal moves generated locally
by the embedded scrabble-solver — the edge carries no board, so the client replays
history), plus nudge/chat/check-word/draft/profile/stats and a gateway-hammer that
verifies the rate limiter. Prints a trip-report summary (per-op latency percentiles,
result codes, live-event tally). Go unit tests cover the pure pieces; the DAWG-backed
move test runs under BACKEND_DICT_DIR.
Contour: add cAdvisor + postgres_exporter + a 'Scrabble - Resources' Grafana
dashboard and the two Prometheus scrape jobs, for the R2/R7 stress-run resource
baseline.
CI: gate ./loadtest/... (path filter + vet/build/test). Docs: TESTING, ARCHITECTURE,
project CLAUDE repo layout.
- backend/go.mod pins gitea.iliadenisov.ru/developer/scrabble-solver v1.0.0; the engine's
imports use the published module path; go.work drops the solver replace (GOPRIVATE fetches
it directly from Gitea). The solver's wordlist/dictdawg are now public packages.
- CI (go-unit, integration): drop the solver sibling-clone, set GOPRIVATE, and download the
dictionary DAWG release artifact (scrabble-dawg-<DICT_VERSION>.tar.gz from the new
scrabble-dictionary repo) for BACKEND_DICT_DIR.
- Docs: ARCHITECTURE §5/§11/§13/§14 + backend/README updated to the published-module +
release-artifact model. PLAN.md re-scoped Stage 14 to the split and added Stages 15 (deploy
infra & test contour), 16 (prod contour), 17 (dual Telegram bots); TODO-1/TODO-2 marked done.
New platform/telegram connector (own container, bot token only there):
- go-telegram/bot long-poll loop: /start deep-links + Mini App launch button.
- gRPC API pkg/proto/telegram/v1 (Telegram service): ValidateInitData, Notify
(renders a localized message + deep-link button), SendToUser/SendToGameChannel
(admin, wired in Stage 10). Generic methods are platform-agnostic (external_id).
- Bot API base override for Telegram's test environment; Dockerfile + compose
(VPN sidecar, no public ingress); README.
Gateway:
- initData validation relocated from the gateway into the connector; the gateway
calls ValidateInitData over gRPC (GATEWAY_CONNECTOR_ADDR), drops the bot token,
and deletes internal/auth.
- Out-of-app push: runPushPump routes events whose recipient has no live in-app
stream to connector.Notify, gated by /internal/push-target + the in-app-only
flag (race-free de-dup); HasSubscribers added to the push hub.
Backend:
- Migration 00007 accounts.notifications_in_app_only (default true) + jetgen.
- ProvisionTelegram seeds a new account's language/display name from the launch
fields; IdentityExternalID reverse lookup; /internal/push-target handler.
UI:
- Telegram Mini App launch: detect initData, apply themeParams, authTelegram,
route the deep-link start_param (g/i/f); /telegram/ guard redirects outside
Telegram. Vite relative base + telegram-web-app.js. In-app-only profile toggle;
share-to-Telegram link for a friend code. Vitest + Playwright coverage.
Wire/docs/CI: fbs Profile/UpdateProfileRequest gain notifications_in_app_only
(Go + TS); go.work uses ./platform/telegram; go-unit.yaml covers it; PLAN,
ARCHITECTURE, FUNCTIONAL (+ru), UI_DESIGN, READMEs updated.
backend/internal/engine wraps the sibling scrabble-solver library in-process:
- Registry: versioned DAWG load via dafsa.Load, keyed by (variant, dict_version),
latest-per-variant; English / Russian / Эрудит handled uniformly.
- Bag: own deterministic, seeded tile bag with Draw + Return (for exchanges),
since the solver's self-play bag cannot return tiles.
- Game: pure rules engine — deal, play/pass/exchange/resign, refill, per-move
scoring, turn order, and end-condition detection (empty bag + empty rack, six
scoreless turns, resignation) with end-game rack adjustment.
- decode/ReplayBoard: dictionary-independent MoveRecords and board replay via
scrabble.Apply (no internal/encoding), realising ARCHITECTURE §9.1.
Wiring: go.work gains "replace scrabble-solver => ../scrabble-solver"; backend
requires scrabble-solver (placeholder) and github.com/iliadenisov/dafsa directly.
Both Go CI workflows clone the public solver sibling (master HEAD, no token) and
set BACKEND_DICT_DIR.
Docs: ARCHITECTURE §5/§14, TESTING engine layer, backend README, and PLAN
refinements + deferred TODOs (publish/version solver; split engine vs dictionary
generator).