[2026-09-28] eagle: personal/projects/balda/balda-mm-delegation-plan.md personal/tech/agents-bridge.md
This commit is contained in:
1 parent
4a6b5bcb70
commit
b219b5c2cb
2 files changed
+81
-12
No files matched your search
@@ -1,9 +1,13 @@
|
|||||||
# Balda 2.0 — Mattermost + распределённое делегирование (план)
|
# Balda 2.0 — Mattermost + распределённое делегирование (план)
|
||||||
|
|
||||||
> Создан 2026-09-25. Статус: **Фазы 1-3 закрыты 2026-09-26** (MM на Kraken · MM-адаптер
|
> Создан 2026-09-25. Статус (обновлён **2026-09-28**): **Фазы 1-3 закрыты** (MM на Kraken ·
|
||||||
> в коде · оркестратор развёрнут, **бот отвечает в MM**). Фаза 2 выведена на **PR
|
> MM-адаптер в коде · оркестратор развёрнут, **бот отвечает в MM**). Фаза 2 выведена на **PR
|
||||||
> `baldaworks/balda#8`** — 8/8 CI PASS, ждёт ревью. Следующая — Фаза 4 (исполнитель).
|
> `baldaworks/balda#8`** — 8/8 CI PASS, `MERGEABLE`, `reviewDecision` пустой → ждёт ревью
|
||||||
> Alex дал команду исполнять без вопросов.
|
> апстрима (внешний блокер, не наша работа).
|
||||||
|
> 🔴 **Фаза 4 собрана и исправна на всех звеньях, КРОМЕ одного: CLI в контейнере-исполнителе
|
||||||
|
> не залогинен.** Любая реальная задача через мост падает `Not logged in · Please run /login`
|
||||||
|
> (аудит 2026-09-28 живыми вызовами). Это последний блокер перед Фазой 5.
|
||||||
|
> Диагноз + два пути фикса: [[personal/plans/balda-mm-phase4-agents]] **§20**.
|
||||||
> Задача: поднять свежую Балду из `baldaworks/balda` (без наших коммитов), завязать на Mattermost,
|
> Задача: поднять свежую Балду из `baldaworks/balda` (без наших коммитов), завязать на Mattermost,
|
||||||
> построить распределённое делегирование (дешёвый оркестратор + подписочные CLI в VM).
|
> построить распределённое делегирование (дешёвый оркестратор + подписочные CLI в VM).
|
||||||
>
|
>
|
||||||
@@ -323,14 +327,27 @@ PR: `gh pr create --repo baldaworks/balda --base main --head mallexxx:feat/matte
|
|||||||
🔴 **Доказано живым тестом: симлинк в Docker НЕ читается** → внешние проекты
|
🔴 **Доказано живым тестом: симлинк в Docker НЕ читается** → внешние проекты
|
||||||
подключать **bind-mount'ом**, не линком. Hardlink — только для файлов внутри
|
подключать **bind-mount'ом**, не линком. Hardlink — только для файлов внутри
|
||||||
одного тома (не пересекает ФС, каталоги не линкует).
|
одного тома (не пересекает ФС, каталоги не линкует).
|
||||||
1. Docker-контейнер (или UTM VM) с Node 24 + `codex` + `claude` + `git`.
|
1. ✅ Docker-контейнер с Node 24 + `codex` + `claude` + `git` — собран, Up.
|
||||||
2. Авторизация: `claude setup-token` на Mac → `CLAUDE_CODE_OAUTH_TOKEN` в контейнер; для codex — свой flow.
|
2. 🔴 **Авторизация — ЕДИНСТВЕННЫЙ НЕЗАКРЫТЫЙ ПУНКТ.** Плановый путь
|
||||||
3. Bind-mount проектной директории (не весь `~`).
|
`claude setup-token` → `CLAUDE_CODE_OAUTH_TOKEN` **НЕ выполнен**: логина в
|
||||||
4. Мост: FastAPI-сервис, принимает задачу от Kraken, запускает CLI headless, возвращает diff/тесты.
|
`~/cli-auth/claude` нет, любая задача падает `Not logged in · Please run /login`
|
||||||
5. Идемпотентность + таймауты + лимит параллельных задач (не более 1-2, чтобы не ловить «unusual traffic patterns»).
|
(проверено 2026-09-28 живым вызовом моста). Диагноз и два пути фикса —
|
||||||
|
[[personal/plans/balda-mm-phase4-agents]] **§20**.
|
||||||
|
3. ✅ Bind-mount проектной директории (не весь `~`).
|
||||||
|
4. ✅ Мост FastAPI (:8790, launchd `com.agents.bridge`), принимает задачу от Kraken,
|
||||||
|
запускает CLI headless, возвращает stdout/exit_code.
|
||||||
|
5. ✅ Идемпотентность + таймаут 900 с + лимит 1 параллельная задача.
|
||||||
|
6. ✅ Балда → мост: MCP-сервер `balda-executor-mcp` (Kraken), образ `balda-mm:0.1.13`,
|
||||||
|
MCP-инструменты **исполняются** (приёмка `executor_health: ok` 2026-09-27).
|
||||||
|
|
||||||
|
📌 **Итог на 2026-09-28:** вся цепочка MM → Балда → MCP → мост → docker exec → CLI
|
||||||
|
собрана и исправна, кроме логина CLI. Закрыть его → Фаза 4 закрыта, Фаза 5 (сквозная
|
||||||
|
приёмка + расходы) открывается.
|
||||||
|
|
||||||
### Фаза 5 — связка и приёмка
|
### Фаза 5 — связка и приёмка
|
||||||
1. Сквозной тест: сообщение в MM → Балда → мост → CLI → результат обратно в MM.
|
1. Сквозной тест: сообщение в MM → Балда → мост → CLI → результат обратно в MM.
|
||||||
|
⏳ **Заблокировано логином CLI** (см. Фаза 4 п.2). Пока проходит только до вызова
|
||||||
|
самого CLI: `executor_health` возвращается, реальная задача — нет.
|
||||||
2. Проверить расходы: DeepSeek-токены оркестратора + подписочные лимиты исполнителя.
|
2. Проверить расходы: DeepSeek-токены оркестратора + подписочные лимиты исполнителя.
|
||||||
3. Записать в Obsidian: как работает, где логи, как откатить.
|
3. Записать в Obsidian: как работает, где логи, как откатить.
|
||||||
|
|
||||||
|
|||||||
@@ -191,6 +191,36 @@ auth-файлов из `~/.claude`/`~/.codex` отклонено, не пред
|
|||||||
порт свободен, форвард выключен администратором. Обход — Cloudflare Tunnel
|
порт свободен, форвард выключен администратором. Обход — Cloudflare Tunnel
|
||||||
(исходящее соединение, не SSH-форвард).
|
(исходящее соединение, не SSH-форвард).
|
||||||
|
|
||||||
|
### 8. 🔴 `GET /task/{id}` и `/tasks` требуют секрет; `/healthz` — нет
|
||||||
|
|
||||||
|
Проверено 2026-09-28. Асимметрия: `/healthz` открыт (это осознанно — по нему проверяют
|
||||||
|
и туннель, и мониторинг), а **чтение задач тоже закрыто**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s http://127.0.0.1:8790/tasks # → {"detail":"unauthorized"}
|
||||||
|
curl -s -H "X-Bridge-Secret: $SECRET" .../tasks # → JSON со списком
|
||||||
|
```
|
||||||
|
|
||||||
|
Симптом ошибки: `jq` падает `Cannot index object with object` — потому что в ответе
|
||||||
|
объект `{"detail":"unauthorized"}`, а не массив. **Не `jq` сломан** — просто нет
|
||||||
|
заголовка. Всегда проверять `head -c 200` перед разбором.
|
||||||
|
|
||||||
|
📌 Полезно: ответ `GET /task/{id}` достаточно детален, чтобы поставить диагноз без логов
|
||||||
|
контейнера — `status`, `exit_code`, `duration_s`, `stdout`, `stderr`, `error`.
|
||||||
|
`duration_s < 1` при `exit_code: 1` = CLI упал мгновенно на своей проверке (напр.
|
||||||
|
авторизации), а не на реальной работе.
|
||||||
|
|
||||||
|
### 9. 🔴 Кратковременные задачи могут стать `failed` без `stdout`/`error`
|
||||||
|
|
||||||
|
В списке `/tasks` видны исторические задачи с `exit_code: 1`, `duration_s ~0.7-0.8`,
|
||||||
|
`stdout: null`, `error: null` — тогда как другая задача того же класса отдала
|
||||||
|
содержательный `error` (`Claude configuration file not found at: /root/.claude.json`).
|
||||||
|
|
||||||
|
Следствие: **пустой `stdout` не значит «задача не запускалась».** Смотреть на связку
|
||||||
|
`status=failed` + `exit_code=1` + `duration_s`, а причину добирать из логов контейнера
|
||||||
|
(`docker logs balda-executor`) или повтором задачи. Не делать вывод «мост не вызывает CLI»
|
||||||
|
по пустому ответу одной задачи.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Порядок ручной приёмки (сработавший)
|
## Порядок ручной приёмки (сработавший)
|
||||||
@@ -200,11 +230,27 @@ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:8790/task \
|
|||||||
-H 'Content-Type: application/json' -d '{"prompt":"hi"}' # → 401 (нет секрета)
|
-H 'Content-Type: application/json' -d '{"prompt":"hi"}' # → 401 (нет секрета)
|
||||||
|
|
||||||
# с секретом и одним Idempotency-Key дважды → один и тот же task_id, 202 затем 200
|
# с секретом и одним Idempotency-Key дважды → один и тот же task_id, 202 затем 200
|
||||||
# GET /task/{id} → status/stdout/exit_code
|
# GET /task/{id} → status/stdout/exit_code (⚠️ С секретом, иначе unauthorized)
|
||||||
```
|
```
|
||||||
|
|
||||||
Тестовый скрипт: `/tmp/test-bridge.sh` (читает секрет из `.env`, в командной строке
|
📌 **Секрет из `.env` в inline-команде собирать скриптом, а не литералом.** Строка вида
|
||||||
секрет не светится).
|
`SECRET=$(grep … .env | cut …)` в инлайн-шелле ломается на кавычках и на редакции секретов
|
||||||
|
Hermes — рабочий вариант: положить в файл (`write_file`) и запустить `bash /tmp/x.sh`.
|
||||||
|
Секрет в командной строке не светится.
|
||||||
|
|
||||||
|
Тестовый скрипт: `/tmp/test-bridge.sh`, `/tmp/probe-exec.sh` (читают секрет из `.env`).
|
||||||
|
|
||||||
|
### Проверка авторизации CLI одной задачей
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ожидаемо при незалогиненном CLI: status=failed, exit_code=1, duration_s<1,
|
||||||
|
# stdout="Not logged in · Please run /login"
|
||||||
|
# при рабочем логине: status=succeeded, реальный текст модели
|
||||||
|
```
|
||||||
|
|
||||||
|
`/tmp/probe-exec.sh <task_id>` — печатает полный JSON задачи **и** состояние ФС
|
||||||
|
контейнера (`ls -la /root/`), чтобы сразу видеть, есть ли `.claude.json`/креды.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -229,3 +275,9 @@ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:8790/task \
|
|||||||
(`AllowTcpForwarding no`).
|
(`AllowTcpForwarding no`).
|
||||||
- **2026-09-26** (MCP): `executor_health` из контейнера `balda-executor-mcp`
|
- **2026-09-26** (MCP): `executor_health` из контейнера `balda-executor-mcp`
|
||||||
(docker-сеть Kraken) вернул живые данные моста — связка Kraken→Mac работает.
|
(docker-сеть Kraken) вернул живые данные моста — связка Kraken→Mac работает.
|
||||||
|
- **2026-09-28**: аудит Фазы 4 живыми вызовами. Мост + туннель + контейнер исправны,
|
||||||
|
задача доходит до CLI, но падает `Not logged in · Please run /login` — **CLI в контейнере
|
||||||
|
не авторизован**, это единственный блокер Фазы 4. В хост-монтировании `~/cli-auth/claude/`
|
||||||
|
**нет** `.claude.json` — только `backups/`, `projects/`, `sessions/`. Добавлены §8
|
||||||
|
(чтение задач требует секрет) и §9 (failed-задачи без stdout). Разбор:
|
||||||
|
[[personal/plans/balda-mm-phase4-agents]] §20.
|
||||||
Reference in new issue
Block a user