---
work_item: ORCH-116
stage: architecture
author_agent: architect
status: proposed
created_at: 2026-06-16
model_used: claude-opus-4-8
---

# 08 — Требования к данным: ORCH-116 — детерминированный test-раннер

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

## 1. Изменения схемы БД — НЕТ (NFR-1)

Новых таблиц / колонок / индексов / миграций **нет**. Раннер использует **существующие** структуры:

| Структура | Использование | Запись? |
|-----------|---------------|---------|
| `tasks` (`stage`, `branch`, `work_item_id`) | резолв полей задачи по `task_id`; гард стадии `testing` в `should_intercept`; продвижение/откат — через **существующий** `advance_stage` (он же пишет стадию под transition-lease ORCH-114) | раннер сам стадию **не** пишет — только через `advance_stage` |
| `jobs` (`status`, `task_content`) | `mark_job(done\|failed)` для строки джоба (через `_run_test_runner_job`); restart-safe счётчик tool-error DEFER — `COUNT(*)` по маркеру `test-runner infra-retry` в `task_content` re-queued джоба | да (`mark_job`, `enqueue_job` — существующие API) |
| `agent_runs` | **НЕ создаётся** — детерминированный раннер не спавнит LLM (happy-path без `_spawn`) | нет |
| `transition_lease` (ORCH-114) | берётся/освобождается **внутри** `advance_stage` на side-effectful переходе | раннер **не трогает** |

## 2. Restart-safe счётчик DEFER — без колонки (зеркало ORCH-115/110)

Бюджет tool-error DEFER (D5) считается **из persisted очереди `jobs`** подсчётом маркера
`test-runner infra-retry` в `task_content` re-queued джоба (зеркало
`staging_runner._infra_retry_count` / `stage_engine._merge_infra_retry_count`). Это переживает
рестарт сервиса **без** новой колонки/таблицы — намеренно, ради NFR-1 (схема БД байт-в-байт).

## 3. Артефакт `13-test-report.md` — контракт frontmatter неизменен (AC-2)

Раннер пишет тот же файл с тем же machine-key, что читает гейт:
- `result: PASS|FAIL` (UPPERCASE) — канонический ключ `_parse_tests_verdict` (`src/qg/checks.py:265`);
  имя/регистр/токены **не меняются**.
- Обязательная 52c-схема: `work_item` / `stage: testing` / `author_agent: test-runner` /
  `status: success|failed` / `created_at` / `model_used: n/a`.
- **Инвариант D6.1 (данные):** `status:` **читается** тем же парсером (`verdict:`/`status:`/`result:`,
  negative-token-priority) → значение `status:` **обязано** быть выровнено по вердикту
  (`success`↔PASS, `failed`↔FAIL); иначе негативный токен в `status:` при `result: PASS` исказит
  вердикт. Это требование к **значению данных**, не к схеме.

## 4. Счётчики наблюдаемости — in-process, не БД

Блок `test_runner` в `GET /queue` питается **in-process** счётчиками `_TEST_RUNNER_COUNTERS`
(`runs`/`pass`/`fail`/`tool_error`/`deferred`) — паттерн `_STAGING_RUNNER_COUNTERS`/
`_MERGE_GATE_COUNTERS`. В БД **не** персистятся (обнуляются при рестарте — приемлемо для
оперативной наблюдаемости).

## 5. Вывод

Требований к изменению данных/схемы **нет**. Совместимость с общей БД (self-hosting + enduro-trails)
сохранена: аддитивных объектов не вводится, существующие read/write идут через существующие API.
