# Миграция: messages → events ## Цель Единая таблица `events` вместо `messages` + `task_actions`. Один event = одно действие в системе. ## Новая схема ### Таблица `events` ```sql 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) ```sql 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.py` - `POST /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_message` tool → отправляет `POST /api/v1/events` с type=task_comment - [ ] `list_messages` tool → `GET /api/v1/events?task_id=X` - [ ] Обновить TrackerClient в picogent ### Этап 5: Frontend - [ ] `lib/api.ts` — новые типы Event, эндпоинты - [ ] `ChatPanel.tsx` — рендерит events вместо messages - `chat_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