background_agents.game_turn_agent module

GameTurnAgent – roster-coverage validator for game choice buttons.

Consumes sg:stream:game:turn consumer-group events. Proofreading of individual choices (strip markdown, trim emojis, truncate labels) is now done inline by the renderer; the helper _proofread_choice / _first_emoji functions in this module remain available but the agent’s runtime job is validation only. When a game turn completes, this agent:

  1. Reads the producer-snapshotted covered/required/dormant player sets

  2. Validates roster coverage from that immutable event payload: - All ACTIVE/IDLE players must have choices - DORMANT players should not have choices

  3. Pings Star via a system message if active/idle players are missing choices

Legacy events without snapshot fields retain a rolling-upgrade fallback that reads the draft/session keys. New events never depend on that mutable state.

class background_agents.game_turn_agent.GameTurnAgent(discord_platform=None, *, consumer_name='game-turn-agent', config=None)

Bases: object

Background agent that proofreads and validates game-turn choices.

Consumes the durable game-turn Redis Stream and, for each completed turn, cleans the channel’s draft choices (via _proofread_choice()) and checks that every active player has a choice – pinging Star through the Discord platform when coverage is missing. Constructed by the agents service bootstrap in agents_main.py and driven by its long-lived listener task (see start()/_listen()); the Redis client is injected at start() time.

Parameters:
  • discord_platform (Any)

  • consumer_name (str)

  • config (Any)

__init__(discord_platform=None, *, consumer_name='game-turn-agent', config=None)

Initialize the roster-coverage validator agent.

Stores the Discord platform handle (used by _ping_star() to send a system message into a channel when active players are missing choices) and sets the listener task slot and running flag to their idle defaults. The Redis client is not supplied here; it is injected later in start(). No subscriptions or I/O happen at construction time.

Constructed by the agents service bootstrap in agents_main.py.

Parameters:
  • discord_platform (Any) – Discord platform object exposing a client that can resolve and send to channels. When None, _ping_star() logs a warning and skips the ping.

  • consumer_name (str)

  • config (Any)

Return type:

None

async start(redis)

Start the durable turn-stream consumer-group task.

Return type:

None

Parameters:

redis (Any)

async stop()

Stop and await the durable stream-consumer task.

Return type:

None

async background_agents.game_turn_agent.publish_turn_complete(redis, channel_id, narrative, game_id='', game_name='', turn_number=0, active_player_names=None, covered_player_names=None, required_player_names=None, dormant_player_names=None, event_id=None, raise_on_error=False)

Durably enqueue one self-contained turn-completion event.

Serializes the turn metadata (channel, a truncated narrative preview, game id/name, turn number, the active player roster, and a timestamp) and publishes it on TURN_COMPLETE_CHANNEL (game:turn:complete) so the GameTurnAgent listener can validate roster coverage for the turn. No-ops when redis is None; publish failures are caught and logged so a Redis hiccup never blocks the send pipeline.

Called from the main message pipeline in message_processor/generate_and_send.py once Star’s narrative has been sent for a game turn.

Parameters:
  • redis (Any) – An async Redis client, or None to skip publishing.

  • channel_id (str) – The Discord channel id the turn belongs to.

  • narrative (str) – Star’s turn narrative; truncated to 3000 chars in the payload.

  • game_id (str) – Optional game identifier carried through to the event.

  • game_name (str) – Optional human-readable game name carried through.

  • turn_number (int) – The turn index, included in the payload and debug log.

  • active_player_names (list[str] | None) – Names of players active this turn, used by the agent for roster-coverage validation; None becomes an empty list.

  • covered_player_names (list[str] | None)

  • required_player_names (list[str] | None)

  • dormant_player_names (list[str] | None)

  • event_id (str | None)

  • raise_on_error (bool)

Return type:

dict[str, str] | None

Returns:

None.