From 494adb09f6a3fc12a44b12d540686f4b793c6beb Mon Sep 17 00:00:00 2001 From: Stream Date: Thu, 21 May 2026 20:40:04 +0300 Subject: [PATCH] auto-sync: 2026-05-21 20:40:02 --- .../DEV_TASK_ORCHESTRATOR_FIXES.md | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md diff --git a/tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md b/tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md new file mode 100644 index 0000000..c6c25ec --- /dev/null +++ b/tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md @@ -0,0 +1,196 @@ +# DEV_TASK_ORCHESTRATOR_FIXES.md — Фикс критических багов Orchestrator + +--- +type: dev-task +priority: high +project: multi-agent +created_at: 2026-05-21 +author: stream +--- + +## Контекст + +Первый полный автоматический прогон ET-002 выявил 5 критических багов, которые мешают автономной работе пайплайна. Без этих фиксов каждый переход между стадиями требует ручного вмешательства. + +## Задачи + +### 1. Установить git в контейнер orchestrator + +**Файл:** `/home/slin/repos/orchestrator/Dockerfile` + +**Что сделать:** Добавить `git` в `apt-get install` (или `apk add git` если alpine). + +**Зачем:** Агенты (Claude CLI) не могут коммитить — используют dulwich (workaround), который коммитит от root и ломает permissions. + +**Проверка:** +```bash +docker exec orchestrator git --version +``` + +### 2. Агенты коммитят от правильного пользователя + +**Файл:** `/home/slin/repos/orchestrator/src/agents/launcher.py` + +**Что сделать:** В env для subprocess.Popen добавить: +```python +env={ + **os.environ, + "HOME": "/home/slin", + "GIT_AUTHOR_NAME": "claude-bot", + "GIT_AUTHOR_EMAIL": "claude-bot@mva154.local", + "GIT_COMMITTER_NAME": "claude-bot", + "GIT_COMMITTER_EMAIL": "claude-bot@mva154.local", +} +``` + +**Зачем:** Коммиты от root ломают `.git/objects` permissions. С git env vars коммиты будут от claude-bot. + +**Проверка:** +```bash +docker exec orchestrator bash -c 'HOME=/home/slin GIT_AUTHOR_NAME=claude-bot git -C /repos/enduro-trails log --format="%an" -1' +``` + +### 3. Привязать task_id к agent_runs + +**Файл:** `/home/slin/repos/orchestrator/src/agents/launcher.py` + +**Что сделать:** Метод `launch()` должен принимать `task_id: int | None = None` и передавать его в INSERT: +```python +def launch(self, agent: str, repo: str, task_content: str = None, task_id: int = None) -> int: + ... + cursor = conn.execute( + "INSERT INTO agent_runs (task_id, agent) VALUES (?, ?)", + (task_id, agent), + ) +``` + +**Файл:** `/home/slin/repos/orchestrator/src/webhooks/plane.py` + +**Что сделать:** В месте вызова `launcher.launch(agent, repo, task_desc)` добавить `task_id=task_id`: +```python +run_id = launcher.launch(agent, repo, task_desc, task_id=task_id) +``` + +**Проверка:** +```bash +docker exec orchestrator python -c " +import sqlite3 +conn = sqlite3.connect('/app/data/orchestrator.db') +conn.row_factory = sqlite3.Row +for r in conn.execute('SELECT id, task_id, agent FROM agent_runs ORDER BY id DESC LIMIT 3').fetchall(): + print(dict(r)) +" +``` +Ожидание: `task_id` не NULL для новых runs. + +### 4. Автоматическое продвижение stage после CI green (без `:approved:`) + +**Файл:** `/home/slin/repos/orchestrator/src/webhooks/gitea.py` + +**Что сделать:** При получении `status` event с `state=success`: +1. Найти task по branch +2. Если task в stage `development` и QG `check_ci_green` проходит → автоматически продвинуть в `review` и запустить Reviewer +3. Аналогично: если task в stage `review` и push содержит review file → проверить QG и продвинуть + +**Логика:** +```python +# В handle_status или handle_push: +if task.stage == "development" and ci_state == "success": + # Check QG + passed, reason = check_ci_green(repo, branch) + if passed: + advance_stage(task_id, "development", "review") + launch_reviewer(task_id, repo, branch) +``` + +**Зачем:** Сейчас после CI green Orchestrator просто логирует "waiting for CI" и ничего не делает. Нужен `:approved:` webhook для продвижения. По BRD это должно быть автоматически. + +**Проверка:** Push в feature-ветку → CI green → stage автоматически меняется на `review` без ручного webhook. + +### 5. Timeout для агентов (30 минут) + +**Файл:** `/home/slin/repos/orchestrator/src/agents/launcher.py` + +**Что сделать:** Добавить background thread/asyncio task который: +1. Через 30 минут после запуска проверяет — жив ли процесс +2. Если жив — kill -9 +3. Записывает в agent_runs: `exit_code=-9, finished_at=now()` +4. Логирует warning + +**Вариант реализации:** +```python +import threading + +def _watchdog(self, pid: int, run_id: int, timeout: int = 1800): + """Kill agent if it exceeds timeout.""" + import time, signal + time.sleep(timeout) + try: + os.kill(pid, signal.SIGKILL) + logger.warning(f"Agent run_id={run_id} killed after {timeout}s timeout") + conn = get_db() + conn.execute( + "UPDATE agent_runs SET finished_at=datetime('now'), exit_code=-9 WHERE id=?", + (run_id,) + ) + conn.commit() + conn.close() + except ProcessLookupError: + pass # Already finished + +# В launch(), после Popen: +t = threading.Thread(target=self._watchdog, args=(proc.pid, run_id), daemon=True) +t.start() +``` + +**Проверка:** Запустить агента с заведомо долгой задачей → через 30 мин процесс убит, в БД exit_code=-9. + +--- + +## Порядок выполнения + +1. Dockerfile (git) → rebuild +2. launcher.py (git env + task_id + timeout) +3. plane.py (task_id в вызове launch) +4. gitea.py (auto-advance после CI green) +5. Rebuild + restart + smoke test + +## Файлы для изменения + +- `/home/slin/repos/orchestrator/Dockerfile` +- `/home/slin/repos/orchestrator/src/agents/launcher.py` +- `/home/slin/repos/orchestrator/src/webhooks/plane.py` +- `/home/slin/repos/orchestrator/src/webhooks/gitea.py` + +## Ограничения + +- НЕ менять порт 8500 +- НЕ менять формат .env +- НЕ менять структуру БД (только использовать существующие колонки) +- НЕ добавлять новые зависимости (threading уже в stdlib) +- Тесты: `cd /home/slin/repos/orchestrator && python -m pytest tests/ -v` + +## Команды проверки после деплоя + +```bash +# 1. Git в контейнере +docker exec orchestrator git --version + +# 2. Health +curl -s http://localhost:8500/health + +# 3. Тесты +cd /home/slin/repos/orchestrator && python -m pytest tests/ -v + +# 4. Smoke: запустить агента и проверить task_id +docker exec orchestrator python -c " +from src.agents.launcher import launcher +run_id = launcher.launch('architect', 'enduro-trails', 'test', task_id=99) +print(f'run_id={run_id}') +import sqlite3 +conn = sqlite3.connect('/app/data/orchestrator.db') +conn.row_factory = sqlite3.Row +r = conn.execute('SELECT * FROM agent_runs WHERE id=?', (run_id,)).fetchone() +print(dict(r)) +" +```