auto-sync: 2026-06-03 10:00:01

This commit is contained in:
Stream
2026-06-03 10:00:01 +03:00
parent a91cf5c534
commit f0aa3b9c9e
3 changed files with 187 additions and 1 deletions

View File

@@ -63,7 +63,21 @@ Dev **запаниковал** на старте orch1b: «параллельн
- Хук `enduro-deploy-hook.sh` (876b, pull+restart+gps-collector) — rollback'а НЕТ. План: rollback В ХУК (prev-image перед рестартом + флаг `--rollback`), в deployer.md → `ssh ... bash $HOOK --rollback`.
- 🟡 **M-5:** architect.md:16 (82.22.50.71), tester.md:27,38,39 (/home/slin/ пути) → env с дефолтами (deployer.md уже параметризован — образец).
- ⚠️ ПРОД-ДЕПЛОЙ: Dev НЕ деплоит на прод, бэкап хука перед правкой, проверка = bash -n. Мерж и прод-применение хука — я со Славой.
- Бэклог orchestrator ЗАКРЫТ: ORCH-1/1b/2/4/6/7 + ORCH-5 в main. ORCH-8 отменена. Мелочь M-6/L-1/L-2 — по настроению.
- Бэклог orchestrator ЗАКРЫТ: ORCH-1/1b/2/4/6/7 + ORCH-5 в main. ORCH-8 отменена.
### Финальная гигиена L-1/L-2/L-3 (Слава: «делай все в т.ч. l-3») — ЗАПУЩЕНО (taskName `orch_cleanup_l1l2l3`, ветка feature/ORCH-cleanup-L1L2L3, ТЗ `DEV_TASK_ORCH_CLEANUP_L1L2L3.md`)
- ⚠️ **Разведка переписала остаток — аудит 02.06 отстал:**
-**L-4 ОТПАЛ** — мусорной папки `{src` уже нет.
-**L-5 ОТПАЛ**`tests/test_launcher.py` УЖЕ есть (18 тестов: запись·verdict·timeout·watchdog SIGTERM→SIGKILL). Долг закрыт по ходу ORCH-1b/2/4.
- ⚠️ **M-6 НЕ мелочь, ОТЛОЖЕНА:** Plane НЕ присылает `sequence_id` в webhook payload (проверила 5 реальных events в БД — ключа нет). Значит «источник правды = Plane sequence» требует ОТДЕЛЬНОГО GET к Plane API по issue_id — сетевой вызов, не правка функции. Рассинхрон теоретический (per-repo инкремент `get_next_work_item_id` работает). Ждёт отдельного решения Славы — нужен ли вообще.
-**L-1+L-2+L-3 ЗАМЕРЖЕНЫ** (PR #7 `be27f506`, прод пересобран, health/queue ok, **151 passed** = 145+6, 9 pre-existing). L-1 шапка stages.py исправлена, L-2 `prune_run_logs` best-effort в lifespan (keep_days=30/keep_max=500, не трогает active), L-3 эмодзи-константы.
-**M-6 ВАЖНА** (Слава подтвердил) — берём в работу. Разведка:
- Инфра УЖЕ ЕСТЬ: `plane_sync.py` — полный httpx-клиент (PLANE_BASE/HEADERS/WORKSPACE из config), уже читает `sequence_id` (стр.99,115), `find_issue_id` ищет issue. M-6 НЕ с нуля.
- Payload `work_item.created` содержит `id` (issue UUID) + `project` — по ним GET к Plane API достаёт настоящий `sequence_id`. `sequence_id` В ПАЙЛОАДЕ НЕТ (проверено), но есть через GET по id.
- 🔴 **БОНУС-баг найден:** `find_issue_id` хардкодит `f"ET-{seq:03d}"` (стр.99) — сломано для ORCH-префикса из ORCH-6. M-6 должна убрать хардкод ET-.
- ⚠️ ГОНКА ВЕТОК: M-6 запускать ТОЛЬКО после мержа L1L2L3 из чистого main (иначе 2 Dev дерутся за рабочую копию). L1L2L3 уже смержен → можно.
- 🔄 **M-6 ЗАПУЩЕН** (taskName `orch_m6_plane_sequence`, ветка feature/ORCH-M6-plane-sequence, ТЗ `DEV_TASK_ORCH_M6_PLANE_SEQUENCE.md`). План: helper `fetch_issue_sequence_id` (GET по issue UUID), в handle_work_item_created seq→`{prefix}-{seq:03d}` или fallback DB-инкремент (автономность!), + фикс хардкода `ET-` в find_issue_id.
- ⚠️ **ТОКЕН Gitea (находка Dev 03.06):** `.env` на сервере содержит УСТАРЕВШИЙ `ORCH_GITEA_TOKEN` (28 симв, HTTP 401). РАБОЧИЙ токен (40 симв) — в env контейнера `orchestrator`: `docker exec orchestrator printenv | grep -i gitea`. ⚠️ Мои прошлые мержи (PR #5/6/7) работали — значит я брала токен из .env и он рабочий?? ПРОВЕРИТЬ при след. мерже M-6 — если .env-токен 401, брать из контейнера. (Славе на заметку: стоит синхронизировать .env с рабочим токеном.)
- Мерж-рецепт (работает): проверить `git log origin/main..origin/ветка` (не пусто!), мой прогон тестов, clean-merge check, мерж через Gitea API `/pulls/N/merge` `{"Do":"merge"}`, пересборка из main.
- ТЗ-образцы: `DEV_TASK_ORCH7_HARDENING.md`, `DEV_TASK_ORCH4_STAGE_ENGINE.md`.
- ⚠️ **Грабля memory-файла:** ранний `write` сделал APPEND (задвоил файл), почистила перезаписью. Для точечных правок memory — `edit`, не `write`.

View File

@@ -0,0 +1,74 @@
# DEV TASK — финальная гигиена: L-1 (комментарий) + L-2 (ротация логов) + L-3 (эмодзи-константы)
**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500)
**Ветка:** `feature/ORCH-cleanup-L1L2L3` из свежего main (git checkout main && git pull && git checkout -b feature/ORCH-cleanup-L1L2L3).
Три мелких независимых пункта, отдельными коммитами. Низкий риск, но аккуратность та же.
⚠️ **ГРАБЛЯ push (ORCH-7, НЕ повтори):** после push `git log origin/main..origin/feature/ORCH-cleanup-L1L2L3` ДОЛЖЕН показать твои коммиты ДО отчёта «PR готов».
---
## L-1 — поправить врущий комментарий в stages.py (1 строка)
**Где:** `src/stages.py:8` — в docstring/шапке `STAGE_TRANSITIONS`:
```
- agent: the agent to launch when entering the NEXT stage
```
**Проблема:** это НЕВЕРНО и именно этот нейминг породил баг ORCH-4 (launcher брал агента не той стадии). Строка 33 уже верная: «agent to launch when advancing FROM this stage».
**Фикс:** привести шапку (стр.8) в соответствие с фактической семантикой:
```
- agent: the agent to launch when advancing FROM this stage (NOT the next stage's agent)
```
Никакого кода не менять — только комментарий. STAGE_TRANSITIONS и логику НЕ трогать.
**Коммит:** `docs(stages): fix misleading STAGE_TRANSITIONS comment (L-1)`
---
## L-2 — ротация логов прогонов (реальная гигиена)
**Где:** логи пишутся в `/app/data/runs/{run_id}.log` (`src/agents/launcher.py:143` и др.). Накапливаются без ограничения.
**Что сделать:**
- Добавить функцию очистки старых логов в `runs/` — например `prune_run_logs(runs_dir, keep_days=30, keep_max=500)`: удаляет .log старше keep_days ИЛИ оставляет только keep_max самых свежих (что наступит раньше). Безопасно: только `*.log` в этом каталоге, не трогать другие файлы.
- Вызывать её в подходящий момент: либо при старте приложения (`init`/lifespan), либо после финализации job (`_finalize_job`). Выбери наименее инвазивный — вызов при старте проще и безопаснее. Best-effort: ошибка ротации НЕ должна ронять приложение (обернуть try/except + logger.warning).
- Параметры (keep_days/keep_max) — через env/config с разумными дефолтами (по образцу resilience-ключей в ORCH-1b, если такой паттерн есть; иначе константы с комментарием).
- НЕ удалять лог текущего/активного прогона.
**Тест:** `tests/test_log_rotation.py` — создать tmp-каталог с N фейковыми .log разного возраста (touch с mtime), вызвать prune, проверить что старые/лишние удалены, свежие и не-.log файлы целы.
**Коммит:** `feat(launcher): prune old run logs (L-2)`
---
## L-3 — эмодзи-литералы → константы (косметика)
**Где:** `src/plane_sync.py` — захардкоженные эмодзи в строках:
- стр.197 `f"🔄 Stage: {old_stage} → {new_stage}"`
- стр.235 `f"⚠️ QG failed at {stage}: ..."`
- стр.242 `"✅ Task completed! ..."`
**Что сделать:** вынести эмодзи в именованные константы в начало модуля (или в маленький общий модуль `src/emoji.py` если хочешь переиспользовать), например:
```python
EMOJI_STAGE = "🔄"
EMOJI_QG_FAIL = "⚠️"
EMOJI_DONE = ""
```
и подставить в f-строки. Текст сообщений НЕ менять — байт-в-байт тот же вывод. Это чисто читаемость.
**Тест:** не обязателен (поведение не меняется); если просто — добавь assert что константы непусты. Главное — существующие тесты plane_sync (если есть) зелёные.
**Коммит:** `refactor(plane_sync): extract emoji literals to constants (L-3)`
---
## Ограничения
- 🚫 НЕ трогай: nginx, openclaw.json, .env-секреты, deploy-хук, Plane-webhook is_active, ORCH-1/1b/2/4/5/6/7, stage_engine.py, STAGE_TRANSITIONS (логику), HMAC, очередь.
- Baseline: **145 passed**, 9 pre-existing 401 — НЕ чинить и НЕ ломать. Новые тесты зелёные, старые не падают.
- Conventional Commits, 3 отдельных коммита по пунктам.
- Тесты в контейнере: `IMG=$(docker inspect orchestrator --format '{{.Config.Image}}'); docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q`
## Проверка (Стрим проверит вживую)
| # | Что | Критерий |
|---|-----|----------|
| 1 | L-1 | stages.py:8 больше не врёт; логика/STAGE_TRANSITIONS не тронуты |
| 2 | L-2 | prune-функция + вызов; ошибка ротации не роняет app; активный лог не трогается; тест зелёный |
| 3 | L-3 | эмодзи в константах; вывод сообщений байт-в-байт прежний |
| 4 | тесты | мой прогон: было 145 → стало 145+новые passed, 9 pre-existing не тронуты |
| 5 | git | PR в main, remote содержит коммиты (грабля ORCH-7) |
## Отчёт
- НЕ деплоить, НЕ мержить (мерж делает Стрим после живой проверки).
- Запушь ветку (ПРОВЕРЬ remote!), открой PR в main. После каждого пункта — короткий отчёт.
- Если ТЗ расходится с кодом — отчитайся, не выдумывай. Один исполнитель, не паникуй про параллельные сессии.

View File

@@ -0,0 +1,98 @@
# DEV TASK — M-6: work_item_id из Plane sequence_id (источник правды = Plane)
**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500)
**Ветка:** `feature/ORCH-M6-plane-sequence` из свежего main (git checkout main && git pull && git checkout -b feature/ORCH-M6-plane-sequence). main содержит ORCH-1/1b/2/4/5/6/7 + L1L2L3.
⚠️ **ГРАБЛЯ push (ORCH-7):** после push `git log origin/main..origin/feature/ORCH-M6-plane-sequence` ДОЛЖЕН показать твои коммиты ДО отчёта «PR готов».
## Проблема (верифицировано по живому коду + реальным payload в БД)
`db.get_next_work_item_id(repo, prefix)` нумерует `work_item_id` как `max(tasks по repo+prefix)+1`**независимо от Plane**. Plane ведёт свой `sequence_id`. При любом рассинхроне (ручное создание/удаление issue в Plane, гонка, пересоздание задачи) номер оркестратора (ET-003/ORCH-005) разъедется с тикетом в Plane → путаница.
**Цель:** источник правды для номера — **Plane `sequence_id`**, маппить детерминированно в `<prefix>-NNN`.
### Что уже есть (НЕ строй с нуля)
- `src/plane_sync.py` — рабочий httpx-клиент Plane: `PLANE_BASE`, `PLANE_HEADERS` (X-API-Key), `WORKSPACE` из `settings`. Уже читает `issue.get("sequence_id")` (стр.~99/115) и ходит на `{PLANE_BASE}/workspaces/{WORKSPACE}/projects/{project_id}/issues/...`.
- В `src/webhooks/plane.py:handle_work_item_created` в момент создания task УЖЕ доступны: `plane_id` (= issue UUID, стр.110), `plane_project_id` (стр.124), `proj.work_item_prefix`. Payload `work_item.created` содержит `id` и `project`, но **НЕ содержит `sequence_id`** (проверено на 5 реальных events) — поэтому нужен GET к Plane API по issue id.
## Что сделать
### 1. Helper: достать sequence_id из Plane по issue UUID (`src/plane_sync.py`)
Добавить функцию, напр.:
```python
def fetch_issue_sequence_id(issue_id: str, project_id: str) -> int | None:
"""GET the Plane issue by UUID and return its sequence_id (the authoritative
per-project number), or None if unavailable (network error / missing field)."""
url = f"{PLANE_BASE}/workspaces/{WORKSPACE}/projects/{project_id}/issues/{issue_id}/"
try:
resp = httpx.get(url, headers=PLANE_HEADERS, timeout=10)
resp.raise_for_status()
seq = resp.json().get("sequence_id")
return int(seq) if seq is not None else None
except Exception as e:
logger.warning(f"fetch_issue_sequence_id failed for {issue_id}: {e}")
return None
```
(подгони под фактический формат ответа Plane — проверь живым GET'ом, см. ниже).
### 2. Использовать sequence_id при создании task (`src/webhooks/plane.py:handle_work_item_created`)
Заменить безусловный `work_item_id = get_next_work_item_id(repo, proj.work_item_prefix)` на:
```python
seq = fetch_issue_sequence_id(plane_id, plane_project_id)
if seq is not None:
work_item_id = f"{proj.work_item_prefix}-{seq:03d}"
else:
# Plane недоступен / нет sequence_id → НЕ блокируем pipeline, падаем на DB-инкремент
work_item_id = get_next_work_item_id(repo, proj.work_item_prefix)
logger.warning(f"Plane sequence_id unavailable, fell back to DB increment: {work_item_id}")
```
⚠️ **Автономность важнее точности:** если Plane не ответил — НЕ ронять создание task, использовать старый fallback. Залогировать что был fallback.
### 3. Фикс хардкода `ET-` в `find_issue_id` (`src/plane_sync.py`, БОНУС, тот же скоуп)
Стр.~99: `identifier = f"ET-{seq:03d}"` — хардкодит префикс ET, сломано для ORCH-проектов из ORCH-6.
- Убрать хардкод `ET-`. Сравнивать по `sequence_id` напрямую с числом из `work_item_id` (распарсить `<prefix>-NNN` → NNN), а не собирать строку с фиксированным `ET-`.
- Аналогично fallback-блок стр.~103 (`if work_item_id.startswith("ET-")`) — обобщить на любой префикс (парсить число после последнего `-`).
- Логику поиска не ломать, только убрать ET-привязку.
## Ограничения
- 🚫 НЕ трогай: nginx, openclaw.json, .env-секреты, deploy-хук, Plane-webhook is_active, ORCH-1/1b/2/4/5/6/7, stage_engine.py, STAGE_TRANSITIONS, HMAC, очередь, `get_next_work_item_id` (оставить как fallback, НЕ удалять).
- Сетевой вызов к Plane — с timeout (есть, 10с) и try/except: ошибка НЕ должна ронять webhook-обработку.
- Baseline: **151 passed**, 9 pre-existing 401 — не чинить, не ломать.
- Conventional Commits: `feat(webhook): derive work_item_id from Plane sequence_id (M-6)`, `fix(plane_sync): drop hardcoded ET- prefix in find_issue_id (M-6)`.
## Разведка перед кодом (сделай ОБЯЗАТЕЛЬНО)
Проверь живым GET'ом РЕАЛЬНЫЙ формат ответа Plane по одному issue (есть ли `sequence_id` в одиночном GET, как называется поле):
```bash
docker exec orchestrator python3 -c "
import httpx
from src.config import settings
base=f'{settings.plane_api_url}/api/v1'; h={'X-API-Key':settings.plane_api_token}; ws=settings.plane_workspace_slug
# взять любой реальный issue_id из tasks
import sqlite3
r=sqlite3.connect('/app/data/orchestrator.db').execute(\"SELECT plane_issue_id, repo FROM tasks WHERE plane_issue_id IS NOT NULL ORDER BY id DESC LIMIT 1\").fetchone()
print('task:', r)
"
```
Если реальных issue в Plane нет (только fake-test-id) — проверь формат по списочному endpoint'у issues проекта. Если `sequence_id` приходит иначе (вложенный, другое имя) — адаптируй helper и **отчитайся что нашёл**, не выдумывай.
## Тесты
`tests/test_m6_sequence.py`:
- `fetch_issue_sequence_id` возвращает int при валидном ответе (мок httpx).
- возвращает None при сетевой ошибке / отсутствии поля (НЕ кидает).
- `handle_work_item_created`: при доступном seq → `work_item_id == f"{prefix}-{seq:03d}"`; при None → fallback на `get_next_work_item_id` (мок, assert вызван).
- `find_issue_id`: больше НЕ хардкодит `ET-` — работает для произвольного префикса (ORCH-005 матчится по sequence_id=5).
Тесты в контейнере: `IMG=$(docker inspect orchestrator --format '{{.Config.Image}}'); docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q`
## Проверка (Стрим проверит вживую)
| # | Что | Критерий |
|---|-----|----------|
| 1 | helper | GET по issue UUID → sequence_id; ошибка → None, не кидает |
| 2 | создание task | seq доступен → prefix-NNN из Plane; недоступен → fallback DB, залогирован |
| 3 | автономность | Plane down НЕ роняет webhook (task всё равно создаётся) |
| 4 | хардкод ET- | убран в find_issue_id, работает для ORCH-/любого префикса |
| 5 | тесты | мой прогон: 151 → 151+новые passed, 9 pre-existing не тронуты |
| 6 | git | PR в main, remote содержит коммиты (грабля ORCH-7) |
## Отчёт
- НЕ деплоить, НЕ мержить (мерж — Стрим после живой проверки). Запушь ветку (ПРОВЕРЬ remote!), открой PR в main.
- В отчёте обязательно: что реально вернул живой GET к Plane (формат sequence_id), какой fallback-путь, результаты прогона.
- После каждого блока — короткий отчёт. Если ТЗ расходится с кодом/Plane — отчитайся, не выдумывай. Один исполнитель, не паникуй про параллельные сессии.