diff --git a/docs/history/LESSONS_ORCH-017.md b/docs/history/LESSONS_ORCH-017.md new file mode 100644 index 0000000..460f1e9 --- /dev/null +++ b/docs/history/LESSONS_ORCH-017.md @@ -0,0 +1,103 @@ +# 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:54` → `pending`; 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//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), а не бесконечные круги.