feat: system messages for task lifecycle (create, assign, status change)
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s

Tracker now auto-generates system messages visible to all members/agents.
No more side effects in router — agents see system messages and act via tools.
This commit is contained in:
markov 2026-02-24 12:11:43 +01:00
parent 050d672836
commit f7ee6d1a7c

View File

@ -80,6 +80,33 @@ class AssignRequest(BaseModel):
# --- Helpers ---
async def _system_message(db: AsyncSession, task: Task, content: str, project_slug: str = ""):
"""Create a system message for a task and broadcast it via WS."""
from tracker.ws.manager import manager
msg = Message(
task_id=task.id,
author_type="system",
author_slug="system",
content=content,
mentions=[],
)
db.add(msg)
await db.flush() # get msg.id
prefix = project_slug[:2].upper() if project_slug else "XX"
key = f"{prefix}-{task.number}"
await manager.broadcast_task_event(str(task.project_id), "message.new", {
"id": str(msg.id),
"task_id": str(task.id),
"task_key": key,
"author_type": "system",
"author_slug": "system",
"content": content,
"created_at": msg.created_at.isoformat() if msg.created_at else "",
})
def _task_out(t: Task, project_slug: str = "") -> dict:
prefix = project_slug[:2].upper() if project_slug else "XX"
return {
@ -200,13 +227,28 @@ async def create_task(
"created_at": task.created_at.isoformat() if task.created_at else "",
}
await manager.broadcast_task_event(str(project.id), "task.created", task_data)
return _task_out(task_full, task_full.project.slug if task_full.project else "")
# System message
slug = project.slug
key = f"{slug[:2].upper()}-{task.number}"
sys_text = f"Задача {key} создана: {task.title}"
if task.assignee_slug:
sys_text += f". Назначена на @{task.assignee_slug}"
await _system_message(db, task_full, sys_text, slug)
await db.commit()
return _task_out(task_full, slug)
@router.patch("/tasks/{task_id}", response_model=TaskOut)
async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(get_db)):
task = await _get_task(task_id, db)
slug = task.project.slug if task.project else ""
prefix = slug[:2].upper() if slug else "XX"
key = f"{prefix}-{task.number}"
old_status = task.status
old_assignee = task.assignee_slug
if req.title is not None:
task.title = req.title
@ -227,6 +269,15 @@ async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(
if req.position is not None:
task.position = req.position
# System messages for important changes
if req.status is not None and req.status != old_status:
await _system_message(db, task, f"{key}: статус изменён {old_status}{req.status}", slug)
if req.assignee_slug is not None and req.assignee_slug != old_assignee:
if req.assignee_slug:
await _system_message(db, task, f"{key}: назначена на @{req.assignee_slug}", slug)
else:
await _system_message(db, task, f"{key}: исполнитель снят (был @{old_assignee})", slug)
await db.commit()
await db.refresh(task)
@ -240,7 +291,7 @@ async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(
}
await manager.broadcast_task_event(str(task.project_id), "task.updated", task_data)
return _task_out(task, task.project.slug if task.project else "")
return _task_out(task, slug)
@router.delete("/tasks/{task_id}")
@ -271,6 +322,13 @@ async def take_task(task_id: str, slug: str = Query(...), db: AsyncSession = Dep
await db.commit()
await db.refresh(task)
# System message
proj_slug = task.project.slug if task.project else ""
prefix = proj_slug[:2].upper() if proj_slug else "XX"
key = f"{prefix}-{task.number}"
await _system_message(db, task, f"{key}: @{slug} взял задачу в работу", proj_slug)
await db.commit()
# Broadcast task.assigned event
from tracker.ws.manager import manager
task_data = {
@ -281,7 +339,7 @@ async def take_task(task_id: str, slug: str = Query(...), db: AsyncSession = Dep
}
await manager.broadcast_task_event(str(task.project_id), "task.assigned", task_data)
return _task_out(task, task.project.slug if task.project else "")
return _task_out(task, proj_slug)
@router.post("/tasks/{task_id}/reject")
@ -323,6 +381,13 @@ async def assign_task(task_id: str, req: AssignRequest, db: AsyncSession = Depen
await db.commit()
await db.refresh(task)
# System message
proj_slug = task.project.slug if task.project else ""
prefix = proj_slug[:2].upper() if proj_slug else "XX"
key = f"{prefix}-{task.number}"
await _system_message(db, task, f"{key}: назначена на @{req.assignee_slug}", proj_slug)
await db.commit()
# Broadcast task.assigned event
from tracker.ws.manager import manager
task_data = {
@ -333,7 +398,7 @@ async def assign_task(task_id: str, req: AssignRequest, db: AsyncSession = Depen
}
await manager.broadcast_task_event(str(task.project_id), "task.assigned", task_data)
return _task_out(task, task.project.slug if task.project else "")
return _task_out(task, proj_slug)
@router.post("/tasks/{task_id}/watch")