[2026-09-26] eagle: personal/projects/balda/balda-mattermost-connector.md personal/projects/balda/balda-mm-delegation-plan.md personal/projects/balda/mattermost-kraken-deploy.md personal/tech/balda-go-fx-pitfalls.md
This commit is contained in:
1 parent
47ff9bfacd
commit
06950241dd
4 files changed
+442
-13
No files matched your search
@@ -1,15 +1,25 @@
|
||||
# Balda — Mattermost коннектор (архитектура)
|
||||
|
||||
> Создано 2026-09-26. Обновлено 2026-09-26. Статус: **КОД НАПИСАН, КОМПИЛИРУЕТСЯ**.
|
||||
> Создано 2026-09-26. Обновлено 2026-09-26 (вечер). Статус: **КОД СОБИРАЕТСЯ, DI-ГРАФ ВАЛИДИРУЕТСЯ**.
|
||||
> Часть проекта [[personal/projects/balda/balda-mm-delegation-plan]] (Фаза 2).
|
||||
> Требование Alex: поддержка **всех** функций Балды, по новым API, production-качество.
|
||||
>
|
||||
> **Проверено:** `go build ./...` → exit 0, `go vet` чист. Тесты ещё не написаны.
|
||||
> Осталось: **добавить импорт `mattermostfx` в `app.go`** (LSP подсветил, не успел),
|
||||
> затем тесты + `go test -race ./...` + `golangci-lint`.
|
||||
> **Проверено (2026-09-26):**
|
||||
> - `go build ./...` → exit 0, `go vet ./...` → exit 0
|
||||
> - **DI-граф собирается целиком** с `balda.mattermost.enabled=true` — проверено реальным
|
||||
> `validateBaldaApplication(prepared)` через fx, а не только компиляцией
|
||||
> - 3 новых теста зелёные (см. §«Тесты»)
|
||||
>
|
||||
> **Осталось:** 2 шага — дописать ожидание маршрута Mattermost в `TestBuiltinRoutes`
|
||||
> (единственная регрессия от наших правок) + `go tool golangci-lint run`.
|
||||
> Подробности в §«🔴 Текущее состояние и следующий шаг».
|
||||
>
|
||||
> Разделы ниже — сначала контракт/разведка, затем **фактически написанные файлы**. Ищи
|
||||
> §«Что фактически написано» для реестра файлов.
|
||||
>
|
||||
> 🔧 **Инструмент правок:** `tools/apply-project-deltas.py` + `tools/run-project-deltas.sh` +
|
||||
> `tools/deltas-mattermost*.json` — правки проекта идут **текстовыми дельтами с якорями**,
|
||||
> не sed/in-place. См. §«Метод правок: дельта-апплаер».
|
||||
|
||||
---
|
||||
|
||||
@@ -252,6 +262,124 @@ MM: `Source` = новый `turncmd.SourceMattermost`, `DeliveryFormat` = `Delive
|
||||
| `turncmd/turn.go` | `SourceMattermost = "mattermost"` |
|
||||
| `internal/apps/balda/app.go` | `mattermostfx.Module` в список модулей (строка ~667) |
|
||||
|
||||
### Wiring-дельты (2026-09-26, вечер — закрыт блокер сборки)
|
||||
|
||||
Это то, что оставалось «незакрытым» в первой половине дня. Применено дельта-апплаером.
|
||||
|
||||
| Файл | Что добавлено | Зачем |
|
||||
|---|---|---|
|
||||
| `internal/apps/balda/app.go` | импорты `channel/mattermost` **и** `channel/mattermost/mattermostfx` | корневой пакет нужен для `mattermost.ValidateConfig`; `mattermostfx` — для `Module` |
|
||||
| `internal/apps/balda/app.go` | **5 fx-провайдеров** зарегистрированных строк: `balda_mattermost_enabled`, `_server_url`, `_token`, `_bot_user_id`, `_bot_username` | модуль запрашивает их через `name:`-теги — без провайдеров fx падает «missing types» |
|
||||
| `internal/apps/balda/app.go` | `validateMattermostConfig(cfg MattermostConfig) error` + вызов рядом с `validateSlackConfig` | fail-fast при `enabled=true` без URL/токена/bot_user_id |
|
||||
| `internal/apps/balda/app.go` | функция вставлена **перед** `validateIdentifierValue` | порядок как у соседей |
|
||||
| `internal/apps/balda/config.go` | поле `Mattermost MattermostConfig` в `BaldaConfig` + тип `MattermostConfig{Enabled, ServerURL, Token, BotUserID, BotUsername}` | конфиг-поверхность |
|
||||
| `cmd/balda/command.go` | `!cfg.Balda.Mattermost.Enabled` в `validateBaldaChannelConfiguration` + подсказка про `BALDA_MATTERMOST_ENABLED` в тексте ошибки | иначе MM-only деплой отбивается «at least one channel is required» |
|
||||
| `cmd/balda/balda.yaml` | дефолтный блок `balda.mattermost` с 5 ключами и комментариями | шаблон генератора |
|
||||
| `docs/reference/configuration.md` | 5 строк описания `balda.mattermost.*` (перед `balda.slack.bot_token`) | документация ключей |
|
||||
| `docs/commands.md` | абзац про поведение MM (mention в каналах, `/slash`-команды, нет typing) + колонка `Mattermost` в таблице + строка `/plugin ...` | таблица доступности команд |
|
||||
|
||||
⚠️ **`app.go` правился в 4 захода** (импорт → провайдер enabled → валидация → остальные 4 строки).
|
||||
Каждый заход — отдельная дельта-группа; всё в `tools/deltas-mattermost*.json`.
|
||||
|
||||
### 🔴 Три реальных дефекта, найденных тестами (не компиляцией)
|
||||
|
||||
Компиляция проходила, но fx-граф был сломан. Поймано только запуском
|
||||
`validateBaldaApplication` с включённым Mattermost.
|
||||
|
||||
**1. `fx.In` нельзя аннотировать внешним `fx.ParamTags`** — дефект в `mattermostfx/module.go`.
|
||||
|
||||
Симптом:
|
||||
```
|
||||
Failed: invalid annotation function func(mattermostfx.IngressParams) (*mattermost.Ingress, error):
|
||||
fx.In structs cannot be annotated with fx.ParamTags or fx.From
|
||||
```
|
||||
|
||||
Причина: в `module.go` `NewIngress` был обёрнут в `fx.Annotate(NewIngress, fx.ParamTags(...))`.
|
||||
Но `IngressParams` **уже** содержит `fx.In` — его теги обязаны жить **внутри** структуры.
|
||||
|
||||
**Правильно (как в `zulipfx`/`zulip.ServerParams`):**
|
||||
```go
|
||||
// ingress.go — теги на полях структуры
|
||||
type IngressParams struct {
|
||||
fx.In
|
||||
Processor mattermost.InboundProcessor `optional:"true"`
|
||||
Client *mattermost.Client
|
||||
Commands *commandcmd.Registry
|
||||
Enabled bool `name:"balda_mattermost_enabled"`
|
||||
BotUserID string `name:"balda_mattermost_bot_user_id"`
|
||||
BotUsername string `name:"balda_mattermost_bot_username"`
|
||||
Logger zerolog.Logger
|
||||
}
|
||||
|
||||
// module.go — голое предоставление, БЕЗ fx.Annotate/fx.ParamTags
|
||||
fx.Provide(NewIngress),
|
||||
```
|
||||
⚠️ `Commands` был `*commandcmd.Registry `optional:"true"`` — заменён на обязательный
|
||||
`*commandcmd.Registry` (как у Zulip). `commandcmd.Registry` в графе есть всегда.
|
||||
|
||||
**2. Отсутствовали провайдеры зарегистрированных строк.**
|
||||
|
||||
Симптом: `missing types: string[name="balda_mattermost_server_url"] (did you mean to Provide it?)`
|
||||
×3 (server_url, token, bot_user_id).
|
||||
|
||||
Причина: модуль читает значения через `fx.ParamTags`/`name:`, а провайдеров этих имён в
|
||||
`app.go` не было. У Zulip они есть (`balda_zulip_*`) — для MM забыли.
|
||||
|
||||
**Правильно:** 5 `fx.Provide(fx.Annotate(func() ..., fx.ResultTags(`name:"balda_mattermost_*"`)))`.
|
||||
Для несуществующих ключей YAML `strings.TrimSpace` даёт `""` — безопасно, как у Zulip.
|
||||
**Каждый новый `name:`-тег в модуле = обязательный провайдер в `app.go`.**
|
||||
|
||||
**3. Отсутствовал импорт корневого пакета `mattermost`.**
|
||||
`app.go:965: undefined: mattermost` — `validateMattermostConfig` вызывает
|
||||
`mattermost.ValidateConfig`, а импортирован был только `mattermostfx`.
|
||||
|
||||
### Тесты (написаны 2026-09-26)
|
||||
|
||||
| Тест | Файл | Что проверяет |
|
||||
|---|---|---|
|
||||
| `TestLoadConfigDocument_AppliesMattermostEnvOverrides` | `cmd/balda/command_test.go` | 5 env-override `BALDA_MATTERMOST_*` доезжают в `doc.Balda.Mattermost` |
|
||||
| `TestValidateCommandRunsValidationWithMattermostTransport` | `cmd/balda/command_test.go` | **реальный** `loadBaldaCommandConfig(true)` + `validateBaldaApplication(prepared)` — весь fx-граф с включённым MM |
|
||||
| — (встроено в существующие) | — | регрессионная проверка `deliveryfmt` |
|
||||
|
||||
🔴 **Ключевой урок:** `go build ./...` = exit 0 **не значит**, что транспорт рабочий.
|
||||
fx-граф проверяется только `validateBaldaApplication` (или `fx.ValidateApp`). Компиляция
|
||||
не видит ни `fx.In`-несовместимость, ни отсутствующие провайдеры.
|
||||
|
||||
**Как воспроизвести проверку графа (эталон):**
|
||||
```go
|
||||
prepared, err := loadBaldaCommandConfig(true) // нужен os.Chdir в tempdir с .config/balda/config.yaml
|
||||
err = validateBaldaApplication(prepared) // ← вот это и есть проверка графа
|
||||
```
|
||||
Конфиг для теста — `mattermost.enabled: true` + `server_url`/`token`/`bot_user_id`/`bot_username`,
|
||||
провайдер `balda_agent: opencode_acp`. Якорь `func TestValidateCommandRunsValidation(t *testing.T) {`
|
||||
использован как точка вставки.
|
||||
|
||||
### 🔴 Текущее состояние и следующий шаг
|
||||
|
||||
**Осталось 2 шага (~5 минут):**
|
||||
|
||||
1. **Дописать ожидание маршрута Mattermost в `TestBuiltinRoutes`.**
|
||||
`internal/apps/balda/deliveryfmt/registry_test.go`, функция `TestBuiltinRoutes`.
|
||||
В map `want` добавить:
|
||||
```go
|
||||
{transport: TransportMattermost, deliveryFormat: DeliveryFormatNone}: NamePlainText,
|
||||
```
|
||||
Симптом без правки: `len(BuiltinRoutes()) = 5, want 4`.
|
||||
Причина: в `routes.go` мы добавили маршрут Mattermost (строка 32), а тест-ожидание не обновили.
|
||||
**Это единственная регрессия, внесённая нашими правками.**
|
||||
2. `go tool golangci-lint run` — ещё не запускался.
|
||||
|
||||
### ✅ Предсуществующие падения тестов — НЕ наши
|
||||
|
||||
13 пакетов падают **и на чистом `upstream/main`**. Проверено `git stash` (правки убраны →
|
||||
тесты перезапущены → правки возвращены). Падающие:
|
||||
`TestInitCommand_NonInteractiveAutoSelectsRootAndGeneratesDetectedAgents` (cmd/balda),
|
||||
`TestMigrateSessionMemoryLayout_*` ×4 (state), `TestWorkspaceMCPImport/Export_*` (session),
|
||||
`TestPluginResolverResolvesContainedArgvFilteredEnvAndLaunchSecrets` (mcpruntime),
|
||||
`TestResolveWorkspaceBaseBranch_ConfigPreferredWhenValid`,
|
||||
`TestCanonicalSessionMemory*` (apps/balda).
|
||||
⚠️ Не пытаться «чинить» их как часть MM-задачи — это дефекты апстрима, отдельная работа.
|
||||
|
||||
### Отклонения от плана разведки (важно)
|
||||
|
||||
1. **`server.go` → `ingress.go`.** Планировался `server.go` по образцу Zulip, но у MM нет
|
||||
@@ -294,6 +422,59 @@ balda_mattermost_bot_username
|
||||
Конкретные имена ключей yaml **проверить в `docs/reference/configuration.md`** перед Фазой 3 —
|
||||
я их не сверял, только зафиксировал DI-теги.
|
||||
|
||||
✅ **Проверено 2026-09-26:** env-имена — `BALDA_MATTERMOST_ENABLED`, `BALDA_MATTERMOST_SERVER_URL`,
|
||||
`BALDA_MATTERMOST_TOKEN`, `BALDA_MATTERMOST_BOT_USER_ID`, `BALDA_MATTERMOST_BOT_USERNAME`.
|
||||
Механизм: `appconfig/loader.go:367` делает `SetEnvPrefix("BALDA")` +
|
||||
`SetEnvKeyReplacer(".", "_")` на leaf-ключах. **Env применяется только к ключам, которые уже есть
|
||||
в YAML** — поэтому блок `balda.mattermost` обязан присутствовать в `cmd/balda/balda.yaml`,
|
||||
иначе env-переменные молча игнорируются. Это же верно для любого нового транспорта.
|
||||
|
||||
### Метод правок: дельта-апплаер (инструмент проекта)
|
||||
|
||||
Правки проекта идут **не sed/не in-place**, а текстовыми дельтами. Причина: правило Alex
|
||||
«никогда не использовать in-place скрипты и sed; писать скрипт, потом исполнять» + нужна
|
||||
идемпотентность при повторных заходах.
|
||||
|
||||
| Файл | Роль |
|
||||
|---|---|
|
||||
| `tools/apply-project-deltas.py` | движок: применяет JSON-дельты, ведёт состояние |
|
||||
| `tools/run-project-deltas.sh` | обёртка: `check` / `apply` / `list` |
|
||||
| `tools/deltas-mattermost.json` | основная группа (12 дельт: wiring + доки) |
|
||||
| `tools/deltas-mattermost-2.json` | импорт корневого `mattermost` |
|
||||
| `tools/deltas-mattermost-3.json` | тест env-override |
|
||||
| `tools/deltas-mattermost-4.json` | тест fx-графа |
|
||||
| `tools/deltas-mattermost-5.json` | фикс `fx.In` + `module.go` |
|
||||
| `tools/deltas-mattermost-6.json` | 4 провайдера строк |
|
||||
| `tools/.deltas-state.json` | состояние (sha256 применённых якорей) |
|
||||
|
||||
**Формат дельты:**
|
||||
```json
|
||||
{ "file": "путь/относительно/корня", "note": "что делаем",
|
||||
"search": "уникальный фрагмент текущего текста",
|
||||
"replace": "чем заменить" }
|
||||
```
|
||||
|
||||
**Три состояния, которые различает апплаер:**
|
||||
- `replace` уже есть в файле → `skipped` (идемпотентно, повторный прогон безопасен)
|
||||
- `search` есть ровно один раз → `applied`
|
||||
- ни `search`, ни `replace` → `error: ЯКОРЬ НЕ НАЙДЕН` (файл изменился не так, как ожидалось)
|
||||
|
||||
**Команды:**
|
||||
```bash
|
||||
cd ~/Developer/balda
|
||||
bash tools/run-project-deltas.sh check tools/deltas-mattermost.json tools/.deltas-state.json
|
||||
bash tools/run-project-deltas.sh apply tools/deltas-mattermost.json tools/.deltas-state.json
|
||||
bash tools/run-project-deltas.sh list tools/deltas-mattermost.json tools/.deltas-state.json
|
||||
```
|
||||
|
||||
📌 `check` **обязателен перед `apply`** — он показывает TODO/MISSING/OK по каждому якорю.
|
||||
Именно на этой проверке я поймал, что дельта валидации указывала в несуществующий
|
||||
`validate()` в `config.go` (валидация живёт в `app.go`) и переписал её до записи.
|
||||
|
||||
⚠️ **Не писать `search` с литеральными секретами** — значения собираются на хосте.
|
||||
⚠️ Дельты с длинным `replace` писать **в JSON-файл через `write_file`**, не в heredoc:
|
||||
кавычки и `\n` ломаются.
|
||||
|
||||
---
|
||||
|
||||
## Объём работ
|
||||
@@ -352,7 +533,8 @@ Conventional Commits. Синхронизация общих веток — **mer
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[personal/projects/balda/mattermost-kraken-deploy]] — сервер MM на Kraken
|
||||
- [[personal/projects/balda/mattermost-kraken-deploy]] — сервер MM на Kraken (Фаза 1, ✅ закрыта)
|
||||
- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план
|
||||
- [[personal/tech/balda-go-fx-pitfalls]] — 🔴 fx-питфоллы (fx.In/ParamTags, провайдеры) + метод дельт
|
||||
- [[personal/projects/balda/balda-valera]] — текущая эксплуатация Балды/Валеры
|
||||
- [[personal/tech/hermes-secret-redaction-bypass]] — как обходить маскировку секретов
|
||||
Reference in new issue
Block a user