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

# ADR-001: Багфикс-трек — пропуск стадии `architecture` через track-aware routing override

Work Item: **ORCH-019** — упрощённый/дешёвый трек для багов (укороченный маршрут конвейера)
Стадия: **architecture**
Сквозная регистрация: **`docs/architecture/adr/adr-0032-bug-fast-track.md`** (решение
кросс-каттинговое: новый leaf-компонент + аддитивная колонка `tasks.track` + семантика
маршрутизации, затрагивающая `advance_stage`).

## Статус
Proposed

## Контекст

Любая задача входит в конвейер через `webhooks/plane.py::start_pipeline`, который
**жёстко** создаёт task-row со стадией `"analysis"` (`create_task_atomic(..., "analysis", ...)`)
и режет ветку. Маршрут стадий полностью управляется `src/stages.py::STAGE_TRANSITIONS` через
`get_next_stage` — `advance_stage` (`src/stage_engine.py`) НЕ зашивает порядок, а спрашивает
`get_next_stage(current_stage)` (строка 214) и `get_agent_for_stage(current_stage)` (строка 464).

Для мелкого бага полный цикл `analysis → architecture → development → …` избыточен: стадия
`architecture` = отдельный прогон агента `architect` (opus, дорогой) + ADR + exit-гейт
`check_architecture_done`. Прецедент: UI z-index баг ET-9/ET-014 прошёл полный цикл ~35 мин.

**Корневой инвариант (NFR-1 BRD, нерушимый):** упрощаем только *аналитику/архитектуру*; ни один
Quality Gate / exit-код deploy-хука / под-гейт (security/merge/coverage/image-freshness) — НЕ
ослаблен. Горький урок ET-8: срезанная *проверка* = недоделка на проде.

**Факты, сверенные с кодом:**
- `src/labels.py::has_label` + `plane_sync.fetch_issue_labels`/`get_project_labels` (ORCH-089) —
  готовый, проверенный аппарат чтения метки Plane (TTL-кэш, нормализация, never-raise,
  fail-safe → False, источник истины Plane API, не payload).
- `advance_stage` маршрутизирует через `get_next_stage`/`get_agent_for_stage` → точка ветвления
  локализуема, `STAGE_TRANSITIONS` ломать не нужно.
- `check_analysis_approved` (exit-гейт `analysis`) вызывает `check_analysis_complete`, требующий
  **01/02/03/04** (`src/qg/checks.py:33`). Это и есть точка риска ложной блокировки облегчённого
  пакета (FR-6).
- `_ensure_column` (`src/db.py:334`) — идемпотентная аддитивная миграция (паттерн
  `tasks.cancelled_at`, ORCH-090).

## Решение

### Сводка

Багфикс-трек — **свойство планировщика/точки входа, не Quality Gate**. Задача с меткой Plane
`Bug` помечается в БД как `track='bug'`; на ребре выхода из `analysis` `advance_stage` применяет
**чистый routing-override**: `next_stage` → `development` (вместо `architecture`), `next_agent`
→ `developer` (вместо `architect`). `STAGE_TRANSITIONS`, реестр `QG_CHECKS`, все `check_*` и
вердикт-ключи — **байт-в-байт прежние**. Распознавание, маршрут и метрика — аддитивно, под
kill-switch, с областью репо, never-raise, fail-safe → полный цикл.

### D1 — Классификация: метка Plane `Bug`, читаемая в `start_pipeline` (FR-1, AC-1)

Новый leaf `src/bug_fast_track.py` (пустой импорт-граф как `serial_gate`/`labels`: только
`config`, лениво `labels`/`plane_sync`/`qg.checks`), never-raise. Публичные функции:
- `bug_fast_track_applies(repo) -> bool` — локальный, без сети, по образцу `_auto_label_applies`:
  `bug_fast_track_enabled=False` → `False`; `bug_fast_track_repos` (CSV) непустой → только
  перечисленные репо; **пусто → self-hosting only** (`is_self_hosting_repo`, см. D6). Проверяется
  **ПЕРВЫМ** → при выключенном флаге нулевой сетевой оверхед, enduro не затронут.
