6.3 KiB
6.3 KiB
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; leafestimator/вызывающие оборачивают в 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) оставляет таблицу пустой/неиспользуемой — безвредно.