From e913eb215b476bc1bb3e0cd6fe38672e76a2c375 Mon Sep 17 00:00:00 2001 From: Malaber Date: Thu, 23 Apr 2026 13:18:02 +0200 Subject: [PATCH] docs: clarify websocket contract --- AGENTS.md | 4 ++++ doc/team_action_lists_api.md | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 54d96fc..a26dc23 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -166,6 +166,7 @@ That means: ## Websocket Live Updates Team-action-list live updates use Rails ActionCable at `/cable`. +This is websocket push from backend to clients, not a webhook callback model. Practical rule: @@ -176,8 +177,11 @@ Practical rule: For team action lists specifically: - subscribe by tournament id +- channel name is `TournamentTeamActionListsChannel` - broadcast full current list snapshot for that tournament +- event payload type is `team_action_lists.updated` - keep write APIs idempotent and item-scoped +- keep websocket read-only; item state changes still go through HTTP `PATCH` - cover both direct-app websocket updates and follower-sync websocket propagation in E2E ## Practical Expectation diff --git a/doc/team_action_lists_api.md b/doc/team_action_lists_api.md index 4ec66fa..9c1fd25 100644 --- a/doc/team_action_lists_api.md +++ b/doc/team_action_lists_api.md @@ -164,6 +164,12 @@ That means: Team action lists also support live updates over ActionCable. +This is a websocket flow, not a webhook flow. + +- backend pushes updates to subscribed clients over websocket +- frontend does not register callback URLs +- writes still happen over normal HTTP `PATCH` requests + Endpoint: - `GET /cable` for websocket upgrade @@ -188,6 +194,9 @@ Behavior: - same broadcast path is used for: - direct source updates - follower sync imports +- both write endpoints trigger the same backend update path before broadcast: + - `PATCH /team_action_items/:id` + - `PATCH /tournaments/:tournament_id/team_action_lists/:key/teams/:team_id` Payload shape: @@ -225,6 +234,16 @@ Frontend guidance: - keep writes item-scoped through HTTP - use websocket only for read/live propagation +Practical frontend pattern: + +1. load initial tournament state over HTTP +2. open websocket subscription for same tournament id +3. render incoming websocket payload as latest live snapshot +4. when user checks or unchecks one team, send one idempotent HTTP `PATCH` for that item only +5. let websocket keep other viewers in sync + +This avoids whole-list writebacks and reduces race-condition risk between multiple clients. + ## Future Extension Guidance When new lists are added, frontend should not hardcode backend internals beyond: