[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/projects/balda/balda-kraken-mm-deploy.md personal/tech/balda-go-fx-pitfalls.md
This commit is contained in:
1 parent
0c7b1f6466
commit
aa5b66a442
3 files changed
+259
-49
No files matched your search
@@ -16,7 +16,7 @@
|
||||
| 4 | Секция «Agents» в Dashboard | ✅ активирована и проверена |
|
||||
| 5 | Связка Балда → мост | ✅ конфиг `type: http`, образ 0.2.0, `tools/list` из docker-сети |
|
||||
| 6 | Obsidian MCP для Балды | ✅ контейнер `obsidian-mcp` на Kraken + `type: sse` в конфиге (транспорт проверен) |
|
||||
| 6a | 🔴 Инструменты MCP в сессии Балды | 🔴 **корень: `session_memory` выключен; при включении на Mattermost падает `/reset`** (`no session-memory classifier for "mattermost"`). См. §9.3 |
|
||||
| 6a | Инструменты MCP в сессии Балды | ✅ **исправлено 2026-09-26** — Mattermost-классификатор добавлен в `ScopeResolver` (`app.go` + `preflight.go`), образ `balda-mm:0.1.11`, `session_memory.enabled: true`. См. §12 |
|
||||
| 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI |
|
||||
|
||||
---
|
||||
@@ -293,54 +293,10 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN
|
||||
- obsidian
|
||||
```
|
||||
См. §11 ниже.
|
||||
3. 🔴 **MCP-инструменты не видны в сессии Балды. КОРЕНЬ НАЙДЕН (2026-09-26).**
|
||||
Оказалось **не** снапшот каталога — снапшот в порядке. Самый свежий (`scope: effective`,
|
||||
`runtime_catalog_snapshot:895d7905…`) содержит всё как надо:
|
||||
```json
|
||||
"sources":[{"kind":"builtin","name":"balda"},
|
||||
{"kind":"configured-mcp","name":"executor-mcp"},
|
||||
{"kind":"configured-mcp","name":"obsidian"}]
|
||||
"mcp_servers":[{"name":"executor-mcp","transport":"streamable-http"},
|
||||
{"name":"obsidian","transport":"sse"}]
|
||||
```
|
||||
|
||||
**Две настоящие причины (обе исправлены в конфиге):**
|
||||
|
||||
**3a. Провайдер не ссылался на MCP-серверы.** Доки Balda:
|
||||
`runtime.providers.<id>.mcp_servers` — серверы подключаются **через секцию провайдера**;
|
||||
`balda.mcp_servers` лишь добавляет ID в сессию. Без провайдерской секции инструменты
|
||||
не грузятся. Формула: `effective = bundled(balda) + runtime.providers.<p>.mcp_servers + balda.mcp_servers`.
|
||||
```yaml
|
||||
runtime:
|
||||
providers:
|
||||
deepseek:
|
||||
type: openai
|
||||
openai: {…}
|
||||
mcp_servers: # 🔴 обязательная секция
|
||||
- executor-mcp
|
||||
- obsidian
|
||||
```
|
||||
|
||||
**3b. 🔴 `balda.session_memory.enabled` — выключатель MCP-биндера, но на Mattermost
|
||||
он ломает `/reset`.** Биндер создаётся как
|
||||
`SessionMCPBinderAdapter{Enabled: cfg.Balda.SessionMemory.Enabled}`
|
||||
(`sessionapp/module.go:191-196`). Значит MCP-инструменты в сессии включаются именно
|
||||
`session_memory`, а не `balda.memory` (это разные секции!).
|
||||
|
||||
НО: при `session_memory.enabled: true` сброс сессии падает —
|
||||
```
|
||||
warn … "observe reset boundary: no session-memory classifier is registered for \"mattermost\""
|
||||
→ в чате: "Could not reset this session."
|
||||
```
|
||||
`ScopeResolver` регистрирует классификаторы только для **telegram / slackagent / zulip**
|
||||
(`preflight.go`), для Mattermost — нет. Итог: сессия не пересоздаётся, MCP не подключаются.
|
||||
|
||||
**Вывод:** MCP-инструменты в сессии Балды на Mattermost сейчас **недостижимы** —
|
||||
упирается в отсутствие Mattermost-классификатора в `ScopeResolver`.
|
||||
`session_memory` оставлен **выключенным** (иначе `/reset` не работает).
|
||||
Для разблокировки нужно либо добавить `ClassifyLocatorScope` для Mattermost в апстриме,
|
||||
либо перейти на канал с поддержкой (telegram/slackagent/zulip).
|
||||
|
||||
3. ✅ **MCP-инструменты не видны в сессии Балды — ИСПРАВЛЕНО (2026-09-26).** Полный разбор в §12.
|
||||
Кратко: причина была **не** в снапшоте каталога (он в порядке) и не только в провайдерском
|
||||
`mcp_servers`. Настоящий выключатель — `balda.session_memory.enabled`, а он на Mattermost
|
||||
падал из-за отсутствующего классификатора scope. Классификатор добавлен — см. §12.
|
||||
⚠️ Тест-пост должен идти **от админского токена** — посты от имени самого бота Балда
|
||||
игнорирует (правильно), в логе `dropping posted event: bot not mentioned`:
|
||||
`docker exec mattermost mmctl --local token generate admin …`.
|
||||
@@ -422,6 +378,163 @@ docker exec mattermost sh -c 'curl -sS -m 8 -H "Accept: text/event-stream" http:
|
||||
📌 У `mcp-proxy` **нет флага `--host`** — в отличие от Python-моста. Передавать только
|
||||
`--port` + `--`. Лишний `--host` роняет контейнер.
|
||||
|
||||
## 12. ✅ MCP-инструменты в сессии Балды — корень и фикс (2026-09-26)
|
||||
|
||||
**Симптом:** `/reset` перечисляет `MCP: balda, executor-mcp, obsidian`, но Балда отвечает
|
||||
«namespace `obsidian` не подключён к сессии» и **не вызывает инструменты**
|
||||
(`function_call_part_count: 0`).
|
||||
|
||||
### Три ложных следа (пройдены, чтобы не повторять)
|
||||
|
||||
| След | Почему ложный |
|
||||
|---|---|
|
||||
| Снапшот каталога `appruntime_catalog_snapshot` пуст | **Не пуст.** Самый свежий (`scope: effective`) содержит `builtin` + `configured-mcp: executor-mcp` + `configured-mcp: obsidian`, транспорты `streamable-http` / `sse` — всё верно. Я предлагал чистить `state.db` — **правка не нужна**, данные целы |
|
||||
| Провайдер `type: openai` не умеет MCP | Умеет. Ходит через agent runtime, ограничения по типу провайдера нет |
|
||||
| `balda.mcp_servers` достаточно | **Недостаточно, но и не корень.** Это лишь список ID для сессии |
|
||||
|
||||
### Настоящий корень — цепочка из двух звеньев
|
||||
|
||||
**Звено 1: серверы подключаются через секцию ПРОВАЙДЕРА.**
|
||||
Доки Balda: MCP-серверы описываются в `runtime.mcp_servers` и **подключаются провайдером**
|
||||
через `runtime.providers.<id>.mcp_servers`. Формула:
|
||||
```
|
||||
effective = bundled(balda) + runtime.providers.<provider>.mcp_servers + balda.mcp_servers
|
||||
```
|
||||
Без провайдерской секции `balda.mcp_servers` игнорируется.
|
||||
|
||||
```yaml
|
||||
runtime:
|
||||
providers:
|
||||
deepseek:
|
||||
type: openai
|
||||
openai: {…}
|
||||
mcp_servers: # 🔴 обязательная секция
|
||||
- executor-mcp
|
||||
- obsidian
|
||||
```
|
||||
|
||||
**Звено 2 (главное): 🔴 `balda.session_memory.enabled` — выключатель MCP-биндера сессии.**
|
||||
Биндер собирается в composition root:
|
||||
```go
|
||||
// internal/apps/balda/app.go:275-278
|
||||
fx.Annotate(
|
||||
func() bool { return cfg.Balda.SessionMemory.Enabled },
|
||||
fx.ResultTags(`name:"balda_session_memory_enabled"`),
|
||||
),
|
||||
// internal/apps/balda/sessionapp/module.go:191-196
|
||||
fx.Annotate(
|
||||
func(broker *sessionmemorymcp.ContextBroker, enabled bool) baldaagent.SessionMCPBinder {
|
||||
return SessionMCPBinderAdapter{Broker: broker, Enabled: enabled}
|
||||
},
|
||||
fx.ParamTags(``, `name:"balda_session_memory_enabled"`),
|
||||
),
|
||||
```
|
||||
⚠️ **`balda.memory` и `balda.session_memory` — РАЗНЫЕ секции.** Первая включает память,
|
||||
вторая — MCP-биндер сессии. В конфиге была только `memory`.
|
||||
|
||||
При включении `session_memory` сессия на Mattermost падала:
|
||||
```
|
||||
warn … "observe reset boundary: no session-memory classifier is registered for \"mattermost\""
|
||||
→ в чате: "Could not reset this session."
|
||||
```
|
||||
|
||||
### Фикс — классификатор Mattermost в `ScopeResolver`
|
||||
|
||||
**Ключевое:** классификатор для Mattermost **уже был написан** (в составе MM-коннектора,
|
||||
`channel/mattermost/locator_codec.go:141-156` — `dm`→personal, `channel`→group).
|
||||
Не хватало ровно **регистрации** в двух картах резолвера.
|
||||
|
||||
| Файл | Строки | Правка |
|
||||
|---|---|---|
|
||||
| `internal/apps/balda/app.go` | 28 | импорт → алиас `baldamattermost` |
|
||||
| `internal/apps/balda/app.go` | 284 | `baldamattermost.ChannelType: baldamattermost.ClassifyLocatorScope,` |
|
||||
| `internal/apps/balda/app.go` | 991 | `mattermost.ValidateConfig` → `baldamattermost.ValidateConfig` (следствие алиаса) |
|
||||
| `internal/apps/balda/preflight.go` | 13 | импорт алиаса |
|
||||
| `internal/apps/balda/preflight.go` | 169 | строка регистрации в карте |
|
||||
|
||||
🔴 **Карт резолвера ДВЕ** — `app.go` (рантайм) и `preflight.go` (валидация).
|
||||
Править **обе**: если поправить одну, preflight разойдётся с рантаймом.
|
||||
|
||||
```go
|
||||
// app.go:280-285 и preflight.go:165-170 — одинаковый список
|
||||
sessionmemoryapp.NewScopeResolver(map[string]sessionmemoryapp.ScopeClassifier{
|
||||
baldatelegram.ChannelType: baldatelegram.ClassifyLocatorScope,
|
||||
baldaslackagent.ChannelType: baldaslackagent.ClassifyLocatorScope,
|
||||
baldazulip.ChannelType: baldazulip.ClassifyLocatorScope,
|
||||
baldamattermost.ChannelType: baldamattermost.ClassifyLocatorScope,
|
||||
})
|
||||
```
|
||||
|
||||
📌 **Урок:** при добавлении нового транспорта в Балду классификатор scope надо
|
||||
**не только написать, но и зарегистрировать в двух картах**. Симптом отсутствия
|
||||
регистрации — `no session-memory classifier is registered for "<channel>"`,
|
||||
проявляется **только** при `session_memory.enabled: true`.
|
||||
|
||||
### Проверка (гейты)
|
||||
|
||||
```bash
|
||||
cd ~/Developer/balda
|
||||
go build ./... # ✅
|
||||
go tool go-arch-lint check --project-path . # ✅ OK
|
||||
gofmt -l internal/apps/balda/app.go internal/apps/balda/preflight.go # ✅ пусто
|
||||
go test -race ./internal/apps/balda/sessionmemoryapp/... \
|
||||
./internal/apps/balda/channel/mattermost/... # ✅ ok
|
||||
```
|
||||
|
||||
⚠️ **Полный `go test -race ./...` = 7 предсуществующих падений** (не регрессия от правок):
|
||||
`cmd/balda` (1), `internal/apps/balda` (2), `mcpruntime` (1), `session` (2), `sessionmemory`
|
||||
layout-тесты. Проверено `git stash` — **те же падения на чистом дереве**.
|
||||
📌 Метод: прежде чем объявлять падения регрессией, снять правки `git stash` и прогнать те же
|
||||
пакеты на базовой ревизии.
|
||||
|
||||
### Деплой
|
||||
|
||||
```bash
|
||||
# сборка (Mac, arm64 builder) — Dockerfile.release, НЕ Dockerfile
|
||||
docker buildx build --builder arm64builder --platform linux/arm64 \
|
||||
-f Dockerfile.release --provenance=false --load -t balda:mm-classifier .
|
||||
docker save balda:mm-classifier -o /tmp/balda-mm-classifier.tar # 20 МБ
|
||||
scp /tmp/balda-mm-classifier.tar kraken:/tmp/
|
||||
ssh kraken 'docker load -i /tmp/balda-mm-classifier.tar && docker tag balda:mm-classifier balda-mm:0.1.11'
|
||||
```
|
||||
|
||||
На Kraken: `docker-compose.yml` → `image: balda-mm:0.1.11`; `config/config.yaml` →
|
||||
`session_memory: enabled: true`; оба файла править **локально**, заливать `scp`
|
||||
(не sed/heredoc). Бэкапы: `config.yaml.bak-mm-classifier-<ts>`,
|
||||
`docker-compose.yml.bak-mm-classifier-<ts>`.
|
||||
|
||||
```bash
|
||||
ssh kraken 'cd /home/kraken/balda && docker compose up -d --force-recreate'
|
||||
```
|
||||
|
||||
✅ **Логи подтверждают фикс:**
|
||||
```
|
||||
SessionMCPBinder … provided
|
||||
session-memory runtime … started
|
||||
session memory … started
|
||||
session manager ready · balda_provider=deepseek
|
||||
bundled MCP listener started · 127.0.0.1:33703 · routes /mcp/balda
|
||||
Balda bot started. Press Ctrl+C to stop.
|
||||
```
|
||||
⚠️ Побочное: `failed to load bot identity for startup log: getMe response missing result` —
|
||||
**не дефект**, только косметика лога идентичности бота.
|
||||
|
||||
### Что проверяет Alex (живая приёмка)
|
||||
|
||||
```
|
||||
@balda /reset
|
||||
→ сессия сбрасывается БЕЗ «Could not reset this session»
|
||||
→ в стартовом сообщении MCP: balda, executor-mcp, obsidian
|
||||
|
||||
@balda какие mcp серверы тебе доступны?
|
||||
→ перечисляет obsidian и executor-mcp как ИНСТРУМЕНТЫ
|
||||
```
|
||||
|
||||
⚠️ На момент записи **живой ответ Балды ещё не подтверждён** — проверка за Alex'ом.
|
||||
Статус шага 6a: код+деплой ✅, приёмка ⏳.
|
||||
|
||||
---
|
||||
|
||||
## Связанные заметки
|
||||
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)
|
||||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅)
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
> Создан 2026-09-26. Обновлён 2026-09-26 (**✅ ФАЗА ЗАКРЫТА — боевая приёмка пройдена**).
|
||||
> Статус: **✅ РАБОТАЕТ. Бот отвечает аккаунту Alex.** Образ `0.1.10` (коммит `9f1e6c9c`,
|
||||
> он же PR baldaworks/balda#8).
|
||||
> ⚠️ **Актуальный рабочий образ на Kraken — `balda-mm:0.1.11`** (2026-09-26, позже этой фазы):
|
||||
> добавлен Mattermost-классификатор scope в `ScopeResolver` + включён
|
||||
> `balda.session_memory` (MCP-инструменты в сессии). Разбор — [[personal/plans/balda-mm-phase4-agents]] §12.
|
||||
> Compose: `/home/kraken/balda/docker-compose.yml` (`image: balda-mm:0.1.11`).
|
||||
> **Доказательство (лента `balda-test`):** `cbwsorzwoigbtrdnteugnx4aqo` (Alex) —
|
||||
> `@balda скажи одно слово` → `edxrgupoxiyig5gzmr79tjypbme` (бот) — `слово`.
|
||||
> `settlement_stage=accepted`, `bad_seq`=0, `RestartCount`=0.
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
> **🔴 замкнутый круг привязки при занятом binding** (§«Дефект 8»).
|
||||
> 🔴 **Новое в этой сессии:** §«Mattermost Team Edition: что недоступно» — push невозможен.
|
||||
> Уточнён §«авторизация — ОДИН механизм»: челлендж НЕ был причиной №1, им был `bad_seq`.
|
||||
> 🔴 **Добавлено 2026-09-26 (сессия MCP-фикса):** §«Scope-классификатор нового транспорта» —
|
||||
> написать мало, надо зарегистрировать в ДВУХ картах (`app.go` + `preflight.go`); смежное —
|
||||
> `balda.session_memory` vs `balda.memory`, MCP через секцию провайдера.
|
||||
> §«Сброс падающих тестов» — сначала `git stash`, потом слово «регрессия»;
|
||||
> `buildx` без `--load` не кладёт образ в `docker images`.
|
||||
> Зеркало секции скилла `devops/docker-balda-service`, добавленной 2026-09-26.
|
||||
> Источник: работа над Mattermost-адаптером Балды (Фаза 2) + деплой на Kraken (Фаза 3).
|
||||
> Проектные доки: [[personal/projects/balda/balda-mattermost-connector]],
|
||||
@@ -114,6 +119,94 @@ case transportMattermost:
|
||||
|
||||
---
|
||||
|
||||
## 🔴🔴 Scope-классификатор нового транспорта: написать мало — надо зарегистрировать в ДВУХ картах
|
||||
|
||||
Найдено 2026-09-26 (Балда на Kraken, MCP-инструменты не подключались). Класс ошибки:
|
||||
**функция есть, регистрации нет** — код компилируется, тесты зелёные, отказ виден только в рантайме.
|
||||
|
||||
**Симптом:** MCP-инструменты не подключаются к сессии. В чате — «namespace `<name>`
|
||||
не подключён к сессии», в логе `function_call_part_count: 0` (модель не вызвала инструмент).
|
||||
При включённом `session_memory` сброс сессии падает:
|
||||
|
||||
```
|
||||
warn … "observe reset boundary: no session-memory classifier is registered for \"mattermost\""
|
||||
→ в чате: "Could not reset this session."
|
||||
```
|
||||
|
||||
**Причина:** `sessionmemoryapp.ScopeResolver` собирается из карты классификаторов по
|
||||
`channelType`. Новый транспорт (для Mattermost — коннектор писал Alex) имел
|
||||
`ClassifyLocatorScope` в `channel/mattermost/locator_codec.go`, но **не был добавлен
|
||||
в карту**. Резолвер fail-closed: `classifier == nil` → `CodeUnsupportedScope`
|
||||
(`sessionmemoryapp/capture.go:68-75`).
|
||||
|
||||
🔴 **Карт ДВЕ, править обе:**
|
||||
- `internal/apps/balda/app.go` — рантайм-граф
|
||||
- `internal/apps/balda/preflight.go` — граф валидации
|
||||
|
||||
Расхождение между ними даёт «конфиг прошёл preflight, но упал в рантайме» (и наоборот).
|
||||
|
||||
```go
|
||||
sessionmemoryapp.NewScopeResolver(map[string]sessionmemoryapp.ScopeClassifier{
|
||||
baldatelegram.ChannelType: baldatelegram.ClassifyLocatorScope,
|
||||
baldaslackagent.ChannelType: baldaslackagent.ClassifyLocatorScope,
|
||||
baldazulip.ChannelType: baldazulip.ClassifyLocatorScope,
|
||||
baldamattermost.ChannelType: baldamattermost.ClassifyLocatorScope, // ← добавить
|
||||
})
|
||||
```
|
||||
|
||||
⚠️ Побочный эффект импорта с алиасом: если пакет уже импортировался без алиаса
|
||||
(`"…/channel/mattermost"`), все обращения вида `mattermost.X` в файле надо перевести
|
||||
на `baldamattermost.X` — иначе `undefined: mattermost` (как в §«Дефект 3»).
|
||||
|
||||
📌 **Добавлять в чек-лист нового транспорта** (продолжение списка из 12 пунктов выше):
|
||||
13. 🔴 `ClassifyLocatorScope` зарегистрирован в карте `ScopeResolver` **в обоих** файлах —
|
||||
`app.go` и `preflight.go`.
|
||||
|
||||
### Смежное: `session_memory` — выключатель MCP-биндера, и это НЕ `memory`
|
||||
|
||||
```go
|
||||
// app.go:275-278 — выключатель
|
||||
fx.Annotate(func() bool { return cfg.Balda.SessionMemory.Enabled },
|
||||
fx.ResultTags(`name:"balda_session_memory_enabled"`))
|
||||
// sessionapp/module.go:191-196 — биндер MCP
|
||||
func(broker *sessionmemorymcp.ContextBroker, enabled bool) baldaagent.SessionMCPBinder {
|
||||
return SessionMCPBinderAdapter{Broker: broker, Enabled: enabled}
|
||||
}
|
||||
```
|
||||
|
||||
⚠️ **`balda.memory` и `balda.session_memory` — разные секции.** Первая включает память,
|
||||
вторая — MCP-биндер сессии. С `memory`-only инструменты в сессию не подключаются.
|
||||
|
||||
📌 **MCP-серверы подключаются через секцию ПРОВАЙДЕРА**, не через `balda.mcp_servers`:
|
||||
```
|
||||
effective = bundled(balda) + runtime.providers.<id>.mcp_servers + balda.mcp_servers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔴 Сброс падающих тестов: сначала `git stash`, потом слово «регрессия»
|
||||
|
||||
Метод 2026-09-26. `go test -race ./...` на ветке `feat/mattermost-channel` даёт 7 падений
|
||||
(`cmd/balda`, `internal/apps/balda`, `mcpruntime`, `session`, тесты layout `sessionmemory`).
|
||||
**Ни одно не было регрессией** от правки — те же падения на чистом дереве.
|
||||
|
||||
```bash
|
||||
git stash
|
||||
go test -race ./internal/apps/balda/ ./internal/apps/balda/session/ \
|
||||
./internal/apps/balda/mcpruntime/ ./cmd/balda/ > /tmp/baseline.log 2>&1
|
||||
git stash pop
|
||||
# сравнить списки `^--- FAIL` — совпали? значит не регрессия
|
||||
```
|
||||
|
||||
📌 Объявлять регрессией только то, чего **нет** в baseline. Иначе доклад «я сломал 7 тестов»
|
||||
при нетронутом коде.
|
||||
|
||||
⚠️ Побочное наблюдение: фоновая сборка `docker buildx build` **без `--load`** кладёт результат
|
||||
только в кеш («No output specified with docker-container driver») — образ не появится в
|
||||
`docker images`, и последующий `docker save` провалится. Собирать с `--load`.
|
||||
|
||||
---
|
||||
|
||||
## 🔴 Прогресс-политика должна совпадать на command- и turn-пути одного транспорта
|
||||
|
||||
Найдено 2026-09-26 (сессия PR-подготовки). `handlersfx/mattermost_ingress.go` в
|
||||
|
||||
Reference in new issue
Block a user