11 KiB
Balda — Mattermost connector: PR-подготовка (закрыто)
Создан 2026-09-26. Обновлён 2026-09-26 (PR-проход, финал). Статус: 🟡 код/тесты/доки/коммиты готовы — но осталось ОДНО решение по
tools/в ветке (см. §«Открытое решение» ниже). После него —git pushи PR. Команда Alex: «Коммит текущего стейта и продолжай пока не стыдно будет открывать PR mattermost connector. Со всеми поддерживаемыми фичами и тестами. Вперёд! Без вопросов и требующих апрува команд.» Часть personal/projects/balda/balda-mm-delegation-plan (Фаза 2 → PR в апстрим).
Итог
Ветка 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 ← база
Quality gates — все зелёные (проверено на закоммиченном дереве)
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
Что сделано в этом проходе
1. Дефект двойного префикса — починен В КОДЕ, а не в БД (главное)
Прошлый фикс был прямым UPDATE в state.db, поэтому дефект вернулся.
Теперь источник закрыт:
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.
2. Исправлено противоречие ProgressPolicy.Typing
Command-путь ставил Typing: true, тогда как inbound-путь — false, а
SendTyping у MM-адаптера осознанный no-op (у ботов MM нет typing API).
Мёртвая ветка + warn-шум на каждом ходу. Приведено к false.
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)
- Тест-ассерт — это гипотеза, а не истина. Дважды падал тест, и оба раза
неверным был ассерт, а не код: (а) «тред и корневой пост = одна сессия» —
на самом деле тред отдельная сессия от канала (проверено пробником:
mm-c-a4aa02efvsmm-t-89bbf64f, два разных ответа в треде дают ОДНУ сессию); (b) «NewIngressфолбэчитbotUserIDиз клиента» — на самом делеonStartотказывается стартовать безbot_user_id. Прежде чем править код под падающий тест — проверить, чему код обязан соответствовать. goconstв тестах — повторяющиеся строковые литералы ("channel-1"×15) валят линтер. Заводить константы (testChannelIDи т.п.) сразу.dupword— сообщение"thread channel = %q, channel channel = %q"поймано линтером как повтор слова. Формулировать иначе.write_file+***в доке — плейсхолдеры видаBALDA_MATTERMOST_TOKEN=***в примерах конфига остаются на диске целыми; искажается только вывод чтения. Проверятьawk 'NR==N', не глазами (см.hermes-secret-redaction-bypass).- Артефакты деплоя — вон из PR.
tools/mm-*.sh(9 скриптов) иtools/deltas-*.json(15 файлов) оставлены незакоммиченными: это ops-инструменты и метод правок, в апстрим-репозиторий им не место.
🔴 Открытое решение: tools/ закоммичены в ветку (выявлено в конце сессии)
Найдено проверкой git diff --stat upstream/main...HEAD: в диапазоне PR — 56 файлов,
и среди них 16 файлов tools/, которых в апстриме нет вообще
(git ls-tree upstream/main tools/ → пусто).
Кто занёс: базовый коммит 004fda2f (из прошлой сессии, не из этого прохода).
Мои 5 новых коммитов чисты — проверено:
git log --oneline --name-only fa1a2855^..HEAD -- 'tools/' # → пусто
Что именно закоммичено в 004fda2f (лишнее для PR):
tools/.deltas-state.json
tools/apply-project-deltas.py
tools/run-project-deltas.sh
tools/deltas-mattermost.json
tools/deltas-mattermost-2.json … -13.json (12 файлов)
Что нужно сделать — по выбору Alex:
| Вариант | Команда | Последствие |
|---|---|---|
| (a) рекомендую | git rm -r --cached tools/ + коммит |
файлы остаются на диске untracked, локальный инструментарий работает, ветка чистая |
| (b) оставить | — | PR содержит 16 лишних файлов, ревьюер спросит |
| (c) отдельная ветка | — | tooling живёт отдельно от PR |
⚠️ Попытка (a) в этой сессии НЕ выполнена. git rm -r --cached tools/ ушла на
апрув и истекла по таймауту — команда не применилась, состояние дерева целое
(подтверждено git status --short: все файлы на месте). Повторять без команды Alex
не стал: удаление из индекса — операция, требующая подтверждения.
📌 Правило на будущее: проверять состав PR через
git diff --stat upstream/main...HEAD перед объявлением готовности, а не только
git status (последний показывает рабочее дерево, не содержимое коммитов). Иначе
мусор из прошлой сессии всплывает последним.
Что осталось
Ничего по коду. Дальше по желанию Alex:
- 🔴 Убрать
tools/из ветки — вариант (a) выше. Блокирует «стыдный» PR. git push -u origin feat/mattermost-channelи открыть PR вbaldaworks/balda.- Фаза 3/4 (эксплуатация на Kraken) — personal/projects/balda/balda-kraken-mm-deploy.
⚠️ Скрипты tools/mm-*.sh (9 шт.) и дельты tools/deltas-mattermost-14…26 +
deltas-goal-command-fix.json + deltas-mm-prefixfix.json — untracked, локальный
инструментарий. Для PR в апстрим не отправлять. Существенное продублировано в
personal/projects/balda/balda-kraken-mm-deploy и personal/tech/balda-go-fx-pitfalls.
Связанные заметки
- 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 — редакция секретов