From 803a53fd052025a8f894f35cdab5d62c5f9d92f7 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Mon, 28 Sep 2026 02:08:25 +0600 Subject: [PATCH] [2026-09-28] eagle: personal/projects/balda/balda-valera.md personal/projects/balda/balda.md personal/tech/balda-go-fx-pitfalls.md --- personal/projects/balda/balda-valera.md | 60 +++++++++++++++++++++++++ personal/projects/balda/balda.md | 18 +++++++- personal/tech/balda-go-fx-pitfalls.md | 45 +++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/personal/projects/balda/balda-valera.md b/personal/projects/balda/balda-valera.md index 01ed5d5a..36219351 100755 --- a/personal/projects/balda/balda-valera.md +++ b/personal/projects/balda/balda-valera.md @@ -53,6 +53,66 @@ > 📌 **Как доказать, что код патча попал в образ** (образ `scratch`, shell нет): распаковать слои > `docker save` из `blobs/sha256/*` и грепнуть бинарник — > `strings …/usr/local/bin/balda | grep tool_call_id`. На OCI-образе `tar -tf` даёт ложный 0. +> +> --- +> ### ✅ ВТОРОЙ PR — ФИКС MCP-ТУЛИНГА ОТПРАВЛЕН (2026-09-27) +> +> Чтобы закрыть обнажившийся баг `duplicate tool`, потребовалась правка **в `runtime`**, а не +> в Балде. Отправлено **отдельным PR** — тема независима от #4. +> +> | Что | Значение | +> |---|---| +> | Форк | `mallexxx/runtime`, ветка `fix/hosted-toolsets-duplicate-endpoint` | +> | **PR в апстрим** | **https://github.com/normahq/runtime/pull/5** | +> | База | `main` (`12f1210`), `0` позади / `1` впереди — rebase не нужен | +> | Коммит | `b01bdc6` | +> | Объём | **2 файла, +263 / −0** | +> | Патч | `agentfactory/agentfactory.go` +74; `agentfactory/duplicate_toolset_test.go` new (6 тестов) | +> +> **Суть фикса.** `hostedToolsets` схлопывает MCP-конфиги, различающиеся **только query/fragment**. +> Балда регистрирует bundled-сервер дважды: `balda` (`…/mcp/balda`) и +> `balda-session-memory-` (`…/mcp/balda?ctx=`). Оба отдают один список инструментов, +> ADK пакует всё в одну карту → `duplicate tool`. +> +> 🔴 **Ключевое решение: выживает SCOPED-запись, не первая по алфавиту.** Её URL несёт +> контекст-токен сессии — это строгое надмножество plain-эндпоинта. Наивный дедуп «первый победил» +> отбросил бы токен и сломал session-scoped MCP-вызовы. Новые функции: `mcpEndpointKey` +> (stdlib `net/url`, срезает `RawQuery` + `Fragment`) и `mcpURLIsScoped`. +> `stdio`-транспорты возвращают `""` → **никогда не схлопываются**. +> +> Скип логируется — видно в проде: +> `[mcp] skipping duplicate toolset "balda": endpoint already served by "balda-session-memory-"` +> +> **Гейты (локально, все зелёные на закоммиченном дереве):** +> +> ``` +> go build ./... → exit 0 +> go vet ./... → exit 0 +> go tool golangci-lint run ./... → 0 issues +> go test -race ./... → все пакеты ok +> ``` +> +> ⚠️ **Питфолл линтеров на тесте** (`duplicate_toolset_test.go:187`): `staticcheck ST1023` +> требует убрать явный тип (`var x tool.Toolset = ts`), а `unconvert` — убрать конверсию +> (`tool.Toolset(ts)`). Оба правы → правильное решение — **не вводить переменную вообще**: +> `if _, ok := any(ts).(requestProcessor); ok {`. После этого импорт `adk/v2/tool` стал +> неиспользуемым — его тоже надо убрать. +> +> 🔴 **CI на PR НЕ ЗАПУСТИЛСЯ** — `mergeStateStatus: UNSTABLE`, `check-runs: total_count 0`. +> Причина: PR из **форка**, GitHub требует **ручного подтверждения** workflows владельцем репо +> («Approve and run workflows»). Это НЕ поломка PR. Локальные гейты пройдены полностью. +> Требуется действие Alex: открыть PR#5 и подтвердить запуск CI. +> +> 📌 **Развилка репозиториев (важно не путать):** фикс MCP-тулинга → `normahq/runtime` (PR#5), +> Mattermost-транспорт → `baldaworks/balda` (PR#8). Это **разные репозитории**, у них разные +> форки (`mallexxx/runtime` / `mallexxx/balda`) и разные клоны на диске +> (`~/Developer/runtime-local/` — git-клон; `~/Developer/balda/` — репо Балды). +> +> ⚠️ **`runtime-local` существует в ДВУХ местах** и их легко перепутать: +> | Путь | Что это | +> |---|---| +> | `~/Developer/runtime-local/` | **git-клон форка** `mallexxx/runtime` — здесь делаются PR | +> | `~/Developer/balda/runtime-local/` | **копия без `.git`** для сборки образа Балды | ## Расположение - **Валера** = `balda-agent-valera-1`, конфиг: `~/Docker/balda-agent/.config/balda/config.yaml` diff --git a/personal/projects/balda/balda.md b/personal/projects/balda/balda.md index 80adbf55..89c5327a 100755 --- a/personal/projects/balda/balda.md +++ b/personal/projects/balda/balda.md @@ -4,10 +4,24 @@ ## Репо -- Upstream: https://github.com/normahq/balda +- Upstream: https://github.com/baldaworks/balda (старое имя `normahq/balda` — редирект) - Fork: https://github.com/mallexxx/balda - Local: ~/Developer/balda -- Ветка: `feat/zulip-transport` +- Ветка: `feat/mattermost-channel` (PR [#8](https://github.com/baldaworks/balda/pull/8), OPEN, 8/8 CI, MERGEABLE) + +### 🔴 Два репозитория — не путать + +| Репо | Что там | PR | +|---|---|---| +| **`baldaworks/balda`** | сам бот, транспорты (Mattermost, Zulip) | [#8](https://github.com/baldaworks/balda/pull/8) Mattermost, OPEN · [#1](https://github.com/baldaworks/balda/pull/1) Zulip, merged | +| **`normahq/runtime`** | библиотека рантайма (`agentfactory`, `hostedagent`) | [#5](https://github.com/normahq/runtime/pull/5) фикс MCP-тулинга, OPEN · [#4](https://github.com/normahq/runtime/pull/4) OpenAI tool_calls, OPEN | + +Форки: `mallexxx/balda`, `mallexxx/runtime` — **разные**, клоны тоже разные. +Детали по runtime-PR → [[personal/projects/balda/balda-valera]]. + +⚠️ **PR из форка: CI не запускается сам.** GitHub требует ручного подтверждения workflows +владельцем репо («Approve and run workflows») → `mergeStateStatus: UNSTABLE` при +`check-runs: total_count 0`. Это не поломка PR. ## Архитектура diff --git a/personal/tech/balda-go-fx-pitfalls.md b/personal/tech/balda-go-fx-pitfalls.md index 5ec1f276..96094c48 100755 --- a/personal/tech/balda-go-fx-pitfalls.md +++ b/personal/tech/balda-go-fx-pitfalls.md @@ -37,6 +37,10 @@ > Ключевой инсайт: апстримный `TurnComplete: true` **маскировал** баг — агент не доходил > до паковки инструментов. Как только патч включил второй раунд, ADK-механика проявилась. > Плюс метод: §«Доказать, что код патча в образе» (распаковать OCI-слои, грепнуть бинарник). +> 🔴 **Добавлено 2026-09-27 (PR фикса MCP-тулинга в `runtime`):** +> §«Два линтера спорят на одной строке теста» — `staticcheck ST1023` vs `unconvert`; +> решение «убрать конструкцию, а не выбирать сторону» + побочный неиспользуемый импорт. +> Там же проверенные гейты перед PR в `runtime` и питфолл таймаута `go test -race ./...` (>600 с). > Источник: работа над Mattermost-адаптером Балды (Фаза 2) + деплой на Kraken (Фаза 3). > Проектные доки: [[personal/projects/balda/balda-mattermost-connector]], > [[personal/projects/balda/balda-kraken-mm-deploy]], @@ -68,6 +72,47 @@ --- +## 🔴 Два линтера спорят на одной строке теста (Go) + +В `runtime`-репо, файл `agentfactory/duplicate_toolset_test.go`: + +```go +var asToolset tool.Toolset = ts // ← staticcheck ST1023: «omit type, it's inferred» +asToolset := tool.Toolset(ts) // ← unconvert: «unnecessary conversion» +``` + +**Оба замечания валидны и взаимоисключающи** — правка одного рождает другое. Цикл: +`ST1023` → чинишь → `unconvert` → чинишь обратно → `ST1023`. + +📌 **Решение — не вводить переменную вообще.** Проверять тип напрямую: + +```go +if _, ok := any(ts).(requestProcessor); ok { + t.Fatalf("MCP toolset unexpectedly implements ProcessRequest; dedup premise changed") +} +``` + +⚠️ **Побочный эффект:** после этого импорт `google.golang.org/adk/v2/tool` становится +неиспользуемым → компилятор валит `"tool" imported and not used`. Убирать импорт +**вместе** с правкой, не отдельным шагом. + +📌 **Правило:** если два линтера требуют противоположного на одной строке — это сигнал +не выбирать сторону, а **убрать конструкцию**, из-за которой возник спор. + +### Гейты перед PR в `runtime` (проверенные) + +```bash +go build ./... # exit 0 +go vet ./... # exit 0 +go tool golangci-lint run ./... # 0 issues +go test -race ./... # все пакеты ok +``` + +⚠️ `go test -race ./...` на этом репо **дольше 600 с** (лимит foreground-таймаута) — +запускать в фоне с `notify_on_complete`, а не ужимать лимит. + +--- + ## 🔴 Чинить в КОДЕ, а не в данных (двойной префикс вернулся) В прошлой сессии я нашёл, что `principal` в `balda_user_bindings` записан как