128 lines
8.5 KiB
Markdown
128 lines
8.5 KiB
Markdown
---
|
||
work_item: ORCH-099
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-10
|
||
model_used: claude-opus-4-8
|
||
---
|
||
|
||
# 03 — Критерии приёмки (Acceptance Criteria): ORCH-099 — FND/F1a: лёгкий `/metrics` в орке
|
||
|
||
Work Item: **ORCH-099** · Repo: **orchestrator** · Стадия: analysis
|
||
|
||
Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL**
|
||
(что считается провалом). Reviewer/tester проверяет их буквально по файлам репозитория и по ответу
|
||
эндпоинта.
|
||
|
||
---
|
||
|
||
## AC-1 — Эндпоинт `/metrics` отдаёт четыре раздела сырья
|
||
|
||
**Условие:** `GET /metrics` возвращает `200` и JSON с разделами `stages`, `queue`, `agents`, `cost`
|
||
(плюс конверт `schema_version` / `generated_at`), с полями из TRZ §3.
|
||
- **PASS:** ответ — валидный JSON-объект; присутствуют ключи `schema_version`, `generated_at`,
|
||
`stages` (список; элемент содержит `work_item`, `stage`, `age_in_stage_s`, `repo`), `queue`
|
||
(содержит `counts`, `breaker`, `max_concurrency`, сырьё ретраев), `agents` (список; элемент
|
||
содержит `agent`, `run_id`, `pid`, `runtime_s` и поле сырья CPU-liveness), `cost` (содержит
|
||
`aggregate` с суммами `cost_usd`/`input_tokens`/`output_tokens`/`cache_read_tokens`/
|
||
`cache_creation_tokens`).
|
||
- **FAIL:** отсутствует любой из четырёх разделов; в `agents` нет `pid`/`runtime_s`; в `stages` нет
|
||
«как давно в стадии»; в `cost` нет агрегата токенов/стоимости; ответ не JSON или статус ≠ 200.
|
||
|
||
---
|
||
|
||
## AC-2 — Аддитивность: `/health`, `/status`, `/queue` не сломаны
|
||
|
||
**Условие:** существующие эндпоинты сохраняют прежний контракт.
|
||
- **PASS:** `GET /health` → `{"status":"ok", ...}`; `GET /status` → `{"active_tasks":[...]}`;
|
||
`GET /queue` отдаёт прежний набор ключей; существующие тесты эндпоинтов (`tests/test_queue_endpoint.py`
|
||
и пр.) зелёные без модификации их ожиданий.
|
||
- **FAIL:** изменён/удалён любой существующий ключ ответа `/health`/`/status`/`/queue`; пришлось
|
||
править существующие тесты под новый контракт; регресс в этих эндпоинтах.
|
||
|
||
---
|
||
|
||
## AC-3 — Лёгкость и быстрая выборка
|
||
|
||
**Условие:** эндпоинт лёгкий — только быстрые локальные SQL + чтение in-memory снапшотов, без
|
||
тяжёлых вычислений и сетевых вызовов.
|
||
- **PASS:** в коде `src/metrics.py` нет сетевых вызовов (HTTP/Plane/Gitea/Anthropic), нет запуска
|
||
подпроцессов кроме безопасного чтения `/proc/<pid>/stat`, нет сканирования git/файлового дерева;
|
||
данные берутся из существующих helper'ов БД и `worker`-снапшота; на типовом объёме ответ
|
||
формируется без заметной задержки.
|
||
- **FAIL:** эндпоинт делает сетевой запрос, запускает агента/тяжёлый процесс, сканирует worktree/git
|
||
или выполняет дорогие агрегаты, заметно тормозящие ответ.
|
||
|
||
---
|
||
|
||
## AC-4 — Never-raise (ошибка поля → `null`, эндпоинт не падает)
|
||
|
||
**Условие:** любая ошибка сбора отдельного поля/раздела не роняет эндпоинт.
|
||
- **PASS:** при недоступном источнике (например, `worker` не инициализирован, `pid` уже мёртв,
|
||
`/proc/<pid>` отсутствует, пустые таблицы) соответствующее поле получает `null`/безопасный дефолт,
|
||
а `GET /metrics` всё равно возвращает `200` и валидный JSON; есть тест, симулирующий сбой раздела
|
||
и проверяющий 200 + `null` в этом поле.
|
||
- **FAIL:** при любом из перечисленных условий эндпоинт возвращает `500` / бросает исключение /
|
||
возвращает невалидный JSON.
|
||
|
||
---
|
||
|
||
## AC-5 — Read-only (ничего не меняет; гейты/схема не тронуты)
|
||
|
||
**Условие:** эндпоинт и модуль строго read-only; конвейерные инварианты целы.
|
||
- **PASS:** `src/metrics.py` и обработчик `/metrics` не выполняют `INSERT`/`UPDATE`/`DELETE`/`CREATE`/
|
||
`ALTER`, не запускают/останавливают процессы, не рестартят, не мутируют состояние демонов;
|
||
`STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, machine-verdict ключи и схема БД (`tasks`/`jobs`/
|
||
`agent_runs` и пр.) — без изменений в диффе; повторный вызов `/metrics` не меняет состояние БД
|
||
(тест: снимок БД до/после идентичен).
|
||
- **FAIL:** дифф трогает `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/схему/machine-verdict; модуль
|
||
выполняет любую запись/мутацию; вызов эндпоинта меняет состояние.
|
||
|
||
---
|
||
|
||
## AC-6 — agent-liveness содержит сырьё для alive-детекта
|
||
|
||
**Условие:** по каждому running-job отдаётся идентификация процесса и сырьё для CPU-детекта
|
||
sidecar'ом.
|
||
- **PASS:** для running-job ответ содержит `agent`, `run_id`, `pid`, `runtime_s` и поле сырья
|
||
CPU-liveness (например `cpu_ticks` из `/proc/<pid>/stat` + базис тиков `clk_tck`, либо
|
||
эквивалент по решению ADR), позволяющее внешнему наблюдателю посчитать CPU-дельту между опросами;
|
||
при `pid is None`/мёртвом процессе CPU-поле = `null` (см. AC-4), прочие поля целы.
|
||
- **FAIL:** liveness-раздел не позволяет sidecar'у отличить «жив» от «завис» (нет ни CPU-сырья, ни
|
||
pid+runtime); отсутствуют `run_id`/`pid`; обращение к мёртвому pid роняет эндпоинт.
|
||
|
||
---
|
||
|
||
## AC-7 — Контракт задокументирован (для sidecar F1b) + CHANGELOG
|
||
|
||
**Условие:** формат `/metrics` зафиксирован как контракт и отражён в журнале изменений.
|
||
- **PASS:** в `docs/architecture/README.md` описан формат ответа `/metrics` (разделы, поля,
|
||
`schema_version`) как стабильный контракт для sidecar (F1b); в `CHANGELOG.md` есть запись
|
||
`## [Unreleased]` с пометкой `ORCH-099`.
|
||
- **FAIL:** формат не задокументирован или описан только в коде; нет записи в `CHANGELOG.md`;
|
||
документация противоречит фактическому ответу эндпоинта.
|
||
|
||
---
|
||
|
||
## AC-8 — pytest зелёный
|
||
|
||
**Условие:** новый тест-набор и полный регресс проходят.
|
||
- **PASS:** `pytest tests/ -q` зелёный; присутствует `tests/test_metrics.py`, покрывающий структуру
|
||
ответа (AC-1), never-raise (AC-4), read-only (AC-5), liveness-сырьё (AC-6) и аддитивность (AC-2).
|
||
- **FAIL:** любой тест красный; новые тесты отсутствуют или не покрывают перечисленные критерии.
|
||
|
||
---
|
||
|
||
## Сводная матрица AC ↔ FR/BR
|
||
| AC | Покрывает |
|
||
|----|-----------|
|
||
| AC-1 | BR-1/BR-2/BR-3/BR-5 / FR-1…FR-5 |
|
||
| AC-2 | BR-6 / FR-4 |
|
||
| AC-3 | NFR-3 / FR-6 |
|
||
| AC-4 | NFR-2 / FR-6 |
|
||
| AC-5 | NFR-1/NFR-4/NFR-5 / FR-5 |
|
||
| AC-6 | BR-4 / FR-3 |
|
||
| AC-7 | BR-7 / FR-5 |
|
||
| AC-8 | NFR-3 (валидация) / все FR |
|