Stage 17: fix the robot-nudge frequency + per-game push language
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 11s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
Two owner-reported defects from a live contour game. A. Frequency: the robot's proactive nudge fired hourly for 12h+ (a 12h idle threshold then the 1h cooldown, uncapped). Replaced with a lengthening, randomized schedule (proactiveNudgeGap): the first nudge ~60-90 min into the human's turn, each later gap growing toward 1-6h (uniform sample in [60min, ceil], ceil ramping 90min->6h over 12h of idle, measured from the previous nudge), so a long wait gets a handful of increasingly-spaced reminders instead of a stream. B. Language: out-of-app push routed by the recipient's GLOBAL service_language (last-login-wins), so after re-logging via the RU bot an English game's nudges came from the RU bot. Now a game push (your_turn, game_over, nudge, match_found) carries the game's own language (engine.Variant.Language) on push.Event, and the gateway routes by it (falling back to service_language for non-game pushes). The New-Game variant-gating guarantees the game's bot is one the player has started, so delivery is never blocked. Tests: proactiveNudgeGap unit + retimed TestRobotProactiveNudge; TestVariantLanguage; emit your_turn/game_over language; TestNudgeRoutedByGameLanguage integration. Docs: ARCHITECTURE (§7 nudge, §10/§13 routing), FUNCTIONAL (+ _ru), PLAN tracker.
This commit is contained in:
+14
-9
@@ -141,8 +141,10 @@ arrive from a platform rather than completing a mandatory registration).
|
||||
gate (it is a product affordance, not a trust boundary). The service language is
|
||||
**persisted** per account (`accounts.service_language`, updated on every Telegram
|
||||
login — last-login-wins) and routes the user's out-of-app push back through the right
|
||||
bot (§10); it is distinct from `preferred_language` (the interface language) and from
|
||||
a game's variant language. Non-Telegram logins (web / email / guest) carry the
|
||||
bot (§10) — **except a game event, which routes by the game's own language** (its variant →
|
||||
en/ru, Stage 17), so a game's notification always comes from the game's bot rather than the
|
||||
recipient's latest login bot. The service language is distinct from `preferred_language` (the
|
||||
interface language) and from a game's variant language. Non-Telegram logins (web / email / guest) carry the
|
||||
gateway's default set (`GATEWAY_DEFAULT_SUPPORTED_LANGUAGES`, all variants by default).
|
||||
- The client holds `session_id` in memory for the app session (browser/OS
|
||||
storage is optional and may be unavailable; losing it means re-login).
|
||||
@@ -339,8 +341,9 @@ English game the Latin pool.
|
||||
**sleeps 00:00–07:00** anchored to the **opponent's** profile timezone with a
|
||||
per-game drift of **±3 h** (fallback UTC), so its night overlaps the human's
|
||||
rather than running anti-phase; on a daytime nudge it replies near the move's lower
|
||||
band; it proactively nudges the human after **12 hours** idle (subject to the
|
||||
once-per-hour chat limit).
|
||||
band; it proactively nudges the idle human on a **lengthening, randomized schedule** — the
|
||||
first ~60-90 min into the turn, each later reminder spaced further out toward 1-6 h — so a long
|
||||
wait gets a handful of increasingly-spaced nudges rather than an hourly stream (Stage 17).
|
||||
- **Observability**: robot accounts accrue ordinary statistics (§9) — the
|
||||
authoritative balance metric (target ≈ 40% robot wins) — and a
|
||||
`robot_games_finished_total` OTel counter plus a per-finish log give a live view.
|
||||
@@ -507,11 +510,13 @@ missed while the app was hidden. **Out-of-app platform push** (Stage 9) is a fal
|
||||
the **gateway** routes from the same firehose: for an event whose recipient has **no
|
||||
live in-app stream** it resolves the backend `/internal/push-target` (their Telegram
|
||||
`external_id`, the **service language** — the bot they last signed in through, falling
|
||||
back to the interface language — and the `notifications_in_app_only` flag) and asks the
|
||||
**Telegram connector** to deliver a localized message with a Mini App deep-link
|
||||
button — only when the recipient has a Telegram identity and has not confined
|
||||
notifications to the app, so the two channels never duplicate. The connector routes by
|
||||
that language to the matching bot and renders the message in it. The out-of-app set is
|
||||
back to the interface language — and the `notifications_in_app_only` flag). A **game** event,
|
||||
however, carries the **game's own language** on the push (Stage 17), and the gateway routes by
|
||||
that instead of the service language — so a game's notification always comes from the game's bot,
|
||||
not the recipient's latest-login bot. It then asks the **Telegram connector** to deliver a
|
||||
localized message with a Mini App deep-link button — only when the recipient has a Telegram
|
||||
identity and has not confined notifications to the app, so the two channels never duplicate. The
|
||||
connector routes by that language to the matching bot and renders the message in it. The out-of-app set is
|
||||
your-turn, game-over, nudge, match-found and the invitation / friend-request notify sub-kinds;
|
||||
the connector renders the message and skips the rest. Operator broadcasts
|
||||
(`SendToUser` / `SendToGameChannel`, §10 admin) instead pick the bot by an
|
||||
|
||||
+4
-2
@@ -35,8 +35,10 @@ the Telegram colours, and — on first contact — seeds the new account's inter
|
||||
language from the Telegram client. The sign-in service also declares the **game
|
||||
languages** it offers (a set of en/ru, at least one), which gate the New Game variant
|
||||
choice in the lobby. Telegram runs a separate bot per language (an English bot and a
|
||||
Russian bot, the same player spanning both); the bot a player signed in through both
|
||||
sets their offered languages and is the bot their out-of-app notifications come from. Guests are session-only with restricted features
|
||||
Russian bot, the same player spanning both); the bot a player signed in through sets their
|
||||
offered languages, and their non-game notifications come from it. A **game's** notifications
|
||||
(your turn, game over, a nudge), though, always come from **that game's** bot — by the game's
|
||||
language, not whichever bot the player signed in through last. Guests are session-only with restricted features
|
||||
(auto-match only; no friends, stats or history); an abandoned guest that never
|
||||
joined a game and has been idle past the retention window is garbage-collected. While the app is open the client
|
||||
keeps a live stream and receives in-app updates in real time — the opponent's move,
|
||||
|
||||
@@ -36,8 +36,10 @@ Mini App** авторизует по подписанным `initData` плат
|
||||
языку Telegram-клиента. Сервис входа также объявляет **языки игры**, которые он
|
||||
предлагает (набор из en/ru, минимум один), и они ограничивают выбор типа партии в
|
||||
лобби. Telegram держит отдельного бота на язык (английский и русский, один игрок
|
||||
охватывает обоих); бот, через которого игрок вошёл, задаёт его доступные языки и
|
||||
является тем ботом, от которого приходят его внеприложенческие уведомления. Гость — только сессия, с урезанными функциями (только
|
||||
охватывает обоих); бот, через которого игрок вошёл, задаёт его доступные языки, и от него
|
||||
приходят его **внеигровые** уведомления. А уведомления по **партии** (ваш ход, конец партии,
|
||||
nudge) приходят от бота **этой партии** — по языку партии, а не по тому боту, через которого
|
||||
игрок входил последним. Гость — только сессия, с урезанными функциями (только
|
||||
авто-подбор; без друзей, статистики и истории); заброшенный гость, не вошедший ни
|
||||
в одну игру и простаивавший дольше окна удержания, удаляется сборщиком. Пока приложение открыто, клиент
|
||||
держит живой стрим и получает обновления в реальном времени — ход соперника, ваш ход,
|
||||
|
||||
Reference in New Issue
Block a user