release: promote development → master (v1.22.0) #274

Merged
developer merged 6 commits from development into master 2026-07-14 23:13:25 +00:00
Owner

Promote the native-polish batch to production. Tag v1.22.0 on master after merge, then dispatch prod-deploy.

Ships (#273)

  • A fix(account): a fresh guest is seeded its interface language from the client locale — no more English ad banner / bot messages for a Russian native guest until reconcile.
  • B feat(link): a guest linking into a durable account auto-merges (seamless sign-in, no prompt); a durable initiator still confirms. Fixes the merged-away device-local game (its seats are re-pointed to the surviving account). Active-game conflict surfaces a clear message.
  • C feat(game): the turn strip reads 'Opponent's turn' for online PvP + vs_ai (hotseat still names the seat).
  • D feat(dict): the build's DICT_VERSION is deploy-authoritative — on boot the backend delivers it onto the volume (add-only) and activates it. This deploy moves prod's active dictionary from v1.3.0 to v1.3.1 (matching the native bundle), so new games stop fetching the dict over the network; in-flight v1.3.0 games keep their pinned version.

Schema

None — no migrations, so the prod deploy needs no maintenance window (rolling image swap, health-gated, auto-rollback).

Verified on #273: full CI green; contour deployed. Local: Go unit+integration, UI svelte-check+vitest, e2e 252/252, UI build.

Promote the native-polish batch to production. Tag `v1.22.0` on `master` after merge, then dispatch prod-deploy. ## Ships (#273) - **A** fix(account): a fresh guest is seeded its interface language from the client locale — no more English ad banner / bot messages for a Russian native guest until reconcile. - **B** feat(link): a guest linking into a durable account **auto-merges** (seamless sign-in, no prompt); a durable initiator still confirms. Fixes the merged-away device-local game (its seats are re-pointed to the surviving account). Active-game conflict surfaces a clear message. - **C** feat(game): the turn strip reads 'Opponent's turn' for online PvP + vs_ai (hotseat still names the seat). - **D** feat(dict): the build's `DICT_VERSION` is deploy-authoritative — on boot the backend delivers it onto the volume (add-only) and activates it. **This deploy moves prod's active dictionary from v1.3.0 to v1.3.1** (matching the native bundle), so new games stop fetching the dict over the network; in-flight v1.3.0 games keep their pinned version. ## Schema None — no migrations, so the prod deploy needs **no maintenance window** (rolling image swap, health-gated, auto-rollback). Verified on #273: full CI green; contour deployed. Local: Go unit+integration, UI svelte-check+vitest, e2e 252/252, UI build.
developer added 6 commits 2026-07-14 23:08:03 +00:00
A guest was provisioned with only its detected time zone; its PreferredLanguage
fell to the 'en' column default. The client already sends its locale in the guest
login (GuestLoginRequest.locale), but the gateway dropped it and ProvisionGuest
took no language — so a Russian user's brand-new native guest got English
language-dependent server content (the ad banner, bot messages) until the client's
later language reconcile. Read the locale in the gateway guest handler, thread it
through GuestAuth -> ProvisionGuest, and seed PreferredLanguage from it (validated;
an unsupported/absent value keeps the 'en' default). Wire field already present —
no schema change.
The dictionary lives on a persistent volume seeded from the image once and never
re-seeded, so a redeploy that bumped BACKEND_DICT_VERSION left the new DAWGs
unreachable (the volume mount shadows the image copy) and the active version stuck
at whatever the admin console last installed — a native offline-first client then
fetched the server's older pinned version over the network instead of using its
bundled dict.

On boot the backend now DELIVERS the build version onto the volume add-only, from a
second unshadowed image copy at BACKEND_DICT_SEED_DIR, into DICT_DIR/<version>/ when
absent (the flat seed and prior uploads untouched, so in-flight games keep theirs),
and InitActiveVersion makes the build version active — except a console-installed
version NEWER than the build (compared numerically) is not downgraded by a restart.
The .seed_version guard still protects the flat seed's label (a bump is a new
subdirectory, never a relabel). The console stays for out-of-band updates.

Docs: ARCHITECTURE.md §5, deploy/README.md, seedmarker.go. Tests: engine.DeliverVersion
(add-only, idempotent, flat-seed skip), compareDictVersions, and the dictionary-update
integration test's deploy-delivers path.
When a guest links an identity (email/TG/VK) that already belongs to a durable account,
the guest-primary rule already made the durable account the survivor and switched the
session — but the client still showed an irreversible 'merge two accounts?' confirmation,
which is nonsense from the user's side (they are simply signing into their account). The
confirm step now merges inline for a GUEST initiator and returns the completed merge (the
switched token); a durable initiator still gets the explicit confirmation (consolidating
two real accounts is consequential). The active-game guard still refuses, surfaced as the
clear error.merge_active_game_conflict message rather than swallowed.

Also fixes the merged-away device-local games: their human/host seat was recorded under
the retired account id, so after the switch the lobby and game header could not identify
'me' and showed every seat as an opponent (the game still played — turn logic is
seat-index based). applyLinkResult now re-points local game seats from the retired id to
the survivor (repointLocalGameSeats).

Docs: FUNCTIONAL.md (+_ru).
feat(game): show 'Opponent's turn' instead of the opponent's name in the turn strip
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m15s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m57s
513b772cd4
When it is not the viewer's turn in an online PvP or vs_ai game, the under-board status
now reads the generic 'Opponent's turn' (game.opponentsTurn) rather than the opponent's
display name or the robot mark, so whose turn it is reads uniformly. The hotseat branch
still names the seat to move (whose device-turn it is matters there), and the seat row
below continues to name each player.
chore(ui): change hints symbol for backward unicode compatibility
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 23s
CI / ui (pull_request) Successful in 1m15s
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m49s
1d306ec0fc
Merge pull request 'native polish: guest-locale banner, seamless guest sign-in (+local-game fix), 'Opponent's turn' label, deploy-authoritative dictionary' (#273) from feature/native-polish-and-dict-deploy into development
CI / changes (push) Successful in 2s
CI / unit (push) Successful in 11s
CI / integration (push) Successful in 23s
CI / ui (push) Successful in 1m16s
CI / conformance (push) Successful in 10s
CI / changes (pull_request) Successful in 2s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m49s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 25s
CI / ui (pull_request) Successful in 1m14s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
8758cbd35d
owner approved these changes 2026-07-14 23:09:50 +00:00
developer merged commit 904e92aac8 into master 2026-07-14 23:13:25 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#274