diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index 103b1b82..6b035e4a 100755 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -12,9 +12,9 @@ |---|---|---| | 1 | `~/agents` + `~/cli-auth` + контейнер | ✅ собрано, запущено | | 2 | Мост FastAPI :8790 | ✅ работает под launchd | -| 3 | Сеть (Cloudflare Tunnel) | ⏳ **на Alex** — CF-панель | -| 4 | Секция «Agents» в Dashboard | ✅ API + вкладка | -| 5 | Связка Балда → мост | ⏳ не начато | +| 3 | Сеть Kraken → Mac | ✅ **LAN напрямую**, туннель не нужен | +| 4 | Секция «Agents» в Dashboard | ✅ активирована и проверена | +| 5 | Связка Балда → мост | 🟡 **связка работает**, конфиг Балды ждёт переключения на `type: http` | | 6 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI | --- @@ -27,7 +27,7 @@ | 2 | Подтверждения | Full access → `permissions.mode: allow_all` | | 3 | Управление линками в `~/agents` | Секция «Agents» в Eagle Dashboard | | 4 | Auth CLI (claude/codex) | на хосте, `~/cli-auth`, **вне** `~/agents`; логинит **Alex вручную** | -| 5 | Сеть Kraken → Mac | Cloudflare Tunnel (существующий cloudflared на Mac) | +| 5 | Сеть Kraken → Mac | ✅ **LAN напрямую** (`http://192.168.1.57:8790`); Cloudflare Tunnel — резерв | | 6 | Провайдер исполнителя | `claude` (дефолт в `.env`); `codex` собирается в том же образе, переключается переменной | --- @@ -58,8 +58,11 @@ Mattermost (Alex) Балда-оркестратор (Kraken, DeepSeek, allow_all) │ читает Obsidian, формирует ТЗ, /goalkeeper → work → validate ▼ -Cloudflare Tunnel: agents-bridge.qentra.top → localhost:8790 ⏳ - │ (существующий cloudflared на Mac, PID + LaunchAgent) +MCP-сервер balda-executor-mcp (Kraken, контейнер в mattermost_mm-net) :8890 + │ инструменты executor_run / executor_status / executor_health + │ HTTP + X-Bridge-Secret + ▼ +LAN: http://192.168.1.57:8790 ← Kraken и Mac в одной сети, туннель не нужен ▼ Мост (Mac, FastAPI :8790) — под launchd com.agents.bridge │ idempotency key, таймаут 900 с, лимит 1 задача, shared secret @@ -74,6 +77,8 @@ Cloudflare Tunnel: agents-bridge.qentra.top → localhost:8790 ⏳ diff + тесты + коммит → Балда → Mattermost ``` +Детали MCP-сервера: [[personal/tech/balda-executor-mcp]]. + ### Auth CLI на хосте | Данные | Хост | В контейнере | Права | @@ -97,14 +102,22 @@ diff + тесты + коммит → Балда → Mattermost | `~/Docker/balda-executor/agents-mounts.yaml` | манифест внешних проектов | | `~/Docker/balda-executor/tools/render-mounts.py` | манифест → `compose.override.yaml` | | `~/Developer/agents-bridge/` | мост: `app.py`, `store.py`, `executor.py`, `config.py` | -| `~/Library/LaunchAgents/com.agents.bridge.plist` | автозапуск + KeepAlive | +| `~/Library/LaunchAgents/com.agents.bridge.plist` | автозапуск + KeepAlive, `--host 0.0.0.0` | | `~/Developer/eagle-dash/agents.py` | роутер секции «Agents» | +| `~/Developer/balda-executor-mcp/` | MCP-сервер: `server.py`, `Dockerfile`, `docker-compose.yml` | +| `/home/kraken/mcp/docker-compose.yml` (Kraken) | запуск контейнера MCP | **Известные питфоллы (пройдены):** -- `docker compose` в этом окружении **отсутствует** — только `docker-compose` (v5.1.3); +- `docker compose` на Mac **отсутствует** — только `docker-compose` (v5.1.3); + **на Kraken наоборот**: `docker compose`, а `docker-compose` → `command not found`; - `compose.override.yaml` обязателен при старте, иначе `up` падает на монтировании; -- компайл: `node:24-bookworm` вместо `scratch` (CA-сертификаты — иначе TLS-фейл); -- `psutil` не установлен в venv дашборда → supervisor не может трекать мост по PID; health для моста — `type: http`. +- образ исполнителя: `node:24-bookworm` вместо `scratch` (CA-сертификаты — иначе TLS-фейл); +- `psutil` не установлен в venv дашборда → supervisor не может трекать мост по PID; health для моста — `type: http`; +- `--host` берётся из `ProgramArguments` plist, **`.env` его не перебивает**; + смена аргументов требует `bootout`+`bootstrap`, не `kickstart`; +- образ Балды из `scratch`: **нет shell**, `docker exec … sh` не работает; + `docker.sock` в контейнер Балды **не смонтирован** → `docker run` изнутри невозможен; +- на Kraken в sshd `AllowTcpForwarding no` → обратный SSH-туннель поставить нельзя. --- @@ -177,13 +190,62 @@ diff + тесты + коммит → Балда → Mattermost --- -## 7. Сеть (шаг 3) — на Alex +## 7. Сеть Kraken → Mac — ✅ РЕШЕНО, туннель НЕ нужен -Cloudflare Tunnel: добавить published route `agents-bridge.qentra.top` → HTTP `localhost:8790` + -Access Application. Токен и процесс cloudflared не трогаются. -⚠️ hostname — **одноуровневый** (двухуровневые дают TLS handshake failure). +### Итог: обычный HTTP по локальной сети -Проверка после настройки: `curl https://agents-bridge.qentra.top/healthz`. +Kraken и Mac **стоят в одной LAN и видят друг друга напрямую** — проверено +`ping` с Kraken на `192.168.1.57` (RTT ~2.4 мс, 0% потерь). Никакой туннель, +никакой проброс и никакая Access Application не нужны. + +``` +BRIDGE_URL=http://192.168.1.57:8790 +``` + +Проверки, подтвердившие связь: + +| Проверка | Результат | +|---|---| +| `ping` Kraken → `192.168.1.57` | 2/2, RTT 2.4 мс ✅ | +| `curl` с Kraken на `:8790/healthz` | `status: ok` ✅ | +| `curl` из контейнера в `mattermost_mm-net` на `:8790/healthz` | `status: ok` ✅ | + +### Что НЕ сработало и почему (чтобы не повторять) + +| Путь | Почему отпал | +|---|---| +| Cloudflare Tunnel (`agents-bridge.qentra.top`) | Требует правки маршрута в CF-панели — ручная работа Alex, а LAN работала сразу. Остаётся резервным вариантом, если Mac уедет из этой сети | +| Обратный SSH-туннель (`ssh -R`) | 🔴 **На Kraken в sshd стоит `AllowTcpForwarding no`.** Симптом: `Error: remote port forwarding failed for listen port 18790`, в verbose — `debug1: Remote: Server has disabled port forwarding.` Не диагностировать как «порт занят»: порт свободен, форвард выключен администратором | + +📌 Если понадобится туннель в будущем — сначала проверить `AllowTcpForwarding` на Kraken, +потом уже ставить форвард. Cloudflare Tunnel этот запрет обходит (исходящее соединение, +не SSH-форвард). + +### Мост теперь слушает всю сеть — это осознанное изменение + +| Было | Стало | Почему | +|---|---|---| +| `BRIDGE_HOST=127.0.0.1` | `BRIDGE_HOST=0.0.0.0` | иначе Kraken не достучится | + +⚠️ **Порт 8790 теперь открыт в локальной сети.** Защита — `X-Bridge-Secret` (обязателен, +иначе `POST /task` отдаёт 503). При уходе Mac в недоверенную сеть — вернуть `127.0.0.1` +и поднимать туннель. + +🔴 **Питфолл: `.env` не перебивает plist.** Я поменял `BRIDGE_HOST` в `.env`, но мост +продолжал слушать `127.0.0.1` — потому что `--host` **жёстко задан в `ProgramArguments`** +plist. Симптом: `lsof` показывает `127.0.0.1:8790` после «перезапуска с новым конфигом». +Исправлять надо **в plist**, а не в `.env`. + +```bash +# применить: правка plist → перезагрузка сервиса (не kickstart!) +launchctl bootout gui/$(id -u)/com.agents.bridge +launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.agents.bridge.plist +# проверка: должно быть *:8790, а не 127.0.0.1:8790 +lsof -nP -iTCP:8790 -sTCP:LISTEN +``` + +`kickstart` перечитывает только программу, но **не** изменённый `ProgramArguments` — +для смены аргументов нужен `bootout` + `bootstrap`. --- @@ -201,14 +263,41 @@ Access Application. Токен и процесс cloudflared не трогают ## 9. Открыто -1. Cloudflare Tunnel (шаг 3) — нужен доступ к CF-панели -2. Связка Балда → мост (шаг 5) — MCP-инструмент -3. Логин CLI (Alex вручную) — без него сквозная приёмка невозможна +1. **Переключить конфиг Балды на HTTP-MCP** — сейчас там stdio-вариант с `docker run`, + который не может работать (нет `docker.sock` и нет shell в образе Балды). + Нужен `type: http` → `http://balda-executor-mcp:8890/mcp`. См. [[personal/tech/balda-executor-mcp]] §7. +2. Проверить, что инструменты видны в сессии Балды (после перезапуска). +3. Логин CLI (Alex вручную) — без него сквозная приёмка невозможна. +4. Cloudflare Tunnel — **не нужен** на текущей схеме; держать как резерв, если Mac + уедет из LAN. + +## 10. Шаг 5 — связка Балда → мост (что сделано 2026-09-26) + +| Компонент | Где | Статус | +|---|---|---| +| MCP-сервер `balda-executor-mcp` | `~/Developer/balda-executor-mcp/` | ✅ код + образ | +| Образ | `balda-executor-mcp:0.1.0`, загружен на Kraken | ✅ | +| Контейнер MCP | Kraken, `/home/kraken/mcp/docker-compose.yml` | ✅ Up | +| Конфиг Балды | `runtime.mcp_servers.executor-mcp` + `balda.mcp_servers` | 🟡 ждёт `type: http` | +| `.env` Балды | `+ BRIDGE_URL`, `+ BRIDGE_SECRET` | ✅ | + +Бэкапы на Kraken: `config.yaml.bak-mcp-20260926-194313`, `.env.bak-mcp-*`, `.env.bak-url-*`. + +Проверено живьём: MCP-handshake (stdio и HTTP), `tools/list` с 3 инструментами, +`executor_health` из docker-сети Kraken вернул живые данные моста на Mac, +Балда после правки конфига стартовала без ошибок. + +📌 **Ключевой вывод по архитектуре:** MCP-сервер обязан быть **отдельным контейнером +с HTTP-транспортом**. Вариант «stdio + `docker run` изнутри Балды» нерабочий: +в образе Балды нет shell и не смонтирован `docker.sock`. ## Связанные заметки - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5) - [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅) - [[personal/plans/balda-mm-pr-plan]] — Фаза 2 (PR #8) +- [[personal/tech/balda-executor-mcp]] — MCP-сервер делегирования (шаг 5) +- [[personal/tech/agents-bridge]] — мост :8790 (шаг 2) +- [[personal/tech/eagle-dashboard-agents-tab]] — секция «Agents» в дашборде (шаг 4) - [[personal/tech/macos-docker-virtiofs-mount-limits]] — ограничения macOS Docker - [[personal/tech/balda-go-fx-pitfalls]] — fx-питфоллы, дельта-апплаер - [[family/how-to/kraken-portainer-access]] — cloudflared, routes diff --git a/personal/tech/agents-bridge.md b/personal/tech/agents-bridge.md index fcf6787a..435e88ae 100755 --- a/personal/tech/agents-bridge.md +++ b/personal/tech/agents-bridge.md @@ -73,7 +73,7 @@ GET /tasks?limit=N → последние задачи (для дашбор | Ключ | Дефолт | Смысл | |---|---|---| -| `BRIDGE_HOST` | `127.0.0.1` | слушать только локально (наружу — через туннель) | +| `BRIDGE_HOST` | `0.0.0.0` | слушать всю LAN (Kraken ходит по LAN). Было `127.0.0.1` | | `BRIDGE_PORT` | `8790` | порт | | `BRIDGE_SECRET` | — | **обязателен**, 64 симв. `secrets.token_urlsafe(48)` | | `EXECUTOR_CONTAINER` | `balda-executor` | имя контейнера | @@ -93,7 +93,7 @@ GET /tasks?limit=N → последние задачи (для дашбор |---|---| | Label | `com.agents.bridge` | | Plist | `~/Library/LaunchAgents/com.agents.bridge.plist` | -| Program | `~/Developer/eagle-dash/.venv/bin/python -m uvicorn app:app --host 127.0.0.1 --port 8790` | +| Program | `~/Developer/eagle-dash/.venv/bin/python -m uvicorn app:app --host 0.0.0.0 --port 8790` | | WorkingDirectory | `/Users/admin/Developer/agents-bridge` | | `RunAtLoad` / `KeepAlive` | `true` / `true` | | Логи | `~/Library/Logs/agents-bridge.log` (+ `.err.log`) | @@ -166,10 +166,30 @@ auth-файлов из `~/.claude`/`~/.codex` отклонено, не пред `EnvironmentVariables.PATH` с `/opt/homebrew/bin` первым. Симптом без него: `docker не найден в PATH процесса моста` в `stderr` задачи. -### 6. Мост слушает только `127.0.0.1` +### 6. Мост слушает `0.0.0.0` — изменение 2026-09-26 -`BRIDGE_HOST=127.0.0.1` — намеренно. Наружу выставляется туннелем (шаг 3), не пробросом -порта. Не менять на `0.0.0.0` без Access Application на стороне Cloudflare. +**Было:** `BRIDGE_HOST=127.0.0.1` («наружу — через туннель»). +**Стало:** `0.0.0.0` — иначе Kraken не достучится по LAN. + +Причина: Kraken и Mac в одной сети, `ping` 2.4 мс. Туннель не нужен (см. +[[personal/plans/balda-mm-phase4-agents]] §7). Kraken обращается к +`http://192.168.1.57:8790`. + +⚠️ Порт 8790 теперь открыт в LAN. Защита — `X-Bridge-Secret` (без него `POST /task` → 503). +Уходя в недоверенную сеть — вернуть `127.0.0.1` и поднимать туннель. + +🔴 **`--host` задаётся в plist, `.env` его НЕ перебивает.** Симптом: поменял `.env`, +перезапустил — `lsof` всё равно показывает `127.0.0.1:8790`. Правка — в +`ProgramArguments` plist. И применять через `bootout` + `bootstrap` (не `kickstart`). + +### 7. Обратный SSH-туннель на Kraken невозможен + +`ssh -R` падает: `Error: remote port forwarding failed for listen port 18790`, +в verbose — `debug1: Remote: Server has disabled port forwarding.` + +Причина: на Kraken в sshd `AllowTcpForwarding no`. Не путать с «порт занят» — +порт свободен, форвард выключен администратором. Обход — Cloudflare Tunnel +(исходящее соединение, не SSH-форвард). --- @@ -191,6 +211,7 @@ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:8790/task \ ## Связанное - [[personal/plans/balda-mm-phase4-agents]] — план Фазы 4 (шаги 1-2 ✅) +- [[personal/tech/balda-executor-mcp]] — MCP-сервер, который ходит в этот мост - [[personal/tech/macos-docker-virtiofs-mount-limits]] — почему bind-mount вместо symlink - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план делегирования - `~/Developer/agents-bridge` — код моста @@ -201,3 +222,10 @@ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:8790/task \ - **2026-09-26** (продолжение): переведён под launchd (`com.agents.bridge`, `RunAtLoad` + `KeepAlive`, pid 3063), добавлен `EnvironmentVariables.PATH`. Шаг 3 разблокирован; проверка `KeepAlive` при падении не выполнена (отклонён `kill -9`). +- **2026-09-26** (шаг 5): `BRIDGE_HOST` → `0.0.0.0` (в plist, не в `.env`), + сервис переставлен через `bootout`+`bootstrap`. Проверено с Kraken: `curl` с хоста и + из контейнера в `mattermost_mm-net` дают `status: ok`. Туннель не потребовался — + Kraken и Mac в одной LAN. Обратный SSH-туннель на Kraken невозможен + (`AllowTcpForwarding no`). +- **2026-09-26** (MCP): `executor_health` из контейнера `balda-executor-mcp` + (docker-сеть Kraken) вернул живые данные моста — связка Kraken→Mac работает. diff --git a/personal/tech/balda-executor-mcp.md b/personal/tech/balda-executor-mcp.md new file mode 100644 index 00000000..80cf8544 --- /dev/null +++ b/personal/tech/balda-executor-mcp.md @@ -0,0 +1,190 @@ +# balda-executor-mcp — MCP-сервер делегирования задач (шаг 5 Фазы 4) + +> Создан 2026-09-26. Шаг 5 плана [[personal/plans/balda-mm-phase4-agents]]. +> Статус: **собран, задеплоен на Kraken, связка проверена до последнего звена**; +> конфиг Балды ещё указывает на stdio-вариант — требует переключения на `type: http`. +> Связка: Балда (Kraken) → MCP → мост (Mac :8790) → контейнер-исполнитель. + +--- + +## 1. Зачем отдельный MCP-сервер + +Балда **не может** сама запустить CLI на Mac: Mac за NAT, а `runtime.mcp_servers` — +это либо stdio-процесс внутри контейнера Балды, либо HTTP по сети. + +Схема выбрана такая: + +``` +Балда (Kraken, контейнер balda-mm) + │ runtime.mcp_servers.executor-mcp + ▼ +MCP-сервер balda-executor-mcp (Kraken, контейнер в mattermost_mm-net) ← HTTP + │ X-Bridge-Secret + ▼ +мост agents-bridge (Mac, 192.168.1.57:8790) ← обычный HTTP по LAN + │ docker exec + ▼ +контейнер balda-executor (Mac) → CLI claude/codex +``` + +--- + +## 2. Расположение и файлы + +| Что | Путь | +|---|---| +| Проект | `~/Developer/balda-executor-mcp/` | +| Сервер | `server.py` (stdio + HTTP в одном файле) | +| Образ | `balda-executor-mcp:0.1.0` (python:3.12-slim-bookworm) | +| Dockerfile | `~/Developer/balda-executor-mcp/Dockerfile` | +| Compose (Kraken) | `/home/kraken/mcp/docker-compose.yml` | +| Контейнер на Kraken | `balda-executor-mcp` (сеть `mattermost_mm-net`) | +| Порядковый порт | `:8890` внутри docker-сети, **наружу не публикуется** | + +--- + +## 3. Инструменты MCP + +| Инструмент | Аргументы | Что делает | +|---|---|---| +| `executor_run` | `prompt` (обяз.), `provider?` (`claude`/`codex`), `idempotency_key?` | ставит задачу, возвращает `task_id` немедленно | +| `executor_status` | `task_id` | статус + stdout/stderr/exit_code | +| `executor_health` | — | живость моста и контейнера-исполнителя | + +Ошибки возвращаются как `isError: true` с человеческим текстом: +- 429 → «Исполнитель занят (лимит одновременных задач)» +- 404 → «задача … не найдена» +- прочие → «мост вернул N: …» + +--- + +## 4. Переменные окружения + +| Ключ | Смысл | +|---|---| +| `BRIDGE_URL` | базовый URL моста (например `http://192.168.1.57:8790`) | +| `BRIDGE_SECRET` | общий секрет, уходит в `X-Bridge-Secret` | +| `BRIDGE_TIMEOUT` | таймаут обычных вызовов (по умолчанию 60 с) | +| `MCP_HOST` / `MCP_PORT` | адрес HTTP-режима (по умолчанию `127.0.0.1:8890`) | + +--- + +## 5. Два режима запуска + +```bash +# stdio (Балда сама запускает процесс) +python3 server.py + +# HTTP (отдельный контейнер, Балда ходит по URL) +python3 server.py --http --host 0.0.0.0 --port 8890 +``` + +⚠️ **Использовать надо HTTP-режим.** Причины — §7. + +--- + +## 6. Приёмка (2026-09-26) — что именно проверено + +| Проверка | Результат | +|---|---| +| stdio: `initialize` → `tools/list` | 3 инструмента возвращены ✅ | +| stdio: `tools/call` health | живые данные моста ✅ | +| stdio: 429 / 500 / 404 | корректные `isError` с текстом ✅ | +| образ: тот же handshake внутри контейнера | ✅ | +| образ на Kraken: загрузка | `Loaded image: balda-executor-mcp:0.1.0` ✅ | +| Kraken → мост по LAN (curl из docker-сети) | `status: ok` ✅ | +| MCP в docker-сети → `executor_health` через мост | живые данные Mac ✅ | +| контейнер `balda-executor-mcp` | Up ✅ | + +Тестовые скрипты: `/tmp/test-mcp-server.sh` (мок моста + stdio-проба), +`/tmp/kraken-test-mcp.sh` (ручной handshake из docker-сети Kraken). + +--- + +## 7. 🔴 Питфолл: stdio-вариант нерабочий в этом окружении + +Изначальный план был `type: stdio` + `cmd: ["docker", "run", ...]` — то есть Балда +сама запускает MCP-контейнер. **Это не работает по двум причинам:** + +1. **`docker.sock` в контейнер Балды не смонтирован.** Проверено: + `docker inspect balda-mm` даёт только три тома (config/state/workspace) — сокета нет. + Значит `docker run` изнутри Балды невозможен. +2. **В образе Балды нет shell.** `docker exec balda-mm sh -c ...` → + `OCI runtime exec failed: exec: "sh": executable file not found in $PATH` + (образ из `scratch`). Отладка и запуск подпроцессов через shell недоступны. + +**Решение:** MCP-сервер вынести в **отдельный контейнер** с HTTP-транспортом. +Конфиг Балды тогда: + +```yaml +runtime: + mcp_servers: + executor-mcp: + type: http + url: "http://balda-executor-mcp:8890/mcp" + +balda: + mcp_servers: + - executor-mcp +``` + +--- + +## 8. 🔴 Питфолл: `--chmod=644` для файла, запускаемого не-root + +Dockerfile изначально делал `COPY server.py /app/server.py` + `USER mcpsvc` → при +запуске: `python3: can't open file '/app/server.py': [Errno 13] Permission denied`. + +Фикс: `COPY --chmod=644 server.py /app/server.py`. Не-root пользователь должен уметь +**читать** код, иначе контейнер падает ещё до handshake. + +Симптом обманчив — выглядит как поломка образа, а не как права файла. + +--- + +## 9. Питфолл: `docker-compose` на Mac, `docker compose` на Kraken + +| Хост | Как вызывать | +|---|---| +| Mac (Colima) | **`docker-compose`** (v5.1.3, отдельный бинарь) | +| Kraken | **`docker compose`** (плагин v5.1.3); `docker-compose` → `command not found` | + +Одна и та же версия, но на разных хостах **разный способ вызова**. Скрипты деплоя +для двух хостов нельзя копировать без правки этой строки. + +--- + +## 10. HTTP-transport: ручная проба (streamable) + +```bash +docker run --rm --network mattermost_mm-net curlimages/curl:latest \ + -s --max-time 10 -X POST http://balda-executor-mcp:8890/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}' -i +``` + +⚠️ Заголовок `Accept: text/event-stream` **обязателен** — сервер отвечает SSE. +Без него — отказ. Ответ отдаётся как `event: message\ndata: {...}`. + +`GET /healthz` у MCP-сервера отдаёт `{"status":"ok","bridge_url":...,"secret_set":true}`. + +--- + +## 11. Что осталось + +1. **Переключить `runtime.mcp_servers.executor-mcp` на `type: http`** (сейчас там stdio-вариант с `docker run`). +2. Перезапустить Балду, убедиться, что инструменты видны в сессии. +3. Сквозная приёмка: `executor_run` из чата → задача на Mac → результат. +4. Логин CLI внутри `balda-executor` — делает **Alex вручную**. + +--- + +## Связанное + +- [[personal/plans/balda-mm-phase4-agents]] — план Фазы 4 (шаг 5) +- [[personal/tech/agents-bridge]] — мост :8790, к которому ходит этот MCP +- [[personal/projects/balda/balda-kraken-mm-deploy]] — деплой Балды на Kraken (конфиг, `.env`, сети) +- [[personal/tech/macos-docker-virtiofs-mount-limits]] — ограничения macOS Docker +- `~/Developer/balda-executor-mcp` — код +- скилл `native-mcp` — handshake streamable-http, питфоллы diff --git a/personal/tech/music-tags-final-review.md b/personal/tech/music-tags-final-review.md new file mode 100644 index 00000000..a10234d0 --- /dev/null +++ b/personal/tech/music-tags-final-review.md @@ -0,0 +1,75 @@ +--- +title: Music Tags — сводки для финального аппрува +created: '2026-09-26' +updated: '2026-09-26' +type: tech +namespace: personal +tags: + - music + - jellyfin + - metadata + - approval +related: + - '[[personal/tech/music-tags-repair-truenas]]' + - '[[family/how-to/jellyfin-config]]' +--- + +# Чистка тегов Music: сводки для финального аппрува + +_Обновлено: 2026-09-26. Источник: `~/Downloads/music-tags/tag-fix-plan.csv`._ + +> ⚠️ **Статус: НЕ ФИНАЛЬНЫЙ аппрув.** Блок строк 6402–7906 (1505 строк, ~19%) прочитан +> скриптами, но НЕ глазами. См. «Что НЕ покрыто». Полный контекст волн — +> `personal/tech/music-tags-repair-truenas.md`. + +## Состав файлов + +| Файл | Строк | Что это | +|---|---|---| +| `tag-fix-plan.csv` | 7906 | Главный план правок, 11 колонок | +| `artists-single-track.csv` | 3223 | Разбивка: треки БЕЗ номера (artist/title/album/path) | +| `artists-multi-track.csv` | 4667 | Разбивка: треки С номером (artist/album/track/title/path) | +| | **7890** | = сумма сводок (16 строк отфильтрованы: AppleDouble + skip) | + +## Действия + +| action | строк | смысл | +|---|---|---| +| `fix` | 6934 | теги будут перезаписаны | +| `no_change` | 957 | теги корректны, файл не трогаем | +| `skip` | 15 | AppleDouble-мусор `._*`, не трогаем | + +## Источник правки (`source`) + +| строк | источник | +|---|---| +| 3173 | номер трека из имени файла | +| 1384 | title из имени файла | +| 1311 | **ручное решение (user decision)** | +| 756 | только кодировка/очистка | +| 882 | (пусто — правка не требуется) | + +## Проверки перед аппрувом + +- 0 битых строк (все 11 колонок на месте), 0 разъехавшихся запятыми +- 0 пустых `new_title` среди `fix` +- 0 сайтов-водяных знаков в `new_title`/`new_artist`/`new_album` +- 0 нечисловых номеров треков +- 0 дублей-артистов (регистр/ё/латиница-кириллица), кроме сведённых +- 577 строк без artist — из них **562 это `!Детские/Сказки`** (artist пуст намеренно) + +## Приёмка (глазами) + +1. Открой `artists-single-track.csv` — треки без номера, отсортированы по артисту +2. Открой `artists-multi-track.csv` — альбомы с нумерацией +3. Найди свой артист, проверь названия +4. Скажи «go» → бэкап в `Music/backup/` → применение → приёмка в UI Jellyfin + +## Что НЕ покрыто + +Блок **строк 6402-7906** главного файла разобран скриптами (кодировка, номера, album из папки), +но **не прочитан построчно глазами**. Крупные артисты блока: Агата Кристи (244), ДДТ (205), +Rammstein (134), Би-2 (128), Ночные Снайперы (96), Muse (91), Пикник (69), Захар Май (67), +Ундервуд (66), Чиж & Со (63). + +Отдельные «не определить без прослушивания»: `ty_49` (`!Radio/y OOF/пакет.mp3`). diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index 155c8677..680fd906 100755 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -4,7 +4,7 @@ created: '2026-09-26' updated: '2026-09-26' type: tech namespace: personal -wave: 25 +wave: 26 tags: - music - jellyfin @@ -20,7 +20,69 @@ related: # Music Tags Repair — TrueNAS (7906 файлов) -> **Статус: ДВАДЦАТЬ ПЯТАЯ ВОЛНА (2026-09-26). ИДЁТ РУЧНОЕ ЧТЕНИЕ ПО ВСЕМУ СПИСКУ.** +> **Статус: ДВАДЦАТЬ ШЕСТАЯ ВОЛНА (2026-09-26). ПРОЧИТАНО ПОСТРОЧНО 2402–6401 ИЗ 7906 (~81%).** +> Продолжение требования Alex: «Проходить весь список руками и валидировать» + «Хули встал» +> (без пауз). В этой волне закрыты блоки **2402–6401** (11 блоков по 200 строк). +> +> ✅ **НОВОЕ (26-я волна) — построчное чтение 2402–6401.** ~230 правок, найденное детекторами +> НЕ ловится: +> | Класс | Строк | Скрипт | Результат | +> |---|---|---|---| +> | Трофим: транслит-титулы из имён файлов (`#1 10 Aristokratiya pomoyki`) | **58** | `fix_trofim.py` | → кириллица; album `Война и мр`→`Война и мир` | +> | Трофим: album-плейсхолдеры `#1`…`#9` | ~30 | `fix_block3000.py` | раскрыты в реальные альбомы (`Аристократия помойки 1`…`Бард-Авангард`) | +> | АК-47: артист в title (`Витя АК & Tip - Грубо говоря`) | 19 | `fix_block2600.py` | артист-префикс срезан | +> | Eminem: split `title='Shop'`+`artist='Candy'`; `Mile`→`8 Mile` | 3 | `fix_block2600.py` | восстановлено | +> | NTL / `!Radio`: префикс `NTL - ` в title | ~15 | `fix_block2800.py` | срезан | +> | `!Radio`: битые префиксы `ÿþ` (DOS-кириллица), мусор `NN Track NN` в artist, сайт `WAP.BITMOB.RU` | ~15 | `fix_block3200.py` | вычищено | +> | Savant: опечатки в `new_album` (`Unrelesed`) → `Unreleased`; `Bit Lightsaber`→`8-Bit Lightsaber`; `Prorophecy`→`Prophecy`; `Clssical`→`Classical` | ~8 | `fix_block3800/4200.py` | исправлено | +> | Банд'Эрос / Потап / Серёга: артист в title (`Серега - Кружим`); `Загубиги`→`Загубили` | 7 | `fix_block4400.py` | исправлено | +> | Пилот: `Илья Черт`→`Илья Чёрт`; `Осеннее пора`→`Осенняя`; 4-точечные многоточия; `Тецкатлипокa` (латинская `a`); Prodigy-плейсхолдеры `2002 - Babyes Got a temper N`→`Baby's Got a Temper` | **93** | `fix_block4600/4800.py` | исправлено | +> | Звери / Земфира: `для тебя`→`Для тебя`; Меамуры транслит (`stekla`,`V reis`,`na udachu`,`Glubzhe`); album `Земфира ()`→`Земфира (2000)`; `Земфира Рамазанова`→`Земфира` | 16 | `fix_block5000/5200.py` | исправлено | +> | Кукрыниксы: полный транслит (`Kukriniksi - 01 Stolknovenie - Serebrianii sentiabr`) | 12 | `fix_block5400.py` | → кириллица | +> | Ногу Свело!: **регрессия** — album `Второй Магаданский` разъехался на 4 варианта; `Исскуство`→`Искусство`; `Французкая`→`Французская` | 24 | `fix_block5800.py` | сведено к одному | +> | The Used: track все `9` (из `3-9 - Title`); `Geschchte`→`Geschichte`; `ПаникА Киев` split | 11 | `fix_block6200.py` | исправлено | +> | AppleDouble `._МИМИКРИЯ.MP3` → `action=skip` | 1 | `fix_block5600.py` | помечено | +> +> 🔴 **ГЛАВНЫЙ ДЕФЕКТ, НАЙДЕННЫЙ ФИНАЛЬНЫМ АУДИТОМ (26-я волна):** строка **3498** разъехалась +> на **12+ полей** — незакавыченная запятая в пути `/!Pop, Club, Любэ/` склеила путь с title, +> `new_artist` и `new_track` поменялись местами (`new_track='DJ Иван Silver & Сява'`). +> Починено `fix_final.py`. **Это 4-й случай повреждения строки запятой в пути** — паттерн +> `!Pop, Club, Любэ/` опасен, проверять его в первую очередь. +> +> 🛠 **Финальные аудиты (read-only, гонять перед «готово»):** +> - `final_audit.py` — воронка: кол-во строк/колонок/битых, `action`, заполненность полей, +> сайты-водяные знаки, нечисловые `new_track`, разбор пустых artist по папкам, +> near-duplicates артистов (регистр/ё/латиница-кириллица). +> - Нашёл и починил: `Fever // By Maddalinio //`→`Fever`; album `Best-Mp3.Ru`→пусто; +> `Муммий Троль / Медведица (Dj Ivan Scratchin T` → `Медведица (DJ Ivan Scratchin Trance Mix)`; +> `YOJI Biomehanika`→`Yoji Biomehanika`. +> +> 📊 **ФИНАЛЬНОЕ СОСТОЯНИЕ (26-я волна):** +> | Показатель | Значение | +> |---|---:| +> | Строк в `tag-fix-plan.csv` | 7906 (+голова = 7907), **0 битых** | +> | `fix` / `no_change` / `skip` | **6934 / 957 / 15** | +> | Пустых `new_title` среди `fix` | **0** | +> | Сайтов-воды в `new_*/` | **0** | +> | Нечисловых `new_track` | **0** | +> | Групп дублей артистов | **0** | +> | Строк без artist | **577** (562 = `!Детские`, намеренно) | +> | Сводные CSV | `artists-single-track.csv` **3223** + `artists-multi-track.csv` **4667** = **7890** | +> +> ⚠️ **НЕ ГОТОВ:** блок **6402–7906 (1505 строк, ~19%)** разобран скриптами, но **построчно +> глазами НЕ читался**. Крупные артисты блока: Агата Кристи (244), ДДТ (205), Rammstein (134), +> Би-2 (128), Ночные Снайперы (96), Muse (91), Пикник (69), Захар Май (67), Ундервуд (66), +> Чиж & Со (63), Несчастный случай (53), Краденое Солнце (48), Knorkator (12). +> ⏭ **ГДЕ ПРОДОЛЖАТЬ: со строки 6402.** Порядок: `read_file` блоками по 200 → править → +> `python3 final_audit.py` → `cp bak-$(date +%H%M%S)-` перед каждым скриптом. +> +> 📄 Сводка для аппрува Alex: `personal/tech/music-tags-final-review.md`. +> **Бэкапы 26-й волны:** `bak-*-trofim`, `bak-*-blk2600/2800/3000/3200/3400/3600/3800/4000/4200/` +> `4600/4800/5000/5200/5400/5600/5800/6000/6200`, `bak-*-presummary`, `bak-*-final`, `bak-*-yoji`. +> +> --- +> +> **Статус предыдущей волны: ДВАДЦАТЬ ПЯТАЯ (2026-09-26). Прочитаны и вычищены строки 1–1401.** > Alex потребовал: «Проходить весь список руками и валидировать» (после 3 отказов принять > «всё готово»). Работа в процессе: **прочитаны и вычищены строки 1–1401 из 7906 (~18%)**. > CSV: 7906 строк, 0 битых, 0 дублей артистов, 0 год-префиксов/плейсхолдеров/сайтов.