Files
wiki/memory/2026-06-08.md
2026-06-08 09:30:01 +03:00

121 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-06-08 — Дневник
## 🎯 ORCH-66 (статусная модель) доехала до прода САМА — историческое
- ORCH-66 = новая статусная модель (Plane-статусы: To Analyse / Analysis / Code-Review / Awaiting Deploy / Deploy / Done + In Review для approve-pending).
- **Прошла весь конвейер автономно**: analyst→architect→dev→reviewer→tester→staging→Phase A. CI зелёный, merge-gate пройден, staging пересобран (`bc2347ab`).
- Стояла на `In Review` = approval-pending прода, ждала **Confirm Deploy** Славы. Символично: статусная модель первой пошла в прод сама.
## 🟢 ORCH-67 заведена — Telegram tracker багфикс+enhancement (seq=67, id=34a8440d-4024-41fa-bf6e-398937e23dee)
ТЗ загружено в Plane как HTML (5458 симв.). Зависит от ORCH-66 (статусные имена) → запускать ПОСЛЕ прода ORCH-66. 4 требования:
1. **Bump заработал** — причина бага найдена и НЕ в коде: bump-логика (`delete + send + repoint`) корректна, но в проде `tracker_mode = "edit"` (дефолт `config.py:345`). Env `ORCH_TRACKER_MODE=bump` не выставлен → режим edit (карточка остаётся вверху). Фикс: включить bump + сделать дефолтом.
2. **Формат карточки со статусами как в Plane** — показывать Plane-статус этапа.
3. **Номер задачи (ORCH-NN) — гиперссылка** на страницу задачи в Plane, внутри карточки.
4. **Во ВСЕХ уведомлениях орка** номер задачи тоже кликабельный → ведёт в Plane.
- Уточнение Славы (учтено в ТЗ): **ожидание согласования BRD = Plane-статус `In Review`** (⏸️ approve-pending между Analysis и Architecture). Отразить как полноценный статус, не только строкой «⏸️ Подтверждение BRD ⏳».
## 🔧 Технические факты по notifications.py / tracker (для будущих задач)
- `update_task_tracker(task_id)` — два режима через `Settings.tracker_mode` (env `ORCH_TRACKER_MODE`), case-insensitive; всё кроме `"bump"``"edit"`. Оба держат инвариант «одна карточка на задачу».
- **edit (DEFAULT):** первый вызов sendMessage (silent) + store message_id; далее editMessageText.
- **bump (ORCH-042):** delete старого → send нового внизу → repoint message_id.
- **`parse_mode: HTML` уже включён** в send/edit → гиперссылки `<a href>` делаются без изменения транспорта.
- `render_task_tracker(task_id)` — stateless рендер из БД: строка `✅ <Stage> <dur> · <in>↓/<out>↑ · <cost> · <model>` на этап + строка `✅/⏸️ Подтверждение BRD <dur> · твоё время[ ⏳]` между Analysis/Architecture.
- `send_telegram(text, disable_notification)` → возвращает message_id; `delete_telegram(message_id)`; есть список Telegram-ошибок «target уже отсутствует» (message_id_invalid и т.п.).
## 🔗 URL/env факты (env орка)
- `ORCH_GITEA_PUBLIC_URL=https://git.mva154.duckdns.org`
- **НЕТ публичного Plane-URL в env орка** — для гиперссылок нужен базовый URL `https://plane.mva154.duckdns.org`. Заложено в ТЗ ORCH-67 как новый конфиг.
- project_id: **ORCH = `8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a`**, Sandbox = `8c5a3025-4f9d-4190-b79f-fa06276bb27e`.
- `ORCH_PLANE_WEBHOOK_SECRET=e7d95e…8b16`.
## 🐛 ORCH-68 root-cause: livelock reconciler = РЕГРЕССИЯ от ORCH-66 (Слава угадал)
- Слава: «это началось после внедрения 66ой» — подтверждено логами минута-в-минуту:
- `22:17:04.019` рестарт после прод-деплоя ORCH-66 → `Reconciler started (interval=120s)`
- `22:17:04.207` (через 0.2с) первый `ET-002 done разблокирована` — ДО 22:17 этого лога не было ever.
- `22:18:33` `Task 52: deploy->done (self)` (ORCH-66 задеплоил себя). Спам каждые 120с, 191+ сообщений.
- Механизм: ORCH-66 ввела новую статусную модель (имена Done/Monitoring after Deploy). Reconciler сверяет локальную стадию↔Plane-статус; новые имена сломали сравнение терминальных → ET-002 (done/Done = синхронизирована) каждый тик считается «потерян webhook» → `_note_unblock` вхолостую (no-op, токены НЕ тратятся, но спам + нарушает инвариант «fires only on actual state change»).
- Чинить в `src/reconciler.py`: маппинг терминальных/пост-деплойных статусов привести к новой модели; done+Done не должен триггерить unblock.
- Root-cause-блок дописан в Plane ORCH-68 (seq=68, id=70fd4d24-4241-4dd5-9245-11e629a3fc60), HTML 3603→5419.
- ⚠️ Грабли: чуть не дописала root-cause в ЧУЖУЮ задачу seq=60 «Reconciler не должен трогать escalated» (тоже про reconciler) — поймала, откатила (вернула 549 симв.). **Plane seq≠work_item_id; искать issue по полному списку и сверять название, не по ключевому слову.**
- ⚠️ Plane API: ручной urllib через двойной SSH+docker молчит/не отдаёт JSON. Рабочий путь — использовать модули орка `src/plane_sync.py` (`PLANE_BASE`, `WORKSPACE`, `PLANE_HEADERS`, httpx) внутри контейнера через файл-скрипт + `docker cp`. PATCH `description_html` работает (200).
## 🚀 ORCH-68 ЗАПУЩЕНА в работу (04:45 UTC) + архитектурный ответ
- Слава: «68 срочно запускай. Правильно ли ориентироваться на статус Plane?»
- **Ответ: ДА, ориентир на Plane корректен по дизайну.** Таблица `tasks` не имеет status-колонки (reconciler.py:201 «live Plane state is the source of truth»). F-2 обязан опрашивать Plane и реплеить пропущенные вебхуки — это сердце автономности.
- **Баг — в МАППИНГЕ, не в идее:** `_reconcile_plane_project` тянет `list_issues_by_state(pid, [to_analyse, approved, rejected])`. ET-002 в Plane=`Done` — её не должно быть в выборке. На **enduro** `get_project_states` схлопывает статусы (reconciler.py:209-211) → `Done` алиасится под `approved` UUID → ветка «Approved but stage never advanced → replay verdict» → `_note_unblock` каждый тик.
- **Ключевая находка:** у проекта **orchestrator** `Done`=group=`completed`, `Approved`=group=`started` (разные UUID, НЕ склеены). На enduro — склеены. ⚠️ Правильный фикс: использовать **`state.group == "completed"`** для терминала, а не голый UUID-маппинг. Заложено в ТЗ.
- **Старт:** Plane-статус ORCH-68 → `To Analyse` (UUID `8acc6109-934e-4cd5-954b-7495672f520c`) = триггер конвейера.
- **QG-0 завернул первый раз:** заголовок >80 симв. → задача ушла в `Blocked`. Укоротила до 60 симв («BUG: reconciler livelock — спам unblock done-задачи (ET-002)») → Backlog → To Analyse → пошла. **Урок: заголовок Plane-задачи для орка — <=80 символов (QG-0 hard check).**
- **Сейчас:** task 53, ORCH-068, stage=analysis; **job 378 analyst running** (pid 3332), ветка `feature/ORCH-068-bug-reconciler-livelock-unbloc`. Аналитик получил полное ТЗ + root-cause + архитектурное уточнение.
- ТЗ ORCH-68 в Plane: 5428→7007 симв (добавлен архитектурный блок).
## ✅ ORCH-68 BRD Approved (04:52 UTC) → analysis→architecture (архитектор run_id=347)
- Аналитик выдал 4 артефакта (BRD/ТЗ/AC/test-plan) в `docs/work-items/ORCH-068/`. Проверила все — эталонная работа.
- **Аналитик углубил мой root-cause:** разделил баг на 2 независимых дефекта: **D1** (терминалы не исключены из actionable-выборки) + **D2** (`_note_unblock` зовётся безусловно после no-op dispatch, нарушает свой docstring). D2 — мина, которую я пропустила.
- **Захватил связанный баг кэша:** `_STATES_CACHE` живёт весь lifetime, `reload_project_states()` есть но не вызывается — именно из-за этого Слава рестартил орк после создания `Confirm Deploy`. Поймали заодно (G5/AC-12, secondary).
- Plane-статусы переходов: To Analyse → Analysis (`cb834d55`) → In Review (`c52e99b9`, approve-pending BRD) → Approved (`63f2c8fe`) → Architecture (`795cc32f`). UUID approved для ORCH = `63f2c8fe-dcda-4ace-952f-dd88bd0118ff`.
- Решение «state.group vs allowlist» правильно оставлено архитектору в ADR.
## 🟢 ORCH-69 заведена и запущена (05:09 UTC) — QG-0 title-лимит в параметр
- Решение Славы: «заведи задачу, сделаем как параметр, 200 по умолчанию».
- **Предыстория:** выяснили что 80 в QG-0 — НЕ техническое ограничение. Проверила все места ниже по течению: slug ветки `re.sub(...)[:30]` (webhooks/plane.py:488) режется независимо + выкидывает кириллицу ([^a-z0-9]→дефис); БД `title TEXT` без лимита; Telegram-карточка `html.escape(title)` без обрезки; worktree-путь по branch (=work_item_id+slug[:30]), не по title. Расширять безопасно.
- **ORCH-69** (seq=69, id=35338a57-d905-4958-b70c-fa1afd66110f): новый `ORCH_QG0_TITLE_MAX` (дефолт 200) в config.py, `_qg0_errors` читает из settings, текст ошибки динамичный. Нижние лимиты (title<5, desc<20) НЕ трогать. Аддитивно/обратносовместимо (200>80).
- Запущена: stage=analysis, ветка `feature/ORCH-069-qg-0-title-orch-qg0-title-max-`. ⚠️ Обе (68 и 69) трогают `webhooks/plane.py` — следить за merge-конфликтом (merge-gate должен поймать, но держать в голове).
## 🎉 ORCH-68 В ПРОДЕ — орк сам себя починил (05:32 UTC, self-deploy)
- ORCH-68 прошла весь конвейер автономно и задеплоилась в прод. Статус → Done (`3738cd3c`), post-deploy monitor HEALTHY.
-**Спам ET-002 = 0** после деплоя (было 191+). Фикс боевой.
- ✅ Код в проде: `_is_terminal_state`, `get_project_state_groups`, `skipped_terminal_total` — D1 через `state.group` (ровно как хотели).
## 🔴 БАГ ORCH-70: `Confirm Deploy` НЕ триггерит Phase B (мёртвый триггер, регрессия ORCH-66)
- **Инцидент:** Слава нажал статус `Confirm Deploy` для прод-деплоя ORCH-68 → орк `no pipeline action`. Деплой пошёл только после ручного перевода в `Approved`.
- **Root cause:** диспетчер `handle_issue_status` (webhooks/plane.py ~158-166) слушает ТОЛЬКО `to_analyse/approved/rejected`. Phase B (stage_engine.py ~215-224) триггерится по `Approved`. `Confirm Deploy` (008597eb) не в тройке → молчит. ORCH-66 добавила статус как **метку (запись)**, но не подключила **обратный путь (чтение/триггер)**.
- **Почему не поймали:** (1) не в scope ORCH-68 (она чинит reconciler, явно N1-N3 «Phase B не трогать»); (2) дыра ORCH-66 — тесты проверяли ЗАПИСЬ статуса, не ОБРАТНЫЙ триггер; (3) staging не покрывает прод-путь — ручной `Confirm Deploy` живёт только на проде (Phase A staging автоматический).
- **Урок:** тестировать ОБРАТНЫЙ путь статусов (нажатие → действие), не только запись. Новый статус = подключить в обе стороны. Прод-only пути нуждаются в явном тесте.
- **Записано в репу орка:** `docs/history/LESSONS_2026-06-08_confirm-deploy-deadtrigger.md` (коммит main, sha 101bd1c5, через Gitea Contents API).
- **ORCH-70** заведена (seq=70, id=bfbc924f-b808-4f7b-87d4-88ac5e976240), Backlog. Цель: `Confirm Deploy` триггерит Phase B + регресс-тест обратного пути. Не запущена — ждёт решения Славы по очереди.
- **Gitea для коммитов в репу орка:** `ORCH_GITEA_URL=http://localhost:3000`, owner=`admin`, repo=`orchestrator`, token `ORCH_GITEA_TOKEN` (env контейнера). Contents API: GET→sha→PUT/POST base64.
## 🚨 КРИТ: ФАНТОМНЫЙ MERGE — прод расходится с main, 4 PR не слиты (08.06)
- **Симптом:** ORCH-67 в To Analyse не подхватился. Причина — прод слушает `in_progress` (старый диспетчер), а не `to_analyse` (ORCH-66).
- **Диагноз (подтверждён md5 + git + PR-статус):** PR#67(022)/68(059)/69(066)/70(068) — ВСЕ **open, merged=False**. Последний реально слитый — PR#66 (ORCH-065, bb03350).
- **md5-сверка:** прод reconciler.py/plane_sync.py == ветка ORCH-068 (≠ main). Прод = снимок ветки ORCH-068, НЕ main.
- **Механизм (Слава угадал — «деплоилась старая версия»):** ветка ORCH-068 срезана от `bb03350` (ORCH-065), А НЕ от кода ORCH-066. В истории ветки-068 по ORCH-066 только `docs staging`, не код (`to_analyse`=0 в ветке-068). Т.е. деплой 068 взял worktree от устаревшего main (065) + фикс reconciler → откатил статусную модель 66 из прода.
- **Таймлайн ET-002:** 22:17 деплой ветки-066 (сломанный reconciler) → спам начался. 05:32 деплой ветки-068 (фикс livelock, но база 065 без 66) → спам=0 после 05:33. Подтверждает: код 66 БЫЛ в проде 22:17-05:32, потом стёрт деплоем 068.
- **КОРЕНЬ:** self-deploy Phase B собирает образ из ВЕТКИ (срезанной от main) + рапортует finalize SUCCESS, НО git-merge в main не отрабатывает (фантом). → следующая задача срезается от устаревшего main → теряет код незалитых предшественников. Накопительная потеря 022→059→066→068.
- **Подозрение:** регресс фикса ORCH-065 (idempotent merge / merge-lease) ЛИБО merge-step после него молчит. ORCH-065 — последний честный merge.
- **Ребейз origin/feature/ORCH-066-plane на origin/main — ЧИСТЫЙ** (конфликтов нет, git разрулил reconciler.py/plane_sync.py — разные места). НО простое слияние 66 затрёт фикс 68 → нужна цепочка 022→059→066→068.
- **РЕШЕНИЕ Славы: «запускай сама, документируй, запиши урок»** — выполняю.
## 🔧 ВОССТАНОВЛЕНИЕ main (08.06, автономно)
- **Находка по ходу:** ORCH-059 = `feat(deploy): Confirm Deploy status triggers prod deploy`**уже реализует то, что я заводила как ORCH-70!** `handle_confirm_deploy` написан в 059, просто не слит (фантом). → ORCH-70 после долива 059 пересмотреть (останется только display-слой Monitoring after Deploy).
- **Матрица src-пересечений:** 022(config/qg/security_gate/stage_engine), 059(plane_sync/stage_engine/webhooks), 066(plane_sync/reconciler/stage_engine/webhooks), 068(config/plane_sync/reconciler). Порядок цепочки 022→059→066→068 (= хронология PR 67<68<69<70).
- **Интеграционная ветка** `integ/restore-main-2026-06-08` (worktree `/tmp/integ_chain` в контейнере): 022+059+066 слиты чисто (docs union). На 068 — код-конфликт reconciler.py (066 to_analyse vs 068 in_progress+D1/D2). Разрешение: каркас 068 + триггер to_analyse 066.
- **Урок в репу:** `docs/history/LESSONS_2026-06-08_phantom-merge.md` (sha 772ccab, CRITICAL постмортем + runbook диагностики).
- **Критбаг ORCH-71** (seq=71, id=017daf29) — root-fix фантома: верификация merge после деплоя + done-гейт по PR.merged + merge до рестарта контейнера. Backlog.
- **Диагностический runbook (4 проверки фантома):** (a) Gitea PR merged-флаги, (b) md5 prod vs `git show origin/main:<file>`, (c) merge-base ветки vs main, (d) таймлайн деплой-логов.
- ⚠️ **БЛОКЕР:** Dev-агент (vibecode/claude-sonnet-4.6) упал на billing error (кредиты исчерпаны, 0 токенов). Конфликт reconciler.py НЕ разрешён. ТЗ готово: `/tmp/DEV_TASK_merge_066_068.md`. Нужен перезапуск Dev на другой модели.
## 🔍 АУДИТ статусной модели ORCH-66 (08.06) — 7 статусов-призраков
- В Plane заведен **21 статус**, код (`_PLANE_NAME_TO_KEY`, plane_sync.py ~119) знает только **14**. Не подключены: Analysis, Code-Review, Awaiting Deploy, Confirm Deploy, Deploying, Monitoring after Deploy (+ To Analyse через отдельный alias).
- **Мёртвый код:** `set_issue_awaiting_deploy/deploying/monitoring` ОПРЕДЕЛЕНЫ (plane_sync.py ~652-679) но НИГДЕ не вызываются (grep пуст).
- `_STAGE_TO_STATE_KEY` ставит СТАРЫЕ статусы: analysis→in_progress (не Analysis), review→review (не Code-Review), deploy→in_progress (не Awaiting Deploy).
- `Monitoring after Deploy` (97b4a6cf) создан, но post-deploy окно идёт ПОВЕРХ Done (stage_engine.py:356 «arm monitor PAST done») → задача показывает Done, хотя ~15мин под наблюдением (это и есть «почему Done» Славы).
- **Вывод:** ORCH-66 ввела красивую модель на доске, но деплойная под-модель (Awaiting Deploy→Confirm Deploy→Deploying→Monitoring) — декоративная, код её не использует.
- **ORCH-70 расширена** (3725→7027): с одного Confirm Deploy до всей деплойной под-модели (G4-G7): подключить статусы, заполнить name→key, привести stage→state, убрать/задействовать мёртвый код. Порядок actionable vs display-only — архитектору в ADR.
## ⚠️ QG-0 — правила заголовка/описания Plane-задач для орка (важно!)
- **QG-0** = первый quality gate конвейера, функция `_qg0_errors(name, description)` в `src/webhooks/plane.py:366`. Проверяет **заголовок (name) и описание** до старта аналитика.
- Три проверки: **Title ≥ 5**, **Title ≤ 80** (хардкод, без конфига), **Description ≥ 20** символов (`.strip()`). `len()` по Unicode-символам (кириллица = 1 символ, не байт).
- **soft vs hard:** при `work_item.created` — soft (только warning, стр.362). При старте конвейера (`Status->In Progress`) — **hard** (стр.435-457): блокирует и кидает в `Blocked`. Вот почему создаётся ок, а при запуске заворачивает.
- 🔑 **ПРАВИЛО: заголовок Plane-задачи для орка — короткий тайтл ≤ 80 символов, вся детализация — в description.** Не впихивать описание в заголовок (моя привычка — забываю). Дважды наступила 08.06 (ORCH-67/68).
- ⚡ Возможный QoL-фикс (предложила Славе, ждёт решения): авто-обрезка заголовка до 80 («…») вместо блокировки, или вынести лимит 80 в конфиг.
## 📝 Грабли инструмента edit (зафиксировать)
- `edit` требует строго: `path` + `edits` (массив), каждый элемент только `{oldText, newText}` — никаких лишних полей. `oldText` должен совпадать дословно (включая пробелы/переводы строк). Несколько фейлов за сессию из-за неверной формы аргументов и неточного oldText.
- `image` (vibecode/claude-sonnet-4.6) падает с **403 Insufficient credits** — генерация картинок недоступна.
## Файлы, тронутые в сессии
- `tasks/orchestrator/STATUS_MODEL_DEEP_ANALYSIS.md`, `STATUS_MODEL_PROPOSAL.md`, `status_workflow.html`
- `/tmp/wi_tracker_desc.md` (ТЗ ORCH-67), `/tmp/wi1_desc.md`
- `temp/DEV_TASK_ORCH-022_test_fix.md`