auto-sync: 2026-05-21 20:40:02

This commit is contained in:
Stream
2026-05-21 20:40:04 +03:00
parent 4a03e01837
commit 494adb09f6

View 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))
"
```