release: promote development → master (v1.25.0) #290
Reference in New Issue
Block a user
Delete Branch "development"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Promote
developmenttomasterfor the v1.25.0 production release. Every change belowis already merged into
developmentand green there (run #1773); this PR is the release gate.What ships
The rule is pinned per game at creation (
games.no_repeat_words), not derived from thevariant, because a game is replayed from its journal on every open: applying it retroactively
would void an already-played repeat and rescore earlier plays. Existing games keep the
unrestricted rule; only new
erudit_rugames get it. Exposed on the wire so the client scoresits local move preview exactly like the server.
Scrabble. Only the column default moves, so a player who deliberately settled on one variant
is not overruled; guests stay on Erudit alone.
nudge, driven by a new per-seat
deletedflag.the guest stubs use the "sign in" wording and Stats links it.
Schema and wire
Two migrations, both expand-contract and rollback-safe:
00017_no_repeat_words.sql— additiveboolean NOT NULL DEFAULT falsecolumn onbackend.games, no backfill; an image rollback ignores it.00018_default_variant_preferences.sql— moves only theaccounts.variant_preferencescolumn default; rewrites no rows, and the previous code reads the two-element set unchanged.
FlatBuffers fields are added trailing (
SeatView.deleted,GameView.no_repeat_words) —backward-compatible for older clients.
Because the release carries migrations, the production rollout opens a maintenance window and
takes a consistent
pg_dumpbefore the roll, perdeploy/README.md.Release
Tagged
v1.25.0onmasterafter this merge; production is then rolled by the manualprod-deploydispatch (confirm=deploy).A deleted account keeps its seats in every shared game, so its opponents still saw the add-friend and block controls on the scoreboard (deletion drops the friendship, so the 🤝 even reappeared for a former friend) and a chat composer nobody was behind. A friend request sent that way was accepted by the server and stayed pending forever. The per-viewer game views now mark such a seat (SeatView.deleted, resolved beside the seat display names by a batch accounts.deleted_at lookup) and the client hides every control aimed at it: add-friend, block, and the chat composer (message + nudge) once no reachable opponent is left. Live events carry the game domain's seat standings and so leave the mark unset, so the delta reducers preserve the cached one. SendFriendRequest and Block against a tombstone are refused with social.ErrAccountDeleted (410 account_deleted) — the source of truth for an older client.Composing an already-played word read as a perfectly legal move: the tiles drew as legal, the caption showed "БРА = 6" and the ✅ was enabled, and only the server refused the play with the bare "illegal move" toast. The on-device evaluator already applied the rule, but it reported the refusal as a plain illegal play, so nothing distinguished a repeated word from a misspelling and the caption fell back to the turn label. It now names the word it rejected, and the game screen reads "БРА: уже использовано" above the scores while the tiles stay marked invalid — the composition is wrong, but for a reason the player cannot read off the board, since the word itself is in the dictionary. The verdict is reached on the device before the play is ever offered to the server, in an online game exactly as in an offline one; the offline source reports the same reason through the same field. Only the main word is ever refused: a cross-word the game has already used still stands (it is incidental to laying the main word) and merely scores nothing, so the rule cannot falsely block a play in a game with several words per turn. The scoring of such a play is now pinned to the point on both sides: the engine test and the client test evaluate the same position — РОТ laid across a standing КО, completing an already-played КОТ — and assert the same totals (10 unrestricted, 5 with the rule), so a divergence between the two engines breaks one of them. The client test also replays the exact test-contour position this was reported from.