- `is_bug_task(work_item_id, project_id) -> bool` — `bug_fast_track_applies` уже проверен
  вызывающим; делегирует в `labels.has_label(work_item_id, settings.bug_fast_track_label,
  project_id)` (дефолт метки `Bug`). Любая ошибка/неоднозначность → `False` (fail-safe → полный
  цикл).

Чтение метки — **только** в `start_pipeline` (момент старта, сетевой вызов приемлем, как
ORCH-089), **никогда** в горячем `claim_next_job` (NFR-4).

### D2 — Хранение типа: аддитивная колонка `tasks.track` (OQ-2, NFR-4)

Идемпотентная миграция `_ensure_column(conn, "tasks", "track", "TEXT DEFAULT 'full'")` рядом с
`tasks.cancelled_at`/`cancel_requested_at` (`src/db.py` init). Значения: `'full'` (дефолт, ВСЕ
существующие и не-баг задачи) | `'bug'`. Хелперы: `db.set_task_track(task_id, track)` (запись),
`db.get_task_track(task_id) -> str` (чтение, дефолт `'full'`). Тип читается из **БД** в
`advance_stage` (NFR-4: горячий путь без сети). Альтернатива «выводить тип повторным чтением
метки» отвергнута — повторный сетевой вызов в горячем пути запрещён.

`create_task_atomic` НЕ меняет сигнатуру: задача создаётся как `'full'` (DEFAULT), затем
`start_pipeline` после успешного `created=True` при `is_bug_task` вызывает
`db.set_task_track(task_id, 'bug')`. Точка входа стадии остаётся `"analysis"` (мини-анализ
сохраняется, OQ-3/BRD §6 — НЕ чистый hotfix).

### D3 — Routing-override: пропуск `architecture` без правки `STAGE_TRANSITIONS` (FR-2, AC-2)

`get_next_stage`/`get_agent_for_stage` остаются **чистыми** (принимают только стадию, 1:1).
Override живёт в `advance_stage`, сразу после строки `next_stage = get_next_stage(current_stage)`:

```python
next_stage = get_next_stage(current_stage)
# ORCH-019: bug-fast-track skips the architecture stage entirely.
if current_stage == "analysis" and bug_fast_track.skips_architecture(track):
    next_stage = "development"
```

и при запуске следующего агента (строка 464):

```python
next_agent = get_agent_for_stage(current_stage)        # "analysis" -> "architect"
if current_stage == "analysis" and next_stage == "development":
    next_agent = "developer"                            # skip architect run
```

`track` читается один раз в начале `advance_stage` (`db.get_task_track(task_id)`). Чистый
предикат `bug_fast_track.skips_architecture(track) -> bool` (== `track == 'bug'` под
`bug_fast_track_enabled`; иначе `False`). Багфикс-задача физически НЕ попадает в стадию
`architecture` → её exit-гейт `check_architecture_done` и требование `06-adr/` не исполняются для
багфикса. Для не-баг задач (`track='full'`) поведение **байт-в-байт** прежнее.

**Сопутствующая правка телеметрии:** строка 386 стампит `mark_brd_review_ended` при
`analysis → architecture`. Для багфикса next_stage = `development`, поэтому условие расширяется до
`current_stage == "analysis" and next_stage in ("architecture", "development")` — чтобы метрика
«твоё время» (ORCH-087) оставалась честной на багфикс-треке. Не влияет на гейты.

### D4 — Quality Gate `analysis`: НЕ трогаем; lite-пакет эмитит все 4 файла (FR-3/FR-6, OQ-6, AC-3)

**Корневой инвариант диктует минимальную поверхность изменения гейтов = ноль.**
`check_analysis_complete` (требует 01/02/03/04) и `check_analysis_approved` остаются **байт-в-байт
прежними**. Багфикс-аналитик (`analyst.md` lite-режим) всё равно эмитит **все 4** файла, но в
облегчённой багфикс-форме: `01-brd.md` = короткий bug-report (симптом / шаги воспроизведения /
локализация / причина), `02-trz.md` + `03-acceptance-criteria.md` = краткие bug-shaped заглушки,
`04-test-plan.yaml` = план **обязательного регресс-теста** (красный до фикса, зелёный после).

Обоснование выбора: доминирующая экономия — пропуск **всей стадии `architecture`** (отдельный
прогон opus-агента `architect` + ADR), а не число файлов analysis (они эмитятся в ОДНОМ прогоне
analyst-агента). Сохранение 4-файлового гейта = **сильнейшая** позиция NFR-1 (нулевая поверхность
правок гейта) ценой почти нулевого оверхеда. Альтернатива «track-aware `check_analysis_complete`
(для bug требовать только 01/04)» рассмотрена и отвергнута для v1 (D-Alt) — она трогает `check_*`
и расширяет поверхность риска без существенной экономии.

### D5 — Эскалация в полный цикл (FR-5, AC-5)

Два пути возврата сложного/архитектурного/визуального бага в полный цикл, оба сбрасывают
`track='bug'` → `'full'` (после чего `advance_stage` маршрутизирует `analysis → architecture`
штатно):
1. **Операторский (ручной, v1-дефолт):** админ-эндпоинт `POST /bug-fast-track/escalate?work_item=<id>`
   (по образцу `POST /serial-gate/unfreeze`, `POST /coverage/baseline`) — `db.set_task_track(...,
   'full')`, лог + Telegram + Plane-коммент, never-raise. Применять, пока задача в `analysis`
   (до выхода) — тогда следующий переход уйдёт в `architecture`.
2. **Решение мини-аналитика:** если на багфикс-треке аналитик определяет, что баг архитектурный,
   он эмитит **полный** analysis-пакет (включая запрос на `06-adr/`) и помечает в bug-report
   `escalate: full-cycle` — оператор подтверждает эскалацию эндпоинтом (1). v1 НЕ включает
   автоматический LLM-авто-триаж сложности (вне объёма, BRD §2.2).

Эскалация обратима, детерминирована, наблюдаема. Багфикс-задача не «застревает» без архитектуры.

### D6 — Область по умолчанию: self-hosting only (OQ-5, NFR-5)

Пустой `bug_fast_track_repos` → **self-hosting only** (`is_self_hosting_repo`, как
ORCH-089/027/058). Это безопасный дефолт: режим обкатывается на самом орке (где метка `Bug`
гарантированно заводится оператором), enduro подключается явным добавлением в CSV. Флаги
(`config.py`): `bug_fast_track_enabled` (kill-switch, env `ORCH_BUG_FAST_TRACK_ENABLED`),
`bug_fast_track_label` (дефолт `Bug`, env `ORCH_BUG_FAST_TRACK_LABEL`), `bug_fast_track_repos`
(CSV, env `ORCH_BUG_FAST_TRACK_REPOS`).

### D7 — Наблюдаемость стоимости (FR-7, AC-7)

- **`GET /queue`** — аддитивный read-only блок `bug_fast_track` (`bug_fast_track.snapshot()`,
  never-raise, по образцу `serial_gate`/`auto_labels`/`coverage`): `enabled`, `repos`, `label`,
  счётчик задач с `track='bug'`, агрегатная метрика экономии (пропущенные стадии / Σ agent-runs /
  токены / время багфикс-трека против среднего полного цикла из существующей телеметрии
  `agent_runs`). Существующие ключи `GET /queue` не меняются.
- **Лог-строка** на решение о маршруте (`analysis → development (bug-fast-track)`).
- **Опц.** отметка `🐞 багфикс-трек` в Telegram-карточке (`notifications.py`, never-raise).

### D8 — Композиция (NFR-7, AC-9)

- **serial-gate (ORCH-088):** багфикс-задача — обычная задача репо, учитывается в serial-очереди
  как есть (FIFO `t2.id < jobs.task_id`); точка входа `analysis` не меняется, defer-branch логика
  не затронута. Маркированный код `serial_gate.py` НЕ правится.
- **auto-label (ORCH-089):** `autoApprove`/`autoDeploy` работают на багфикс-треке — autoApprove
  врезка в `_handle_analysis_approved_flow` вызывает `advance_stage(finished_agent=None)`, который
  применяет D3-override и уходит в `development`. Переиспользуем `labels.has_label`.
- **coverage-gate (ORCH-027):** союзник BR-4 (структурно ловит «код без теста») — исполняется
  штатно на ребре `deploy-staging → deploy`.
- **merge-gate (ORCH-043):** не затронут.

Правки маркированного кода (`advance_stage` несёт врезки ORCH-088/089/027/059/094) — точечные,
со сверкой их `06-adr/`; зафиксированные инварианты (порядок под-гейтов, merge-lease,
terminal-sync) НЕ нарушаются: ORCH-019 добавляет ветвление ТОЛЬКО на ребре выхода из `analysis`,
до всех deploy-edge под-гейтов.

## Альтернативы

- **Track-aware `get_next_stage(stage, task)` / новая стадия в `STAGE_TRANSITIONS`** — отвергнуто:
  ломает чистоту `stages.py` и риск задеть структуру таблицы (AC-2 FAIL при структурном изменении).
  Override в `advance_stage` локальнее и держит `STAGE_TRANSITIONS` неизменным.
- **Track-aware `check_analysis_complete` (bug → только 01/04)** — отвергнуто для v1 (D-Alt):
  трогает `check_*`, расширяет поверхность риска NFR-1 ради почти нулевой экономии (см. D4).
  Оставлено как возможное будущее уточнение, если потребуется реальный отказ от 02/03.
- **Чистый hotfix `start_pipeline → development`, минуя `analysis`** — отвергнуто как дефолт
  (BRD §6): теряется фиксация регресс-теста как контракта приёмки и трассируемость (урок ET-8).
- **Тип задачи из payload вебхука / повторное чтение метки в `claim_next_job`** — отвергнуто:
  payload не несёт `type` (источник истины — Plane API); сеть в горячем claim запрещена (NFR-4).
- **Чтение типа без БД-колонки** — отвергнуто: потребовало бы сетевого вызова в горячем пути.

## Последствия

- **+** Багфикс минует целую стадию `architecture` (один прогон opus-агента `architect` + ADR) —
  основная экономия токенов/времени; гейты качества **байт-в-байт** сохранены.
- **+** Полностью аддитивно: kill-switch `False` или неприменимый репо → путь старта и маршрут
  идентичны текущим (AC-6, нулевая регрессия для enduro и orchestrator).
- **+** Переиспользует проверенный аппарат ORCH-089 (label-чтение) и паттерн leaf+флаги+snapshot.
- **−** Багфикс-аналитик всё равно эмитит 02/03 (краткие заглушки) ради неизменности гейта —
  принятый компромисс (D4); экономия на их содержании, не на их наличии.
- **−** Эскалация v1 требует операторского действия (эндпоинт) — авто-триаж сложности отложен
  (BRD §2.2). Митигатор: путь эскалации документирован, обратим, наблюдаем (D5).
- **Откат:** `bug_fast_track_enabled=False` (мгновенно, 1:1 прежнее поведение); колонка
  `tasks.track` остаётся (аддитивна, дефолт `'full'`, безвредна). Полный откат — revert PR;
  миграция идемпотентна, остаточная колонка не мешает.

## Ссылки
- BRD: `docs/work-items/ORCH-019/01-brd.md`
- TRZ: `docs/work-items/ORCH-019/02-trz.md`
- Acceptance: `docs/work-items/ORCH-019/03-acceptance-criteria.md`
- Сквозной ADR: `docs/architecture/adr/adr-0032-bug-fast-track.md`
- Data: `docs/work-items/ORCH-019/08-data-requirements.md`
- Infra: `docs/work-items/ORCH-019/07-infra-requirements.md`
- Риски: `docs/work-items/ORCH-019/10-tech-risks.md`
- Сверено по коду: `src/stages.py`, `src/stage_engine.py` (advance_stage:175-477),
  `src/webhooks/plane.py::start_pipeline` (505-684), `src/labels.py`,
  `src/qg/checks.py` (check_analysis_complete:33, check_analysis_approved:286,
  check_architecture_done:62), `src/db.py` (_ensure_column:334, create_task_atomic:433)
</content>
</invoke>
