150 lines
9.0 KiB
YAML
150 lines
9.0 KiB
YAML
work_item: ORCH-020
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-17
|
||
model_used: claude-opus-4-8
|
||
title: "Оценка задачи, запускаемая Plane-статусом «Оценка»: триггер/возврат в Backlog/массовость/пере-оценка + прогноз {токены,время,стоимость,story points}, запись в Plane, карточка, леджер прогноз↔факт, leaf-инварианты"
|
||
framework: pytest
|
||
scope: >
|
||
Покрывается: распознавание статуса «Оценка» как триггера (handle_estimate),
|
||
fail-closed при отсутствии статуса, авто-возврат issue в Backlog + анти-loop,
|
||
анти-disruption in-flight (no-op при активном job), массовость (N вебхуков -> N оценок),
|
||
идемпотентная пере-оценка (UPSERT по work_item_id), расчёт прогноза из истории (usage-агрегаты),
|
||
маппинг величин -> story-point bucket {1,2,3,5,8} (чистая функция), never-raise/bootstrap при
|
||
пустой истории, запись прогноза в estimate_point и факта в point (через guard ORCH-117, fail-safe
|
||
при отсутствии estimate-конфига), пункт "Оценка" в Telegram-карточке, read-only блок estimator в
|
||
GET /queue, аддитивная таблица task_estimates (ключ work_item_id, task_id нуллабелен),
|
||
kill-switch + скоуп (пусто -> self-hosting only).
|
||
Вне покрытия: адаптивный выбор модели (Шаг 2, вне объёма), авто-уточнение модели оценки (ORCH-8),
|
||
автопереключение трека по сложности (ORCH-19).
|
||
notes: >
|
||
Тесты используют изолированную временную SQLite-БД (фикстура init_db во временном файле) и
|
||
замоканные plane_sync/notifications/usage/get_project_states — без сети, без боевого Plane/Telegram,
|
||
без LLM. Триггер тестируется на уровне handle_issue_updated/handle_estimate с подставленными
|
||
proj_states (UUID статуса "Оценка"). Запись в Plane проверяется на уровне вызова write-хелперов под
|
||
guard (ORCH-117 autouse-floor conftest держит opt-in OFF — сетевая запись физически невозможна из
|
||
теста). Control-path анти-регресс: STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict/схемы
|
||
существующих таблиц не меняются; полный регресс tests/ остаётся зелёным.
|
||
|
||
tests:
|
||
- id: TC-01
|
||
type: integration
|
||
description: "Триггер: new_state == proj_states['estimate'] -> handle_estimate вызывается; estimate-статус добавлен в _PLANE_NAME_TO_KEY как 'Оценка'->'estimate' (AC-T1)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-02
|
||
type: integration
|
||
description: "Fail-closed: 'estimate' отсутствует в _DEFAULT_STATES; на проекте без статуса proj_states.get('estimate') is None -> ветка инертна, handle_estimate не зовётся, нет KeyError (AC-T5)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-03
|
||
type: integration
|
||
description: "handle_estimate на backlog-issue (нет pipeline-задачи): прогноз вычислен, записан, затем set_issue_backlog -> issue возвращён в Backlog (AC-T1, AC-T2)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-04
|
||
type: integration
|
||
description: "Анти-disruption: issue с активным job (has_active_job_for_task=True) -> handle_estimate no-op + лог, оценка не запускается, статус не меняется (AC-T6)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-05
|
||
type: integration
|
||
description: "Анти-loop: возврат в Backlog не алиасит триггер-ветки (Backlog-UUID != estimate/stop/to_analyse/confirm_deploy/approved/rejected) -> входящий 'state->Backlog' webhook = no-op-эхо (AC-T6)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-06
|
||
type: integration
|
||
description: "Массовость: N issue.updated со state='Оценка' -> N независимых вызовов handle_estimate, каждый даёт прогноз; один webhook не гасит остальные (AC-T3)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-07
|
||
type: integration
|
||
description: "Идемпотентная пере-оценка: повторный перевод в 'Оценка' -> UPSERT по work_item_id обновляет одну строку task_estimates и estimate_point, не дублирует (AC-T4)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-08
|
||
type: unit
|
||
description: "estimate() возвращает {forecast_tokens,forecast_seconds,forecast_cost_usd,story_points}, story_points в {1,2,3,5,8} (AC-1)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-09
|
||
type: unit
|
||
description: "Маппинг величин -> story-point bucket: точная семантика 1/2/3/5/8 на граничных входах (AC-2)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-10
|
||
type: unit
|
||
description: "Пустая история -> bootstrap-дефолт, не исключение; estimate() never-raise при битых данных (AC-1, AC-9)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-11
|
||
type: unit
|
||
description: "Расчёт факта на done из usage-агрегатов (токены/время/стоимость) маппится в story-point bucket (AC-4)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-12
|
||
type: integration
|
||
description: "Прогноз пишется в estimate_point через set_issue_estimate_point; факт — в point через set_issue_point; поля не перепутаны, прогноз не затирается (AC-3, AC-4)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-13
|
||
type: integration
|
||
description: "Telegram-карточка содержит пункт 'Оценка' (время/токены/стоимость); пустой прогноз -> пункт опускается, карточка не падает (AC-5)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-14
|
||
type: integration
|
||
description: "Plane-коммент с прогнозом постится через add_comment (best-effort) (AC-6)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-15
|
||
type: unit
|
||
description: "kill-switch estimator_enabled=false -> модуль инертен (handle_estimate no-op, нет записей в Plane/карточку/таблицу); applies() локален и first (AC-9)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-16
|
||
type: unit
|
||
description: "Скоуп estimator_repos пуст -> активен только self-hosting orchestrator; enduro-trails -> no-op (AC-9)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-17
|
||
type: integration
|
||
description: "GET /queue содержит read-only блок estimator (флаг/скоуп/счётчики прогнозов/записей/возвратов); existing-поля не меняются (AC-9)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-18
|
||
type: unit
|
||
description: "Аддитивная таблица task_estimates: CREATE TABLE IF NOT EXISTS идемпотентна; record_estimate/set_actual/get_estimate хранят прогноз+факт+дельту с ключом work_item_id (task_id нуллабелен); существующие таблицы не изменены (AC-12)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-19
|
||
type: integration
|
||
description: "fail-safe записи в Plane: estimate-система не настроена -> set_issue_estimate_point/point best-effort пропуск + лог, без падения; авто-возврат в Backlog всё равно отрабатывает (AC-12, AC-T2, NFR-7)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|
||
|
||
- id: TC-20
|
||
type: unit
|
||
description: "Анти-регресс control-path: STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict-ключи, resolve_agent_model/resolve_agent_effort не изменены; статус 'Оценка' не добавлен как ребро стадий (AC-10, AC-11)"
|
||
module: tests/test_orch020_estimator.py
|
||
expected: PASS
|