Files
obsidian-vault/personal/plans/balda-mm-pr-plan.md
T

11 KiB
Raw Blame History

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)

  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-инструменты и метод правок, в апстрим-репозиторий им не место.

🔴 Открытое решение: 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:

  1. 🔴 Убрать tools/ из ветки — вариант (a) выше. Блокирует «стыдный» PR.
  2. git push -u origin feat/mattermost-channel и открыть PR в baldaworks/balda.
  3. Фаза 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.

Связанные заметки