auto-sync: 2026-06-03 10:00:01
This commit is contained in:
74
tasks/orchestrator/DEV_TASK_ORCH_CLEANUP_L1L2L3.md
Normal file
74
tasks/orchestrator/DEV_TASK_ORCH_CLEANUP_L1L2L3.md
Normal 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. После каждого пункта — короткий отчёт.
|
||||
- Если ТЗ расходится с кодом — отчитайся, не выдумывай. Один исполнитель, не паникуй про параллельные сессии.
|
||||
98
tasks/orchestrator/DEV_TASK_ORCH_M6_PLANE_SEQUENCE.md
Normal file
98
tasks/orchestrator/DEV_TASK_ORCH_M6_PLANE_SEQUENCE.md
Normal 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 — отчитайся, не выдумывай. Один исполнитель, не паникуй про параллельные сессии.
|
||||
Reference in New Issue
Block a user