From f771a11e7c52feae2c5c33bccfe1a7d4d93357a5 Mon Sep 17 00:00:00 2001 From: Stream Date: Tue, 2 Jun 2026 20:00:01 +0300 Subject: [PATCH] auto-sync: 2026-06-02 20:00:01 --- memory/2026-06-02.md | 23 + .../DEV_TASK_ORCHESTRATOR_FIXES.md | 522 ++++++++++++------ tasks/multi-agent/DOCS_AUDIT_2026-06-02.md | 93 ++++ .../dev-2026-06-02-orchestrator-fixes.md | 38 ++ 4 files changed, 502 insertions(+), 174 deletions(-) create mode 100644 tasks/multi-agent/DOCS_AUDIT_2026-06-02.md create mode 100644 tasks/multi-agent/reports/dev-2026-06-02-orchestrator-fixes.md diff --git a/memory/2026-06-02.md b/memory/2026-06-02.md index 43bb3c4..47353c7 100644 --- a/memory/2026-06-02.md +++ b/memory/2026-06-02.md @@ -73,6 +73,29 @@ ssh slin@82.22.50.71 " --- +## Глубокий аудит оркестратора (мультиагенты) — 2026-06-02 + +### Проведено +- **Аудит кода:** `tasks/multi-agent/AUDIT_2026-06-02.md` — 3 BLOCKER, 5 SERIOUS, 7 MEDIUM, 5 LOW +- **Аудит документации:** `tasks/multi-agent/DOCS_AUDIT_2026-06-02.md` +- **ТЗ для Dev:** `tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md` (9 задач) + +### Корневые баги (BLOCKER) +- **B-1:** нет `docker` бинарника в контейнере orchestrator → `_write_task_file` через `docker run` падает молча → агент читает старый `.task-*.md` +- **B-2:** `Popen` + PIPE + daemon-поток → claude-зомби, `exit_code` теряется (в БД `exit=None` для всех ET-009) +- **B-3:** `.task-*.md` тащатся между задачами + коммитятся в git + +### Ключевая находка по докам +- **P1 zombie из `LESSONS_ET006.md` (22 мая) = мой B-2.** Баг известен 11 дней, не починен → повторился на ET-008/009 +- Системная проблема: lessons пишутся, но не конвертируются в задачи +- Расхождение код↔док: README говорит review QG=`check_review_approved`, код использует `check_reviewer_verdict` + +### Dev-агент запущен +- Model: **tokenator/claude-opus-4-8** (modelApplied: true) +- taskName: `orch_fixes`, runId: 2f18ca88 +- Deploy-хук `/home/slin/bin/enduro-deploy-hook.sh` СУЩЕСТВУЕТ и корректен +- В orchestrator были незакоммиченные правки (M launcher/config/notifications/plane_sync) — Dev разберёт в Task 1 + ## ET-009 Artifacts - **Plane issue**: `64e98247-509f-4c30-9955-a5531ab7d1ee` (sequence #5) - **Plane workspace**: `ag_proj`, project `7a79f0a9-5278-49cd-9007-9a338f238f9c` diff --git a/tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md b/tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md index c6c25ec..fa0fe4a 100644 --- a/tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md +++ b/tasks/multi-agent/DEV_TASK_ORCHESTRATOR_FIXES.md @@ -1,196 +1,370 @@ -# DEV_TASK_ORCHESTRATOR_FIXES.md — Фикс критических багов Orchestrator +# DEV TASK: Устранение багов оркестратора (по аудиту 2026-06-02) + +**Статус:** Ready for dev +**Проект:** multi-agent (orchestrator) +**Источник:** `tasks/multi-agent/AUDIT_2026-06-02.md` +**Исполнитель:** Dev-агент (model: tokenator/claude-opus-4-8) --- -type: dev-task -priority: high -project: multi-agent -created_at: 2026-05-21 -author: stream + +## Цель + +Вернуть автономность мультиагентного pipeline: после фиксов задача уровня ET-009 должна +пройти analyst→architect→developer→reviewer→tester→deploy **без ручного запуска агентов**. + +## Архитектура (корень проблем) + +Два BLOCKER-бага убивают автономность: +1. **Нет `docker` бинарника** в контейнере orchestrator → запись `.task-*.md` через `docker run` падает молча → агент читает старый таск-файл. +2. **`Popen` + PIPE + daemon-поток** → claude-процессы становятся зомби, `exit_code` теряется (в БД `exit=None`). + +Оба чинятся без docker: писать таск-файл напрямую в смонтированный volume `/repos`, а stdout агента перенаправлять сразу в файл на уровне ОС. + +## Стек / Зависимости + +- Python 3.12, FastAPI, uvicorn +- SQLite (`/app/data/orchestrator.db`) +- Claude CLI (`/opt/claude-code/bin/claude.exe`) +- Docker Compose (хост) + --- -## Контекст +## Инфраструктура -Первый полный автоматический прогон ET-002 выявил 5 критических багов, которые мешают автономной работе пайплайна. Без этих фиксов каждый переход между стадиями требует ручного вмешательства. +| Параметр | Значение | +|----------|----------| +| Сервер | `slin@82.22.50.71` (SSH key, без пароля) | +| Sudo пароль (если нужен) | `motoZ@yaz2010` (env MVA154_SUDO_PASS) | +| Репо оркестратора | `/home/slin/repos/orchestrator/` | +| Репо проекта | `/home/slin/repos/enduro-trails/` | +| Контейнер | `orchestrator` (network_mode: host, port 8500) | +| Volume repos | `/home/slin/repos` → `/repos` (RW) внутри контейнера | +| Деплой орка | `cd /home/slin/repos/orchestrator && docker compose up -d --build` | +| Health | `curl -s http://localhost:8500/health` → `{"status":"ok"}` | +| Тесты орка | `cd /home/slin/repos/orchestrator && .venv/bin/python -m pytest tests/ -v` | + +⚠️ **ВАЖНО:** В репо orchestrator есть НЕЗАКОММИЧЕННЫЕ правки (`git status`: M launcher.py, config.py, notifications.py, plane_sync.py). **СНАЧАЛА** прочитай их (`git diff`), пойми что там, **не потеряй** при своих изменениях. Если правки осмысленные — закоммить их отдельным коммитом ПЕРЕД своими фиксами. + +--- + +## Файловая карта + +| Действие | Файл | Ответственность | +|----------|------|-----------------| +| Изменить | `src/agents/launcher.py` | `_write_task_file` без docker; Popen stdout→файл; убрать PIPE-поток | +| Изменить | `Dockerfile` | (опц.) если оставляешь docker-вариант — не нужно; основной путь без docker | +| Изменить | `src/qg/checks.py` | `check_reviewer_verdict` читает `verdict:` из frontmatter | +| Изменить | `src/stages.py` + `src/qg/checks.py` | QG тестов гоняет сам оркестратор (make test), не Gitea CI | +| Изменить | `/repos/enduro-trails/.gitignore` | добавить `.task*.md` | +| Изменить | `enduro-trails/.openclaw/agents/reviewer.md` | требовать `verdict:` во frontmatter | +| Создать | `tests/test_launcher.py` | покрытие критичных функций launcher | +| Изменить | `src/main.py` | orphan-recovery: реально проверять/убивать pid | + +--- ## Задачи -### 1. Установить git в контейнер orchestrator +### Task 1: Прочитать контекст и закоммитить существующие правки -**Файл:** `/home/slin/repos/orchestrator/Dockerfile` +**Шаги:** +- [ ] **1.1** Прочитай `tasks/multi-agent/AUDIT_2026-06-02.md` целиком (он на твоём управляющем хосте у Стрим; если недоступен — Стрим передаст содержимое). Ключевые баги: B-1, B-2, B-3, S-1, S-5, M-1. +- [ ] **1.2** На сервере: `cd /home/slin/repos/orchestrator && git diff` — изучи незакоммиченные правки в launcher.py, config.py, notifications.py, plane_sync.py. +- [ ] **1.3** Если правки осмысленные и рабочие — закоммить: `git add -A && git commit -m "chore: save WIP changes before audit fixes"`. Если мусор — обсуди со Стрим, не удаляй молча. -**Что сделать:** Добавить `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. +**Критерий готовности:** рабочее дерево orchestrator чистое, история сохранена. --- -## Порядок выполнения +### Task 2: B-1 — запись `.task-*.md` без docker -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 +**Файл:** `src/agents/launcher.py` метод `_write_task_file` -## Файлы для изменения +**Проблема:** сейчас пишет через `docker run --rm -i python:3.12-slim bash -c "cat > {full_path}"`. Бинарника docker в контейнере НЕТ → падает молча. -- `/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` +**Шаги:** +- [ ] **2.1** Переписать `_write_task_file` на прямую запись в смонтированный volume. Репо смонтировано как `/repos` (это `settings.repos_dir`). Писать надо в `/repos//`, НЕ в host-путь. -## Ограничения - -- НЕ менять порт 8500 -- НЕ менять формат .env -- НЕ менять структуру БД (только использовать существующие колонки) -- НЕ добавлять новые зависимости (threading уже в stdlib) -- Тесты: `cd /home/slin/repos/orchestrator && python -m pytest tests/ -v` - -## Команды проверки после деплоя +```python +def _write_task_file(self, repo: str, task_file: str, content: str): + """Write task file directly to the mounted repo volume (/repos).""" + container_repo_path = os.path.join(settings.repos_dir, repo) # /repos/ + full_path = os.path.join(container_repo_path, task_file) + try: + with open(full_path, "w", encoding="utf-8") as f: + f.write(content) + logger.info(f"Task file written: {full_path} ({len(content)} bytes)") + except OSError as e: + logger.error(f"Failed to write task file {full_path}: {e}") + raise RuntimeError(f"Failed to write task file: {e}") +``` +- [ ] **2.2** Обнови вызов в `launch()`: сейчас `self._write_task_file(host_repo_path, config["task_file"], task_content)`. Поменяй на передачу `repo` (имя), а не host-пути: +```python +if task_content: + self._write_task_file(repo, config["task_file"], task_content) +``` +- [ ] **2.3** Проверка записи: ```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 " +docker exec orchestrator python3 -c " +import sys; sys.path.insert(0,'/app') 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)) +launcher._write_task_file('enduro-trails', '.task-test-write.md', 'hello-from-fix') +print(open('/repos/enduro-trails/.task-test-write.md').read()) +" +# Ожидаемо: hello-from-fix +# Потом удали тестовый файл: docker exec orchestrator rm /repos/enduro-trails/.task-test-write.md +``` + +**Критерий готовности:** таск-файл пишется без docker, при ошибке записи бросается исключение (не молчит). + +--- + +### Task 3: B-2 — Popen stdout прямо в файл, убрать PIPE-поток + +**Файл:** `src/agents/launcher.py` методы `launch`, `_monitor_agent` + +**Проблема:** `Popen(stdout=PIPE)` + поток-читатель с select → PIPE-deadlock + зомби при рестарте. `exit_code` теряется. + +**Шаги:** +- [ ] **3.1** В `launch()`: открывать лог-файл и передавать его дескриптор Popen напрямую: +```python +# output_path уже формируется выше +log_fh = open(output_path, "w") +proc = subprocess.Popen( + ["bash", "-c", cmd], + stdout=log_fh, + stderr=subprocess.STDOUT, + env={...}, # как было +) +# log_fh закроется в _monitor_agent после wait +``` +- [ ] **3.2** Упростить `_monitor_agent`: убрать весь блок чтения PIPE (select/readline/startup-timeout по «отсутствию вывода»). Оставить: +```python +def _monitor_agent(self, proc, run_id, agent, repo, branch, output_path=None, log_fh=None): + import time as _time + _start_ts = _time.time() + exit_code = proc.wait() # синхронно ждём, без PIPE + if log_fh: + try: log_fh.close() + except Exception: pass + _duration_s = int(_time.time() - _start_ts) + logger.info(f"Agent run_id={run_id} ({agent}) finished exit={exit_code}") + # ... дальше как было: UPDATE agent_runs, notify, git commit/push, advance +``` +- [ ] **3.3** Startup-timeout (агент молчит) больше НЕ нужен — watchdog по общему `AGENT_TIMEOUT` остаётся как есть (он по pid). Убедись, что watchdog корректно работает и не конфликтует. +- [ ] **3.4** Прокинуть `log_fh` в поток `_monitor_agent` через `args`. + +**Критерий готовности:** после прогона агента в `agent_runs.exit_code` записывается реальный код (0 при успехе), лог-файл непустой, зомби не остаются (`ps` в контейнере не показывает `` claude после завершения). + +--- + +### Task 4: B-3 — `.task-*.md` в gitignore + не коммитить + +**Шаги:** +- [ ] **4.1** В `/repos/enduro-trails/.gitignore` добавить строку: +``` +.task*.md +``` +- [ ] **4.2** Убрать уже закоммиченные таск-файлы из индекса (если есть): на ветке main проверь `git ls-files | grep '.task'`. Если они трекаются — `git rm --cached .task-*.md` и закоммить «chore: stop tracking runtime task files». +- [ ] **4.3** В `launcher._monitor_agent` git-commit логике: убедись, что `git add docs/` и `git add src/ tests/` НЕ цепляют `.task-*.md` (они и не должны после gitignore). + +**Критерий готовности:** `.task-*.md` не попадают в коммиты; `git status` чист после записи таск-файла. + +--- + +### Task 5: S-5 — машиночитаемый verdict ревьюера + +**Файлы:** `src/qg/checks.py` (`check_reviewer_verdict`), `enduro-trails/.openclaw/agents/reviewer.md` + +**Проблема:** парсинг ищет подстроки `APPROVED`/`REQUEST_CHANGES` во всём тексте → ложные срабатывания на таблицах с этими словами. + +**Шаги:** +- [ ] **5.1** В `reviewer.md` добавить требование: отчёт `12-review.md` ОБЯЗАН начинаться с YAML-frontmatter с полем `verdict`: +```markdown +--- +type: review +work_item_id: +verdict: APPROVED # либо REQUEST_CHANGES +version: +--- +``` +(аналогично tester'у, у которого уже `verdict: PASS`) +- [ ] **5.2** Переписать `check_reviewer_verdict` — читать ТОЛЬКО frontmatter: +```python +def check_reviewer_verdict(repo, work_item_id): + import yaml + repo_path = os.path.join(settings.repos_dir, repo) + review_path = os.path.join(repo_path, f"docs/work-items/{work_item_id}/12-review.md") + if not os.path.isfile(review_path): + return False, "Review report not found (12-review.md)" + try: + with open(review_path) as f: + content = f.read() + verdict = None + if content.startswith("---"): + parts = content.split("---", 2) + if len(parts) >= 3: + fm = yaml.safe_load(parts[1]) or {} + verdict = str(fm.get("verdict", "")).upper() + if verdict == "APPROVED": + return True, "Reviewer verdict: APPROVED" + if verdict == "REQUEST_CHANGES": + return False, "Reviewer verdict: REQUEST_CHANGES" + # Fallback (старый формат без frontmatter) — оставить старый парсинг как запасной + return False, f"No machine-readable verdict in frontmatter (got: {verdict!r})" + except Exception as e: + return False, f"Error reading review: {e}" +``` +- [ ] **5.3** Проверка на реальном файле ET-009: +```bash +docker exec orchestrator python3 -c " +import sys; sys.path.insert(0,'/app') +from src.qg.checks import check_reviewer_verdict +print(check_reviewer_verdict('enduro-trails','ET-009')) " ``` +(если у ET-009 12-review.md нет frontmatter с verdict — это ок, fallback вернёт False; главное чтобы НЕ падало) + +**Критерий готовности:** verdict читается из frontmatter, таблицы с APPROVED/REQUEST_CHANGES в тексте больше не влияют. + +--- + +### Task 6: S-1 — QG тестов гоняет сам оркестратор (не Gitea CI) + +**Файлы:** `src/qg/checks.py`, `src/stages.py` + +**Проблема:** `development → review` QG = `check_ci_green` дёргает Gitea status. CI в Gitea НЕ настроен → всегда false → автопереход не происходит, ложные алерты. + +**Шаги:** +- [ ] **6.1** Добавить новый QG `check_tests_local` — оркестратор сам запускает тесты проекта: +```python +def check_tests_local(repo, branch): + """Run project test suite locally in /repos/, judge by exit code.""" + import subprocess + repo_path = os.path.join(settings.repos_dir, repo) + try: + # checkout нужной ветки + subprocess.run(["git","-C",repo_path,"fetch","origin"], capture_output=True, timeout=30) + subprocess.run(["git","-C",repo_path,"checkout",branch], capture_output=True, timeout=30) + # запуск тестов (enduro-trails: make test) + r = subprocess.run(["make","test"], cwd=repo_path, capture_output=True, text=True, timeout=600) + if r.returncode == 0: + return True, "Local tests passed" + tail = (r.stdout + r.stderr)[-500:] + return False, f"Local tests failed: ...{tail}" + except subprocess.TimeoutExpired: + return False, "Local tests timed out" + except Exception as e: + return False, f"Local test run error: {e}" +``` +⚠️ **Грабля:** локальный прогон тестов в shared `/repos` делает checkout — на этом этапе других активных задач быть не должно (см. S-4, отдельная задача worktree — НЕ в этом ТЗ). Пока что приемлемо. +- [ ] **6.2** В `stages.py` поменять QG перехода `development`: +```python +"development": {"next": "review", "agent": "reviewer", "qg": "check_tests_local"}, +``` +- [ ] **6.3** Зарегистрировать в `QG_CHECKS`: `"check_tests_local": check_tests_local`. +- [ ] **6.4** В `launcher._try_advance_stage` и `webhooks/plane._try_advance_stage` добавить ветку аргументов: `check_tests_local` принимает `(repo, branch)` как `check_ci_green`. +- [ ] **6.5** Убрать/смягчить ложные «CI failed» алерты в `webhooks/gitea.handle_ci_status` — если CI не сконфигурирован, не слать notify_error. (Можно: реагировать на `status` только если task в development И state явно `failure` с непустым контекстом; иначе debug-лог.) + +**Критерий готовности:** переход development→review происходит после успешного локального прогона тестов, без зависимости от Gitea CI; ложных «CI failed» нет. + +--- + +### Task 7: M-1 — нормальный orphan-recovery + +**Файл:** `src/main.py` lifespan + +**Проблема:** сейчас просто `UPDATE ... exit_code=-1 WHERE finished_at IS NULL AND started_at < now-35min` — маскирует зомби, не убивает, не уведомляет. + +**Шаги:** +- [ ] **7.1** При старте — для каждого orphan-run: пометить exit=-1 (как сейчас) И поставить связанную задачу в стейт, требующий внимания (лог + Telegram-уведомление, что задача X прервана и нужен ручной перезапуск/проверка). НЕ пытайся автоперезапускать (можно зациклить). +- [ ] **7.2** Лог явно: `logger.warning(f"Orphan run {id} (task {tid}, agent {agent}) recovered — manual check needed")`. + +**Критерий готовности:** orphan'ы не молча списываются — есть уведомление. + +--- + +### Task 8: тесты + документация + +**Шаги:** +- [ ] **8.1** Создать `tests/test_launcher.py`: покрыть `_write_task_file` (пишет в правильный путь, бросает при ошибке) и парсинг verdict (`check_reviewer_verdict` с frontmatter APPROVED/REQUEST_CHANGES/без verdict). Использовать tmp-пути/моки, без реального запуска claude. +- [ ] **8.2** Прогнать всё: `cd /home/slin/repos/orchestrator && .venv/bin/python -m pytest tests/ -v` — всё зелёное. +- [ ] **8.3** Обновить `docs/ARCHITECTURE.md` и `README.md`: + - Убрать упоминание, что `.task` пишется через docker. + - Исправить таблицу QG: `development` QG теперь `check_tests_local` (не `check_ci_green`); `review` QG = `check_reviewer_verdict` (в README сейчас ошибочно `check_review_approved`). + - Добавить раздел «Известные ограничения» (shared /repos checkout — гонки при параллельных задачах; worktree запланирован отдельно). +- [ ] **8.4** Создать `docs/BUGFIXES_2026-06-02.md` — что починено (B-1, B-2, B-3, S-1, S-5, M-1), как проверено. + +**Критерий готовности:** тесты зелёные, доки отражают реальность. + +--- + +### Task 9: Деплой и проверка автономности + +**Шаги:** +- [ ] **9.1** Закоммитить всё (Conventional Commits, отдельные коммиты по задачам), запушить в main orchestrator. +- [ ] **9.2** Пересобрать и поднять оркестратор: +```bash +cd /home/slin/repos/orchestrator && docker compose up -d --build +sleep 5 && curl -s http://localhost:8500/health +``` +- [ ] **9.3** **Тест автономности (главный критерий):** запустить тестовую задачу через нормальный путь (НЕ ручной base64). Способ согласуй со Стрим — вариант: создать Plane work item или дёрнуть `launcher.launch("analyst", ...)` штатно и проверить, что: + - таск-файл записался свежий (B-1) + - агент отработал, `exit_code=0` в БД (B-2) + - зомби не осталось + - автопереход на следующую стадию сработал +- [ ] **9.4** Отчитаться Стрим: что прошло автономно, что нет. + +**Критерий готовности:** хотя бы один полный stage-переход прошёл БЕЗ ручного запуска агента. + +--- + +## Проверка (Acceptance) — что Стрим проверит + +| # | Проверка | Команда | Ожидаемо | +|---|----------|---------|----------| +| 1 | Запись таск-файла без docker | Task 2.3 | файл создан, без docker | +| 2 | exit_code пишется | прогон агента → `SELECT exit_code FROM agent_runs ORDER BY id DESC LIMIT 1` | не NULL | +| 3 | Нет зомби | `docker exec orchestrator ps aux \| grep defunct` | пусто после завершения | +| 4 | verdict из frontmatter | Task 5.3 | читается корректно | +| 5 | tests-QG локально | переход dev→review | по make test | +| 6 | `.task` не в git | `git status` после записи | clean | +| 7 | тесты орка зелёные | `pytest tests/ -v` | all pass | +| 8 | health | `curl :8500/health` | `{"status":"ok"}` | +| 9 | доки обновлены | чтение ARCHITECTURE/README | соответствуют коду | + +--- + +## Ограничения и контекст + +- ⚠️ **НЕ потеряй незакоммиченные правки** в orchestrator (Task 1) — сначала разберись с ними. +- ⚠️ **Запись только в `/repos`** (смонтированный volume), НЕ в host-пути — внутри контейнера host-путь невалиден. +- ⚠️ **Docker внутри контейнера orchestrator НЕДОСТУПЕН** — не закладывайся на docker-команды в коде орка. Деплой проекта идёт через SSH-хук `/home/slin/bin/enduro-deploy-hook.sh` (он рабочий, не трогай). +- ⚠️ **shared `/repos` checkout** — git worktree НЕ в этом ТЗ (отдельная задача S-4). Просто не ломай текущее поведение. +- ⚠️ **Deploy-хук существует и корректен** — `enduro-deploy-hook.sh` делает `git pull + docker compose up -d app [+ gps-collector]`. НЕ переписывай. +- 🚫 **НЕ трогай** nginx, `openclaw.json`, секреты в `.env`. +- 🚫 **НЕ меняй** Plane states mapping без необходимости. +- 🚫 **НЕ удаляй** мёртвый код `_auto_merge_pr` молча — оставь, отметь TODO (отдельная чистка). + +## Что НЕ входит в это ТЗ (отдельные задачи на потом) +- S-2/S-3 (rollback деплоера в shared-репо) — отдельно +- S-4 (git worktree per task) — отдельно, крупное +- M-3 (единый stage-engine, дубль `_try_advance_stage`) — желательно, но осторожно; если успеешь и уверен — можно, иначе отдельно +- F-2b (очередь задач вместо daemon-потоков) — крупный рефактор, отдельно +- M-7 (идемпотентность webhook) — отдельно + +--- + +## Деплой-чеклист +- [ ] Существующие WIP-правки разобраны и закоммичены +- [ ] B-1, B-2, B-3, S-5, S-1, M-1 реализованы +- [ ] Тесты launcher созданы и зелёные +- [ ] Доки обновлены (ARCHITECTURE, README, BUGFIXES_2026-06-02) +- [ ] orchestrator пересобран и поднят, health ok +- [ ] Тест автономности пройден (хотя бы 1 stage без ручника) +- [ ] Нет ошибок в логах (`docker logs orchestrator --tail 50`) +- [ ] Отчёт Стрим: что автономно, что нет + +--- + +*Создано: 2026-06-02 | Автор ТЗ: Стрим | Исполнитель: Dev-агент (Opus 4.8 Tokenator)* diff --git a/tasks/multi-agent/DOCS_AUDIT_2026-06-02.md b/tasks/multi-agent/DOCS_AUDIT_2026-06-02.md new file mode 100644 index 0000000..1768a33 --- /dev/null +++ b/tasks/multi-agent/DOCS_AUDIT_2026-06-02.md @@ -0,0 +1,93 @@ +# Аудит документации оркестратора — 2026-06-02 + +Проверка всех доков `orchestrator/docs/` + `README.md` + агентских промптов на старьё и неактуальность. + +## Сводка + +| Документ | Дата | Статус | Вердикт | +|----------|------|--------|---------| +| `README.md` | — | ⚠️ Частично устарел | Правка (QG-таблица врёт) | +| `docs/ARCHITECTURE.md` | 31.05 | ⚠️ Частично устарел | Правка (QG, docker-запись) | +| `docs/BACKLOG_PIPELINE.md` | 23.05 | ✅ Актуален | Оставить (открытые вопросы валидны) | +| `docs/BUGFIXES_2026-05-21.md` | 22.05 | ✅ Исторический | Оставить как есть (лог) | +| `docs/LESSONS_ET006.md` | 22.05 | 🔴 Критично актуален | Баг P1 НЕ починен — эскалировать | +| `docs/SETUP_WEBHOOKS.md` | 21.05 | ✅ Актуален | Оставить (инструкция рабочая) | + +--- + +## 🔴 Главная находка: P1 zombie из ET-006 жив до сих пор + +`LESSONS_ET006.md` (22 мая) раздел **P1. Zombie processes**: +> Monitor threads умирают при `docker compose up --build`, agent процессы остаются zombie. +> Daemon threads в Python не переживают restart, child processes (claude.exe) наследуются init. + +**Это ТОЧНО тот же баг B-2 из моего аудита 02.06.** Предложены 3 решения (A: safe.directory, B: orphan recovery, C: tini). Применены только частично: +- ✅ Решение B (orphan recovery) — есть в `main.py`, но грубый (M-1) +- ✅ tini — есть в compose (`tini -s -- node`... но это для node, не для orchestrator-контейнера; **проверить**) +- 🔴 Корневой PIPE-deadlock + daemon-поток — НЕ устранён + +**Вывод:** lessons-док зафиксировал баг 11 дней назад, но фикс не довели. Урок не выучен → повторился на ET-008 и ET-009. Это системная проблема: **lessons пишутся, но не конвертируются в задачи.** + +Аналогично: +- **P2 stale reviews** — workaround «ручной re-approve», корня (отключить dismiss_stale_approvals) не сделали → ручник повторяется. +- **P3 Plane sync 404** — помечен LOW, до сих пор сломан (в моём аудите M-6). +- **P6 tester timeout** — actual на ET-009 (collector 25 мин). + +--- + +## Постраничный разбор + +### README.md — ⚠️ правка +**Устарело:** +- Таблица «Стадии пайплайна»: `development | developer | CI green` — на деле CI в Gitea нет, QG не отрабатывает. После фикса S-1 будет `local tests`. +- `review | reviewer | PR approved (no stale)` — но код использует `check_reviewer_verdict` (файловый), не `check_review_approved` (PR-based). **Расхождение код↔док.** +- Структура `src/` описана верно. + +**Действие:** входит в Task 8 Dev-ТЗ (Dev обновит QG-таблицу). + +### docs/ARCHITECTURE.md — ⚠️ правка +**Устарело:** +- Таблица QG: `check_review_approved` для review — но в `stages.py` стоит `check_reviewer_verdict`. Док отстал от кода. +- Не упоминает, что `.task` пишется через docker (и что это сломано). +- Описывает «идеальный» автономный pipeline, который из-за B-1/B-2 не работает. + +**Действие:** Dev обновляет в Task 8. Добавить раздел «Известные ограничения». + +### docs/BACKLOG_PIPELINE.md — ✅ актуален +BL-001 (аудит вне work item) и BL-002 (управление бэклогом) — открытые архитектурные вопросы, всё ещё валидны. BL-002 даже отмечает «Plane sync сломан» — подтверждает M-6. +**Действие:** оставить. Можно добавить отметку, что Plane sync — кандидат на фикс. + +### docs/BUGFIXES_2026-05-21.md — ✅ исторический лог +5 багов ET-005, все помечены исправленными. Это лог-запись, трогать не надо. Полезен как история. +**Действие:** оставить как есть. + +### docs/LESSONS_ET006.md — 🔴 критично актуален +См. выше. Документ ОТЛИЧНЫЙ по содержанию, но его выводы не реализованы. +**Действие:** НЕ удалять. Наоборот — использовать как доказательство, что фиксы назрели. После Dev-фиксов добавить пометку «P1 устранён в BUGFIXES_2026-06-02». + +### docs/SETUP_WEBHOOKS.md — ✅ актуален +Инструкция по настройке webhook'ов Gitea/Plane. URL, события, секреты — соответствуют конфигу. Рабочий референс. +**Действие:** оставить. + +--- + +## Расхождения код ↔ документация (для исправления) + +| # | Док говорит | Код делает | Где править | +|---|-------------|-----------|-------------| +| 1 | review QG = `check_review_approved` | `check_reviewer_verdict` | README, ARCHITECTURE | +| 2 | development QG = CI green | CI нет, не работает | README, ARCHITECTURE (после S-1) | +| 3 | `.task` запись (не описано) | через docker (сломано) | ARCHITECTURE | +| 4 | pipeline автономен | ручной запуск из-за B-1/B-2 | ARCHITECTURE «Известные ограничения» | + +--- + +## Рекомендации по процессу (мета) + +1. **Lessons → задачи.** `LESSONS_ET006` зафиксировал баги, но их не конвертировали в трекаемые задачи → повтор на ET-008/009. Завести правило: каждый P0/P1 из lessons → задача в бэклоге с дедлайном. +2. **Один changelog фиксов.** `BUGFIXES_2026-05-21` + будущий `BUGFIXES_2026-06-02` — ок, но нужен сводный индекс «что починено / что висит». +3. **Doc-as-code QG.** В идеале — тест, проверяющий что таблица QG в README совпадает с `stages.py`. Чтобы доки не расходились с кодом. + +--- + +*Аудитор: Стрим | Дата: 2026-06-02 | Связан с: AUDIT_2026-06-02.md, DEV_TASK_ORCHESTRATOR_FIXES.md* diff --git a/tasks/multi-agent/reports/dev-2026-06-02-orchestrator-fixes.md b/tasks/multi-agent/reports/dev-2026-06-02-orchestrator-fixes.md new file mode 100644 index 0000000..2a67b32 --- /dev/null +++ b/tasks/multi-agent/reports/dev-2026-06-02-orchestrator-fixes.md @@ -0,0 +1,38 @@ +# Dev Report: Устранение багов оркестратора (аудит 2026-06-02) +Дата: 2026-06-02 +Статус: IN PROGRESS + +## Задача +Реализовать фиксы B-1, B-2, B-3, S-5, S-1, M-1 по ТЗ DEV_TASK_ORCHESTRATOR_FIXES.md. +Вернуть автономность мультиагентного pipeline. + +## Окружение (факты) +- Сервер slin@82.22.50.71, репо /home/slin/repos/orchestrator +- Контейнер orchestrator: Python 3.12, pytest 9, yaml/httpx есть. Health ok. +- ⚠️ Расхождение с ТЗ: `.venv` на хосте — пустые symlink'и на system python3.10, pytest НЕ установлен. + Тесты гоняю ВНУТРИ контейнера: `docker exec orchestrator bash -c "cd /repos/orchestrator && PYTHONPATH=/repos/orchestrator pytest tests/ -q"`. +- ⚠️ Dockerfile делает `COPY src/`, НЕ mount. Значит мои правки src/ применятся только после rebuild. + `/repos` смонтирован → тесты в контейнере видят свежий код через /repos/orchestrator. +- Pre-existing: 9 тестов test_webhooks падают (401/signature, env-зависимо) — НЕ мои, были до меня. test_qg — 16 passed. + +## Сделано +- [x] Task 1.1 — прочитал AUDIT + DEV_TASK +- [x] Task 1.2 — изучил git diff (launcher, config, notifications, plane_sync, qg/checks, stages) +- [ ] Task 1.3 — закоммитить WIP +- [ ] Task 2 — B-1 _write_task_file без docker +- [ ] Task 3 — B-2 Popen stdout→файл +- [ ] Task 4 — B-3 .gitignore +- [ ] Task 5 — S-5 verdict frontmatter +- [ ] Task 6 — S-1 check_tests_local +- [ ] Task 7 — M-1 orphan recovery +- [ ] Task 8 — тесты + доки +- [ ] Task 9 — деплой + автономность + +## Изменённые файлы +(заполняется по ходу) + +## Проблемы и решения +- venv хоста сломан → тесты в контейнере. + +## Следующий шаг +Закоммитить WIP, затем Task 2.