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:
@@ -96,11 +96,20 @@ func (s *Service) maybeMove(ctx context.Context, rt game.RobotTurn, oppID uuid.U
|
||||
return s.act(ctx, rt, now)
|
||||
}
|
||||
|
||||
// maybeNudge sends a proactive nudge once the human has been idle past the
|
||||
// threshold. The social service enforces the once-per-hour-per-game limit and
|
||||
// rejects a nudge on the robot's own turn, so any such rejection is benign.
|
||||
// maybeNudge sends a proactive nudge on a lengthening, randomized schedule (proactiveNudgeGap):
|
||||
// the first lands ~60-90 min into the human's turn, and each one waits longer than the last, so a
|
||||
// long idle turn gets a handful of increasingly-spaced reminders rather than an hourly stream. The
|
||||
// gap is measured from the previous nudge (or the turn start for the first). The social service
|
||||
// still enforces the once-per-game floor and rejects a nudge on the robot's own turn, so any such
|
||||
// rejection is benign.
|
||||
func (s *Service) maybeNudge(ctx context.Context, rt game.RobotTurn, now time.Time) error {
|
||||
if now.Sub(rt.TurnStartedAt) < proactiveNudgeIdle {
|
||||
ref := rt.TurnStartedAt
|
||||
if last, ok, err := s.social.LastNudgeAt(ctx, rt.GameID, rt.RobotID); err != nil {
|
||||
return err
|
||||
} else if ok && last.After(rt.TurnStartedAt) {
|
||||
ref = last
|
||||
}
|
||||
if now.Sub(ref) < proactiveNudgeGap(ref.Sub(rt.TurnStartedAt), rt.Seed) {
|
||||
return nil
|
||||
}
|
||||
if _, err := s.social.Nudge(ctx, rt.GameID, rt.RobotID); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user