[2026-09-26] eagle: personal/projects/balda/balda-mm-delegation-plan.md personal/projects/balda/balda-valera.md personal/projects/balda/fast-rlm-integration.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-26 00:17:24 +06:00
1 parent 9eb5cf001e
commit 335478ef65
3 files changed
+268

No files matched your search

@@ -0,0 +1,243 @@
# Balda 2.0 — Mattermost + распределённое делегирование (план)
> Создан 2026-09-25. Статус: **черновик, ждёт апрува Alex**.
> Задача: поднять свежую Балду из `normahq/balda` (без наших коммитов), завязать на Mattermost,
> построить распределённое делегирование (дешёвый оркестратор + подписочные CLI в VM).
---
## 0. Ключевые находки разведки (факты, проверено в коде и доках)
### 0.1 Апстрим Балды
- Апстрим переехал: `normahq/balda` → **`baldaworks/balda`**. Релиз: `ghcr.io/baldaworks/balda:latest`, npm `@baldaworks/balda`.
- Наш форк `~/Developer/balda` отстал на **900 файлов** (+135k/−28k строк).
- Архитектура переписана полностью: `handlers/*` → `handlersfx/*`, Zulip вынесен в `internal/apps/balda/channel/zulip/*`, добавлены backoffice (встроенный web UI + админ/логин/аудит), Slack Agent, unified user security state, sessionmemory на Badger.
- Наши коммиты (промежуточные статусы Zulip) в апстрим **не влиты** — `05159d15` есть только у нас.
### 0.2 Mattermost — в апстриме НЕТ
- `grep -i mattermost` по всему `upstream/main` → **0 совпадений**. Ни кода, ни доков.
- Каналы в апстриме: `telegram`, `zulip`, `slackagent`, `webhook` + `channel/router.go`.
- Значит MM = **новый транспортный адаптер**, писать самим.
- Хорошая новость: есть явный контракт для этого — `docs/architecture/transport-presentation-boundary.md`.
Требование: shared-пакеты владеют семантикой, транспортные пакеты владеют рендерингом/ingress/delivery,
регистрация через DI в composition root. Образец для копирования: `channel/zulip/adapter.go` (331 стр.) + `client.go` (289 стр.) — по объёму это 2-4 дня работы.
#### MM — это НОВЫЙ chat surface, а не просто «ещё один webhook»
В README апстрима заявлены ровно три chat surface: **Telegram, Zulip, Slack Agent**. MM не входит.
Значит адаптер затрагивает не только `channel/mattermost/*`, но и весь слой, где перечисляются транспорты.
Найденные touchpoints (проверять при реализации):
- `channel/router.go` — регистрация/диспетчеризация транспорта
- `handlersfx/module.go` — composition root (DI-вайринг)
- `internal/apps/balda/state/` — `channel_type` в каноническом locator (`channel_type:address_key`)
- `docs/commands.md` — таблица доступности команд по транспортам (Telegram/Zulip/Slack — колонки)
- `docs/reference/messaging.md`, `docs/architecture/multi-channel-token-auth.md` — контракты auth/доставки
**Следствие:** в MM-адаптере сразу закладывать полный набор: `/start owner=`, `/start invite=`,
`/topic`, `/goalkeeper`, `/auto`, `/reset`, `/skill`, `/cancel`, `/locator`, `/user`.
Scheduler/webhook-таргеты (`balda.scheduler.jobs` → `target: locator`) должны резолвиться в MM-локатор.
### 0.2.1 Что именно есть только у нас (инвентаризация перед «начать с чистого»)
Решение «поднять чистую Балду без наших коммитов» — обосновано. Инвентаризация:
**`~/Developer/balda`** (наши уникальные коммиты):
- `05159d15` — промежуточные статусы Zulip (правит `internal/apps/balda/handlers/balda.go` +25). В `main` нашего форка.
- `bc5fd03c` — то же + Telegram + `messageID` для threading. Лежит на `backup/our-main-before-upstream` и `feat/zulip-transport-intermediate`, в `main` не попала.
- `5ad65955` — **не фича**, а `replace normahq/norma => ./norma-local` в `go.mod`. При переезде удалить.
- `feat/zulip-events-polling` — `c619731d` Events API long-poll + stream_only_with_session фильтр; плюс два коммита про valera Dockerfile/compose.
**`~/Developer/norma-local`** (наши уникальные коммиты):
- `56a6e1e` = `91c2c0a` (дубли) — DeepSeek tool_calls: `pkg/runtime/hostedagent/openai.go` (+286),
`pkg/runtime/agentfactory/agentfactory.go` (+3), `openai_test.go` (+312).
- **Критично:** в `norma v2` (`f8a7336`) эти файлы **удалены целиком**; модуль стал
`github.com/normahq/norma/v2`, апстрим использует `github.com/normahq/runtime/v2 v2.0.11`.
Наш патч не переносится — либо он уже есть в `normahq/runtime/v2`, либо пишется заново.
- `norma-local` отстал от `origin/master` на **26 коммитов** (включая теги `v0.0.11`–`v0.0.13`, `v2.0.0`, `v2.0.1`).
**Проверить перед началом:** есть ли в `normahq/runtime/v2` поддержка tool_calls для OpenAI-совместимых
провайдеров. Если да — наш DeepSeek-фикс не нужен вообще, и потерь нет. Если нет — фикс надо
переписать в новый модуль (см. скилл `adk-hostedagent-openai-mcp`).
### 0.3 Провайдеры Балды (ACP)
Реестр в `cmd/balda/init.go`:
| id | type | model | binary |
|---|---|---|---|
| codex | `codex_acp` | gpt-5-codex | `codex` |
| opencode | `opencode_acp` | opencode/big-pickle | `opencode` |
| copilot | `copilot_acp` | gpt-5-codex | `copilot` |
| gemini | `gemini_acp` | gemini-3-flash-preview | `gemini` |
| claude_code | `claude_code_acp` | claude-sonnet-4-6 | `claude` |
- Балда говорит с CLI через **ACP (Agent Client Protocol)** — через `github.com/normahq/go-adk-acpagent/v2 v2.2.6`.
- Это ровно то, что нужно для «подписочных CLI»: Балда запускает **настоящий** `claude`/`codex` бинарь, а не дёргает API.
- **Мультипровайдерность есть:** несколько провайдеров в `runtime.providers`, переключение через `profiles.<name>.balda.provider`. Но у **одной сессии один провайдер** — переключается профилем, не внутри сессии.
### 0.4 GoalKeeper — уже готовая база для делегирования
- `/goalkeeper <objective>`: цикл work → validate до `verdict: pass` или `max_iterations`.
- Отдельные ADK-сессии для worker и validator (изоляция контекста).
- При workspace mode: изолированный worktree, автогенерация Conventional Commit, squash-merge в `base_branch`, удаление workspace.
- Артефакты в финале: изменённые файлы, ветка, коммит, вывод валидации, next step.
- **Вывод:** это уже «оркестратор + исполнитель», просто оба на одном провайдере.
### 0.5 Auto mode — автономное продолжение
- `/auto on|off` — сессионный режим: после каждого turn планируется внутренний synthetic turn со стирингом.
- Сентинелы `AUTO_DECISION:DONE` / `AUTO_DECISION:WAIT_FOR_USER`.
- Наш прошлый кейс (агент сам не продолжал) — в апстриме закрыт этой фичей.
### 0.6 Юридическая граница по подпискам — КРИТИЧНО
Проверено по официальным докам Anthropic (`code.claude.com/docs/en/legal-and-compliance`) и разбору Zylos (2026-07-13):
**Разрешено:**
- Запуск **немодифицированного** официального бинаря `claude`, залогиненного **под своим** аккаунтом, для своих задач и автоматизации. Claude Code сам является «explicit permission» для автоматизации.
- `claude setup-token` → `CLAUDE_CODE_OAUTH_TOKEN` на headless-машине (Pro/Max). Это документированный путь, не дыра.
- Provisioning своих API-ключей для своих авторизованных пользователей.
**Запрещено:**
- Извлекать OAuth-токен подписки и **переиспользовать его в другом клиенте** (прокси, ре-серв как OpenAI-совместимый API). Anthropic реализовал блокировки на сервере и банит аккаунты. Февраль 2026: OAuth-токены consumer-планов «in any other product, tool, or service — including the Agent SDK» не разрешены.
- Спуфить идентичность клиента Claude Code.
**Следствие для архитектуры:** Балда через ACP = хорошо (запускает настоящий бинарь). Прокси-мост, отдающий подписочный токен наружу как API = бан.
Дополнительно: лимиты Pro/Max рассчитаны на «ordinary, individual usage» — параллельные агенты под одним аккаунтом это серая зона даже с официальным бинарём. Codex CLI от OpenAI в этом смысле либеральнее (инженер OpenAI публично поддержал использование подписочного токена в сторонних харнессах); Gemini CLI — как Anthropic, вплоть до закрытия аккаунта.
---
## 1. Где разворачивать — решение
Требование Alex: постоянная доступность + сборка проектов + подписочные CLI без токенов.
Вывод: **не «или-или», а разделение ролей между двумя контейнерами.**
| Роль | Где | Почему |
|---|---|---|
| Mattermost + оркестратор Балда (DeepSeek) | **Kraken** | 24/7, уже есть docker stack, Zulip-подобный топиковый UX, дешёвый по CPU/RAM |
| Исполнитель (codex/claude CLI по подписке) | **Mac (Eagle)**, в VM | нужен доступ к `~/Developer`, git, тяжёлые сборки; подписка залогинена на маке |
### Почему не всё на Mac
Постоянная доступность Mac не гарантирована (сон, ребуты), а оркестратор должен жить в чате 24/7.
### Почему не всё на Kraken
- RPi5 ARM64: тяжёлые сборки/macOS-таргеты невозможны, `~/Developer` не там.
- Подписочный CLI на Kraken = логин подписного аккаунта на чужом хосте, вне зоны контроля → лишний риск бана.
### Почему VM, а не голый Mac
Изоляция: агент-исполнитель получает полный доступ к файлам и сети. VM даёт откат, снапшоты, ограничение диска. Плюс чистая авторизация (`claude setup-token` внутри VM, вне основного профиля Mac).
### Вариант, который стоит рассмотреть вместо VM
**Одноразовый Docker-контейнер на Mac** (не VM): том с проектом, `CLAUDE_CODE_OAUTH_TOKEN` из env. Плюсы: запуск за минуты, изоляция не хуже, легко убить. Минус: на macOS Docker работает через VM — доступ к хостовому диску только через bind-mount, производительность FS ниже.
**Рекомендация: начать с Docker-контейнера; если понадобится полный desktop/gui или сложный toolchain — переехать в UTM/Parallels VM.** Это на порядок быстрее в запуске.
### Транспорт между оркестратором и исполнителем
- Вариант A: **общий Mattermost-канал** — Балда на Kraken и исполнитель в VM оба боты. Просто, но грязно (лишний шум, конфликты сессий).
- Вариант B: **Мост на Mac**: небольшой сервис (FastAPI), который принимает задачи от Kraken по HTTP/Webhook и запускает CLI внутри. Идемпотентность, очередь, статусы.
- Вариант C: **вторая Балда в VM** и связка через webhooks Балды (в апстриме есть `balda.webhooks.*`).
- **Рекомендация: начать с B**, потому что он не требует ни форка Балды, ни второго канала. Позже можно перейти на C.
---
## 2. Сценарий делегирования (как это работает для Alex)
```
Mattermost (Alex)
│ «почини баг X в проекте Y»
▼
Балда-оркестратор (Kraken, DeepSeek — дёшево)
│ 1. собирает контекст: читает Obsidian, ищет в web, формирует ТЗ
│ 2. /goalkeeper → цикл work→validate
│ 3. на этапе work вызывает мост → задание в VM
▼
Мост (Mac, FastAPI) ──► codex/claude CLI (VM, подписка)
│ делает: код, сборка, тесты
│◄──── возврат: diff, вывод тестов, коммит
▼
Балда-оркестратор
│ 4. валидирует результат, обновляет доку в Obsidian,
│ трекает исполнение, отвечает в Mattermost
▼
Mattermost (Alex) — итог + артефакты
```
Ключевое разделение расходов:
- **оркестратор** — DeepSeek (дёшево) — контекст, поиск, документирование, трекинг, коммуникация.
- **исполнитель** — подписочный CLI (фикс-цена) — код, сборка, тесты.
---
## 3. Фазы
### Фаза 0 — подготовка (бэкапы, ~30 мин)
1. `backup/pre-normahq-sync` в `~/Developer/balda` и `~/Developer/norma-local`.
2. Сохранить стэш `whale-20260623` в отдельную ветку (не в stash — стэши теряются).
3. Снять текущий compose Валерии/Клавдия в бэкап (`~/Docker/balda-agent/`, `~/Docker/claudio-agent/`).
### Фаза 1 — Mattermost (Kraken, ~2-3 ч)
1. Решить вопрос лицензии MM (free self-hosted Team Edition — ограничения по числу пользователей смотреть на момент установки).
2. Развернуть MM на Kraken (`docker-compose`, postgres, volume на USB-HDD-разделе с учётом mount-race).
3. Настроить: reverse proxy / порт, вход, создать команду + канал для агентов.
4. Создать бота (Personal Access Token / bot account) для Балды.
### Фаза 2 — адаптер Mattermost в Балде (~2-4 дня)
1. Склонировать чистый `baldaworks/balda` в **новую** директорию (`~/Developer/balda-mm`) — наши коммиты не переносим.
2. Изучить `channel/zulip/*` как образец: adapter, client, inbound, server, presentation.
3. Написать `channel/mattermost/*` по контракту `transport-presentation-boundary.md`:
- ingress: WebSocket events (или outgoing webhook) → семантические типы;
- delivery: рендеринг (Markdown MM), треды/посты;
- registration через DI в composition root.
4. Собирать локально на Mac (arm64) и/или на Kraken (arm64) — проверить целевую платформу.
### Фаза 3 — оркестратор на Kraken (~1-2 дня)
1. Развернуть Балду с MM-адаптером на Kraken, провайдер — **DeepSeek** (через API-ключ, не подписка).
2. Настроить `balda.mcp_servers` (Obsidian MCP + Knowl при необходимости).
3. Проверить `/goalkeeper`, `/auto`, `/topic`, `/skill` в MM.
4. Верификация Alex в UI MM (не по логам).
### Фаза 4 — исполнитель в VM/Docker на Mac (~2-3 дня)
1. Docker-контейнер (или UTM VM) с Node 24 + `codex` + `claude` + `git`.
2. Авторизация: `claude setup-token` на Mac → `CLAUDE_CODE_OAUTH_TOKEN` в контейнер; для codex — свой flow.
3. Bind-mount проектной директории (не весь `~`).
4. Мост: FastAPI-сервис, принимает задачу от Kraken, запускает CLI headless, возвращает diff/тесты.
5. Идемпотентность + таймауты + лимит параллельных задач (не более 1-2, чтобы не ловить «unusual traffic patterns»).
### Фаза 5 — связка и приёмка
1. Сквозной тест: сообщение в MM → Балда → мост → CLI → результат обратно в MM.
2. Проверить расходы: DeepSeek-токены оркестратора + подписочные лимиты исполнителя.
3. Записать в Obsidian: как работает, где логи, как откатить.
---
## 4. Риски
| Риск | Оценка | Митигация |
|---|---|---|
| Бан подписочного аккаунта за «unusual traffic» | средний | только официальный бинарь; не более 1-2 параллельных задач; не проксировать токен; мониторить письма Anthropic |
| MM-адаптер разъедется с апстримом | высокий | держать как отдельный fork-патч, ребейзить на каждый релиз; либо предложить в апстрим PR |
| Kraken: USB-диск нестабилен, mount-race | известный | учесть в compose (см. `docker-data-root-mount-race`), volume не на data-root |
| RPi5 не тянет сборку Балды | средний | собирать образ на Mac arm64, на Kraken только `pull`/`up` |
| Утечка секретов в MM-канал | средний | `balda.permissions.mode: ask`; redaction в job-events уже есть в апстриме |
| Mattermost free-лицензия ограничивает нужное | низкий | проверить лимиты до установки |
---
## 5. Открытые вопросы к Alex
1. **Mattermost:** поднимаем self-hosted Team Edition (free) или есть платный/облачный аккаунт?
2. **VM vs Docker:** начинаем с Docker-контейнера на Mac (быстро) или сразу VM (UTM/Parallels)?
3. **Kraken:** MM на USB-HDD-разделе или на SD (риск износа)? Учитывая нестабильность USB-диска — возможно, вынести MM на SD с бэкапом.
4. **Провайдер оркестратора:** DeepSeek (как сейчас у Валеры) или другой дешёвый?
5. **Судьба текущей Валеры/Клавдия:** гасим или держим параллельно на время миграции?
---
## 6. Что понадобится прочитать перед реализацией
- `docs/architecture/transport-presentation-boundary.md` — контракт транспортов
- `docs/architecture/runtime-contract.md` — инварианты рантайма
- `docs/reference/configuration.md` — конфиг провайдеров и MCP
- `docs/reference/plugins.md` — расширения
- `docs/goal-workflow.md` — контракт GoalKeeper
- `docs/architecture/auto-mode.md` — автономное продолжение
- `docs/reference/job-runtime.md` — durable jobs, scheduler, webhooks
- `internal/apps/balda/channel/zulip/*` — образец адаптера
## История
- **2026-09-25**: создан по итогам разведки апстрима и юридической проверки подписок.
+14
View File
@@ -1,5 +1,19 @@
# Балда / Валера — эксплуатация & MCP debug
> ## ⚠️ УСТАРЕЛО (2026-09-25) — читать с оговоркой
> Этот док описывает **старый форк-стек**: свой патч `norma-local` (`hostedagent/openai.go`),
> `go.mod replace` на локальную норму, DeepSeek через OpenAI-совместимый рантайм.
>
> **Апстрим ушёл далеко вперёд.** Актуально: `normahq/balda` → **`baldaworks/balda`**,
> архитектура переписана (`handlers/*` → `handlersfx/*`, Zulip → `channel/zulip/*`),
> провайдеры теперь **ACP** (`claude_code_acp`, `codex_acp`, ...) вместо hostedagent/openai.
>
> Планируется поднять **чистую Балду без наших коммитов** → см.
> [[personal/projects/balda/balda-mm-delegation-plan|balda-mm-delegation-plan]].
>
> Что здесь всё ещё верно: грабли эксплуатации (NATS, owner, `DOCKER_OPTS`, `stream_only_with_session`),
> бранчи/стэши, диагностика молчания. Что мертво: раздел про `openai.go`/`norma-local` replace как «текущее».
## Расположение
- **Валера** = `balda-agent-valera-1`, конфиг: `~/Docker/balda-agent/.config/balda/config.yaml`
- **Клавдий** = `claudio-agent-claudio-1`, конфиг: `~/Docker/claudio-agent/.config/balda/config.yaml`
@@ -1,5 +1,16 @@
# fast-rlm MCP HTTP integration with Balda
> ## ⚠️ СТАТУС (2026-09-25)
> Док описывает интеграцию с **прежней Балдой** (`balda-agent-valera-1` на нашем форке).
> Планируется переезд на чистый апстрим `baldaworks/balda` — см.
> [[personal/projects/balda/balda-mm-delegation-plan|balda-mm-delegation-plan]].
>
> **Что переносится:** сама идея MCP HTTP sidecar в сети Балды (`runtime.mcp_servers` +
> `balda.mcp_servers`) и `OPENAI_BASE_URL`/`OPENAI_API_KEY` для fast-rlm — в апстриме MCP-механика
> сохранилась (`docs/reference/configuration.md`: `runtime.mcp_servers.<id>` c `type: http|sse|stdio`).
> **Что перепроверить:** в апстриме у Балды встроенная fact-memory (`balda.memory.remember`) и
> опциональная Knowl-сайдкар — возможно, RLM-сжатие частично закрывается ими, оценить перед переносом.
## Цель
Дать Балде (`balda-agent-valera-1`) инструмент рекурсивного сжатия контекста (RLM) через MCP **HTTP** сервер в отдельном Docker контейнере.