From d953cb73abf35fe3973cb293a7f87fba38a66c66 Mon Sep 17 00:00:00 2001 From: markov Date: Fri, 27 Feb 2026 14:21:36 +0100 Subject: [PATCH] Inject project_id into all WS broadcast data - broadcast_message: injects project_id into message dict - broadcast_task_event: injects project_id into task data - chat.send handler: resolves project_id from chat or task - _system_message: adds project_id to both task and chat broadcasts Agents always know which project a message belongs to. --- src/tracker/api/tasks.py | 10 +++++++--- src/tracker/ws/handler.py | 10 ++++++++++ src/tracker/ws/manager.py | 5 +++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tracker/api/tasks.py b/src/tracker/api/tasks.py index 9662b82..df5074d 100644 --- a/src/tracker/api/tasks.py +++ b/src/tracker/api/tasks.py @@ -157,10 +157,12 @@ async def _system_message( created_at=task_msg.created_at.isoformat() if task_msg.created_at else now_iso, ) project_id = str(task.project_id) + task_data = task_msg_out.model_dump() + task_data["project_id"] = project_id await manager.broadcast_message( project_id, - task_msg_out.model_dump(), - author_id=None, # system has no member_id + task_data, + author_id=None, ) # Broadcast chat message @@ -177,7 +179,9 @@ async def _system_message( attachments=[], created_at=chat_msg.created_at.isoformat() if chat_msg.created_at else now_iso, ) - await manager.broadcast_message(project_id, chat_msg_out.model_dump(), author_id=None) + chat_data = chat_msg_out.model_dump() + chat_data["project_id"] = project_id + await manager.broadcast_message(project_id, chat_data, author_id=None) async def _get_task(task_id: str, db: AsyncSession) -> Task: diff --git a/src/tracker/ws/handler.py b/src/tracker/ws/handler.py index 9ce72d8..08aae1b 100644 --- a/src/tracker/ws/handler.py +++ b/src/tracker/ws/handler.py @@ -317,12 +317,22 @@ async def _handle_chat_send(session_id: str, data: dict): msg_data = _to_message_out(msg, member).model_dump() + # Resolve project_id and inject into message data project_id = None if chat_id: chat_result = await db.execute(select(Chat).where(Chat.id == uuid.UUID(chat_id))) chat = chat_result.scalar_one_or_none() if chat and chat.project_id: project_id = str(chat.project_id) + elif task_id: + from ..models import Task as TaskModel + task_result = await db.execute(select(TaskModel).where(TaskModel.id == uuid.UUID(task_id))) + task_obj = task_result.scalar_one_or_none() + if task_obj: + project_id = str(task_obj.project_id) + + if project_id: + msg_data["project_id"] = project_id elif chat and chat.kind == ChatKind.LOBBY: await manager.broadcast_all( {"type": WSEventType.MESSAGE_NEW, "data": msg_data}, diff --git a/src/tracker/ws/manager.py b/src/tracker/ws/manager.py index d8b80d8..2092ded 100644 --- a/src/tracker/ws/manager.py +++ b/src/tracker/ws/manager.py @@ -93,6 +93,9 @@ class ConnectionManager: - If chat_listen == "mentions": only if member_id in mention IDs - System messages: only if member_id in mention IDs """ + # Always inject project_id into message data + message["project_id"] = project_id + raw_mentions = message.get("mentions", []) # Extract member IDs from mentions (objects or strings) mention_ids: set[str] = set() @@ -148,6 +151,8 @@ class ConnectionManager: async def broadcast_task_event(self, project_id: str, event_type: str, data: dict): """Broadcast task events. Humans get everything, agents filtered.""" + # Always inject project_id + data["project_id"] = project_id assignee_id = data.get("assignee_id") reviewer_id = data.get("reviewer_id") watcher_ids = data.get("watcher_ids", [])