5.5 KiB
5.5 KiB
Миграция: messages → events
Цель
Единая таблица events вместо messages + task_actions. Один event = одно действие в системе.
Новая схема
Таблица events
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES projects(id),
type VARCHAR(50) NOT NULL, -- см. типы ниже
actor_id UUID REFERENCES members(id), -- кто сделал
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, -- nullable
parent_id UUID REFERENCES events(id), -- для тредов
payload JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_events_project ON events(project_id, created_at);
CREATE INDEX idx_events_task ON events(task_id, created_at);
CREATE INDEX idx_events_type ON events(type);
Таблица attachments (без изменений, переименуем FK)
ALTER TABLE attachments RENAME COLUMN message_id TO event_id;
Типы событий
| type | payload | Где видно |
|---|---|---|
chat_message |
{content, mentions?, thinking?, voice_url?} |
Проектный чат |
task_comment |
{content, mentions?, thinking?, tool_log?} |
Комментарии задачи |
task_created |
{title, status, priority, assignee?} |
Чат + задача |
task_status |
{from, to} |
Чат + задача |
task_assigned |
{assignee, previous?} |
Чат + задача |
task_unassigned |
{previous} |
Чат + задача |
task_updated |
{field, from, to} |
Задача |
task_label_add |
{label} |
Задача |
task_label_remove |
{label} |
Задача |
Что удаляем
- Таблица
messages→ замененаevents - Таблица
task_actions→ замененаevents(type = task_*) - Таблица
chats→ удаляем (project_id в events заменяет chat routing) - Колонка
author_type→ заменена наtype+actor_id(system events = actor_id NULL)
Что НЕ меняем
tasks,projects,members,agent_configs,labels,steps,project_files,task_labels,task_links
План миграции (6 этапов)
Этап 1: Backend — модели
- Создать
models/event.pyс Event model - Удалить
models/message.py,models/task_action.py - Удалить
models/chat.py - Обновить
__init__.py— импорты
Этап 2: Backend — API
- Переписать
api/messages.py→api/events.pyPOST /api/v1/events— создать event (chat_message, task_comment)GET /api/v1/events?project_id=X&types=chat_message,task_status,...&limit=N— лента проектаGET /api/v1/events?task_id=X— лента задачи
- Обновить
api/tasks.py— создавать events вместо messages + task_actions- Одна запись вместо двух (task comment + chat message → один event)
- Обновить
api/schemas.py— EventOut, EventCreate - Удалить эндпоинты
/messages(или оставить как alias)
Этап 3: Backend — WebSocket
- WS broadcast:
event.newвместоmessage.new - Streaming:
agent.stream.*— без изменений (task_id привязка) - Обновить
ws/manager.py— broadcast по project_id (уже есть)
Этап 4: Backend — Picogent tools
send_messagetool → отправляетPOST /api/v1/eventsс type=task_commentlist_messagestool →GET /api/v1/events?task_id=X- Обновить TrackerClient в picogent
Этап 5: Frontend
lib/api.ts— новые типы Event, эндпоинтыChatPanel.tsx— рендерит events вместо messageschat_message→ обычное сообщениеtask_status→ системное "TE-4: backlog → done"task_assigned→ системное "TE-4: назначена на @coder"task_created→ системное "TE-4 создана: ..."
TaskModal.tsx— комментарии из events (type=task_comment + task_status + ...)MentionInput.tsx— без изменений- WS: слушать
event.newвместоmessage.new - Streaming — без изменений
Этап 6: База данных
- DROP TABLE messages, task_actions, chats
- CREATE TABLE events (через SQLAlchemy create_all при dev start)
- Seed: создать тестовый проект заново
Запросы для фронта
Проектный чат:
GET /api/v1/events?project_id=X&types=chat_message,task_created,task_status,task_assigned,task_unassigned&limit=30
Комментарии задачи:
GET /api/v1/events?task_id=X&limit=50
Timeline проекта (всё):
GET /api/v1/events?project_id=X&limit=100
Оценка
- Backend: ~4 часа
- Frontend: ~2 часа
- Тесты: ~1 час
- Итого: ~1 день
Риски
- Bridge (Telegram) — нужно обновить, он создаёт messages через API
- Picogent — нужно обновить send_message/list_messages tools
- Тесты — переписать test_chat.py, test_messages.py