auto-sync: 2026-06-03 08:30:01

This commit is contained in:
Stream
2026-06-03 08:30:01 +03:00
parent 3c9c844898
commit 266861700c
3 changed files with 132 additions and 2 deletions

View File

@@ -0,0 +1,65 @@
# DEV TASK — ORCH-7: cleanup + hardening (M-4 + M-2)
**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500)
**Ветка:** создать `feature/ORCH-7-hardening` из свежего main (main уже содержит ORCH-1/1b/2/6).
## Цель
Две medium-находки из аудита 2026-06-02. Маленький фокусный PR, без поведенческих изменений конвейера.
---
## M-4 — удалить мёртвый код `_auto_merge_pr`
**Факт:** `src/agents/launcher.py:706` `def _auto_merge_pr(...)` определён, но НИГДЕ не вызывается (проверено grep'ом — 0 вызовов). Merge давно делает deployer-агент.
**Сделать:**
- Удалить метод `_auto_merge_pr` целиком из `launcher.py`.
- Проверить что не остались импорты/ссылки (`grep -rn auto_merge src/`).
- Если метод тянул вспомогательные хелперы, которые больше нигде не нужны — удалить и их (но ТОЛЬКО если 0 других вызовов).
---
## M-2 — graceful timeout вместо мгновенного SIGKILL + конфигурируемость
**Факт:** `launcher._watchdog` (строки ~212-235): `time.sleep(timeout)` → сразу `os.kill(pid, SIGKILL)`. SIGKILL не даёт claude дописать артефакты → полу-записанные файлы. Таймаут хардкод `AGENT_TIMEOUT = 1800`.
**Сделать:**
1. **Graceful kill:** при таймауте сначала `SIGTERM`, ждать grace-период (config `agent_kill_grace_seconds`, дефолт 20с), если процесс ещё жив → `SIGKILL`. Логировать обе фазы.
- Использовать `os.kill(pid, signal.SIGTERM)`, затем поллить `os.kill(pid, 0)` (проверка жив ли) в цикле до grace, потом SIGKILL.
- Обработать `ProcessLookupError` на каждом шаге (процесс мог сам завершиться).
2. **Конфигурируемый таймаут per-agent:** добавить в config.py:
```python
agent_timeout_seconds: int = 1800 # дефолтный общий таймаут агента
agent_kill_grace_seconds: int = 20 # пауза SIGTERM->SIGKILL
# опционально per-agent override через JSON, напр.:
agent_timeout_overrides_json: str = "" # {"reviewer": 3600, "architect": 2700}
```
- `_watchdog` берёт таймаут: override для данного агента (если есть в `agent_timeout_overrides_json`) иначе `agent_timeout_seconds`.
- Сохранить обратную совместимость: `AGENT_TIMEOUT` оставить как ссылку на `settings.agent_timeout_seconds` ИЛИ заменить все использования на конфиг. Не сломать вызовы `_watchdog(timeout=...)`.
3. Watchdog должен корректно взаимодействовать с ORCH-1 job-логикой: после kill `proc.wait()` в мониторе вернёт kill-exit-code и драйвит retry/fail — НЕ ломать это (kill exit обрабатывается как permanent или по существующей логике, проверить что не зацикливается).
---
## Ограничения
- 🚫 НЕ трогать: nginx, openclaw.json, .env-секреты (новые ORCH_-ключи можно), deploy-хук, Plane-webhook is_active.
- ⚠️ НЕ менять поведение очереди ORCH-1/resilience ORCH-1b, фиксы B-1/B-2/M-1, worktree ORCH-2, multi-repo ORCH-6.
- ⚠️ M-5 (хардкод инфры в промптах) — НЕ в этой задаче (это репо enduro-trails, отдельный PR).
- Conventional Commits, отдельные коммиты: `refactor(launcher): remove dead _auto_merge_pr (M-4)`, `feat(launcher): graceful SIGTERM->SIGKILL + configurable agent timeout (M-2)`, `test(...)`, `docs(...)`.
## Тесты (в контейнере)
- `test_watchdog.py` или дополнить: мок процесса → таймаут → проверить порядок SIGTERM, затем SIGKILL после grace; процесс, который завершился сам в grace → SIGKILL НЕ шлётся; ProcessLookupError не валит watchdog.
- Конфиг: override таймаута для агента подхватывается; дефолт для остальных.
- Прогон: `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`
- Baseline: новые зелёные, 9 pre-existing webhook-401 не трогать, остальные не сломать.
## Acceptance (проверит Стрим)
| # | Что | Критерий |
|---|-----|----------|
| 1 | M-4 | `_auto_merge_pr` удалён, 0 ссылок, тесты зелёные |
| 2 | M-2 graceful | SIGTERM → grace → SIGKILL; артефакты не рвутся |
| 3 | M-2 config | таймаут конфигурируем + per-agent override |
| 4 | очередь/resilience | ORCH-1/1b не сломаны, kill-exit драйвит retry корректно |
| 5 | деплой | контейнер пересобран из ветки, health ok, /queue ok |
## Деплой + отчёт
- `docker compose up -d --build && sleep 6 && curl -s :8500/health && curl -s :8500/queue`
- После каждого блока (M-4 / M-2 / тесты / деплой) — короткий отчёт + результат проверки.
- Если ТЗ расходится с реальностью кода — отчитайся, не выдумывай.
- Запушить ветку, открыть PR в main. НЕ мержить — мерж делает Стрим после проверки.

View File

@@ -0,0 +1,47 @@
# Dev Report: ORCH-7 cleanup + hardening (M-4 + M-2)
Дата: 2026-06-03
Статус: DONE
## Задача
Маленький фокусный PR на репо orchestrator:
- M-4: удалить мёртвый метод `_auto_merge_pr` в `src/agents/launcher.py`.
- M-2: graceful timeout (SIGTERM→grace→SIGKILL) + конфигурируемый per-agent таймаут в `_watchdog`.
Не трогать ORCH-1/1b/2/6, nginx, openclaw.json, секреты, deploy-хук, Plane-webhook. НЕ мержить.
## Сделано
- [x] Ветка `feature/ORCH-7-hardening` из свежего main
- [x] M-4: удалён `_auto_merge_pr` (0 ссылок, `_ensure_pr` сохранён)
- [x] M-2: graceful kill + конфиг `agent_timeout_seconds` / `agent_kill_grace_seconds` / `agent_timeout_overrides_json` + `_resolve_timeout()`
- [x] Тесты: watchdog ordering, graceful-skip, ProcessLookupError, override/default/malformed
- [x] Прогон тестов в контейнере: 118 passed / 9 pre-existing webhook-401
- [x] Деплой `docker compose up -d --build`, health+queue ok
- [x] Push ветки + PR #4 (НЕ смержен)
## Изменённые файлы
- `src/agents/launcher.py` — удалён `_auto_merge_pr`; переписан `_watchdog` (graceful SIGTERM→grace-poll→SIGKILL, ProcessLookupError на каждом шаге); добавлены `_resolve_timeout()` (статик, per-agent override) и `_record_kill()`; watchdog-поток получает `agent`; `AGENT_TIMEOUT` теперь алиас на `settings.agent_timeout_seconds`; добавлены импорты `json`, `time` (module-level).
- `src/config.py``agent_timeout_seconds=1800`, `agent_kill_grace_seconds=20`, `agent_timeout_overrides_json=""`.
- `tests/test_launcher.py` — классы `TestDeadCodeRemoved`, `TestResolveTimeout`, `TestWatchdogGracefulKill` (8 новых тестов).
## Коммиты (Conventional Commits, раздельные по смыслу)
1. `refactor(launcher): remove dead _auto_merge_pr (M-4)` — 41 deletions only
2. `feat(launcher): graceful SIGTERM->SIGKILL + configurable agent timeout (M-2)`
3. `test(launcher): watchdog graceful kill ordering + timeout config + M-4 removal`
(Первый прогон случайно слил M-4+M-2 в один коммит → сделал soft-reset и пересобрал три чистых коммита.)
## Результат
- `grep -rn auto_merge src/ --include=*.py` → 0 совпадений.
- Тесты в контейнере (`docker run --rm ... pytest tests/ -q`): **118 passed, 9 failed** — все 9 failed = pre-existing webhook-401 (test_webhooks.py), их не трогал. Baseline был 110 passed → +8 новых = 118.
- `tests/test_launcher.py` отдельно: **18 passed** (10 старых + 8 новых).
- Деплой: контейнер пересобран из ветки. `/health``{"status":"ok",...}`. `/queue` → counts все 0, breaker closed, preflight_ok true (2.1.142).
- PR: #4 → https://git.mva154.duckdns.org/admin/orchestrator/pulls/4 (открыт, НЕ смержен).
## Проверка ORCH-1 совместимости (Acceptance #4)
Таймаут-kill пишет `exit_code=-9` в agent_runs (как раньше). `_monitor_agent.proc.wait()` возвращает реальный сигнал-exit и зовёт `_finalize_job`. Лог-tail после kill не содержит 429/overload → `classify_log_file` даёт `permanent``_finalize_permanent`: bounded `attempts < max_attempts` requeue, потом `failed`. Зацикливания ретраев нет. Очередь/resilience не изменены.
## Проблемы и решения
- scp недоступен на dev-хосте → файлы переносил через `ssh 'cat > file' < local`.
- Первый коммит M-4 захватил и M-2-правки (один файл) → soft-reset + раздельная пересборка коммитов.
## Следующий шаг
Готово к ревью. Мерж делает Стрим. После мержа можно почистить worktree-файлы в `workspace-dev/orch7/`.