From dd9e0812737e6c9f1d67e320e65d66e6b9285fe8 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sat, 26 Sep 2026 18:04:35 +0600 Subject: [PATCH] [2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/plans/music-tags-review/music-tags-review.md personal/tech/agents-bridge.md personal/tech/music-tags-repair-truenas.md --- personal/plans/balda-mm-phase4-agents.md | 114 ++++++++--- .../music-tags-review/music-tags-review.md | 68 +++++++ personal/tech/agents-bridge.md | 165 ++++++++++++++++ personal/tech/music-tags-repair-truenas.md | 177 +++++++++++++++++- 4 files changed, 492 insertions(+), 32 deletions(-) create mode 100644 personal/tech/agents-bridge.md diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index d809bc62..892c9d87 100755 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -1,8 +1,10 @@ # Фаза 4 — исполнитель на Mac + `~/agents` + секция «Agents» в Eagle Dashboard -> План. **Шаг 1 выполнен** (2026-09-26): образ собран, контейнер `balda-executor` Up (healthy), +> План. **Шаги 1-2 выполнены** (2026-09-26): образ собран, контейнер `balda-executor` Up (healthy), > `claude 2.1.283` + `codex 0.157.1` внутри, монтирования проверены в обе стороны. -> ⏳ Осталось в шаге 1: логин CLI (нужен апрув на копирование auth-файлов). +> Мост `agents-bridge` :8790 поднят и протестирован (auth, идемпотентность, сквозной вызов CLI). +> 🔴 Осталось: **логин CLI делает Alex сам** (копирование auth-файлов отклонено); мост не в launchd; +> шаги 3-6 не начаты. > Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фазы 4-5). > Предыдущее: [[personal/projects/balda/balda-kraken-mm-deploy]] (Фаза 3 ✅ — бот отвечает Alex). @@ -71,13 +73,12 @@ diff + тесты + коммит → Балда → Mattermost | Codex auth | `~/cli-auth/codex` | `/root/.codex` | 0700 | | OAuth-токен Claude | `~/Docker/balda-executor/.env` | env | 0600 | -📌 **Основной путь авторизации — не `claude setup-token`, а копия auth-файлов** из -`~/.claude`/`~/.codex`. Токен в `.env` — запасной вариант для headless без монтирования. -Выбранный вариант: копируются `~/.claude/.credentials.json` + `~/.claude/.claude.json` и -`~/.codex/auth.json` + `~/.codex/config.toml`. Полные каталоги **не** копируются: -`~/.codex` содержит ~640 МБ sqlite-логов/состояний (`logs_2.sqlite`, `state_5.sqlite`, -`memories_1.sqlite`, `queue_1.sqlite`) — исполнителю не нужны, а вес и шум дают. +📌 **Авторизацию CLI делает Alex сам** (решение 2026-09-26). Копирование auth-файлов из +`~/.claude`/`~/.codex` в `~/cli-auth` **отклонено** — я эти файлы не читаю и не копирую. +До его логина любая задача вернёт `Not logged in · Please run /login` (это ответ Claude Code, +а не ошибка моста — конвейер при этом доказанно рабочий). +Токен `CLAUDE_CODE_OAUTH_TOKEN` в `.env` контейнера — запасной путь для headless без монтирования. Пересборка образа и обновление CLI auth не сбрасывают. Токены вне `/agents` → агент их не видит. --- @@ -180,25 +181,77 @@ docker-compose restart # проверка переживан ⚠️ `deploy.resources.limits.memory: 8G` из черновика **убран**: в не-swarm compose эта секция молча игнорируется, лимит создавал ложное ощущение защиты. -#### ⏳ Осталось в шаге 1: логин CLI (ждёт апрува) +#### ✅ Логин CLI — зона Alex (не автоматизируется) -`~/cli-auth/claude` пуст, в `~/cli-auth/codex` только `tmp/`. Копировать **только auth-файлы**, -не целые каталоги (`~/.codex` ≈ 640 МБ служебных sqlite — агенту не нужны): +`~/cli-auth/claude` пуст, в `~/cli-auth/codex` только `tmp/`. -| CLI | Хост | Куда | -|---|---|---| -| claude | `~/.claude/.credentials.json`, `~/.claude/.claude.json` | `~/cli-auth/claude/` | -| codex | `~/.codex/auth.json`, `~/.codex/config.toml` | `~/cli-auth/codex/` | +🔴 Копирование auth-файлов **отклонено Alex** («авторизую я, копировать из кредсы не надо»). +Не предлагать повторно, не читать `~/.claude/.credentials.json`, `~/.codex/auth.json`. +Признак незалогиненного исполнителя в ответе задачи: `Not logged in · Please run /login`. -📌 В `~/cli-auth` кладётся **копия** (оригиналы на месте, `~/.claude` не переезжает): -у агента-исполнителя свои токены, изолированные от рабочих. Права `~/cli-auth` = 0700. +### ✅ Шаг 2 ВЫПОЛНЕН (2026-09-26) — мост `agents-bridge` -**2. Мост (~3-4 ч)** -`~/Developer/agents-bridge/`, FastAPI :8790, `POST /task` (idempotency), `GET /task/{id}`, `GET /healthz`; -запуск CLI headless в контейнере; лимит 1-2 задач; shared secret. +Проект: `~/Developer/agents-bridge/` (FastAPI, python из `~/Developer/eagle-dash/.venv`, py3.14). -**3. Сеть (~1 ч)** -Cloudflare Tunnel: добавить published route `agents-bridge.qentra.top` → HTTP `localhost:8790` + +| Файл | Роль | +|---|---| +| `app.py` | роуты, авторизация, лимит параллельности, ThreadPoolExecutor | +| `store.py` | SQLite WAL: `tasks` + UNIQUE-индекс по `idempotency_key` | +| `executor.py` | `docker exec` в контейнер, провайдеры claude/codex, таймаут, обрезка вывода | +| `config.py` | чтение `.env` (минимальный парсер, без python-dotenv на старте) | +| `.env` | 0600, `BRIDGE_SECRET` (64 симв.), **не коммитить** | +| `.env.example` | шаблон без секретов | +| `state.sqlite3` | статусы задач, WAL | + +**API:** +``` +GET /healthz → статус моста + контейнера (без секрета) +POST /task → 202 {task_id, status} | 200 (идемпотентный повтор) | 401 | 429 | 400 +GET /task/{task_id} → статус + stdout/stderr/exit_code +GET /tasks?limit=N → последние задачи +``` + +**Авторизация:** заголовок `X-Bridge-Secret` на всех `/task*`. Идемпотентность — заголовок +`Idempotency-Key`; повтор возвращает **тот же** `task_id` со HTTP **200** (первый ответ — 202). + +**Проверено живьём (2026-09-26):** + +| Проверка | Результат | +|---|---| +| `POST /task` без секрета / с неверным | 401 / 401 ✅ | +| Два POST с одним `Idempotency-Key` | один `task_id` ✅ | +| Задача дошла до CLI в контейнере | ответ CLI вернулся ✅ | +| Статус в SQLite (WAL) | ✅ | +| `/healthz` видит контейнер | `container_running: true` ✅ | + +Ответ `/healthz`: +```json +{"status":"ok","container":"balda-executor","container_running":true, + "provider":"claude","secret_configured":true,"active_tasks":0,"max_concurrent":1} +``` + +Результат сквозной пробы (до логина CLI): `status=failed, exit_code=1, +stdout='Not logged in · Please run /login'` — **это ожидаемо** и доказывает, что +путь Kraken → Mac → контейнер → CLI проходит целиком. + +**Запуск (вручную, до launchd):** +```bash +cd ~/Developer/agents-bridge +~/Developer/eagle-dash/.venv/bin/python -m uvicorn app:app --host 127.0.0.1 --port 8790 +``` + +#### 🔴 Питфолл: `BRIDGE_SECRET` и redaction Hermes + +Команда, создающая `.env` через `python -c` с f-строкой `BRIDGE_SECRET={...}`, в логе выглядит +обрезанной (`E...cret written`) — **это редакция секретов Hermes, файл на диске цел**. +Проверять содержимое не чтением файла, а через awk с маскировкой: +```bash +/usr/bin/awk -F= '{ if ($1 ~ /SECRET|TOKEN|KEY|PASSWORD/) print $1"="; else print }' .env +``` +`read_file` на `.env` отказывает по определению (secret-bearing file). + +**3. Сеть (~1 ч)** — Cloudflare Tunnel +Добавить published route `agents-bridge.qentra.top` → HTTP `localhost:8790` + Access Application. Токен и процесс cloudflared не трогаются. Проверка: `curl` извне на `/healthz`. ⚠️ hostname — **одноуровневый** (двухуровневые дают TLS handshake failure). @@ -228,16 +281,23 @@ MCP-инструмент в Балде → `POST /task`; проба `echo ok > / ## 8. Открыто -1. ~~Провайдер исполнителя — claude или codex~~ → **claude** (оба в образе, переключение `EXECUTOR_PROVIDER`) -2. ~~Нужен ли `ask`-апрув в будущем~~ → при `allow_all` не нужен; при потребности начинать с варианта B (апрув в мосте, ~0.5 дня) -3. Hostname для моста (`agents-bridge.qentra.top`?) — ждёт шага 3 -4. ⏳ **Апрув на копирование auth-файлов CLI** в `~/cli-auth` — блокирует завершение шага 1 -5. Порт моста `8790` — проверено, свободен (`lsof -nP -i :8790`) +1. ~~Провайдер исполнителя~~ → **claude** (оба в образе, переключение `EXECUTOR_PROVIDER`) +2. ~~Нужен ли `ask`-апрув~~ → при `allow_all` не нужен; при потребности начинать с B (апрув в мосте, ~0.5 дня) +3. 🔴 **Логин CLI — делает Alex сам.** Копирование auth-файлов отклонено; до логина задачи + возвращают `Not logged in` +4. 🔴 **Мост не в launchd** — поднят вручную (`uvicorn` в фоне), при перезагрузке Mac умрёт. + Плейт `com.agents.bridge` **обязателен до шага 3**: туннель опубликует URL, который может + вести в закрытый порт. Предложено, ждёт команды Alex +5. Hostname моста — `agents-bridge.qentra.top` (шаг 3, ждёт public route в Cloud Zero Trust) +6. Порт `8790` — проверено, свободен (`lsof -nP -i :8790`), занят мостом после запуска +7. ⚠️ В дашборде в `services.yaml` **нет** записи `agents_bridge` — добавить в шаге 4 + (`health.type: http`, `url: http://127.0.0.1:8790/healthz`, `port: 8790`) ## Связанные заметки - [[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/agents-bridge]] — мост :8790, API, питфоллы (шаг 2 ✅) - [[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/plans/music-tags-review/music-tags-review.md b/personal/plans/music-tags-review/music-tags-review.md index 999dee2c..d162e125 100755 --- a/personal/plans/music-tags-review/music-tags-review.md +++ b/personal/plans/music-tags-review/music-tags-review.md @@ -20,6 +20,74 @@ updated: '2026-09-26' | [[2-medium-confidence\|Средняя уверенность]] | 5813 | | [[3-high-confidence\|Высокая уверенность]] | 959 | +## 🔴 Пятнадцатая волна (2026-09-26): КЛАСС 3 ЗАКРЫТ, КЛАСС 4 НАЧАТ + +### ✅ Класс 3 (номер трека в `new_title`) — ЗАКРЫТ ПОЛНОСТЬЮ, 156 строк + +Блок `!Детские/Детский хит/` доведён до конца. Фильтр остатка → **пусто**: +`grep 'Детский хит/' tag-fix-plan.csv | grep 'artist<-cleared-junk'`. + +Паттерн фикса (единый): + +``` +БЫЛО: title = `143 БЮРО НАХОДОК`, artist = (пусто), track = (пусто), source = artist<-cleared-junk +СТАЛО: title = `БЮРО НАХОДОК`, artist = (пусто), track = 143, source = manual (user decision) +``` + +**artist пустой намеренно** — сборник детских песен из мультфильмов без единого исполнителя. +`album = Детский Хит Сезона` — название сборника, **остаётся**. + +Полный список исправленного — в `personal/tech/music-tags-repair-truenas.md`, раздел «ПЯТНАДЦАТАЯ ВОЛНА». + +### ⚠️ Ложные срабатывания детектора «номер в title» + +`grep -E ',[0-9]{1,3} [А-ЯЁA-Z]'` ловит **нормальные** названия: +`32 Leaves` (группа), `3 Doors Down` (группа), `2 кота` (часть названия), +`Зинчук` (номер в **пути**, папка диска), `Амадеус N 146 2002` (номер альбома). + +> 🔴 **Правило: цифра в title — НЕ автоматически дефект.** Номер трека стоит **в начале** title +> через пробел и дублируется как `NNN_` в имени файла. Разбирать 3-5 примеров глазами до +> объявления классом. + +### 🔄 Класс 4 (транслит) — ~20 из ~40 + +Метка `translit artist tag; folder is Cyrillic` = **116 строк**, но большинство **ложные** +(западные группы: Joe Hisaishi, Ian Oliver, Yello, Muse, Gorillaz, Europe, Pain, Scooter). +**Реальные — русские группы латиницей.** + +Исправлено: `!A Not sorted/Sbornik_Novij_Rok` (13 строк: Мумий Тролль, Мара, Дельфин, Фактор Страха, +Гроза, Диверсанты, Курара, Лакмус, Смысловые Галлюцинации, Скрябин, Бумбокс, Декабрь, Бумбокс), +Глюкоза, Потап и Настя Каменских, Каста (`Бабка`, `Горячее время`), **Наутилус Помпилиус** +(альбомы Атлантида 11, Раскол 10, Подъём 12). + +### 🔴 Три собственные ошибки, пойманные в diff + +1. `Берегитесь, мальчики` — запятая без кавычек → строка распалась на лишние колонки. +2. В «Крепкий орешек» подставил title `Дождь`. +3. `Goryacheye Vremya` приписал artist `Баста` вместо `Каста`. + +> **Правило: читать diff после каждой правки `patch`.** Все три были видны именно в diff. + +### 📊 Состояние + +| Показатель | Значение | +|---|---:| +| Битых строк | **0** | +| Ручных правок | **~500** | +| Класс 5/C «склейка в title» | ✅ ЗАКРЫТ | +| Класс A «техпапка в artist» | ✅ ЗАКРЫТ | +| Класс 3 «номер в title» | ✅ ЗАКРЫТ (156) | +| Класс 4 «транслит» | 🔄 ~20 / ~40 | +| `no_change` (~1100) | 🔴 НЕ ПРОВЕРЯЛИСЬ | +| Применение на NAS | НЕ начато | + +### ⏭ Осталось + +1. **Класс 4** — остаток Наутилуса (`1998 Яблокитай` и далее), `!Рэп/НТЛ` (`RacinG_Portal`, + `PaDoNkÈ[NTL]`, `Fristaile`), `Df1` (вероятно НЕ трогать — западные). +2. **Строки `no_change` (~1100) — последний непроверенный пласт.** +3. Затем бэкап в `Music/backup/` и применение (по «go» Alex) → приёмка в UI → Music-библиотека. + ## 🔴 Четырнадцатая волна (2026-09-26): КЛАСС A ЗАКРЫТ, КЛАСС 3 НАЧАТ ### ✅ Класс A («имя техпапки в artist/album») закрыт полностью — 0 остатков diff --git a/personal/tech/agents-bridge.md b/personal/tech/agents-bridge.md new file mode 100644 index 00000000..a07a53c6 --- /dev/null +++ b/personal/tech/agents-bridge.md @@ -0,0 +1,165 @@ +# agents-bridge — мост Mac ↔ Kraken (FastAPI :8790) + +> Создан 2026-09-26. Компонент Фазы 4 [[personal/plans/balda-mm-phase4-agents]] (шаг 2 ✅). +> Назначение: принять задачу от оркестратора Балды (Kraken) по HTTP, запустить +> CLI-исполнитель в контейнере `balda-executor`, вернуть вывод. + +--- + +## Расположение + +| Что | Путь | +|---|---| +| Проект | `~/Developer/agents-bridge/` | +| Python | `~/Developer/eagle-dash/.venv/bin/python` (3.14, fastapi 0.115.0, uvicorn 0.30.6) | +| Конфиг + секрет | `~/Developer/agents-bridge/.env` (**0600, не коммитить**) | +| Шаблон | `~/Developer/agents-bridge/.env.example` | +| Состояние | `~/Developer/agents-bridge/state.sqlite3` (WAL) | +| Лог (ручной запуск) | `/tmp/agents-bridge.log` | +| Контейнер-исполнитель | `balda-executor` (`~/Docker/balda-executor/`) | + +Своего venv у проекта **нет** — используется venv дашборда (там уже fastapi/uvicorn/httpx). + +--- + +## Файлы + +| Файл | Роль | +|---|---| +| `app.py` | роуты, проверка секрета, лимит параллельности, `ThreadPoolExecutor` | +| `store.py` | SQLite: таблица `tasks`, UNIQUE-индекс по `idempotency_key`, `reap_stale()` | +| `executor.py` | `docker exec` в контейнер; провайдеры `claude`/`codex`; таймаут; обрезка вывода | +| `config.py` | чтение `.env` встроенным парсером + `Settings` (frozen dataclass) | +| `requirements.txt` | fastapi==0.115.0, uvicorn==0.30.6, httpx, python-dotenv | + +--- + +## API + +``` +GET /healthz → статус моста и контейнера (без секрета) +POST /task → 202 | 200 (идемпотентный повтор) | 401 | 429 | 400 +GET /task/{task_id} → статус + stdout/stderr/exit_code +GET /tasks?limit=N → последние задачи (для дашборда/отладки) +``` + +**Авторизация:** заголовок `X-Bridge-Secret` на всех `/task*`. Если `BRIDGE_SECRET` не +настроен — `POST /task` возвращает **503**, мост не принимает задачи. + +**Идемпотентность:** заголовок `Idempotency-Key`. Повтор возвращает **тот же** `task_id` +с HTTP **200** (первый ответ — 202). Гарантия на уровне БД: UNIQUE-индекс + `IntegrityError` +как обработка гонки. CLI запускается **ровно один раз** на задачу. + +**Лимит параллельности:** `MAX_CONCURRENT_TASKS` (по умолчанию **1**). При исчерпании — +**429**. Больше 2 задач под одним подписочным аккаунтом — риск бана за «unusual traffic». + +--- + +## Провайдеры + +`executor.PROVIDERS` — argv для headless-запуска: + +| Провайдер | argv | +|---|---| +| `claude` | `claude -p --output-format text ` | +| `codex` | `codex exec --skip-git-repo-check ` | + +Дефолт — `EXECUTOR_PROVIDER=claude`. Команда собирается **списком аргументов** (без +`shell=True`), `WORKDIR=/agents` внутри контейнера — наружу не выйти. + +--- + +## Переменные окружения + +| Ключ | Дефолт | Смысл | +|---|---|---| +| `BRIDGE_HOST` | `127.0.0.1` | слушать только локально (наружу — через туннель) | +| `BRIDGE_PORT` | `8790` | порт | +| `BRIDGE_SECRET` | — | **обязателен**, 64 симв. `secrets.token_urlsafe(48)` | +| `EXECUTOR_CONTAINER` | `balda-executor` | имя контейнера | +| `EXECUTOR_PROVIDER` | `claude` | `claude` или `codex` | +| `MAX_CONCURRENT_TASKS` | `1` | одновременно выполняемых | +| `TASK_TIMEOUT_SECONDS` | `900` | таймаут задачи, процесс убивается | +| `MAX_OUTPUT_BYTES` | `200000` | вывод обрезается | +| `DB_PATH` | `state.sqlite3` | SQLite | + +--- + +## Запуск + +**Сейчас — вручную** (launchd ещё не сделан, см. открытый пункт 4 плана): + +```bash +cd ~/Developer/agents-bridge +~/Developer/eagle-dash/.venv/bin/python -m uvicorn app:app --host 127.0.0.1 --port 8790 +``` + +Проверка: +```bash +curl -s http://127.0.0.1:8790/healthz +``` + +⚠️ На macOS `docker compose` **не работает** — только `docker-compose` (см. план, шаг 1). + +--- + +## Питфоллы + +### 1. `Not logged in` — это ответ CLI, а не ошибка моста + +Сквозная проба до логина CLI вернула: +``` +status=failed exit_code=1 stdout='Not logged in · Please run /login' +``` +Это **признак рабочего конвейера**: docker поднял процесс, `claude` ответил. Если видишь +эту строку — auth у исполнителя, а не мост. Логин делает **Alex сам**; копирование +auth-файлов из `~/.claude`/`~/.codex` отклонено, не предлагать. + +### 2. Редакция секретов Hermes искажает команды создания `.env` + +`python -c` с f-строкой `BRIDGE_SECRET={s}` печатает в лог обрезанный фрагмент +(`E...cret written`) — **редакция Hermes, файл на диске цел**. Не «чинить». + +Проверять `.env` только так: +```bash +/usr/bin/awk -F= '{ if ($1 ~ /SECRET|TOKEN|KEY|PASSWORD/) print $1"="; else print }' .env +``` +`read_file` на `.env` отказывает по определению (secret-bearing file) — это норма. + +### 3. Зависшие задачи после рестарта + +`store.reap_stale()` на старте помечает `running`-задачи старше `TASK_TIMEOUT_SECONDS` +как `timeout` с причиной `bridge restarted while task was running`. Иначе они навсегда +висят активными и блокируют слот лимита. + +### 4. Мост не переживает перезагрузку + +Пока нет launchd-плейта `com.agents.bridge`, процесс живёт только в текущей сессии. +Это **блокер для шага 3** (туннель опубликует URL, ведущий в закрытый порт). + +--- + +## Порядок ручной приёмки (сработавший) + +```bash +curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:8790/task \ + -H 'Content-Type: application/json' -d '{"prompt":"hi"}' # → 401 (нет секрета) + +# с секретом и одним Idempotency-Key дважды → один и тот же task_id, 202 затем 200 +# GET /task/{id} → status/stdout/exit_code +``` + +Тестовый скрипт: `/tmp/test-bridge.sh` (читает секрет из `.env`, в командной строке +секрет не светится). + +--- + +## Связанное + +- [[personal/plans/balda-mm-phase4-agents]] — план Фазы 4 (шаги 1-2 ✅) +- [[personal/tech/macos-docker-virtiofs-mount-limits]] — почему bind-mount вместо symlink +- [[personal/projects/balda/balda-mm-delegation-plan]] — общий план делегирования + +## История +- **2026-09-26**: создан, поднят на :8790, протестирован (auth 401, идемпотентность, + сквозной вызов CLI). Не в launchd. Логин CLI — за Alex. diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index dae0c40e..13ee65d7 100755 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -4,6 +4,7 @@ created: '2026-09-26' updated: '2026-09-26' type: tech namespace: personal +wave: 15 tags: - music - jellyfin @@ -19,14 +20,19 @@ related: # Music Tags Repair — TrueNAS (7906 файлов) -> **Статус: ЧЕТЫРНАДЦАТАЯ ВОЛНА (2026-09-26). КЛАСС C (склейка) И КЛАСС A (техпапка) ЗАКРЫТЫ. -> КЛАСС 3 (номер в title) — В РАБОТЕ (~25 из ~80). NAS ДОСТУПЕН, применение НЕ начато.** +> **Статус: ПЯТНАДЦАТАЯ ВОЛНА (2026-09-26). КЛАСС C, КЛАСС A, КЛАСС 3 — ЗАКРЫТЫ ПОЛНОСТЬЮ. +> КЛАСС 4 (транслит) — В РАБОТЕ (~20 из ~40). NAS ДОСТУПЕН, применение НЕ начато.** > Работа — ручной проход по CSV `tag-fix-plan.csv` построчно; правка скриптов ЗАПРЕЩЕНА.** > ✅ **Класс A («имя техпапки в artist/album») — ЗАКРЫТ ПОЛНОСТЬЮ: 0 остатков.** > (`Забугр`, `disk`, `disk_v2`, `1 DRIVE`, `5 POPSA`, `3 SHANSON` — все вычищены.) -> ✅ **Класс 3 (номер трека в `new_title`) — в работе: блок `!Детские/Детский хит` ~25 из ~80.** -> ❌ Осталось: класс 3 (~55 строк), транслит (~40), строки с `no_change` (~1100, не проверялись). -> Детали и таблицы — «ЧЕТЫРНАДЦАТАЯ ВОЛНА». +> ✅ **Класс 3 (номер трека в `new_title`) — ЗАКРЫТ ПОЛНОСТЬЮ.** Блок `!Детские/Детский хит/` +> — все **156 строк**: номер → `new_track`, title чистый, artist пустой, album `Детский Хит Сезона`. +> ✅ **Класс 4 (транслит) — в работе: ~20 из ~40.** Закрыты: `!A Not sorted/Sbornik_Novij_Rok` +> (10 строк), Мумий Тролль, Мара, Дельфин, Каста, Глюкоза, Потап и Настя, Наутилус Помпилиус +> (альбомы Атлантида / Раскол / Подъём). +> ❌ Осталось: транслит Наутилуса (`Яблокитай` и далее), строки с `no_change` (~1100, не проверялись), +> транслит в `!Рэп/НТЛ`, `Df1`. +> Детали и таблицы — «ПЯТНАДЦАТАЯ ВОЛНА». > ✅ **Проверка целостности (быстрая, без python):** > `/usr/bin/awk -F'"' 'NR>1 && NF%2==0 {print NR": "NF}' tag-fix-plan.csv` → пусто = 0 битых. > **CSV: 7907 строк (7906 данных + заголовок), 0 битых. Ручных правок: 311+.** @@ -64,6 +70,167 @@ related: > Прогонялись **девять волн** проверок: см. таблицу волн ниже. > Итог v7: «требует решения» **4**, medium **5811**, high **958**, mojibake = 0, остаточный mojibake = 0. +## 🖐 ПЯТНАДЦАТАЯ ВОЛНА: КЛАСС 3 ЗАКРЫТ, КЛАСС 4 НАЧАТ (2026-09-26) + +### ✅ КЛАСС 3 (номер трека в `new_title`) — ЗАКРЫТ ПОЛНОСТЬЮ (156 строк) + +Блок `!Детские/Детский хит/` — **все строки** доведены до конца. +Паттерн фикса (единый для всего блока): + +``` +БЫЛО: title = `143 БЮРО НАХОДОК`, artist = (пусто), track = (пусто), source = artist<-cleared-junk +СТАЛО: title = `БЮРО НАХОДОК`, artist = (пусто), track = 143, source = manual (user decision) +``` + +**Почему artist пустой:** это сборник детских песен из мультфильмов, у него нет единого +исполнителя. `album = Детский Хит Сезона` — осмысленное название сборника, **остаётся**. + +Второй подпаттерн в блоке (строки, где номер уже был убран ранее): +`source = track<-filename;artist<-cleared-junk` → просто менялся `source` на +`manual (user decision)`, поля уже были верные. + +**Примеры правок (все 156):** НЕ ХОДИТЕ В АФРИКУ (128), РАССКАЖИ СНЕГУРОЧКА (105), +МАРИЯ МИРАБЕЛЛА (123), ПЕСЕНКА БОБРЯТ (154), ЧЕБУРАШКА (140), ПЕСНЯ ШАПОКЛЯК (132), +ЧЕМУ УЧАТ В ШКОЛЕ (109), ХЭППИ ЭНД (156), АНТОШКА (145), ПЕСНЯ ВОЛКА (103), +ПЕСЕНКА БЕЛЯНКИ (135), ПЕСЕНКА ЦАРЯ (155), ПЕСЕНКА ПАРОВОЗИКА (126), +ПЕСНЯ ПРО КОНДИТЕРСКУЮ ФАБРИКУ (114), ЛЕСНОЙ ОЛЕНЬ (152), ФИЛЬМ ФИЛЬМ ФИЛЬМ (116), +Я ВОДЯНОЙ (106), ПЕСНЯ СИНЕГЛАЗКИ (147), ПЕСНЯ ПИРАТА (108), ПЕСНЯ КОТА (113), +КТО ХОДИТ В ГОСТИ ПО УТРАМ (139), ТРИ БЕЛЫХ КОНЯ (124), ПЕСНЯ УРРИ (30), +ПЕРВОКЛАШКА (51), ПЕСНЯ ПРИВИДЕНИЙ (88), ПРИХОДИ СКАЗКА (97), КРЫЛАТЫЕ КАЧЕЛИ (89), +ПЕСЕНКА О ЖИРАФЕ (33), БАРМАЛЕЙ (71), ПЕСНЯ КОЩЕЯ (40), КРУЧУ КРУЧУ КРУЧУ ПЕДАЛИ КРУЧУ (56), +ПРЕКРАСНОЕ ДАЛЕКО (7), МОЖЕТ БЫТЬ ВОРОНА (8), ПЕСЕНКА ХОМЫ И СУСЛИКА (99), +ЛЯГУШАЧЬЯ ЛАМБАДА (16), ПЕСНЯ КЛЫКА-ПОТРОШИТЕЛЯ (91), КОЛЫБЕЛЬНАЯ МЕДВЕДИЦЫ (41), +ОБЖОРЫ (68), ТОЧКА ТОЧКА ЗАПЯТАЯ (3), ПЕСЕНКА ВАСИЛИСЫ (39), ПЕСНЯ СОЛДАТА (38), +БОЛЬШОЙ СЕКРЕТ ДЛЯ МАЛЕНЬКОЙ КАМПАНИИ (94), ПЕСНЯ О ХОТТАБЫЧЕ (64), ПЕСНЯ БЕЛОЧКИ (35), +МАНИ-МАНИ (50), ПЕСЕНКА КВАКИ (23), ПЕСНЯ БАБЫ-ЯГИ (118), ПЕСНЯ БАРСУКА БОНИФАЦИЯ (54), +ПЕСЕНКА УМКИ (5), РОМАНС ЧЕРЕПАХИ ТОРТИЛЛЫ (134), ГОВОРЯТ МЫ БЯКИ-БУКИ (75), +БУРАТИНО (1), ЧУНГА-ЧАНГА (2), КОГДА МОИ ДРУЗЬЯ СО МНОЙ (59), ТАНЕЦ МАЛЕНЬКИХ УТЯТ (6), +СПОР МАШИ И ВИТИ О СКАЗКАХ (53), РЫЖИЙ РЫЖИЙ КОНОПАТЫЙ (77), ПЕСНЯ ПРО РОБИН-ГУДА (43), +ОХ РАНО ВСТАЕТ ОХРАНА (9), ЧАСТУШКИ БАБОК-ЕЖЕК (11), БЮРО НАХОДОК (143), ПЕСНЯ СЫЩИКА (151), +УТРО НАЧИНАЕТСЯ (149), ЧЕЛОВЕК СОБАКЕ ДРУГ (90), НА ДАЛЕКОЙ АМАЗОНКЕ (37), ПЕСНЯ РУСАЛОЧКИ (61), +БАЮ БАЮШКИ БАЮ (18), ПЕСНЯ РЫБЫ-ПИЛЫ (52), ГИМН НЕЗНАЙКИ (17), ПОПУРРИ ИЗ М-Ф БРЕМЕНСКИЕ +МУЗЫКАНТЫ (82), А МОЖЕТ БЫТЬ ВОРОНА (133), ПУСТЬ НЕТУ НИ КОЛА И НИ ДВОРА (81), +ГДЕ ВОДЯТСЯ ВОЛШЕБНИКИ (141), В МИРЕ МНОГО СКАЗОК (57), ПЕСНЯ ХОМЯКА (127), +НЕПРИЯТНОСТЬ ЭТУ МЫ ПЕРЕЖИВЕМ (21), ВМЕСТЕ ВЕСЕЛО ШАГАТЬ (150), ЖИВИ В ВОЛШЕБНОМ ЯЩИЧКЕ (66), +ТАКАЯ-СЯКАЯ СБЕЖАЛА ИЗ ДВОРЦА (22), КАБЫ НЕ БЫЛО ЗИМЫ (120), СПИ МОЯ РАДОСТЬ УСНИ (137), +ПЕСНЯ ВАНИ-ПЕЧНИКА (148), А Я ТАКОЙ ГОЛОДНЫЙ (73), ДВАЖДЫ ДВА - ЧЕТЫРЕ (4), +САМОВАРО-ПАРОВОЗО-ВЕРТОЛЕТ (111), ПЕСЕНКА ЛЬВЕНКА И ЧЕРЕПАХИ (60), ПЕСЕНКА ДРАКОНЧИКА (49), +ПЕСНЯ ИОХИМА ЛИСА (104), ПОЙ ВАСЯ (101), И Я ТОГО ЖЕ МНЕНИЯ (86), ПЕСЕНКА ВИННИ-ПУХА (47), +КАКОЙ ЧУДЕСНЫЙ ДЕНЬ (131), ПЕСНЯ СОРНЯКОВ (142), ПЕСЕНКА КРОКОДИЛА ГЕНЫ (112), +НАСТОЯЩИЙ ДРУГ (15), ПЕСНЯ БАЗИЛИО И АЛИСЫ (85), НИЧЕГО НА СВЕТЕ ЛУЧШЕ НЕТУ (80), +ДВОЙКА (65), СОБАКА БЫВАЕТ КУСАЧЕЙ (48), ДВА ВЕСЕЛЫХ ГУСЯ (138), ПО СЕКРЕТУ ВСЕМУ СВЕТУ (146), +ПЕСНЯ КАРАБАСА БАРАБАСА (46), ВЕСЕННЯЯ КАПЕЛЬ (117), У ПОНИ ДЛИННАЯ ЧЕЛКА ИЗ НЕЖНОГО ШЕЛКА (79), +ДУЭТ ТРУБОДУРА И ПРИНЦЕССЫ (29), В ТРАВЕ СИДЕЛ КУЗНЕЧИК (107), МЫ БАНДИТО-ГАНГСТЕРИТО (12), +ПЕСНЯ КОТА МАТВЕЯ (119), ПЕСЕНКА О ЛЕТЕ (70), В ЛЕСУ РОДИЛАСЬ ЕЛОЧКА (14), +ДУЭТ ГЛУПОГО КОРОЛЯ И ПРЕКРАСНОЙ ПРИНЦЕССЫ (74), ПЕСЕНКА ПАУЧКА АНАСИ (32), КАРУСЕЛЬ (10), +ХВОСТ ЗА ХВОСТ (67), ДА НУЖЕН ОТДЫХ ПАРУСАМ (45), ПЕСЕНКА КОТЕНКА (96), ПЕСЕНКА СТРАХОВ (115), +ДИАЛОГ ВОЛКА И КОЗЛЯТ (13), ПОЛЕ ЧУДЕС (130), ВТОРАЯ ПЕСНЯ КОЗЫ (153), ЛОМАТЬ КРУШИТЬ (42), +ПЕСНЯ ДУРЕМАРА (92), ПЕСНЯ ЛЕШЕГО (136), СДАЕТСЯ КВАРТИРА С РЕБЕНКОМ (122), ПЕСЕНКА ДРУЗЕЙ (144), +ПЕСНЯ ДИКИХ ГИТАР (62), ОБЛАКА (25), ПЕСЕНКА КРАСНОЙ ШАПОЧКИ (121), ЗВЕЗДНАЯ СТРАНА (102), +ГОЛУБОЙ ВАГОН (78), КУДА ТЫ ТРОПИНКА МЕНЯ ПРИВЕЛА (63), КОЛЫБЕЛЬНАЯ (58), +В КАЖДОМ МАЛЕНЬКОМ РЕБЕНКЕ (19), ПЕСНЯ ПТИЦЕЛОВА БЕНКАНА (129), ПЕСНЯ ВОЛКОВ (84), +ОСЬМИНОЖКИ (93), ПЕСЕНКА ПРО НЕПРИЯТНОСТИ (44), ВСЕ МЫ ДЕЛИМ ПОПОЛАМ (100), +ПЕСНЯ О ЕЛОЧКЕ (20), ПЕСНЯ МИКРОБУСА (110), ПЕСНЯ КОЗЫ (87), ЗЕМЛЯНИЧНОЕ ВАРЕНЬЕ (125), +КОРОБКА С КАРАНДАШАМИ (28), ПЕСНЯ СТАМПА (69). + +**Фильтр остатка (после закрытия = 0):** +`grep 'Детский хит/' tag-fix-plan.csv | grep 'artist<-cleared-junk'` → пусто. + +### ⚠️ ВАЖНО: ЛОЖНЫЕ СРАБАТЫВАНИЯ ДЕТЕКТОРА «номер в title» + +`grep -E ',[0-9]{1,3} [А-ЯЁA-Z]'` даёт **ложные** на нормальных названиях: + +| Строка | Почему НЕ баг | +|---|---| +| `145_32 Leaves - All Is Numb` | `32 Leaves` — название группы | +| `189_3 Doors Down - It's Not My Time` | `3 Doors Down` — название группы | +| `007_Татьяна Зыкина - 2 кота` | `2 кота` — часть названия песни | +| `002 Одинокий в ночи 2002/04 Miks` | `Зинчук` — номер в **пути** (папка диска), title чистый | +| `001 Амадеус N 146 2002` | `N 146` — номер альбома в названии | + +> 🔴 **Правило: цифра в title — НЕ автоматически дефект.** Номер трека отличается тем, что +> стоит **в начале** title и **отделён пробелом**, а сами данные в файле начинаются с `NNN_` +> в имени файла. Перед объявлением классом — разобрать 3-5 примеров глазами (тот же урок, +> что с детекторами в одиннадцатой и девятой волнах). + +### 🛠 КЛАСС 4 (транслит) — НАЧАТ, ~20 из ~40 + +Метка `translit artist tag; folder is Cyrillic` стоит на **116 строках**, но большинство — +**ложные**: для западных групп латиница корректна (Joe Hisaishi, Ian Oliver, Yello, Ace of Base, +Muse, Gorillaz, Europe, Pain, Scooter, Nautilus в некоторых альбомах и т.д.). +**Реальные кандидаты — русские группы, записанные латиницей.** + +**Исправлено в этой волне:** + +| Файл / было | Стало | +|---|---| +| `Kontrabandy` / `Mumiy Troll'` | `Контрабанды` / `Мумий Тролль` | +| `Ya Popala` / `Mara` | `Я попала` / `Мара` | +| `Na Zare` / `Evgeniy Grishkovec & Bigudi & Renars Kaupers` | `На заре` / `Евгений Гришковец & Бигуди & Ренарс Кауперс` | +| `Bez Nas` / `Del'fin` | `Без нас` / `Дельфин` | +| `Vyshe Sil` / `Faktor Straha` | `Выше сил` / `Фактор Страха` | +| `Nad Eniseem` / `Groza` | `Над Енисеем` / `Гроза` | +| `Beregites' Mal'chiki` / `Diversanty` | `Берегитесь мальчики` / `Диверсанты` | +| `Nas Dvoe` / `Kurara` | `Нас двое` / `Курара` | +| `My Vstretimsya Tam` / `Lakmus` | `Мы встретимся там` / `Лакмус` | +| `Parni Ne Plachut` / `Smyslovye Gallyucinacii` | `Парни не плачут` / `Смысловые Галлюцинации` | +| `Fantastika` / `Mumiy Troll'` | `Фантастика` / `Мумий Тролль` | +| `Mumitrol'` / `Skryabin` | `Мумитроль` / `Скрябин` | +| `Ta Chto` / `Bumboks` | `Та что` / `Бумбокс` | +| `PIPEC` / `GLJUKOZA` | `Пипец` / `Глюкоза` | +| `Mama, My Vse Soshli S Uma` / `Dekabr'` | `Мама, мы все сошли с ума` / `Декабрь` | +| `Krepkiy Oreshek+++` / `Potap & Nastya Kamenskih` (×2) | `Крепкий орешек` / `Потап и Настя Каменских` | +| `babka` / `kasta` | `Бабка` / `Каста` | +| `Goryacheye Vremya` / `kasta` | `Горячее время` / `Каста` | +| `Nautilus Pompilius` → **`Наутилус Помпилиус`** | альбомы **Атлантида** (11), **Раскол** (10), **Подъём** (12) | + +**Папка `!A Not sorted/Sbornik_Novij_Rok`** дала основной улов транслита (13 строк) — +там собраны русские рок-группы, у которых теги были латиницей. + +> ⚠️ **Питфолл метода:** `replace_all` по паре `,Nautilus Pompilius,,,no_change,translit artist tag; folder is Cyrillic` +> безопасен только при точном указании альбома — иначе `Found 3 matches`. Наутилус правился +> по альбомам отдельно (Атлантида, Раскол, Подъём). +> **Остаток:** `1998 Яблокитай` и далее — там метка ещё стоит. + +### 🔴 ТРИ СОБСТВЕННЫЕ ОШИБКИ, ПОЙМАННЫЕ И ИСПРАВЛЕННЫЕ В ЭТОЙ ВОЛНЕ + +| # | Ошибка | Как поймано | Фикс | +|---|---|---|---| +| 1 | `,Берегитесь, мальчики,Диверсанты` — запятая в title без кавычек → строка распалась на лишние колонки | diff показал сдвиг полей | переписал без запятой: `Берегитесь мальчики` | +| 2 | В строку «Крепкий орешек» подставил title `Дождь` (не то название) | визуальная проверка diff | вернул `Крепкий орешек` | +| 3 | `Goryacheye Vremya` приписал artist `Баста` вместо `Каста` | визуальная проверка diff | `Горячее время` / `Каста` | + +> 🔴 **Правило (закрепить):** после КАЖДОЙ правки читать diff в ответе `patch` — три ошибки +> этой волны были видны именно в diff. Значение с запятой внутри **обязательно** в кавычках. + +### 📊 СОСТОЯНИЕ НА КОНЕЦ ПЯТНАДЦАТОЙ ВОЛНЫ + +| Показатель | Значение | +|---|---:| +| Строк в CSV | 7906 (+1 заголовок = 7907 в файле) | +| Битых строк | **0** | +| Ручных правок (`source = manual (user decision)`) | **~500** | +| Класс C «склейка artist-title в `new_title`» | ✅ **ЗАКРЫТ** | +| Класс A «имя техпапки в artist/album» | ✅ **ЗАКРЫТ (0 остатков)** | +| Класс 3 «номер трека в title» | ✅ **ЗАКРЫТ (156 строк блока `Детский хит`)** | +| Класс 4 «транслит вместо русского» | 🔄 **~20 / ~40** | +| Строки `no_change` (~1100) | 🔴 **НЕ ПРОВЕРЯЛИСЬ** | +| Применение на NAS | **НЕ начато** | + +### ⏭ ЧТО ДЕЛАТЬ ПЕРВЫМ ДЕЛОМ В СЛЕДУЮЩЕЙ СЕССИИ + +1. **Класс 4, остаток** — транслит Наутилуса (`/!Rock&Folk/Наутилус Помпилиус/1998 Яблокитай/` + и следующие альбомы). Детектор: `grep 'translit artist tag'` → отсеять западные группы + глазами → править русские. +2. **Транслит в `!Рэп/НТЛ`** — `RacinG_Portal` / artist `PaDoNkÈ[NTL]` (стр. 2823), `Fristaile`, + `DrakRacing`, `Kasta - Kto budet pit' s nami` → русский. +3. **Транслит в `Df1`** — проверить глазами, там в основном западные (Paffendorf, Ian Oliver) — + вероятно НЕ трогать. +4. **Проверить строки `no_change`** (~1100) — часть заведомо грязная (David Guetta был вытащен + вручную). Это последний непроверенный пласт. +5. Только после этого — **бэкап в `Music/backup/` и применение** (по «go» Alex), затем приёмка + в UI, затем Music-библиотека Jellyfin. + ## 🖐 ЧЕТЫРНАДЦАТАЯ ВОЛНА: КЛАСС A ЗАКРЫТ + КЛАСС 3 НАЧАТ (2026-09-26) ### 🔴 ГЛАВНЫЙ УРОК ВОЛНЫ: `awk -F','` НЕ ГОДИТСЯ И ДЛЯ ДЕТЕКТА ПОЛЕЙ — ТОЛЬКО ДЛЯ КАВЫЧЕК