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

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:
Ilia Denisov
2026-06-09 08:06:58 +02:00
parent 265e442252
commit bf7dca0a09
21 changed files with 257 additions and 45 deletions
+11 -7
View File
@@ -207,8 +207,8 @@ func TestMatchmakerSubstitutesRobotEndToEnd(t *testing.T) {
}
}
// TestRobotProactiveNudge checks the robot nudges the human after the idle
// threshold on the human's turn.
// TestRobotProactiveNudge checks the robot's lengthening proactive-nudge schedule on the
// human's turn: nothing before the ~60-90 min first gap, exactly one once it has elapsed.
func TestRobotProactiveNudge(t *testing.T) {
ctx := context.Background()
svc := newGameService()
@@ -232,14 +232,18 @@ func TestRobotProactiveNudge(t *testing.T) {
t.Fatalf("create: %v", err)
}
// Midnight start, driven 13 hours later (>12h idle) at a daytime hour awake for
// every drift.
start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
// A daytime turn start (the robot is awake for every ±3h drift between 07:30 and 12:00). No
// nudge before the 60-min floor of the first gap; exactly one once past its 90-min ceiling.
start := time.Date(2024, 1, 1, 10, 0, 0, 0, time.UTC)
setTurnStarted(t, g.ID, start)
robots.Drive(ctx, start.Add(13*time.Hour))
robots.Drive(ctx, start.Add(30*time.Minute))
if n := countNudges(t, g.ID, robotID); n != 0 {
t.Errorf("robot nudges = %d at 30m idle, want 0 (before the first gap)", n)
}
robots.Drive(ctx, start.Add(2*time.Hour))
if n := countNudges(t, g.ID, robotID); n != 1 {
t.Errorf("robot nudges = %d, want 1 after 13h idle on the human's turn", n)
t.Errorf("robot nudges = %d at 2h idle, want 1 (after the first gap)", n)
}
}