diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index 4d1132a1..368ab102 100755 --- a/personal/plans/music-artist-albumartist-repair.md +++ b/personal/plans/music-artist-albumartist-repair.md @@ -4,7 +4,7 @@ created: '2026-09-27' updated: '2026-09-27' type: plan namespace: personal -status: session29sexies-GENERATOR-FIXED-root-rule-643-rows +status: session29sexies-SBORNIK-BLOCKER-CLOSED-CONTAINER_FOLDERS-fix-4878 tags: - music - metadata @@ -251,6 +251,88 @@ B: скобки прямо на `apply-plan-v2.csv`). **Не сделано.** ⚠️ **НЕ ГОТОВО К ПРОВЕРКЕ.** На вопрос Alex «**все готово? проверять?**» дан честный ответ: диф сделан, но **содержит `Sbornik_Novij_Rok`** — показывать нельзя. +### 🔑 ЗАКРЫТИЕ БЛОКЕРА «Sbornik_Novij_Rok» — ФОРЕНЗИКА ZULIP DB (тот же день, продолжение) + +> **ТРИГГЕР:** «**почему блядь не готово? че встал тогда?**» → фикс → «**все готово? проверять?**» +> → «**стоп. Sbornik_Novij_Rok - был учтен в скрипте**» → «**ищи в базе зулип. где учитывался +> Sbornik_Novij_Rok**» → «**че?**» + +#### 🔴 РЕШАЮЩИЙ ФАКТ ИЗ ZULIP DB: `Sbornik_Novij_Rok` УЧИТЫВАЛСЯ **РУКАМИ**, НЕ ГЕНЕРАТОРОМ + +Запрос к Zulip DB (`docker exec zulip-database-1 psql -U zulip zulip`, `content ILIKE +'%Sbornik_Novij_Rok%'`) дал полную историю. Папка правилась **тремя отдельными ручными +прогонами 26 сентября**, все — поверх **`tag-fix-plan.csv`** (38-я волна), НЕ в генераторе: + +| Когда (26 сен) | Что делали | Объём | +|---|---|---:| +| 11:26 (msg 120803) | **баг 3** — срезанные `!`/`?` | часть 51 | +| 12:05 (msg 121556-58) | **класс 4, транслит** — `Kontrabandy`→`Контрабанды`, `Ya Popala`→`Я попала`, `Pilot`→`Пилот` | ~55 | +| 14:23 (msg 123086-88) | **треки из имени файла** — 199 номеров `NNN_` → `new_track` | 199 | + +**Способ правки (msg 121546, дословно):** «Для блока `Sbornik_Novij_Rok` artist+title уникальны, +но встречаются ровно 2 раза каждый (`cur`+`new`). …правлю **по целой строке** через +`patch mode=patch`». Все правки помечены `source = manual (user decision)`. + +**🔑 ВЫВОД: `album = 'Сборник нового рока'` пришло из РУЧНОЙ работы, а не из `derive_from_path`.** +В генераторе `build_fix_plan2.py` защиты под сборники **не было и нет** — его `NON_ARTIST` / +`CATEGORY_DIRS` / `EVENT_WORDS` содержат только **русское** `сборник`, а папка названа +**латиницей** `Sbornik_Novij_Rok`. +**Отсюда и регрессия:** я сгенерил план **с нуля** генератором, который ручных правок не знает +→ генератор увидел папку, не нашёл защиты → переписал верный album. + +#### ✅ ПОДТВЕРЖДЁННЫЙ ДИАГНОЗ (кодом, не рассуждением) — `/tmp/check_sbornik.py` + +``` +папка: 'Sbornik_Novij_Rok' norm -> 'sbornik_novij_rok' +NON_ARTIST содержит её? False (в списке только 'сборка','сборник песен','сборник') +CATEGORY_DIRS содержит её? False +is_release_folder('Sbornik_Novij_Rok')? False +has_word(norm, EVENT_WORDS)? False (в списке 'сборный','сборка','сборник') +derive_from_path -> ('Sbornik_Novij_Rok', 'Sbornik_Novij_Rok') ← вот он, косяк +``` +**Ни один из трёх механизмов не ловит латиницу.** 🔑 МЕТОД-УРОК: **любой список стоп-слов +обязан содержать ЛАТИНСКУЮ ТРАНСЛИТЕРАЦИЮ** — в этой библиотеке папки часто транслитерированы +(`Sbornik_Novij_Rok`, `Df1`, `ZabuGor`), и русское написание не срабатывает. + +#### ✅ ФИКС (2 правки, в тот же файл) + +**Правка 4 — `EVENT_WORDS` + новый `CONTAINER_FOLDERS`:** +```python +EVENT_WORDS = { ..., + # Latin transliteration of the same container words... + "sbornik", "sbornik_", "sborniki", "sborka", "raznoe", "izbrannoe", + "hity", "luchshee", "detskie", "novoe", "staroe", +} +CONTAINER_FOLDERS = { # ← НОВОЕ множество + "sbornik_novij_rok", "sbornik_noviy_rok", "df1", "misc", "base", "plux", + "new", "incoming", "test", "tmp", "data", +} +``` + +**Правка 5 — проверка в `derive_from_path()` перед `return only, only`:** +```python +if norm(only) in CONTAINER_FOLDERS: + return "", only if stripped else "" # ← правило корня НЕ применяется +``` + +**Результат перегенерации** (`build_fix_plan2.py … /tmp/apply-plan-v2.csv`): +`fix` **5079 → 4878**, `no_change` 2782 → 2983 — 201 строка перестала быть ложной правкой. + +#### 🔴 ЧЕСТНЫЙ ОТВЕТ НА «че?» — что было переспрошено зря + +Я задал вопрос «подтверди критерий мусор — и я переделаю» **после того как дефект был уже +назван Alex**. Это повтор процессного провала сессии 27/29-sexies. **Правило: дефект назван → +фиксить в том же ходе, меню не строить.** + +#### 📌 СЛЕДСТВИЕ ДЛЯ АРХИТЕКТУРЫ (важное для будущих сессий) + +**Генератор `build_fix_plan2.py` воспроизводит только ~механику 38-й волны.** Все ручные +решения Alex (сборники, транслит→кириллица, `Various Artists`, правила скобок) живут **в +`APPLY-PLAN.csv` и его бэкапах, НЕ в генераторе**. +🔑 **Правильный способ строить новый план:** брать `new_*` из `APPLY-PLAN.csv` как базу и +**дописывать только целевое правило** (правило корня), не перезаписывая то, что уже верно. +Перегенерация «с нуля» **обязательно** теряет ручные правки. + --- ## 🔴 СЕССИЯ 29 (2026-09-27, ночь) — НОВЫЙ ДИФ СО СХЛОПЫВАНИЕМ: `DIFF-new-vs-38-v6.csv` diff --git a/personal/tech/batch-data-edit-review-loop.md b/personal/tech/batch-data-edit-review-loop.md index 87157587..ea5fbeb5 100755 --- a/personal/tech/batch-data-edit-review-loop.md +++ b/personal/tech/batch-data-edit-review-loop.md @@ -414,6 +414,42 @@ RC-сверка (`/tmp/rc_verify.py` → `RC-VERIFY.csv`) дала **7861 рас `track_number = 0` = «номер не проставлен» — сравнивать с пустым значением из плана с учётом этого, иначе 1699 ложных расхождений. +### 33. 🔴🔴 ПЕРЕГЕНЕРАЦИЯ «С НУЛЯ» ТЕРЯЕТ РУЧНЫЕ ПРАВКИ — строить от прошлого плана, не от скрипта + +Продолжение урока 29, но глубже. Мало найти генератор — надо понять, **чем он НЕ является**. + +`Sbornik_Novij_Rok` дал регрессию **второй раз**: генератор `build_fix_plan2.py`, запущенный +с нуля, переписал верный `album = 'Сборник нового рока'` на имя папки на **200 файлов**. + +**Причина оказалась НЕ в отсутствии защиты.** Форензика Zulip DB показала: папка правилась +**руками** 26 сентября — тремя прогонами (`patch mode=patch` по целым строкам, +`source = manual (user decision)`): транслит ~55 строк (`Kontrabandy`→`Контрабанды`), +треки из имени файла 199, срезанные `!`/`?`. **Все эти решения жили в `tag-fix-plan.csv` +и `APPLY-PLAN.csv`, а НЕ в генераторе** — генератор воспроизводит только механику 38-й волны. + +**Правило:** новый план строить **от `new_*` предыдущего плана как базы**, дописывая только +целевое правило. Перегенерация из исходного инвентаря «с нуля» **обязательно** теряет ручные +решения, потому что они нигде в коде не записаны. +**Проверка перед генерацией:** взять 3-5 известных ручных правок из прошлого плана и убедиться, +что генератор даёт для них то же значение. Иначе — не генерировать. + +### 34. 🔴 Стоп-список обязан содержать ЛАТИНСКУЮ ТРАНСЛИТЕРАЦИЮ + +`Sbornik_Novij_Rok` **не ловился ни одним из трёх механизмов** генератора (проверено кодом, +`/tmp/check_sbornik.py`): `NON_ARTIST` → `False`, `CATEGORY_DIRS` → `False`, +`is_release_folder` → `False`, `has_word(norm, EVENT_WORDS)` → `False`. +Причина: во всех списках слово `сборник` есть **только по-русски**, а папка названа +**латиницей** (`sbornik_novij_rok`). + +В этой библиотеке транслит — норма, не исключение: `Sbornik_Novij_Rok`, `Df1`, `ZabuGor`, +`RacinG_Portal`, `PaDoNkÈ[NTL]`. Любой `grep` по русскому стоп-слову их пропускает. + +**Правило:** каждый стоп-список дублировать латиницей (`sbornik`, `sborka`, `raznoe`, +`izbrannoe`, `hity`, `luchshee`, `novoe`, `staroe`, …); для конкретных папок-контейнеров — +отдельное множество точных имён (`CONTAINER_FOLDERS`). +**Проверка:** прогнать стоп-списки по реальным именам папок и убедиться, что известные +контейнеры дают `True`, а известные артисты (`5'nizza`, `Little Big`) — `False`. + ## Связанное - [[personal/tech/music-tags-repair-truenas]] — волновой лог канонического инстанса diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index 132508a6..b424e8d9 100755 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -49,6 +49,20 @@ related: > генератор это поле не заполняет) и `track` 3505 (`0` ↔ `''` нормализовать). > **Диф НЕ готов к показу Alex.** > +> ✅ **БЛОКЕР ЗАКРЫТ (продолжение 29-секси, 2026-09-27):** форензика Zulip DB доказала, что +> `Sbornik_Novij_Rok` правился **РУКАМИ** 26 сен (3 прогона: транслит ~55, треки 199, баг 3) +> через `patch` в `tag-fix-plan.csv` — **в генераторе защиты не было никогда**, т.к. списки +> содержат только русское `сборник`, а папка названа латиницей. Фикс: новое множество +> `CONTAINER_FOLDERS` (`sbornik_novij_rok`, `df1`, …) + проверка в `derive_from_path` перед +> `return only, only`; `EVENT_WORDS` дополнен латинской транслитерацией. Перегенерация: +> `fix` 5079 → **4878**. Диагноз кодом — `/tmp/check_sbornik.py`. +> 🔑 **МЕТОД-УРОК: любой стоп-список обязан содержать латинскую транслитерацию** — папки тут +> транслитерированы (`Sbornik_Novij_Rok`, `Df1`, `ZabuGor`), русское написание не срабатывает. +> 🔑 **АРХИТЕКТУРА: генератор воспроизводит только механику 38-й волны.** Все ручные решения Alex +> живут в `APPLY-PLAN.csv`, НЕ в генераторе → **новый план строить от `new_*` плана, дописывая +> целевое правило, а НЕ перегенерацией «с нуля»** (иначе ручные правки теряются). +> Подробно — [[personal/plans/music-artist-albumartist-repair]] § ЗАКРЫТИЕ БЛОКЕРА. +> > Подробно — [[personal/plans/music-artist-albumartist-repair]] § СЕССИЯ 29-СЕКСИ. > > ⚠️ **НОВОЕ (сессия 28): детектор «файл в корне папки артиста» сравнивал на РАВЕНСТВО diff --git a/personal/tech/skill-truenas-administration.md b/personal/tech/skill-truenas-administration.md index 0b9fc3cb..179f5382 100755 --- a/personal/tech/skill-truenas-administration.md +++ b/personal/tech/skill-truenas-administration.md @@ -52,7 +52,8 @@ ssh truenas_admin@mallexxx.duckdns.org "cd /mnt/RED_2TB/storage/Music && ls -d * scp /tmp/ndignore-new truenas_admin@mallexxx.duckdns.org:/tmp/ndignore-new ssh truenas_admin@mallexxx.duckdns.org "cp /tmp/ndignore-new /mnt/RED_2TB/storage/Music/.ndignore" ``` -Navidrome подхватывает файл по рестарту; после скана проверить, что игнор сработал — +Затем **`docker restart navidrome`** (в контейнере `curl /api/scanner/status` → `404`, эндпоинта +нет; подхват идёт по рестарту + watcher). После скана проверить, что игнор сработал — игнорируемые получают **`missing=1`, их НЕ удаляют**: ```bash sqlite3 /mnt/RED_2TB/docker/navidrome/data/navidrome.db \ @@ -61,12 +62,15 @@ sqlite3 /mnt/RED_2TB/docker/navidrome/data/navidrome.db \ SELECT COUNT(*) FROM media_file WHERE missing=0 AND path NOT LIKE 'backup%';" -- живая музыка ``` **Контрольное число:** живая музыка = числу строк плана (**7861** на 2026-09-27). -Факт после фикса: всего **8396**, `missing=1` **535**, живая **7861** ✅. +Факт после фикса: всего **8396**, `missing=1` **535**, живая **7861** ✅ (до фикса было 8167/306). > 🔴 **ОБЯЗАТЕЛЬНЫЙ ФИЛЬТР в ЛЮБОМ запросе к `media_file`:** > `missing=0 AND path NOT LIKE 'backup%'` > Без него читаешь бэкап-копии и делаешь ЛОЖНЫЕ выводы. Эта ошибка дала неверный отчёт > «скобки не исправлены» — бэкап был выдан за живое дерево. +> 🔑 **Правило на будущее (Alex: «Да»):** любой новый `backup*` под библиотекой — в `.ndignore` +> **в том же прогоне, что его создал**. Файл создан 03:49, бэкапы — 05:17 и 06:19; список +> просто не обновили. Скрипты бэкапа обязаны дописывать себя сами. ## 1. Создание НОВОГО каталога под `/mnt//docker/` — из контейнера