19 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-019 | architecture | architect | proposed | 2026-06-10 | 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):
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):
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
штатно):
- Операторский (ручной, 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. - Решение мини-аналитика: если на багфикс-треке аналитик определяет, что баг архитектурный,
он эмитит полный analysis-пакет (включая запрос на
06-adr/) и помечает в bug-reportescalate: 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)