{"version":"0.17.1","events":{"your_action_required":{"target":"team","description":"Unified action prompt (T84). Sent via SSE to solicit one action from one team. action_type is engine-declared (battleship: 'placement', 'move'). Engine-specific payload fields are spread at the top level next to the envelope — no nested payload wrapper. REST correlation on POST /api/team/action is by (token, game_id, action_type).","fields":{"game_id":{"type":"str","description":"Game identifier","required":true},"action_type":{"type":"str","description":"Engine-declared action label (engine.ACTION_TYPES)","required":true},"retries_left":{"type":"int","description":"Remaining attempts for this action","required":false},"time_budget_ms":{"type":"int","description":"Effective action budget in ms (equals platform enforcement timeout)","required":false},"error":{"type":"dict","description":"Validation error from previous attempt: {violation_code, detail}","required":false}}},"game_start":{"target":"team","description":"Notification that a game is starting. Per-team engine config and platform rules.","fields":{"game_id":{"type":"str","description":"Game identifier","required":true},"game_type":{"type":"str","description":"Game type identifier (e.g. battleship)","required":true},"game_mode":{"type":"str","description":"Game mode (tournament, practice, ranked)","required":true},"opponent":{"type":"str","description":"Opponent team name","required":true},"game_config":{"type":"dict","description":"Per-team opaque engine config via engine.get_team_game_config","required":true},"rules":{"type":"dict","description":"Platform safety nets (max_placement_retries, max_move_retries, max_game_duration_ms)","required":true}}},"game_end":{"target":"team","description":"Per-team β shape (T95 §4.14): 4 self-sufficient fields. A bot listening only to its per-team channel can understand the outcome of its own game without depending on broadcast availability (per-team channel self-sufficiency principle 12).","fields":{"game_id":{"type":"str","description":"Game identifier","required":true},"terminal_reason":{"type":"str","description":"Engine-vocabulary code (∈ engine.TERMINAL_REASONS, e.g. \"all_ships_sunk\", \"placement_timeout\", \"aborted\", \"duration_cap\").","required":true},"winner_team_id":{"type":"str|null","description":"Platform-layer team_id of the winner; null for draw/abort/no-winner outcomes.","required":true},"your_end_state":{"type":"str|null","description":"Per-team end state for the recipient: null for normal completion, or a string ∈ engine.TEAM_END_STATES (e.g. \"forfeited\").","required":true}}},"game_started":{"target":"broadcast","description":"Observer: a game started.","fields":{"game_id":{"type":"str","description":"Game identifier","required":true},"match_id":{"type":"str","description":"Parent match identifier","required":true},"game_number":{"type":"int","description":"Game number within match","required":true},"game_type":{"type":"str","description":"Game type identifier (e.g. battleship)","required":true},"game_mode":{"type":"str","description":"Game mode: tournament or practice","required":true},"teams":{"type":"list","description":"Teams list [{name}]; index 0 = first mover","required":true},"config":{"type":"dict","description":"Per-game config; shape depends on game_type. For battleship: {board_width, board_height, fleet, time_limit_ms}. Renderer reads board dimensions from here — the game is the unit of render.","required":true}}},"game_ended":{"target":"broadcast","description":"Observer: a game ended. Payload carries an `outcome` sub-object (winner_team_name + team_end_states + terminal_reason + terminal_reason_description + ended_at) and engine-specific stats.","fields":{"game_id":{"type":"str","description":"Game identifier","required":true},"match_id":{"type":"str","description":"Parent match identifier (empty for practice games)","required":true},"outcome":{"type":"dict","description":"T95 outcome sub-object: winner_team_name | None, team_end_states (list, co-indexed with game.team_ids), terminal_reason (∈ engine.TERMINAL_REASONS), terminal_reason_description, ended_at (ISO-8601 UTC).","required":true},"stats":{"type":"dict","description":"Game statistics","required":false}}},"action_resolved":{"target":"broadcast","description":"Unified resolved-action event (T84 + T95 Phase 6). Emitted once per ActionLogEntry — every team action attempt (valid + invalid + error + timeout) AND every system event (e.g. _team_forfeit, _admin_abort, _duration_cap, _mutual_disconnect). Envelope is platform-owned; `summary` + `detail` come from the engine (ActionResult.summary / ActionResult.detail). Consumers filter by `result_code` and `action_type`.","fields":{"game_id":{"type":"str","description":"Game identifier","required":true},"seq":{"type":"int","description":"Monotonic per-game sequence (1-indexed; assigned at action_log append from len(action_log)+1)","required":true},"created_at":{"type":"str","description":"ISO-8601 UTC timestamp, microsecond precision (transport-layer)","required":true},"action_type":{"type":"str","description":"Engine-declared action label (engine.PHASE_ACTIONS) for team actions; system event identifier (engine.SYSTEM_EVENTS, leading underscore) for runner/engine-induced events.","required":true},"team":{"type":"int|null","description":"Zero-based team index for team actions; null for system events (T95 §4.5).","required":true},"result_code":{"type":"str","description":"One of \"valid\" | \"invalid\" | \"error\" | \"timeout\" (T95 §4.5). System events always emit \"valid\".","required":true},"summary":{"type":"str","description":"Engine-supplied human-readable single-line summary","required":true},"match_id":{"type":"str","description":"Parent match identifier (tournament games only)","required":false},"detail":{"type":"dict","description":"Engine-opaque payload for renderers","required":false}}},"tournament_created":{"target":"broadcast","description":"A tournament was created.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"name":{"type":"str","description":"Tournament name","required":true},"format":{"type":"str","description":"Tournament format","required":true}}},"tournament_ready":{"target":"broadcast","description":"Pairings generated, awaiting start.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"name":{"type":"str","description":"Tournament name","required":true},"format":{"type":"str","description":"Tournament format (round_robin, single_elimination)","required":true},"team_count":{"type":"int","description":"Number of participating teams","required":true},"pairings":{"type":"list","description":"Match pairings with team names","required":true},"layout_name":{"type":"str","description":"Layout name (platform metadata)","required":true},"layout_config":{"type":"dict","description":"Opaque layout config from Layout.config","required":true}}},"tournament_started":{"target":"broadcast","description":"Tournament underway.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"name":{"type":"str","description":"Tournament name","required":true},"format":{"type":"str","description":"Tournament format (round_robin, single_elimination)","required":true},"team_count":{"type":"int","description":"Number of participating teams","required":true},"standings":{"type":"list","description":"Initial standings","required":true},"layout_name":{"type":"str","description":"Layout name (platform metadata)","required":true},"layout_config":{"type":"dict","description":"Opaque layout config from Layout.config","required":true}}},"tournament_paused":{"target":"broadcast","description":"Tournament paused between rounds.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"reason":{"type":"str","description":"Pause reason (round_boundary, admin_request)","required":true},"round_completed":{"type":"int","description":"Round number just completed","required":false}}},"tournament_resumed":{"target":"broadcast","description":"Tournament resumed.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true}}},"tournament_ended":{"target":"broadcast","description":"Tournament finished. Winner, final standings.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"winner":{"type":"str","description":"Winning team name","required":false},"final_standings":{"type":"list","description":"Ordered standings list","required":false}}},"tournament_aborted":{"target":"broadcast","description":"Tournament cancelled.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"reason":{"type":"str","description":"Abort cause (e.g. 'driver_exception' on uncaught tournament exception)","required":false}}},"match_started":{"target":"broadcast","description":"A match (best-of-N) started.","fields":{"match_id":{"type":"str","description":"Match identifier","required":true},"teams":{"type":"list","description":"Teams list [{name}]; index 0 = bracket position","required":true},"best_of":{"type":"int","description":"Best-of-N","required":true}}},"match_ended":{"target":"broadcast","description":"A match ended.","fields":{"match_id":{"type":"str","description":"Match identifier","required":true},"teams":{"type":"list","description":"Teams list [{name, wins}]","required":true},"winner":{"type":"str","description":"Winning team name","required":true},"score":{"type":"str","description":"Match score","required":true}}},"standings_updated":{"target":"broadcast","description":"Rankings changed.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"standings":{"type":"list","description":"Current standings list","required":true}}},"pairings_updated":{"target":"broadcast","description":"Bracket advancement.","fields":{"tournament_id":{"type":"str","description":"Tournament identifier","required":true},"pairings":{"type":"list","description":"Current pairings","required":true}}},"team_registered":{"target":"broadcast","description":"A team registered.","fields":{"team_id":{"type":"str","description":"Team identifier","required":true},"name":{"type":"str","description":"Team name","required":true},"team_count":{"type":"int","description":"Total registered teams","required":true}}},"team_connected":{"target":"broadcast","description":"Team SSE stream connected (online).","fields":{"team_id":{"type":"str","description":"Team identifier","required":true},"team_name":{"type":"str","description":"Team name","required":true}}},"team_disconnected":{"target":"broadcast","description":"Team SSE stream disconnected (offline).","fields":{"team_id":{"type":"str","description":"Team identifier","required":true},"team_name":{"type":"str","description":"Team name","required":true}}},"team_deregistered":{"target":"broadcast","description":"Team deregistered (self-service or admin).","fields":{"team_id":{"type":"str","description":"Team identifier","required":true},"team_name":{"type":"str","description":"Team name","required":true}}},"teams_cleared":{"target":"broadcast","description":"All teams cleared (server reset).","fields":{"reason":{"type":"str","description":"Reason for clearing (e.g. server_reset)","required":true}}},"registration_opened":{"target":"broadcast","description":"Registration is now open.","fields":{}},"registration_closed":{"target":"broadcast","description":"Registration is now closed.","fields":{}},"bot_created":{"target":"broadcast","description":"A server bot was created.","fields":{"team_id":{"type":"str","description":"Bot team identifier","required":true},"team_name":{"type":"str","description":"Bot name","required":true},"strategy_name":{"type":"str","description":"Strategy module name","required":true}}},"bot_updated":{"target":"broadcast","description":"A server bot was updated.","fields":{"team_id":{"type":"str","description":"Bot team identifier","required":true},"team_name":{"type":"str","description":"Bot name","required":true},"strategy_name":{"type":"str","description":"Strategy module name","required":true}}},"bot_deleted":{"target":"broadcast","description":"A server bot was removed.","fields":{"team_id":{"type":"str","description":"Bot team identifier","required":true},"team_name":{"type":"str","description":"Bot name","required":true}}},"system_message":{"target":"broadcast","description":"System announcement.","fields":{"message":{"type":"str","description":"Message text","required":true},"severity":{"type":"str","description":"Display tier: info | important | error. Clients default to info.","required":false}}},"challenge_created":{"target":"team","description":"Incoming challenge from another team.","fields":{"challenge_id":{"type":"str","description":"Challenge identifier","required":true},"challenger":{"type":"str","description":"Challenger team name","required":true},"mode":{"type":"str","description":"Game mode (practice/ranked)","required":true},"layout_id":{"type":"str","description":"Layout identifier","required":true},"expires_at":{"type":"str","description":"Expiry timestamp (ISO 8601)","required":true}}},"challenge_accepted":{"target":"team","description":"Your outgoing challenge was accepted.","fields":{"challenge_id":{"type":"str","description":"Challenge identifier","required":true},"game_id":{"type":"str","description":"Game identifier","required":true},"opponent":{"type":"str","description":"Opponent team name","required":true}}},"challenge_declined":{"target":"team","description":"Your outgoing challenge was declined.","fields":{"challenge_id":{"type":"str","description":"Challenge identifier","required":true},"opponent":{"type":"str","description":"Opponent team name","required":true}}},"challenge_expired":{"target":"both","description":"A challenge expired without response.","fields":{"challenge_id":{"type":"str","description":"Challenge identifier","required":true},"challenger":{"type":"str","description":"Challenger team name","required":true},"opponent":{"type":"str","description":"Opponent team name","required":true}}},"challenge_cancelled":{"target":"team","description":"An incoming challenge was cancelled by the challenger.","fields":{"challenge_id":{"type":"str","description":"Challenge identifier","required":true},"challenger":{"type":"str","description":"Challenger team name","required":true}}},"queue_joined":{"target":"team","description":"Team joined the matchmaking queue.","fields":{"mode":{"type":"str","description":"Game mode","required":true},"position":{"type":"int","description":"Position in queue","required":true},"timeout_s":{"type":"int","description":"Seconds until queue timeout","required":true}}},"queue_matched":{"target":"team","description":"Queue match found — game starting.","fields":{"game_id":{"type":"str","description":"Game identifier","required":true},"opponent":{"type":"str","description":"Opponent team name","required":true}}},"queue_left":{"target":"team","description":"Team left the matchmaking queue.","fields":{"reason":{"type":"str","description":"Reason (manual, timeout, matched)","required":true},"mode":{"type":"str","description":"Game mode that was left","required":true}}},"rating_updated":{"target":"team","description":"Team rating changed after a ranked/tournament game.","fields":{"rating":{"type":"float","description":"New rating","required":true},"rating_deviation":{"type":"float","description":"New RD","required":true},"change":{"type":"float","description":"Rating change from previous","required":true}}},"leaderboard_updated":{"target":"broadcast","description":"Leaderboard changed after a rating update.","fields":{}}},"games":{"battleship":{"actions":{"place_ships":{"description":"Place fleet on the board before the game starts.","receives":{"board_width":"int — board width in columns","board_height":"int — board height in rows","fleet":"list[dict] — ships to place, each with ship/type/size"},"returns":{"placements":"list[dict] — each with ship, start, end coordinates"}},"move_request":{"description":"Choose a coordinate to fire at during the game.","receives":{"board_width":"int — board width","board_height":"int — board height","your_board":"dict — your ships and incoming hits","opponent_board":"dict — your shots and their results","turn":"int — current turn number"},"returns":{"coordinate":"str — target coordinate (e.g. 'A1', 'J10')"}}},"timeouts":{"placement_s":30.0,"move_s":10.0},"default_config":{"board_width":10,"board_height":10,"fleet":[{"ship":"carrier","type":"carrier","size":5},{"ship":"battleship","type":"battleship","size":4},{"ship":"cruiser","type":"cruiser","size":3},{"ship":"submarine","type":"submarine","size":3},{"ship":"destroyer","type":"destroyer","size":2}]}}}}