# 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. НЕ мержить — мерж делает Стрим после проверки.