Files
orchestrator/docs/work-items/ORCH-020/08-data-requirements.md
claude-bot 6c204548a7
All checks were successful
CI / test (push) Successful in 1m14s
architect(ET): auto-commit from architect run_id=798
2026-06-17 21:16:50 +03:00

6.3 KiB
Raw Blame History

work_item, stage, author_agent, status, created_at, model_used
work_item stage author_agent status created_at model_used
ORCH-020 architecture architect proposed 2026-06-17 claude-opus-4-8

08 — Требования к данным: ORCH-020 — Оценка задачи, запускаемая статусом «Оценка»

Work Item: ORCH-020 · Repo: orchestrator · Стадия: architecture

When-applicable / информационный (гейтом не парсится). Одна новая аддитивная таблица; существующие таблицы (tasks/agent_runs/jobs/…) — не изменяются (NFR-8).

Изменения схемы БД

Новая аддитивная таблица task_estimates (CREATE TABLE IF NOT EXISTS в db.init_db(), паттерн coverage_baseline/lessons/transition_lease; идемпотентно, restart-safe на общей прод-БД):

CREATE TABLE IF NOT EXISTS task_estimates (
    id                    INTEGER PRIMARY KEY AUTOINCREMENT,
    work_item_id          TEXT NOT NULL UNIQUE,   -- ключ/UPSERT-цель (issue может не иметь task на момент оценки)
    task_id               INTEGER,                -- FK tasks.id; НУЛЛАБЕЛЕН до старта пайплайна
    repo                  TEXT,
    -- Прогноз (на момент перевода в «Оценка»):
    forecast_tokens       INTEGER,
    forecast_seconds      INTEGER,
    forecast_cost_usd     REAL,
    forecast_story_points INTEGER,                -- из {1,2,3,5,8}
    -- Факт (на момент перехода задачи в `done`):
    actual_tokens         INTEGER,
    actual_seconds        INTEGER,
    actual_cost_usd       REAL,
    actual_story_points   INTEGER,                -- из {1,2,3,5,8}
    -- Метаданные:
    source                TEXT,                   -- 'status' | 'manual' | 'api'
    estimate_count        INTEGER NOT NULL DEFAULT 1,  -- число пере-оценок (инкремент при UPSERT)
    created_at            TEXT NOT NULL DEFAULT (datetime('now')),
    updated_at            TEXT
);
CREATE INDEX IF NOT EXISTS idx_task_estimates_repo    ON task_estimates (repo);
CREATE INDEX IF NOT EXISTS idx_task_estimates_task_id ON task_estimates (task_id);
  • UNIQUE(work_item_id) — несущий инвариант идемпотентной пере-оценки (BR-T4/AC-T4): повторный перевод в «Оценка» делает UPSERT (INSERT … ON CONFLICT(work_item_id) DO UPDATE …), обновляя одну строку и инкрементируя estimate_count; дублей строк нет.
  • Дельта прогноз↔факт не хранится отдельной колонкой — вычисляется на чтение из forecast/actual (избегаем рассинхрона; калибровке достаточно обеих величин). При желании реализатор может добавить материализованные delta_* — не обязательно (BR-10 требует «обе величины + дельту»; вычисляемая дельта это удовлетворяет).
  • Индексы: по repo (выборка/снапшот по проекту) и task_id (связь с задачей). По work_item_id индекс создаётся автоматически (UNIQUE).

Новые/изменённые сущности

  • Хелперы db.py (каждый открывает/закрывает свою connection, паттерн coverage_baseline/lessons; leaf estimator/вызывающие оборачивают в never-raise):
    • record_estimate(work_item_id, repo, task_id=None, forecast_*=…, source='status') -> int — UPSERT прогноза по work_item_id; инкремент estimate_count, стамп updated_at.
    • set_actual(work_item_id, actual_tokens, actual_seconds, actual_cost_usd, actual_story_points, task_id=None) -> bool — запись факта; не трогает forecast-поля.
    • get_estimate(work_item_id) -> dict | None — текущая строка прогноз/факт.
    • estimates_snapshot(limit=…) -> dict — read-only для блока estimator в GET /queue.
  • Read-only агрегат истории db.completed_task_stats(repo, track) -> {n, mean_tokens, mean_cost_usd, mean_seconds} — поверх agent_runs (токены/стоимость, как task_usage_summary) и tasks (stage='done', время = updated_at created_at с отсечкой estimator_wall_cap_s). Только чтение существующих таблиц; новых колонок не вводит.

Совместимость данных / миграции

  • Аддитивность (NFR-8): только новая таблица + новые read/write-хелперы; ни одной правки существующих таблиц/колонок/индексов. STAGE_TRANSITIONS/QG_CHECKS/machine-verdict-ключи независимы от данных оценки.
  • Идемпотентность миграции: CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS — no-op на уже созданной таблице; безопасно на живой общей прод-БД (enduro не затронут — таблица общая, но писать в неё будет только self-hosting-скоуп; строки enduro не появляются, пока репо вне estimator_repos).
  • Restart-safe: строки task_estimates переживают рестарт; прогноз, сделанный на бэклоге (с task_id=NULL), сохраняется до старта пайплайна и связывается с task_id позже (best-effort).
  • Влияние на общую прод-БД: таблица малая (одна строка на оценённый issue), индексы лёгкие; нагрузка на hot-path нулевая (claim/queue не читают task_estimates). Откат (ORCH_ESTIMATOR_ENABLED=false) оставляет таблицу пустой/неиспользуемой — безвредно.