From 39d3d6d4ba18510e0c469a706655da41b808aa3a Mon Sep 17 00:00:00 2001 From: Markov Date: Tue, 17 Mar 2026 11:48:54 +0100 Subject: [PATCH] docs: events migration plan --- migration-events.md | 131 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 migration-events.md diff --git a/migration-events.md b/migration-events.md new file mode 100644 index 0000000..e033046 --- /dev/null +++ b/migration-events.md @@ -0,0 +1,131 @@ +# Миграция: 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