87 lines
5.5 KiB
Markdown
87 lines
5.5 KiB
Markdown
# Требования к схеме БД — ORCH-087
|
||
|
||
Все изменения — **строго аддитивные и идемпотентные** (`CREATE TABLE IF NOT EXISTS`
|
||
/ `_ensure_column`), restart-safe на живой ОБЩЕЙ прод-БД (SQLite). Данные
|
||
enduro-trails не трогаются. Существующие колонки/таблицы не ломаются. Точка врезки —
|
||
`src/db.py::init_db` (рядом с прочими `_ensure_column`/`executescript`).
|
||
|
||
---
|
||
|
||
## 1. Колонка `agent_runs.effort` (BR-EFF, обязательно)
|
||
|
||
```python
|
||
_ensure_column(conn, "agent_runs", "effort", "TEXT")
|
||
```
|
||
|
||
- Тип `TEXT`, nullable. Хранит РЕАЛЬНО ушедшее в `--effort` значение
|
||
(`low|medium|high|xhigh|max`) или `NULL`, если флаг не подавался (резолв вернул "").
|
||
- Заполняется в `launcher._spawn` сразу после `resolve_agent_effort(agent,
|
||
project_id)` через `UPDATE agent_runs SET effort=? WHERE id=run_id`
|
||
(`effort or None`).
|
||
- Читается в `render_task_tracker` (добавить `effort` в SELECT `agent_runs`).
|
||
- Исторические строки (до миграции) → `effort IS NULL` → суффикс эффорта в карточке
|
||
опускается; допустим fallback на `resolve_agent_effort(run["agent"])`.
|
||
- Идемпотентность: `_ensure_column` — no-op при уже существующей колонке (AC-E.1,
|
||
TC-09).
|
||
|
||
## 2. Таблица-леджер `tracker_messages` (BR-G1, вариант A1 ADR-001)
|
||
|
||
```sql
|
||
CREATE TABLE IF NOT EXISTS tracker_messages (
|
||
task_id INTEGER NOT NULL,
|
||
message_id INTEGER NOT NULL,
|
||
created_at TEXT DEFAULT (datetime('now')),
|
||
deleted_at TEXT, -- NULL = карточка ещё жива (незакрыта)
|
||
PRIMARY KEY (task_id, message_id)
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_tracker_messages_open
|
||
ON tracker_messages(task_id) WHERE deleted_at IS NULL;
|
||
```
|
||
|
||
- Авторитетный учёт ВСЕХ созданных карточек задачи; `deleted_at IS NULL` ⇔ карточка
|
||
считается живой и подлежит зачистке на следующем bump.
|
||
- Логический FK на `tasks.id` без `REFERENCES` (зеркалит `jobs.task_id`/`job_deps`) —
|
||
миграция не падает на pre-existing БД.
|
||
- Частичный индекс `WHERE deleted_at IS NULL` — дешёвая выборка незакрытых mid в
|
||
горячем пути рендера/зачистки.
|
||
- `PRIMARY KEY (task_id, message_id)` — идемпотентность INSERT (повторный mid не
|
||
дублируется); защита от двойного учёта при гонке.
|
||
|
||
**Новые геттеры/сеттеры в `src/db.py` (предложение, точная сигнатура — за разработчиком):**
|
||
|
||
| Функция | Назначение |
|
||
|---------|-----------|
|
||
| `add_tracker_message(task_id, message_id)` | INSERT нового mid (после успешного `send`). `INSERT OR IGNORE` для идемпотентности. |
|
||
| `get_open_tracker_messages(task_id) -> list[int]` | Все `message_id` с `deleted_at IS NULL`. |
|
||
| `mark_tracker_message_deleted(task_id, message_id)` | `UPDATE … SET deleted_at=datetime('now')` для успешно удалённых / «already gone». |
|
||
|
||
Контракт — как у существующих хелперов БД (never-raise по месту вызова в
|
||
notifications: ошибка БД не валит конвейер).
|
||
|
||
### Сосуществование со скаляром `tasks.tracker_message_id`
|
||
|
||
- `tasks.tracker_message_id` **СОХРАНЯЕТСЯ** без изменения семантики — указатель на
|
||
ТЕКУЩУЮ карточку (читатели `get_tracker_message_id`/`set_tracker_message_id` не
|
||
трогаются). Обратная совместимость полная.
|
||
- Леджер `tracker_messages` — НАДмножество: источник истины для зачистки сирот.
|
||
- Одноразовый бэкфилл скаляра в леджер **не требуется** (старые сироты всё равно за
|
||
48ч-окном Telegram). Новый поток ведёт леджер с нуля.
|
||
|
||
## 3. Что НЕ меняется
|
||
|
||
- `tasks` (кроме отсутствия изменений — скаляр сохранён), `jobs`, `events`,
|
||
`job_deps`, прочие колонки `agent_runs` (`model`, токены, cost, exit_code) — без
|
||
изменений.
|
||
- Никаких `DROP`/`ALTER … DROP`/переименований/перетипизаций (SQLite-небезопасно на
|
||
живой БД).
|
||
- `STAGE_TRANSITIONS` / `QG_CHECKS` — вне зоны БД, не затрагиваются.
|
||
|
||
## 4. Идемпотентность и restart-safety (проверка)
|
||
|
||
- Двойной вызов `init_db` → без ошибок (`IF NOT EXISTS` / `_ensure_column` no-op) —
|
||
TC-09.
|
||
- Леджер переживает рестарт орка: незакрытые mid читаются из БД → следующий bump
|
||
подчищает старые карточки (TC-05, AC-1.3).
|
||
- Миграция на БД с существующими данными enduro: только добавляет колонку/таблицу,
|
||
данные нетронуты (AC-X.5).
|