[2026-09-26] eagle: personal/plans/balda-mm-pr-plan.md personal/projects/balda/balda-mattermost-connector.md personal/tech/balda-go-fx-pitfalls.md
This commit is contained in:
1 parent
4ab53a2a63
commit
519f7c443c
3 files changed
+301
-47
No files matched your search
@@ -1,68 +1,111 @@
|
||||
# Balda — Mattermost connector: план подготовки PR в апстрим
|
||||
# Balda — Mattermost connector: PR-подготовка (закрыто)
|
||||
|
||||
> Создан 2026-09-26. Статус: **в работе**.
|
||||
> Создан 2026-09-26. Обновлён 2026-09-26 (PR-проход). Статус: **✅ ЗАКРЫТО — код, тесты, доки, коммиты готовы**.
|
||||
> Команда Alex: «Коммит текущего стейта и продолжай пока не стыдно будет открывать PR mattermost connector.
|
||||
> Со всеми поддерживаемыми фичами и тестами. Вперёд! Без вопросов и требующих апрува команд.»
|
||||
> Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фаза 2 → PR в апстрим).
|
||||
|
||||
## Цель
|
||||
## Итог
|
||||
|
||||
PR в `baldaworks/balda`: полный Mattermost-транспорт — все поддерживаемые фичи Балды,
|
||||
тесты, зелёные quality gates, доки. Ветка `feat/mattermost-channel`, 1 коммит уже есть
|
||||
(`004fda2f`), рабочий счёт — 11 изменённых + 21 новых файлов в worktree.
|
||||
Ветка `feat/mattermost-channel`, **6 коммитов** (базовый `004fda2f` + 5 новых). Готово к `git push` и PR.
|
||||
|
||||
## Что уже готово (не переделывать)
|
||||
```
|
||||
58f27120 docs(mattermost): document the transport setup and behavior
|
||||
80d4f41d test(mattermost): cover the transport, the ingress and the handler
|
||||
c7f18654 fix(mattermost): send a monotonic seq on every client frame
|
||||
6dc3077a feat(mattermost): add the inbound handler and wire it into DI
|
||||
fa1a2855 feat(auth): support the Mattermost channel subject
|
||||
004fda2f feat(mattermost): add Mattermost transport adapter ← база
|
||||
```
|
||||
|
||||
- Пакет `channel/mattermost/` (client, locator_codec, inbound, adapter, media, ingress, formatting, presentation, mattermostfx) — закоммичен `004fda2f`, 40 файлов.
|
||||
- Все quality gates были зелёные на `004fda2f`.
|
||||
- 6 причин тишины найдены и устранены живым запуском на Kraken (см. `balda-kraken-mm-deploy.md`).
|
||||
- `handlersfx/mattermost_ingress.go` — написан в worktree, НЕ закоммичен.
|
||||
## Quality gates — все зелёные (проверено на закоммиченном дереве)
|
||||
|
||||
## Пробелы, найденные ревизией кода (2026-09-26, вечер)
|
||||
```
|
||||
go build ./... → exit 0
|
||||
go vet ./... → exit 0
|
||||
go tool golangci-lint run → 0 issues
|
||||
go tool go-arch-lint check --project-path . → OK - No warnings found
|
||||
go test -race ./channel/mattermost/ ./handlersfx/ ./auth/ ./deliveryfmt/
|
||||
./actors/command/... → все ok, включая start и auth
|
||||
```
|
||||
|
||||
### P1 — дефекты, которые полезут в PR и подведут ревью
|
||||
## Что сделано в этом проходе
|
||||
|
||||
| # | Дефект | Где | Почему важно |
|
||||
|---|---|---|---|
|
||||
| P1.1 | Возможен двойной транспортный префикс в `principal` | `actors/command/start/handler.go:~420` | Прошлый фикс был только в БД → дефект вернулся. В PR пойдёт сырой код |
|
||||
| P1.2 | `channel/mattermost/*_test.go` — **0 файлов** | `channel/mattermost/` | «Со всеми тестами» — сейчас только 2 теста в `cmd/balda`. Апстрим-транспорт zulip имеет ~4 тест-файла |
|
||||
| P1.3 | `handlersfx/mattermost_ingress.go` не покрыт тестами и не закоммичен | `handlersfx/` | Ядро интеграции, написанное «на веру» |
|
||||
| P1.4 | README / `docs/balda.md` не упоминают Mattermost | корень, docs/ | Требование `AGENTS.md`; осталось открытым из Фазы 2 |
|
||||
| P1.5 | `SendDraftPlain` — проверить, что edit-in-place покрыт | `adapter.go` | Отклонение от плана, надо подтвердить тестом |
|
||||
| P1.6 | `SendTyping` — no-op; в `ProgressPolicy` Typing:false | `adapter.go`, `inbound.go` | Проверить, что нет мёртвого кода (линтер уже проходил, но повторно) |
|
||||
### 1. Дефект двойного префикса — починен В КОДЕ, а не в БД (главное)
|
||||
|
||||
### P2 — качество PR
|
||||
Прошлый фикс был прямым `UPDATE` в `state.db`, поэтому дефект вернулся.
|
||||
Теперь источник закрыт:
|
||||
|
||||
- Прогнать `go test -race ./...` на затронутых пакетах, отделить апстримные фейлы (`git stash`).
|
||||
- `golangci-lint`, `go-arch-lint` — перепроверить после новых файлов.
|
||||
- Конвенциональные коммиты, разбивка: transport code / wiring / tests / docs.
|
||||
- Убрать из дерева артефакты, которым в PR не место (`tools/mm-*.sh`, дельта-JSON) —
|
||||
решить: в отдельный коммит с `tools/` или не пушить вовсе.
|
||||
- `auth.MattermostSubject()` **идемпотентен** — срезает уже имеющийся префикс
|
||||
`mattermost:` перед повторной квалификацией;
|
||||
- из `actors/command/start/handler.go` убран локальный `TrimPrefix`-костыль,
|
||||
ветка стала как у остальных транспортов: `auth.MattermostSubject(trimmed)`.
|
||||
|
||||
## План работ (по шагам)
|
||||
Почему это ломало прод: `canonicalSubject` режет subject по **первому** `:`, а
|
||||
`GetUserByBinding` сравнивает **точно**. Значение `mattermost:mattermost:<id>`
|
||||
в `principal` не совпадает ни с чем → каждый пост Alex отбивался
|
||||
`settlement_stage=unauthorized`.
|
||||
**Тест-регрессия:** `TestMattermostSubjectIsIdempotent`
|
||||
(`handlersfx/mattermost_ingress_test.go`) + `TestMattermostHandlerSendsChannelQualifiedPrincipalExactlyOnce`.
|
||||
|
||||
1. **Зафиксировать базу.** `check` + `apply` всех дельт в worktree → `go build ./...`.
|
||||
2. **Починить P1.1** (двойной префикс) в коде `handler.go`.
|
||||
3. **Тесты транспорта** (`channel/mattermost/`): `locator_codec_test.go`, `inbound_test.go`, `adapter_test.go` (по образцу zulip, `httptest`-сервер для REST).
|
||||
4. **Тесты хендлера** (`handlersfx/mattermost_ingress_test.go`).
|
||||
5. **P1.4** — README + `docs/balda.md`.
|
||||
6. **Quality gates** — build / vet / golangci-lint / go-arch-lint / race-тесты.
|
||||
7. **Коммиты** — по смыслу, Conventional Commits.
|
||||
8. **Обновить Obsidian** — итог и что осталось.
|
||||
### 2. Исправлено противоречие `ProgressPolicy.Typing`
|
||||
|
||||
## Definition of Done
|
||||
Command-путь ставил `Typing: true`, тогда как inbound-путь — `false`, а
|
||||
`SendTyping` у MM-адаптера **осознанный no-op** (у ботов MM нет typing API).
|
||||
Мёртвая ветка + warn-шум на каждом ходу. Приведено к `false`.
|
||||
|
||||
- `go build ./...`, `go vet ./...` = 0.
|
||||
- `golangci-lint run` = 0 issues.
|
||||
- `go-arch-lint check --project-path .` = OK.
|
||||
- Тесты транспорта + хендлера зелёные, `-race`.
|
||||
- Ни одного дефекта из `balda-go-fx-pitfalls` в коде.
|
||||
- Доки описывают Mattermost.
|
||||
- Коммиты в `feat/mattermost-channel`, готово к `git push` и PR.
|
||||
### 3. Тесты транспорта — 6 новых файлов (~90 тестов)
|
||||
|
||||
| Файл | Покрытие |
|
||||
|---|---|
|
||||
| `channel/mattermost/locator_codec_test.go` | локаторы, **thread↔channel изоляция**, `LocatorFromAddressKey`, scope, UserID |
|
||||
| `channel/mattermost/inbound_test.go` | `NormalizeInbound`, mention-фильтр, `ParseCommand`, whitelist команд, `PostIDFromWebSocket` |
|
||||
| `channel/mattermost/adapter_test.go` | все `deliverycmd.Operation`, **plain-fallback только на content-rejection**, draft-as-post, typing no-op без сети, `root_id` в треде |
|
||||
| `channel/mattermost/client_test.go` | ws/wss derivation, `Bearer`, `APIError`, `isContentRejectedError`, контекст |
|
||||
| `channel/mattermost/ingress_test.go` | 🔴 **`bad_seq`-регрессия**, монотонность и уникальность `seq` под 16 горутинами, фильтры, panic-recovery, семафор |
|
||||
| `handlersfx/mattermost_ingress_test.go` | публикация всех команд, **одиночный** префикс principal, `/start` до авторизации, отказ чужим, nil-safety |
|
||||
|
||||
### 4. Доки — `README.md`, `docs/balda.md`, новый `docs/mattermost.md`
|
||||
|
||||
`docs/mattermost.md` — полный гайд: создание бота, минтинг токена (отдаётся
|
||||
ОДИН раз), membership в команде/канале, 5 ключей конфига, поведение в
|
||||
DM/канале/треде, troubleshooting (`bad_seq`, `unauthorized`, `x509`).
|
||||
|
||||
## Уроки метода (дополнить `balda-go-fx-pitfalls`)
|
||||
|
||||
1. **Тест-ассерт — это гипотеза, а не истина.** Дважды падал тест, и оба раза
|
||||
неверным был ассерт, а не код: (а) «тред и корневой пост = одна сессия» —
|
||||
на самом деле тред **отдельная** сессия от канала (проверено пробником:
|
||||
`mm-c-a4aa02ef` vs `mm-t-89bbf64f`, два разных ответа в треде дают ОДНУ
|
||||
сессию); (b) «`NewIngress` фолбэчит `botUserID` из клиента» — на самом деле
|
||||
`onStart` **отказывается стартовать** без `bot_user_id`. Прежде чем править
|
||||
код под падающий тест — проверить, чему код **обязан** соответствовать.
|
||||
2. **`goconst` в тестах** — повторяющиеся строковые литералы (`"channel-1"` ×15)
|
||||
валят линтер. Заводить константы (`testChannelID` и т.п.) сразу.
|
||||
3. **`dupword`** — сообщение `"thread channel = %q, channel channel = %q"`
|
||||
поймано линтером как повтор слова. Формулировать иначе.
|
||||
4. **`write_file` + `***` в доке** — плейсхолдеры вида `BALDA_MATTERMOST_TOKEN=***`
|
||||
в примерах конфига остаются на диске **целыми**; искажается только вывод
|
||||
чтения. Проверять `awk 'NR==N'`, не глазами (см. `hermes-secret-redaction-bypass`).
|
||||
5. **Артефакты деплоя — вон из PR.** `tools/mm-*.sh` (9 скриптов) и
|
||||
`tools/deltas-*.json` (15 файлов) оставлены **незакоммиченными**: это
|
||||
ops-инструменты и метод правок, в апстрим-репозиторий им не место.
|
||||
|
||||
## Что осталось
|
||||
|
||||
**Ничего по коду.** Дальше по желанию Alex:
|
||||
|
||||
1. `git push -u origin feat/mattermost-channel` и открыть PR в `baldaworks/balda`.
|
||||
2. Фаза 3/4 (эксплуатация на Kraken) — [[personal/projects/balda/balda-kraken-mm-deploy]].
|
||||
|
||||
⚠️ Незакоммиченные `tools/mm-*.sh` + `tools/deltas-*.json` — **локальный
|
||||
инструментарий**. Если они нужны в репо — отдельным коммитом с пометкой, но
|
||||
для PR в апстрим я бы их не отправлял.
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[personal/projects/balda/balda-mattermost-connector]] — Фаза 2 (код)
|
||||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (деплой)
|
||||
- [[personal/projects/balda/balda-mattermost-connector]] — Фаза 2 (код коннектора)
|
||||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (деплой, боевые причины тишины)
|
||||
- [[personal/tech/balda-go-fx-pitfalls]] — fx-питфоллы + дельта-апплаер
|
||||
- [[personal/plans/balda-mm-endtoend-plan]] — сквозной путь
|
||||
- [[personal/tech/hermes-secret-redaction-bypass]] — редакция секретов
|
||||
Reference in new issue
Block a user