Files
orchestrator/docs/work-items/ORCH-087/08-data-requirements.md

5.5 KiB
Raw Blame History

Требования к схеме БД — 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, обязательно)

_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)

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).