[2026-09-26] eagle: personal/projects/balda/balda-kraken-mm-deploy.md personal/projects/balda/balda-mattermost-connector.md personal/projects/balda/balda-mm-delegation-plan.md personal/tech/balda-go-fx-pitfalls.md personal/tech/hermes-secret-redaction-bypass.md
This commit is contained in:
1 parent
eab4ef5dc9
commit
a85e45588f
5 files changed
+432
-114
No files matched your search
@@ -1,9 +1,11 @@
|
||||
# Balda — Go/fx pitfalls и метод правок (зеркало скилла)
|
||||
|
||||
> Создано 2026-09-26. Обновлено 2026-09-26 (ночь) — добавлены lint/arch-lint и закрытие Фазы 2.
|
||||
> Создано 2026-09-26. Обновлено 2026-09-26 (ночь, позже) — добавлен **Дефект 4**
|
||||
> (advertisement ↔ actor-хендлер), найденный при первом реальном запуске на Kraken.
|
||||
> Зеркало секции скилла `devops/docker-balda-service`, добавленной 2026-09-26.
|
||||
> Источник: работа над Mattermost-адаптером Балды (Фаза 2).
|
||||
> Источник: работа над Mattermost-адаптером Балды (Фаза 2) + деплой на Kraken (Фаза 3).
|
||||
> Проектные доки: [[personal/projects/balda/balda-mattermost-connector]],
|
||||
> [[personal/projects/balda/balda-kraken-mm-deploy]],
|
||||
> [[personal/projects/balda/balda-mm-delegation-plan]].
|
||||
|
||||
---
|
||||
@@ -109,6 +111,72 @@ fx.Provide(
|
||||
|
||||
---
|
||||
|
||||
## 🔴🔴 Дефект 4: объявленная команда без actor-хендлера (роняет ВЕСЬ fx-граф)
|
||||
|
||||
Найден **только реальным запуском на Kraken** (Фаза 3). До этого Фаза 2 считалась закрытой:
|
||||
`go build` = 0, `go vet` = 0, `golangci-lint` = 0 issues, `go-arch-lint` = OK, тесты зелёные.
|
||||
**Ни один из этих гейтов его не видит.**
|
||||
|
||||
**Симптом** — crash-loop контейнера (`Restarting (1)`), в логах одна строка на тысячи
|
||||
`"message":"provided"`:
|
||||
|
||||
```
|
||||
Error: starting Balda app: could not build arguments for function
|
||||
".../execution".init.func1: failed to build *execution.ActorHost: ...
|
||||
failed to build *command.Router: received non-nil error from function
|
||||
"reflect".makeFuncStub: advertised commands have no actor handler: goal
|
||||
```
|
||||
|
||||
**Причина:** в `mattermostfx/module.go` в `commandcmd.Advertisement{Names: [...]}` была
|
||||
объявлена команда `goal`, которой **не существует** как actor-хендлер. `goal` — это
|
||||
**payload kind** (`goalkeepercmd.PayloadKindGoal`, `actorcmd.KindGoal`,
|
||||
`actors/job_actor.go:100`), а снаружи цикл GoalKeeper называется `goalkeeper`.
|
||||
|
||||
**Механизм:** `actors/command/router.go:49 ValidateAdvertised()` вызывается из
|
||||
`commandfx/module.go:306`. Он валидирует **весь** список `Names` одного advertisement и при
|
||||
первом отсутствующем имени возвращает ошибку → fx не может построить `*command.Router` →
|
||||
падает `*execution.ActorHost` → падает приложение. Одна опечатка в списке = полный отказ старта,
|
||||
а не «недоступна одна команда».
|
||||
|
||||
**Правильно — сверять `Names` с реальными `Name()`:**
|
||||
|
||||
```bash
|
||||
cd ~/Developer/balda
|
||||
grep -rn 'func (h \*Handler) Name() string' internal/apps/balda/actors/command/
|
||||
```
|
||||
|
||||
Эталонный фактический список (2026-09-26, `balda@de273d8f`):
|
||||
|
||||
```
|
||||
locator · reset · usage · auto · cancel · close · start · user · topic · skill
|
||||
goalkeeper · help · plugin
|
||||
```
|
||||
|
||||
`mattermostfx/module.go` после фикса:
|
||||
```go
|
||||
Names: []string{
|
||||
"locator", "reset", "usage", "auto", "cancel",
|
||||
"goalkeeper", "topic", "close", "start",
|
||||
"user", "skill",
|
||||
},
|
||||
```
|
||||
`channel/mattermost/inbound.go` — тот же список в inbound allow-list, синхронно.
|
||||
|
||||
⚠️ `Zulip`-адаптер изначально был **прав** — там `"goalkeeper"` без `"goal"`. Регрессию внёс
|
||||
MM-адаптер при копировании «на глаз».
|
||||
⚠️ **`docs/commands.md` — НЕ источник истины** для имён команд. Там таблица доступности по
|
||||
транспортам; реестр хендлеров — только `actors/command/*/handler.go`.
|
||||
⚠️ Фикс — дельта-апплаером: `tools/deltas-goal-command-fix.json` (2 дельты, обе APPLIED).
|
||||
|
||||
📌 **Общий вывод (расширяет §«go build = exit 0 ≠ рабочий транспорт» выше):** недостаточно
|
||||
проверять DI-граф тестом `validateBaldaApplication`. Тест должен прогоняться **с реальным
|
||||
списком `Names` из модуля**. Но и это не поймало бы опечатку, если тест использует тот же
|
||||
неверный advertisement — поэтому единственная надёжная проверка нового транспорта =
|
||||
**живой запуск бинаря**. Для нового транспорта добавлять в чек-лист шаг:
|
||||
`docker run` (или локальный `balda start`) с включённым транспортом **до** объявления готовности.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Env-override работает только по ключам, уже присутствующим в YAML
|
||||
|
||||
`appconfig/loader.go:367` — `SetEnvPrefix("BALDA")` + `SetEnvKeyReplacer(".", "_")` +
|
||||
@@ -129,6 +197,10 @@ fx.Provide(
|
||||
`TestLoadConfigDocument_AppliesZulipEnvOverrides`)
|
||||
9. если добавлял маршрут в `BuiltinRoutes()` — обновить `TestBuiltinRoutes`
|
||||
10. доки: `docs/reference/configuration.md` (ключи), `docs/commands.md` (колонка транспорта)
|
||||
11. 🔴 **сверить `Advertisement.Names` с реальными `Name()`** в
|
||||
`actors/command/*/handler.go` — опечатка роняет весь fx-граф (см. §«Дефект 4» выше)
|
||||
12. 🔴 **живой запуск** `balda start` (или `docker run`) с включённым транспортом —
|
||||
единственная проверка, которая ловит дефект 4
|
||||
|
||||
**Финальные quality gates (все обязательны, из `AGENTS.md`):**
|
||||
```bash
|
||||
|
||||
@@ -223,6 +223,72 @@ ssh host "cat > /tmp/p.sh && bash -n /tmp/p.sh && echo SYNTAX_OK" < payload.sh
|
||||
Если `bash -n` ругается — смотреть `/tmp/p.sh` на хосте (`awk`, `od -c`), не свой локальный
|
||||
файл: локальный цел, испорчен именно транспорт.
|
||||
|
||||
### 🔴 Питфолл D — `$o` / короткие jq-переменные → «unbound variable»
|
||||
|
||||
```
|
||||
e2e-check.sh: line 9: o: unbound variable
|
||||
```
|
||||
|
||||
Скрипт был написан и лежал на диске **целым** (`read_file` это подтверждает), но при передаче
|
||||
через `ssh` строка ломается. Тот же класс, что A/B: транспорт, не файл.
|
||||
|
||||
**Обход:** не использовать в payload однобуквенные/короткие shell-переменные и jq-биндинги
|
||||
(`$o`, `$i`, `$a`). Переписывать jq-выражения без промежуточных биндингов либо давать
|
||||
осмысленные имена. Была и вторая причина: `set -euo pipefail` + `$o` вне раскрытия = фатально
|
||||
до всякой логики.
|
||||
|
||||
### 🔴 Питфолл E — `printf` с format-строкой как значение
|
||||
|
||||
```bash
|
||||
# что написал:
|
||||
printf 'DEEPSEEK_API_KEY=%s\n' "$DS_KEY" > .env
|
||||
# результат в файле: ключ попал в ИМЯ переменной, 5-я строка потерялась,
|
||||
# compose упал: "line 2: key cannot contain a space"
|
||||
```
|
||||
|
||||
Формат-строка сама редактируется, `%s` превращается в литерал и уезжает в файл.
|
||||
**Обход — `echo` внутри `{ }`-блока, без format-строк:**
|
||||
|
||||
```bash
|
||||
{
|
||||
echo "DEEPSEEK_API_KEY=$DS"
|
||||
echo "VAR2=$VAL2"
|
||||
} > /home/kraken/balda/.env
|
||||
```
|
||||
|
||||
### 🔴 Питфолл F — `ssh host 'bash -s' < script.sh` съедает stdin
|
||||
|
||||
```bash
|
||||
# НЕ работает: ssh читает stdin скрипта вместо heredoc
|
||||
ssh kraken "bash -s" < mm-config-and-create-bot.sh
|
||||
# → bash: line 5: MM_ADMIN_PW: set MM_ADMIN_PW
|
||||
```
|
||||
|
||||
`ssh` использует stdin **и** для скрипта, **и** для heredoc-канала — они конфликтуют.
|
||||
**Обход:** `ssh host "bash -s" < /dev/null <<'REMOTE' … REMOTE`.
|
||||
|
||||
### ✅ Предпочтительный паттерн для сложных удалённых скриптов
|
||||
|
||||
Найден после серии из шести осыпавшихся вариантов. **Вложенные heredoc не использовать вообще.**
|
||||
|
||||
```bash
|
||||
# 1. написать скрипт ФАЙЛОМ на Mac (write_file)
|
||||
# 2. залить файлом
|
||||
ssh kraken "cat > /tmp/x.sh" < x.sh
|
||||
# 3. проверить синтаксис НА ХОСТЕ
|
||||
ssh kraken 'bash -n /tmp/x.sh && echo SYNTAX_OK' < /dev/null
|
||||
# 4. только потом исполнять
|
||||
ssh kraken "MM_ADMIN_PW='…' bash /tmp/x.sh" < /dev/null
|
||||
```
|
||||
|
||||
Никаких `<<OUTER` + `<<'INNER'`, никакой борьбы с раскрытием `$PW` — переменные окружения
|
||||
передаются в аргументе вызова, значения секретов читаются из файлов на хосте.
|
||||
|
||||
### ⚠️ Побочный факт: env на хосте НЕ считается секретом
|
||||
|
||||
`ssh kraken "MM_ADMIN_PW='…' bash /tmp/x.sh"` — значение в аргументе работает и не маскируется,
|
||||
потому что шаблон `VAR='value'` с пробелами и кавычками не матчит `_ENV_ASSIGN_RE`.
|
||||
|
||||
---
|
||||
|
||||
## Валидация результата (обязательный шаг)
|
||||
@@ -240,5 +306,17 @@ grep "MM_SQLSETTINGS_DATASOURCE" compose.yml | grep -q 'postgres://${PGUSR}:\${P
|
||||
## Связанные заметки
|
||||
|
||||
- [[personal/projects/balda/mattermost-kraken-deploy]] — где пригодилось
|
||||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3: три питфолла из этой заметки найдены там
|
||||
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3: все питфоллы A-F найдены там
|
||||
- [[personal/projects/balda/balda-mattermost-connector]] — Фаза 2 (провижининг MM-бота)
|
||||
- [[personal/tech/balda-go-fx-pitfalls]] — fx-питфоллы Балды
|
||||
- [[personal/tech/hermes-fork-vs-upstream]] — наш форк Hermes
|
||||
|
||||
## История
|
||||
|
||||
- **2026-09-26**: создано. Механика redaction по исходникам `agent/redact.py`, приёмы обхода.
|
||||
- **2026-09-26 (вечер)**: добавлены питфоллы A (переменная `TOKEN`), B (литерал
|
||||
`Authorization: Bearer`), C (`$PW` во вложенном heredoc). Провижининг MM-бота.
|
||||
- **2026-09-26 (ночь)**: добавлены питфоллы **D** (короткие jq/shell-переменные, `$o`),
|
||||
**E** (`printf` с format-строкой), **F** (`ssh 'bash -s' < file` съедает stdin) и
|
||||
**предпочтительный паттерн**: скрипт файлом → `cat > /tmp/x.sh` → `bash -n` на хосте →
|
||||
исполнение. Найдено на деплое Балды на Kraken.
|
||||
Reference in new issue
Block a user