auto-sync: 2026-05-21 20:40:02
This commit is contained in:
196
tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md
Normal file
196
tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md
Normal file
@@ -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))
|
||||
"
|
||||
```
|
||||
Reference in New Issue
Block a user