Files
orchestrator/docs/history/LESSONS_ORCH-017.md
2026-06-05 19:49:50 +00:00

8.6 KiB
Raw Permalink Blame History

Lessons Learned — ORCH-017 (Telegram approve-ping links)

Дата: 2026-06-05

Задача: ORCH-017 — Прямые ссылки на BRD и Plane-таску в Telegram-уведомлении об апруве BRD

Итог: смержено вручную (PR #37, merge 26c6f267) после ~5 застреваний конвейера


TL;DR

Косметическая задача (две HTML-ссылки в уведомлении) 5 раз застряла в конвейере и каждый раз требовала ручного пинка. Корень — не баг задачи, а дыры автономности конвейера. Код был готов и зелёный (434 теста), но пайплайн не мог довести его до merge сам. В итоге — ручной merge Owner-ом; четыре системные дыры заведены в бэклог (ORCH-44/45/46/47).


Хронология застреваний

  1. Auth claude после rebuild — агенты падали Not logged in (root-owned creds + /root/.claude пустышка). См. отдельный разбор в memory/INCIDENT по auth. → починено + защищено монтированием.
  2. check_ci_green race — гейт опросил CI один раз в 17:58:54pending; CI дозеленел в 17:58:55 (промах на 1 секунду). Повторного опроса нет → задача висит насмерть с зелёным CI.
  3. Петля dev↔review↔testing → max retries reached (MAX_DEVELOPER_RETRIES=3).
  4. Откат неполный — убрали код shared-гейта, но оставили 2 doc-строки про него → рассинхрон код↔доки → reviewer снова REQUEST_CHANGES.
  5. Объективный дедлок (см. ниже) → ручной merge.

Корневые проблемы (→ бэклог)

P1. check_ci_green промахивается на гонке CI (→ ORCH-45)

Гейт читает статус CI ровно один раз сразу после developer. Если CI ещё pending — задача застревает молча, без повторного опроса. Нужен polling с ретраем: pending → ждать N×15с, success → advance, failure → rollback, вечный pending → уведомить (не застревать молча).

P2. Developer не понимает замечаний reviewer/tester — "испорченный телефон" (→ ORCH-46)

Это прямой удар по автономности. Три причины, почему dev повторял одну и ту же ошибку:

  • Испорченный телефон. При REQUEST_CHANGES stage_engine.py:~421 шлёт developer-у только "Fix findings in docs/work-items/<WI>/12-review.md"без текста претензий, лишь ссылку на файл. Ключевую governance-мысль легко проскочить. → Вклеивать ТЕКСТ findings прямо в task_desc.
  • Противоречивые сигналы. После tester прилетает "Tests FAILED. Fix failures" (толкает чинить связанное с тестами → dev лез в test-gate). После reviewer — "не трогай gate". Два противоположных приказа. → Склеивать замечания tester+reviewer в одно непротиворечивое ТЗ.
  • Нет памяти между кругами. Каждый запуск developer — новый чистый агент, не помнит прошлых заворотов. Видит "тесты падают" → снова лезет в gate. → Передавать историю прошлых REQUEST_CHANGES/ FAIL ("на чём уже погорел, чего НЕ делать"). Можно: ранняя эскалация к Owner при повторе.

P3. check_tests_passed игнорирует поле result: (→ ORCH-47)

_parse_tests_verdict (src/qg/checks.py) читал только verdict:/status: из frontmatter 13-test-report.md. НО промпт tester-агента (.openclaw/agents/tester*) предписывает писать result: PASS | FAIL. Честный тестер (отчёт ORCH-017: result: PASS, без verdict:/status:) проваливал гейт ложным «Tests FAILED» → откат на development. ORCH-016 проходил лишь потому, что дублировал verdict: И result:. → Гейт должен читать result: как первоклассное машинное поле. ВАЖНО: это shared-гейт (влияет на ВСЕ проекты общего прода) → требует отдельного ADR (CLAUDE.md правило 2), потому вынесено в свой work item, не в ORCH-017.

P4. Preflight слеп к auth и битым флагам (→ ORCH-44)

claude --version отвечает даже без логина → preflight=ok, а реальный запуск падает Not logged in. Плюс --effort с CLI 2.1.142 + --print/--output-format json гасит вывод. Нужны: дешёвая проверка auth без токенов (права+дата истечения OAuth в .credentials.json), фикс effort, «пустой лог + job running + процесс мёртв → failed».


Главный урок: объективный дедлок shared-инфры

ORCH-017 попала в неразрешимый автономно дедлок из-за того, что тест-отчёт уже написан под новый контракт (result: PASS):

  • С фиксом гейта в ветке → reviewer заворачивает (governance: shared-инфра без ADR).
  • Без фикса гейтаcheck_tests_passed не видит result: → ложный FAIL → откат.

Вывод: изменение shared quality-gate нельзя протаскивать внутри прикладной задачи. Оно создаёт циклическую зависимость (артефакты задачи зависят от изменённого гейта, а гейт нельзя менять без отдельного ADR). Менять shared-гейты — только отдельным work item со своим ADR. Если артефакты уже написаны под новый контракт — задача физически не пройдёт, пока не приедет фикс гейта.


Урок про роль ассистента/оператора

Когда оператор раз за разом пинает гейты и чистит за dev вручную — это сигнал «конвейер не тянет автономно». Честнее предложить Owner-у ручной merge/эскалацию, чем гонять карусель кругов и доказывать конвейеру то, что уже готово (код зелёный, reviewer: «технически корректно», претензии процедурные).


Урок про откат

При откате кода обязательно откатывать и доки/CHANGELOG, иначе возникает обратный рассинхрон код↔доки (доки описывают фичу, которой в коде уже нет) → reviewer заворачивает. Откат — это код + доки + changelog + (при необходимости) тест-отчёт одним согласованным движением.


Что сработало хорошо

  • Reviewer ловит governance-нарушения — корректно завернул протаскивание shared-гейта в прикладную задачу. Процедурно прав, даже когда код технически верный.
  • Безопасный ручной пинок гейта через stage_engine.advance_stage(...) — без ребилда/мержа, перевызывает QG внутри процесса орка.
  • Ручной merge как осознанный выход из дедлока (с явным ОК Owner), а не бесконечные круги.