378 lines
25 KiB
Markdown
Executable File
378 lines
25 KiB
Markdown
Executable File
# Balda — Mattermost connector: PR-подготовка
|
||
|
||
> Обновлён 2026-09-28. Обновлён 2026-09-29 (ревизия: PR СМЕРЖЕН, ветки снесены).
|
||
> Статус: **✅ PR #8 СМЕРЖЕН 2026-09-29T06:06:48Z — `bf4a6ae4`.** MM-транспорт в `upstream/main`.
|
||
> https://github.com/baldaworks/balda/pull/8
|
||
> 🔧 **2026-09-28: правки по ревью применены — голова `05342c5a` (9 коммитов), CI 8/8 PASS.**
|
||
> Ревью `metalagman` опубликовано в PR (`reviews`, state `COMMENTED`, inline-комментов **0**).
|
||
> Закрыты: slash-команды (HTTP-приёмник + setup в доке), `/locator` (`locatorref` +
|
||
> guard-тест), групповой DM (сессия по чату + тест двух отправителей) + **регистрация в
|
||
> `ScopeResolver`, которой не было в PR**. Сверка построчно —
|
||
> [[personal/plans/balda-mm-pr8-review-fixes]] §0a.
|
||
> 📌 **Мерж прошёл без `APPROVED`-ревью** — `reviewDecision` оставался пустым, PR влит вручную
|
||
> мейнтейнером. Мерж-коммит `bf4a6ae4` = merge `86643007` (голова PR, 29 коммитов,
|
||
> 72 файла, +7921/−74) в `93c7ac89` (`upstream/main` на тот момент).
|
||
> Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фаза 2 → PR в апстрим).
|
||
|
||
---
|
||
|
||
## 0. ✅ ПОСТ-МЕРЖ: git-гигиена (2026-09-29)
|
||
|
||
Ветки снесены, prune выполнен, worktree почищены, `/tmp` и форки `norma` вычищены.
|
||
Полный разбор — [[personal/plans/balda-runtime-rebuild-mm-validation]] блок «2026-09-29».
|
||
|
||
```bash
|
||
cd ~/Developer/balda
|
||
git checkout main && git pull --no-rebase origin main
|
||
git branch -D feat/mattermost-channel feat/zulip-transport feat/zulip-transport-intermediate \
|
||
backup/our-main-before-upstream feat/zulip-events-polling
|
||
git push origin --delete feat/mattermost-channel feat/zulip-transport
|
||
git remote prune origin && git remote prune upstream
|
||
# ⚠️ СНАЧАЛА worktree, потом ветки — иначе branch -D падает
|
||
git worktree remove --force <11 путей> && git worktree prune
|
||
git branch -D codex/mattermost-prod-fix
|
||
rm -rf ~/Developer/balda/personal
|
||
rm -rf /tmp/balda-* /private/tmp/balda-* # ~130 файлов
|
||
rm -rf ~/Developer/norma-local ~/Developer/norma-fork ~/Developer/norma-upstream-check # 291 МБ
|
||
```
|
||
|
||
**Итог: `~/Developer/balda` = только `main`, один worktree, чистое дерево** (кроме `.gitignore`).
|
||
|
||
🔴 **Питфолл: `git worktree list` показывал 10 несуществующих путей** `/private/tmp/balda-mm-pr8-*`.
|
||
Каталоги снесены из `/tmp`, но `.git/worktrees/<name>` живы и **держат ветку**:
|
||
`git branch -D` → `error: cannot delete branch 'X' used by worktree at '/path'`.
|
||
Порядок обязателен: `worktree remove --force` → `worktree prune` → `branch -D`.
|
||
|
||
🔴 **Питфолл при сносе форков `norma`:** в `~/Developer/norma-fork` лежал **непушенный** коммит
|
||
`70ff876 feat: support custom OpenAI base URL via OPENAI_BASE_URL env var` (ветка
|
||
`feat/custom-openai-base-url`). Это тот самый `OPENAI_BASE_URL`, на котором держится запуск Балды.
|
||
Проверять `git log --branches --not --remotes --oneline` **перед** `rm -rf` любого репо.
|
||
|
||
📌 **`.gitignore` изменён (не закоммичен):** добавлены `/personal/` и `/tools/` — доки Obsidian
|
||
дважды попадали в корень репо из-за `write_file` с vault-относительным путём при cwd = репо.
|
||
|
||
📌 **`~/Developer/balda/personal/` был дубликатом вотла, не уникальным контентом** — обе версии
|
||
в `~/obsidian` новее по размеру и дате. Сверять перед сносом, а не удалять по имени файла.
|
||
|
||
⚠️ **Локальная `main` отстала от `upstream/main`.** MM-транспорт ушёл в **апстрим**, не в
|
||
`origin/mallexxx/balda`, поэтому `git pull origin main` → `Already up to date` — это не «всё чисто»,
|
||
а «origin не содержит мержа». Догонять через `git merge upstream/main` (не rebase — `AGENTS.md`).
|
||
Решение по этому шагу **не принято**.
|
||
|
||
## 1. Состояние (актуально на 2026-09-29)
|
||
|
||
| | |
|
||
|---|---|
|
||
| PR | **baldaworks/balda#8** — `feat(mattermost): add Mattermost channel transport` |
|
||
| Состояние | ✅ **MERGED** 2026-09-29T06:06:48Z · merge-commit **`bf4a6ae4`** |
|
||
| База / голова | `baldaworks/balda:main` ← `mallexxx:feat/mattermost-channel` @ `86643007` |
|
||
| Объём на момент мержа | **72 файла, +7921 / −74**, 29 коммитов |
|
||
| Ревью | `COMMENTED` от `metalagman`, `APPROVED` не было — мерж вручную |
|
||
| Ветка в форке | 🗑️ удалена локально и в `origin` 2026-09-29 |
|
||
|
||
```
|
||
architecture-lint pass 2m2s
|
||
ingress-e2e pass 30s
|
||
jetstream-integration pass 1m0s
|
||
lint pass 2m46s
|
||
postgres-integration pass 57s
|
||
projection-replay pass 26s
|
||
sqlite-integration pass 1m15s
|
||
test pass 6m0s ← главный: полный go test на чистом раннере
|
||
```
|
||
|
||
**Ревью состоялось 2026-09-28** (`metalagman`, state `COMMENTED`) — правки применены, голова
|
||
`05342c5a`, CI 8/8 на новом коммите. См. §0a [[personal/plans/balda-mm-pr8-review-fixes]].
|
||
|
||
🔴 **Блокер: `reviewDecision` пустой = ревью было COMMENTED, а не APPROVED.** Мержа не будет,
|
||
пока ревьюер не сменит состояние на approval. Наш ход — после правок дождаться ре-ревью;
|
||
периодически перепроверять `gh pr view 8 --repo baldaworks/balda --json reviewDecision,reviews`.
|
||
|
||
🔴 **Целевой репозиторий — `baldaworks/balda`, НЕ `normahq/balda`.** Проверено через API,
|
||
не по названию remote'а:
|
||
|
||
```
|
||
gh repo view mallexxx/balda --json parent
|
||
→ parent: baldaworks/balda ← форк от него
|
||
gh repo view normahq/balda --json owner
|
||
→ owner.id: O_kgDOEgAHrw (= baldaworks) ← редирект, не отдельный репо
|
||
```
|
||
|
||
`origin` = `mallexxx/balda` (форк), `upstream` = `normahq/balda` (редирект на baldaworks).
|
||
PR создавать **в `baldaworks/balda`**, head указывать как `mallexxx:<branch>` — не полный
|
||
URL форка, иначе `gh` путается.
|
||
|
||
---
|
||
|
||
## 2. Боевая приёмка — ПРОЙДЕНА 2026-09-26 (образ `0.1.10`, коммит `9f1e6c9c`)
|
||
|
||
Сквозной путь работает **от аккаунта Alex**. Это первая настоящая приёмка — прошлая была
|
||
ложной (от `testuser`).
|
||
|
||
### Доказательство — лента канала `balda-test`
|
||
|
||
```
|
||
1790413414689 | edxrgupoxiyig5gzmr79tjypbme | слово ← ОТВЕТ БОТА
|
||
1790413409055 | cbwsorzwoigbtrdnteugnx4aqo | @balda скажи одно слово ← пост ALEX
|
||
```
|
||
|
||
Запрос — `cbwsorzwoigbtrdnteugnx4aqo` (**аккаунт Alex**, `admin`), ответ — бот.
|
||
В логах: **`settlement_stage=accepted`** (НЕ `unauthorized`), `assembled provider user content`,
|
||
`delivery settled`.
|
||
|
||
### Привязка владельца — починена
|
||
|
||
```
|
||
balda_user_bindings:
|
||
channel_type = mattermost
|
||
principal = cbwsorzwoigbtrdnteugnx4aqo ← БЕЗ префикса ✅ (иначе unauthorized)
|
||
display_name = mattermost:cbwsorzwoigbtrdnteugnx4aqo ← ОДИНАРНЫЙ префикс ✅ (нет мусора)
|
||
```
|
||
|
||
Дефект двойного префикса вылечен **в коде** (идемпотентный `MattermostSubject`), в БД мусора нет.
|
||
|
||
### WS-здоровье и старт
|
||
|
||
| Метрика | Значение |
|
||
|---|---|
|
||
| `bad_seq` | **0** ✅ |
|
||
| `Invalid sequence` | **0** ✅ |
|
||
| `pong` | **6** (пинг раз в 30 с работает) |
|
||
| `RestartCount` | **0** ✅ |
|
||
| Контейнер | `balda-mm:0.1.10` Up стабильно |
|
||
|
||
```
|
||
mattermost transport configured server_url=http://mattermost:8065 bot_user_id_set=true
|
||
session manager ready balda_provider=deepseek
|
||
mattermost websocket connected ws://mattermost:8065/api/v4/websocket
|
||
Balda bot started.
|
||
```
|
||
|
||
Никакого crash-loop, `advertised commands have no actor handler` отсутствует.
|
||
|
||
### Два не-блокера
|
||
|
||
1. **`warn: getMe response missing result`** при старте — косметика: startup-лог не смог
|
||
прочитать identity бота. На работу НЕ влияет (`bot_user_id_set=true` из конфига).
|
||
Low priority, чинить в апстриме.
|
||
2. **`transport_user_id = c6udeze8dfrdxm5jbgq55bd58r`** в логах сессии — это `testuser`,
|
||
id прошлого владельца, зашитый в persisted-метаданные сессии `mm-c-7aafea13` (создана
|
||
в прошлой сессии от testuser). Авторизация прошла верно (по `principal` из bindings),
|
||
ответ ушёл Alex'у, **но сессия хранит чужой `user_id`**. Чистится `/reset` в канале.
|
||
На работу не влияет (проверено: ответ пришёл).
|
||
|
||
---
|
||
|
||
## 3. Сборка и деплой (воспроизводимо)
|
||
|
||
```bash
|
||
# 1. образ из PR-коммита (на Mac)
|
||
cd ~/Developer/balda
|
||
docker buildx build --builder arm64builder --platform linux/arm64 \
|
||
-f Dockerfile.release --build-arg VERSION=v0.3.1-mm-pr8 \
|
||
--build-arg COMMIT=$(git rev-parse --short HEAD) \
|
||
--build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
|
||
--provenance=false --load -t balda-mm:0.1.10 .
|
||
|
||
# 2. перенос (распаковка zstd НА MAC — на хосте zstd 1.5.4 не читает фрейм macOS)
|
||
docker save balda-mm:0.1.10 -o /tmp/balda-0110.tar
|
||
zstd -3 -T0 -o /tmp/balda-0110.tar.zst --force /tmp/balda-0110.tar
|
||
zstd -dc /tmp/balda-0110.tar.zst | ssh kraken 'docker load'
|
||
|
||
# 3. переключение с бэкапом compose
|
||
ssh kraken "cat > /tmp/mm-switch-image.sh" < tools/mm-switch-image.sh
|
||
ssh kraken 'bash -n /tmp/mm-switch-image.sh' < /dev/null # SYNTAX_OK
|
||
ssh kraken 'bash /tmp/mm-switch-image.sh' < /dev/null
|
||
```
|
||
|
||
| Скрипт | Что делает |
|
||
|---|---|
|
||
| `tools/mm-switch-image.sh` | смена тега образа в compose + бэкап + `--force-recreate` |
|
||
| `tools/mm-whoami.sh` | какой MM-аккаунт у PAT (`/users/me`) — нужен для приёмки от заказчика |
|
||
|
||
⚠️ **Проверка CA в образе:** `docker save` даёт **OCI**-формат, files в `blobs/sha256/*`;
|
||
`tar -tf image.tar | grep ca-certificates` даст **0** (ложный минус). Проверять по слоям или
|
||
верить `docker inspect` → `SSL_CERT_FILE` в `Config.Env`.
|
||
|
||
⚠️ **Приёмка обязывает иметь PAT заказчика.** `/tmp/admin-tok` = PAT Alex
|
||
(`cbwsorzwoigbtrdnteugnx4aqo`, `admin`) — сверено через `mm-whoami.sh` **до** постинга.
|
||
Без этого тест снова был бы от `testuser` = ложная приёмка.
|
||
|
||
---
|
||
|
||
## 4. Что в PR
|
||
|
||
### Коммиты (7)
|
||
|
||
```
|
||
9f1e6c9c chore: ignore local project-editing tooling
|
||
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 ← база прошлой сессии
|
||
```
|
||
|
||
База ветки идеальная: `git rev-list --left-right --count upstream/main...HEAD` → `0 7`
|
||
(0 позади, 7 впереди), `merge-base` == `upstream/main` == `de273d8f`. Rebase не потребовался.
|
||
|
||
### Гейты — все зелёные на закоммиченном дереве
|
||
|
||
```
|
||
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 (затронутые пакеты) → все ok
|
||
тесты mattermost + handlersfx (verbose) → 126 PASS, 0 FAIL, 0 SKIP
|
||
```
|
||
|
||
📌 **Настоящая приёмка сборки — CI на чистом раннере.** Локально `cmd/balda` падал на
|
||
`TestInitCommand_NonInteractiveAutoSelectsRootAndGeneratesDetectedAgents` — в CI он **прошёл**,
|
||
то есть падение было особенностью локальной среды, а не дефектом. Локальные гейты —
|
||
необходимое, но не достаточное условие; ждать CI обязательно.
|
||
|
||
### Тесты — 103 добавлено (`grep -c '^func Test'`)
|
||
|
||
| Файл | Тестов | Покрытие |
|
||
|---|---|---|
|
||
| `channel/mattermost/ingress_test.go` | 23 | 🔴 `bad_seq`-регрессия, монотонность и уникальность `seq` под 16 горутинами, фильтры, panic-recovery, семафор |
|
||
| `channel/mattermost/inbound_test.go` | 18 | `NormalizeInbound`, mention-фильтр, `ParseCommand`, whitelist команд, `PostIDFromWebSocket` |
|
||
| `channel/mattermost/adapter_test.go` | 17 | все `deliverycmd.Operation`, plain-fallback только на content-rejection, draft-as-post, typing no-op без сети, `root_id` в треде |
|
||
| `handlersfx/mattermost_ingress_test.go` | 17 | публикация всех команд, **одиночный** префикс principal, `/start` до авторизации, отказ чужим, nil-safety |
|
||
| `channel/mattermost/locator_codec_test.go` | 15 | локаторы, thread↔channel изоляция, `LocatorFromAddressKey`, scope, UserID |
|
||
| `channel/mattermost/client_test.go` | 13 | ws/wss derivation, `Bearer`, `APIError`, `isContentRejectedError`, контекст |
|
||
|
||
### Что починено по существу
|
||
|
||
1. **Дефект двойного префикса — в коде, а не в БД.** Прошлый фикс был прямым `UPDATE` в
|
||
`state.db`, поэтому дефект вернулся. Теперь `auth.MattermostSubject()` идемпотентен (срезает
|
||
уже имеющийся префикс `mattermost:` перед повторной квалификацией), а из
|
||
`actors/command/start/handler.go` убран локальный `TrimPrefix`-костыль.
|
||
Почему ломало прод: `canonicalSubject` режет subject по **первому** `:`, а `GetUserByBinding`
|
||
сравнивает **точно** → `mattermost:mattermost:<id>` не совпадает ни с чем → каждый пост Alex
|
||
отбивался `settlement_stage=unauthorized`.
|
||
Регрессия: `TestMattermostSubjectIsIdempotent`,
|
||
`TestMattermostHandlerSendsChannelQualifiedPrincipalExactlyOnce`.
|
||
2. **Противоречие `ProgressPolicy.Typing`.** Command-путь ставил `Typing: true`, inbound-путь —
|
||
`false`, а `SendTyping` у MM-адаптера осознанный no-op (у ботов MM нет typing API). Мёртвая
|
||
ветка + warn-шум на каждом ходу → приведено к `false`.
|
||
3. **Доки** — `README.md`, `docs/balda.md`, новый `docs/mattermost.md` (создание бота, минтинг
|
||
токена — отдаётся ОДИН раз, membership в команде/канале, 5 ключей конфига, поведение в
|
||
DM/канале/треде, troubleshooting `bad_seq`/`unauthorized`/`x509`).
|
||
|
||
### Предсуществующие падения — НЕ наши (проверено `git stash`)
|
||
|
||
5 пакетов: `cmd/balda`, `apps/balda`, `mcpruntime`, `session`, `state`. Те же 4 теста падают
|
||
**без** наших правок:
|
||
`TestInitCommand_NonInteractiveAutoSelectsRootAndGeneratesDetectedAgents`,
|
||
`TestCanonicalSessionMemoryRuntimeComposesPortableCapabilities`,
|
||
`TestCanonicalSessionMemoryRuntimeMigratesAndReopensGroupedStore`,
|
||
`TestResolveWorkspaceBaseBranch_ConfigPreferredWhenValid`.
|
||
|
||
### Команды PR (воспроизводимо)
|
||
|
||
```bash
|
||
cd ~/Developer/balda
|
||
git fetch upstream --quiet
|
||
git rev-list --left-right --count upstream/main...HEAD # 0 7
|
||
git push -u origin feat/mattermost-channel
|
||
|
||
gh pr create --repo baldaworks/balda --base main \
|
||
--head mallexxx:feat/mattermost-channel \
|
||
--title "feat(mattermost): add Mattermost channel transport" \
|
||
--body-file /tmp/mm-pr-body.md
|
||
|
||
gh pr view 8 --repo baldaworks/balda --json state,mergeable,changedFiles,additions
|
||
gh pr checks 8 --repo baldaworks/balda
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Пять файлов на диске — зачем они, НЕ трогать
|
||
|
||
В `~/Developer/balda` пять файлов изменены относительно коммита `9f1e6c9c` и **в PR не входят**.
|
||
Это **не фиксы** и **не потерянный код** — так и задумано.
|
||
|
||
**Откуда взялось:** 2026-09-25 18:58 (Zulip `personal/Балда`, msg `116952`) ветка пересоздана
|
||
от чистого апстрима: `git branch -f feat/mattermost-channel upstream/main` — «без мержа наших
|
||
коммитов». WIP прошлых сессий остался висеть на диске. Работа продолжилась в этой же ветке и
|
||
завершилась PR #8; существенное вошло в коммит `9f1e6c9c`.
|
||
|
||
| Файл | Что это | В PR? |
|
||
|---|---|---|
|
||
| `go.mod` | `replace github.com/normahq/runtime/v2 => ./runtime-local` — потомок `5ad65955` | ❌ |
|
||
| `Dockerfile.release` | `COPY runtime-local/` перед `go mod download` — иначе сборка падает | ❌ |
|
||
| `go.sum` | −2 хэша runtime v2.0.11 под тот же replace | ❌ |
|
||
| `internal/apps/balda/app.go` | WIP прошлых сессий (MM-импорты без псевдонима) — артефакт переключения ветки | ❌ |
|
||
| `internal/apps/balda/preflight.go` | то же + `ClassifyLocatorScope` во вторую карту | ❌ |
|
||
|
||
**Зачем они нужны:** без `runtime-local` стенд Balda на Kraken **не собирается**. `go.mod`
|
||
берёт не апстримовый runtime, а локальный форк (592 KB) с патчем `tool_calls` для hostedagent
|
||
(авторский PR `normahq/runtime#4`) — без него делегирование в CLI-агентов не работает.
|
||
Остальные 4 файла обслуживают этот же replace: `Dockerfile.release` копирует папку в образ,
|
||
`go.sum` больше не содержит хэшей апстримового runtime.
|
||
|
||
**Почему не в PR:** `replace` на локальную папку ломает сборку у любого, кто склонирует
|
||
`baldaworks/balda`. Такое апстрим не принимает. Место этому — в локальной ветке (`local/*`).
|
||
|
||
**Решение (2026-09-28, Alex):** оставлено как есть, отдельная ветка не делалась.
|
||
|
||
⚠️ **Риск:** `git checkout -- .` / `git stash` / `git reset --hard` в `~/Developer/balda`
|
||
снесёт эти 5 файлов. Перед любой такой командой — сверить `git status --short`.
|
||
|
||
---
|
||
|
||
## 6. Следующие шаги, чтобы «всё работало» — по приоритету
|
||
|
||
| # | Шаг | Тип |
|
||
|---|---|---|
|
||
| 1 | Дождаться ревью #8 → правки в ту же ветку (push обновит PR) | ⛔ внешний блокер |
|
||
| 2 | **Фаза 4 — исполнитель (делегирование)** — превращает «бот отвечает» в «бот работает» | 🎯 основной фронт |
|
||
| 3 | Боевая перепроверка приёмки на Kraken (образ `0.1.10` примлен 26.09 — повторять только если что-то менялось) | ↩ по необходимости |
|
||
| 4 | Замержить PR после ревью → Фаза 2 закрывается официально | ⏳ после 1 |
|
||
|
||
📌 **Пока PR висит на ревью — работать над Фазой 4 в ветке `local/*`, НЕ в
|
||
`feat/mattermost-channel`.** Иначе новые коммиты уедут в открытый PR и обесценят пройденный CI.
|
||
|
||
---
|
||
|
||
## 7. Питфоллы, которые стоили времени
|
||
|
||
1. **`gitignore` НЕ отвязывает уже отслеживаемые файлы.** База `004fda2f` затащила в ветку
|
||
**16 файлов `tools/`**, которых в апстриме нет вообще (`git ls-tree upstream/main tools/` →
|
||
пусто). Найдено проверкой `git diff --stat upstream/main...HEAD`. Фикс (коммит `9f1e6c9c`):
|
||
`/tools/` в `.gitignore` **плюс** `git rm -r --cached tools/` — файлы остались на диске
|
||
(39 файлов), ушли только из git. Нужны оба шага, в этом порядке.
|
||
⚠️ `git rm -r --cached` уходит на апрув (рекурсивное удаление) — первая попытка истекла по
|
||
таймауту и не применилась, состояние было целое. Проверять `git diff --cached --name-only`.
|
||
2. **Проверять состав PR через `git diff --stat upstream/main...HEAD`, а не по своим коммитам.**
|
||
Свои 5 коммитов были чистыми (`git log upstream..HEAD -- tools/` → пусто), но ветка в
|
||
целом — нет, из-за базового коммита. `git status` показывает рабочее дерево, не содержимое
|
||
коммитов.
|
||
3. **Тест-ассерт — это гипотеза, а не истина.** Дважды падал тест, и оба раза неверным был
|
||
ассерт, а не код: (а) «тред и корневой пост = одна сессия» — на самом деле тред **отдельная**
|
||
сессия от канала (пробник: `mm-c-a4aa02ef` vs `mm-t-89bbf64f`); (b) «`NewIngress` фолбэчит
|
||
`botUserID` из клиента» — на самом деле `onStart` отказывается стартовать без `bot_user_id`.
|
||
Прежде чем править код под падающий тест — проверить, чему код **обязан** соответствовать.
|
||
4. **`goconst` в тестах** — повторяющиеся строковые литералы (`"channel-1"` ×15) валят линтер.
|
||
Заводить константы (`testChannelID`) сразу. **`dupword`** — `"thread channel = %q, channel
|
||
channel = %q"` пойман как повтор слова.
|
||
5. **`write_file` + `***` в доке** — плейсхолдеры вида `BALDA_MATTERMOST_TOKEN=***` в примерах
|
||
конфига остаются на диске **целыми**; искажается только вывод чтения. Проверять
|
||
`awk 'NR==N'`, не глазами (см. [[personal/tech/hermes-secret-redaction-bypass]]).
|
||
6. **⛔ zstd-фрейм macOS.** `zstd -d` на Kraken не читает фрейм, записанный на macOS (хостовый
|
||
zstd 1.5.4). Распаковывать **на Mac**: `zstd -dc file.tar.zst | ssh kraken 'docker load'`.
|
||
|
||
---
|
||
|
||
## Связанные заметки
|
||
|
||
- [[personal/projects/balda/balda-mattermost-connector]] — Фаза 2 (код коннектора)
|
||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (деплой, боевые причины тишины)
|
||
- [[personal/projects/balda/balda-mm-delegation-plan]] — Фаза 4 (делегирование)
|
||
- [[personal/tech/balda-go-fx-pitfalls]] — fx-питфоллы + дельта-апплаер
|
||
- [[personal/plans/balda-mm-endtoend-plan]] — сквозной путь
|
||
- [[personal/tech/hermes-secret-redaction-bypass]] — редакция секретов
|