132 lines
5.5 KiB
Markdown
132 lines
5.5 KiB
Markdown
# Миграция: 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
|