[2026-09-28] eagle: personal/plans/balda-mm-phase4-agents.md personal/plans/balda-mm-pr-plan.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-28 11:44:54 +06:00
1 parent b49ef14a34
commit a03203fc96
2 files changed
+336 -363

No files matched your search

+136 -2
View File
@@ -25,6 +25,7 @@
| 6g | A/B-эксперимент откатом на `0.1.11` | ✅ **доведён 2026-09-27** — `0.1.11` падает С ТЕМ ЖЕ `duplicate tool`. Гипотеза §16.2 («форк обнажил») **ОПРОВЕРГНУТА**: баг был всегда, независимо от раунда. См. §16.4 |
| 6h | Фикс дублирующегося тулсета | ✅ **ИСПРАВЛЕНО 2026-09-27, `balda-mm:0.1.13` задеплоен** — дедуп по эндпоинту в `hostedToolsets` (`runtime-local/agentfactory/agentfactory.go`) + 6 тестов. `go build`/`go vet`/`go test ./...` ✅. Приёмка от владельца: `@balda вызови executor_health` → ответ `**executor_health:** ok`, `turn_complete: true`, `duplicate tool:` = **0**, deadletter = **0**. См. §19.3-19.5 |
| 7 | Приёмка от аккаунта Alex | ✅ **ПРОЙДЕНА 2026-09-27** — пост владельца `cbwsorzwoigbtrdnteugnx4aqo` → ответ бота `edxrgupoxiyig5gzmr79tjypbme`: `**executor_health:** ok`. `settlement_stage: accepted`, `turn_complete: true`, инструмент исполнился. См. §17, §19.5 |
| 8 | 🔴 **CLI исполнителя НЕ залогинен → реальная задача не проходит** | 🔴 **Открыто 2026-09-28** — весь конвейер живой, упирается ТОЛЬКО в логин CLI. `POST /task` через мост → `exit_code: 1`, `duration 0.8 c`, `stdout: "Not logged in · Please run /login"`. Диагноз, доказательства и два пути фикса — **§20**. Ждёт решения Alex |
---
@@ -315,8 +316,12 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN
🔴 **Продолжение 2 (§§15-16, 2026-09-27):** инструменты всё равно не исполнялись — в
`runtime v2` вырезан tool_calls. Форк собран и задеплоен (`0.1.12`), и **обнажил** двойную
паковку инструментов (`duplicate tool` вернулся). Разбор — §16.
4. 🟡 Логин CLI (Alex вручную) — метод приёмки в обход (admin PAT, §17) есть, но сквозная
приёмка упирается в `duplicate tool` (§16).
4. 🟡 Логин CLI (Alex вручную) — **ЭТО ЕДИНСТВЕННЫЙ ОСТАВШИЙСЯ БЛОКЕР Фазы 4**
(перепроверено 2026-09-28). Прежняя формулировка «сквозная приёмка упирается в
`duplicate tool` (§16)» **устарела** — тот дефект закрыт 27.09 (`0.1.13`, §19.5).
Сейчас путь такой: конвейер целиком исправен, `executor_health` вызывается и отвечает,
но **любая настоящая задача падает на `Not logged in · Please run /login`** — потому что
CLI в контейнере-исполнителе не авторизован. Полный диагноз и два пути фикса — **§20**.
5. 🔴 **Завести Access Application** на `agents-bridge.qentra.top` — сейчас туннель
отвечает 200 без авторизации (проверено). Защита — только `X-Bridge-Secret`.
6. ⚠️ **`~/Developer/balda-executor-mcp/docker-compose.yml` в репо → `0.2.0`**;
@@ -1298,6 +1303,135 @@ I/O-wait на HDD). Симптомы, которые **не** являются
---
## 20. 🔴 Проверка 2026-09-28: единственный блокер — CLI не залогинен
Сессия началась с вопроса Alex: «Что далее чтобы заставить все это работать?». Проведён
**аудит всей цепочки живыми вызовами** (не по докам). Итог: Фаза 4 завершена везде, кроме
одного звена — **авторизации CLI внутри контейнера-исполнителя**.
### 20.1 Что проверено живыми вызовами и РАБОТАЕТ
| Узел | Проверка | Результат |
|---|---|---|
| Балда + бот MM на Kraken | `docker ps`, `docker-compose.yml` | `balda-mm:0.1.13` Up 19h ✅ |
| Логи Балды | `docker logs balda-mm --since 6h \| grep -c error` | **0** ошибок ✅ |
| `duplicate tool` не вернулся | grep по логам | чисто ✅ |
| MCP-сервер исполнителя | `initialize` из контейнера `mattermost` | HTTP 200, `serverInfo.name=balda-executor-mcp` ✅ |
| Мост на Mac | `curl 127.0.0.1:8790/healthz` | `status: ok`, `container_running: true` ✅ |
| Cloudflare Tunnel | `curl https://agents-bridge.qentra.top/healthz` | 200 с обеих сторон ✅ |
| Контейнер-исполнитель | `docker ps` | `balda-executor:0.1.0` Up 40h (healthy) ✅ |
| Тулчейн в контейнере | `docker exec … which` | `claude` + `codex` + `node` + `git` на месте ✅ |
| PR #8 | `gh pr view 8` | `OPEN`, `MERGEABLE`, `reviewDecision: ""` — внешний блокер, не наш ✅ |
### 20.2 Что СЛОМАНО — ровно одна вещь
Задача через мост (`POST /task`, `provider: claude`):
```bash
cd ~/Developer/agents-bridge
SECRET=*** # из .env, поле BRIDGE_SECRET
curl -s -X POST http://127.0.0.1:8790/task \
-H "Content-Type: application/json" -H "X-Bridge-Secret: $SECRET" \
-H "Idempotency-Key: probe-auth-$(date +%s)" \
-d '{"prompt":"Reply with exactly: BRIDGE_OK"}'
# → {"task_id":"48fcd4a7d0a44a0c","status":"queued",…}
```
Результат задачи (`GET /task/<id>` с заголовком `X-Bridge-Secret`):
```json
{ "status": "failed", "exit_code": 1, "duration_s": 0.8,
"stdout": "Not logged in · Please run /login\n", "stderr": "" }
```
📌 **Конвейер рабочий**: задача дошла до CLI, CLI ответил, ответ вернулся через мост.
Падение — внутри CLI, на проверке авторизации.
### 20.3 Доказательство, что логина нет НА ХОСТЕ (а не просто в контейнере)
```bash
docker exec balda-executor sh -c 'jq -c "keys" /root/.claude.json'
# → ["firstStartTime","firstStartVersion","hasResetAutoModeOptInForDefaultOffer",
# "machineID","migrationVersion","opusProMigrationComplete","pluginUsage",
# "seenNotifications","sonnet1m45MigrationComplete","userID"]
```
Это **свежий стартовый файл**: 9 служебных ключей, **ключа `oauthAccount` нет**.
Никаких следов авторизации.
```bash
ls -la ~/cli-auth/claude/
# → backups/ projects/ sessions/ ← монтируется в /root/.claude
ls -la ~/cli-auth/claude/backups/
# → .claude.json.backup.1790423979201 (84 байта)
# .claude.json.backup.1790435884422 (84 байта)
```
🔴 **В хост-монтировании `~/cli-auth/claude/` НЕТ файла `.claude.json`.**
Лежат только `backups/`, `projects/`, `sessions/`. Тот файл в контейнере (`692 байта`,
`Sep 26 15:18`) — это **контейнерная копия, созданная самим CLI**, а не проброшенный
логин. Бэкапы по 84 байта — тоже не креды.
**Корень:** логин никогда не был посажен туда, откуда он переживает пересоздание контейнера.
`docker compose up -d` пересоздаёт контейнер, монтирование отдаёт пустой набор файлов
(только каталоги), CLI генерирует себе пустой `.claude.json` с нуля → каждый раз «Not logged in».
### 20.4 Контекст: как CLI вызывается
`~/Developer/agents-bridge/executor.py:21-25`:
```python
PROVIDERS: dict[str, list[str]] = {
"claude": ["claude", "-p", "--output-format", "text"],
"codex": ["codex", "exec", "--skip-git-repo-check"],
}
# :41 — запуск происходит НЕ на хосте, а через docker exec в контейнер
return ["docker", "exec", "-w", WORKDIR, "@container", *base, prompt]
```
Значит авторизация нужна **внутри контейнера**, а не на Mac. `WORKDIR = "/agents"`.
### 20.5 Два пути фикса (решение за Alex — ждёт ответа)
**Путь A (рекомендован) — `claude setup-token`, токен в переменную окружения.**
Почему лучше: долгоживущий токен **не файл** → пересоздание контейнера его не затирает.
Документированный Anthropic путь для headless (см. §0.6 «Юридическая граница» — разрешён,
не дыра).
1. **Alex на Mac:** `claude setup-token` (открывает браузер, аккаунт подписки) → получает токен.
2. Токен кладём в `~/Docker/balda-executor/.env` как `CLAUDE_CODE_OAUTH_TOKEN=…`
(`.env` — `0600`, уже в `env_file` compose → в контейнер попадает автоматически).
3. Проверка: задача через мост → ждём ответ модели вместо `Not logged in`.
⚠️ **Питфолл записи в `.env`:** `write_file` редактирует/калечит секреты
(`personal/tech/hermes-secret-redaction-bypass`) — значение собирать **на хосте**
скриптом, не литералом.
**Путь B — залогиниться в контейнере, затем СКОПИРОВАТЬ креды на хост.**
1. **Alex:** `docker exec -it balda-executor claude` → `/login`.
2. Я **копирую** (не перемещаю) `~/.claude.json` и `.credentials.json` из контейнера в
`~/cli-auth/claude/` **с бэкапом** существующего каталога.
3. Проверка: `docker compose up -d` (пересоздание) → повторить задачу → логин должен выжить.
Почему B хуже: зависит от того, что CLI держит всё в файлах и что структура не поменяется;
`setup-token` — официально поддерживаемый для headless путь.
**Не делать:** логиниться на голом Mac в `~/.claude` и ждать, что это проедет в контейнер —
монтируется `~/cli-auth/claude`, **не** `~/.claude`. На хосте Mac логин вообще не участвует.
### 20.6 Что проверить ПОСЛЕ фикса (сквозная приёмка Фазы 4)
1. `POST /task` через мост → реальный ответ модели, `exit_code: 0`.
2. Пост от **владельца** в MM (`@balda …`) → Балда → `executor_run` → мост → CLI → ответ в чат.
Метод постинга от владельца — §17.1 (admin PAT через `mmctl --local token generate`).
3. `codex` тоже проверить (второй провайдер в том же образе).
4. Обновить §0: строка 8 → ✅, Фаза 4 → закрыта; Фаза 5 (связка и приёмка) в
[[personal/projects/balda/balda-mm-delegation-plan]].
---
## Связанные заметки
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅)