diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index beb906c1..8fac08b3 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: session12-diff-sort-fixed-track-repaired-51-radio-folders-pending-alex +status: session13-trackscroll-rollback-genres-fixed-8-album-questions-open tags: - music - metadata @@ -55,7 +55,111 @@ related: > ✅ Класс `A2` **дочищен**: `find_va_misuse.py` → 0, `find_va_author.py` → остались только > 2 защитимые папки (`Блестящие, Фриске`, `Eminem,50cent`). -## 🟢 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 12 (2026-09-27) — читать это первым +## 🟢 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 13 (2026-09-27) — читать это первым +> +> ``` +> APPLY-PLAN.csv 7861 строка md5 ced5078ebf9b06f25e8bcc2d4ae6fec9 +> DIFF-new-vs-38.csv 4996 строк md5 1884b1429e5e9e9f08ecd6abd772fc7c +> tag-fix-plan.csv 7861 строка md5 203726ebfd2814e2328636df31337238 (НЕ ПРАВИТЬ) +> ``` +> +> **Состав диффа:** `ПАПКА` 1123 · `ТРЕК` 3747 · `НЕТ-В-38` 41 · `ТОЛЬКО-В-38` 85. +> Полнота: `ВСЕГО правок 18639 · OK: потерь нет`. + +### ✅ СДЕЛАНО в сессии 13 + +**1. ⚠️ ОТКАТ дефекта из сессии 12: сквозная нумерация многодисковых изданий УНИЧТОЖЕНА мной, восстановлена.** +Класс A из `fix_track_bugs.py` (269 строк) сбрасывал `14..26 → 1..13` и т.п. — это **было неверно**. +Нумерация `14..26` в `Алиса в Стране Чудес (Диск2)` — это **правильный сквозной номер трека +в многодисковом издании**, а не «сдвиг, который надо чинить». +Alex: «ты нахуя сквозную нумерацию двухдисковую — ВЕРНУЮ БЛЯДЬ В 38 csv — сбросил блядь??» +Откат: `/tmp/rollback_track_fix.py`, источник `APPLY-PLAN.csv.bak-trackfix-20260927-143611`. +Вернулись `Алиса (Диск2)` 14..26, `Трое из Простоквашино (Диск 2)` 10..20, `Принц и нищий (Диск 2)` 8..14. + +> 🔴 **ПРАВИЛО (выведено из отката):** сквозная нумерация CD2/CD3 многодискового издания → +> **ОСТАВЛЯТЬ КАК ЕСТЬ**. Класс «ряд сдвинут на N» в `fix_track_bugs.py` — **УДАЛИТЬ/не применять**. +> Класс B («нет полного ряда» → номер из имени файла, иначе пусто) — тоже **под вопросом**, не применён. + +**2. Потеря `new_track` — 4 строки восстановлены из `old_38` (оставлено из сессии 12, подтверждено верным).** +``` +old_38=17 → '' !Детские/Сказки/Алиса в Стране Чудес (Диск2)/04 - Песня про ребенка-поросенка.mp3 +old_38= 8 → '' !Pop, Club, Любэ/Света/2003 - Зеркала/08 - Жизнь моя.mp3 +old_38= 0 → '' !Pop, Club, Любэ/!Radio/t 2009-07 c6Or_NICK/00 Geo da Silva - ....mp3 +old_38= 0 → '' !Rock&Folk/ЗАХАР МАЙ/2011 - Казань против диабета/00-intro-family-affair.mp3 +``` +Скрипт `/tmp/restore_lost_track.py`, бэкап `APPLY-PLAN.csv.bak-trackrestore-20260927-143257`. +После откатa дефекта (п.1) эти 4 строки **остались восстановленными** — верно, номер из имени файла. + +**3. Жанры — 26 строк исправлено (класс «жанр унаследован от `cur_genre` без проверки»).** +Корень дефекта: план копирует `cur_genre` как есть и переводит в свой словарь, не проверяя, +относится ли жанр к этому артисту/альбому. + +| Папка/файл | было | стало | строк | скрипт | бэкап | +|---|---|---|---|---|---| +| `!Rock&Folk/Несчастный Случай/День радио` | `Детские` | `Rock` | 20 | `/tmp/fix_denradio.py` | `.bak-denradio-…143810` | +| `!Retro/Несчастный случай - Радио (как бы).mp3` | `Детские` | `Rock` | 1 | `/tmp/fix_nesg.py` | `.bak-nesg-…143748` | +| `!Retro/Колибри - Желтый лист осенний.mp3` | `Psychedelic` | `Rock` | 1 | `/tmp/fix_psy.py` | `.bak-psy-…143916` | +| `!Rock&Folk/Агата Кристи/Триллер(Радио).mp3` | `Psychedelic` | `Rock` | 1 | `/tmp/fix_psy.py` | `.bak-psy-…143916` | +| `IM/Unreleased/Other/IM - Other Track's` ×3 | `Folk` | `Electronic` | 3 | `/tmp/fix_imfolk.py` | `.bak-imfolk-…143931` | +| `IM Vs. The Prodigy - Supervized Firestarter` | `Blues` | `Electronic` | 1 | `/tmp/fix_imblues.py` | `.bak-imblues-…143957` | + +Alex: «НС - это рок», «очевидноъ». Все файлы — `cur_genre='Детская Музыка'` / `'Psychedelic Rock'` / +`'Folk'` / `'Blues'` унаследовано из тега, значение не выдумано планом. + +**Infected Mushroom после фикса:** `Psy-Trance` 126 · `Psychedelic` 54 · `Electronic` 44 · +`Trance` 15 · `Goa` 4 · `Ambient` 1. `Folk` и `Blues` — **0 из 244**. +> ✅ 51 строка `Psychedelic` у IM — **корректна**, не трогать (IM = психоделик-транс). +> Там же план исправляет опечатку `Psychadelic` → `Psychedelic` — это плюс. + +### ❓ ВОПРОСЫ К ALEX — ОТКРЫТЫ (заданы в конце сессии 13, ответа нет) + +Задан ручной проход по всему `album`-блоку диффа. Найдены классы подозрительного `new_album`. +**Ничего не правил — жду ответа.** Ключевой спор: + +🔴 **«Куда слетели альбомы?»** Alex: «мы с тобой ставили album в соответствие названию +корня папки — ищи этот apply-plan backup!» Про **`Benassi Bros`**: ожидает `album='Benassi Bros'` +для 15 файлов корня. **Проверено — НЕ подтвердилось:** во **всех 45 бэкапах** +`APPLY-PLAN.csv.*` корень Benassi Bros = `'Benassi Bros'` только у **4 из 15**, остальные 11 +несут значения из тегов (`Phobia`, `Rock N Rave`, `Hypnotica`, `Techno 2003`, +`Miami Sampler 2002 CDS`, `Energy New Release CD`, `Loneliness Promo_Vinyl`, +`Loveparade Compilation 2003`, `I Love My Sex VINYL`, `Pumphonia`, `Non spécifié`, `NULL`). +Скрипт поиска `/tmp/find_benassi2.py` (важно: фильтр по `.csv` **отсекает** бэкапы +`APPLY-PLAN.csv.bak-*` — сначала я на этом ошибся и получил «всего 1 файл»). +➡️ **Искать источник нужно в другом файле:** `tag-inventory3.csv`, `APPLY-PLAN-RAW.csv`, +`archive/*`, `milestones/music-csv-2026-09-26/`. + +Остальные вопросы (прочитаны строки `album/ПАПКА` 1-75 и `album/ТРЕК` 76-319): + +| # | Что | Пример | +|---|---|---| +| 1 | **Мусор в `album`** | `Non spécifié`, `NULL`, `no title`, `Album`, `2005`, `Неизвестное название`, `melody.az - КняZz - Корсар`, `(Муммий Тролль)` ×8 (скобки в названии) | +| 2 | **`album` = название песни** | `Алексин/Алексин - Шалавы.mp3 → 'Шалавы'`; `Дискотека Авария/Песенка разбойников.mp3 → 'Песенка разбойников'` | +| 3 | **`album` = имя подпапки** (правило сессии 7, но список длинный) | `!Radio/z Старое/disk/2 ALT,PUNK/* → 'RADIO 2 ALT,PUNK'`; `Rammstein → 'misc'`; `Красная Плесень/misc → 'Красная Плесень'`; `Пилот/misc/Про гитару → 'Сказка'`; `Пилот/misc/Хана человечки → 'Война'` | +| 4 | 🔴 **4 разных формата `album` внутри одной `!Radio`** | `'Radio - OOF'` / `'Radio - Старое'` (префикс ✅) · `'RADIO 1 DRIVE'`, `'RADIO 5 POPSA'` (без префикса) · `'2009-04 Radio'`, `'2008-07 Radio'`, `'2008-04 Radio'` (суффикс!) · `'2009-10 Oktoberfest'` (без Radio вообще) | +| 5 | `z Другое, Enigma, Ленинград` корень → `'Сосни попса'` | это точно альбом? | +| 6 | `album` из ниоткуда | `detskiy_hor_benassi_satisfaction.mp3 → 'Горячие Синглы 2004'`; `Бутусов & Ю-Питер/Девушка По Городу.mp3 → 'Горячие синглы - июнь 2004'` | +| 7 | **корень артиста → `album` = имя артиста** | `Жуки`, `Потап & Каменских`, `Света`, `Серёга`, `Алексин`, `КняZz` — подпапок нет, это корень | +| 8 | **склейка `<Артист> - <Подпапка>` в `album`** (запрещена правилом сессии 7) | `IM - Xerox Gravity Waves.mp3 → 'Infected Mushroom - Compilations Tracks'` | + +### 🔴 МНОГОДИСКОВЫЕ ИЗДАНИЯ — правило для будущих сессий + +Обнаруженные в библиотеке (нумерация **сквозная, НЕ трогать**): +`Алиса в Стране Чудес (Диск2)` · `Трое из Простоквашино (Диск 2)` · `Принц и нищий (Диск 2)` · +`Малыш и Карлсон (Диск 2)` · `Остров сокровищ (Диск 2)` · `Али-Баба и сорок разбойников_2` · +`Final Fantasy VI - Balance and Ruin/MP3/Disc 2..5` (18..34, 35..50, 51..60, 61..74) · +`Rammstein/1999 Live Aus Berlin CD2` (13..18) · `Би-2/Нечетный Воин/2005 Нечетный Воин/CD1,CD2` · +`Неголубой огонёк НГ 2004/CD1,CD2`. + +**Инструменты сессии 13 (все в `/tmp`, только чтение/отчёты + точечные правки с бэкапом):** +`diff_audit.py`, `diff_audit2.py`, `diff_audit3.py` (структура `DIFF` явным списком переходов) · +`find_lost_track.py` (потери `track`) · `classify_track.py` (привязан/сирота) · +`show_remaining51.py` · `rollback_track_fix.py` · `walk_diff.py` (полный ручной проход → `/tmp/diff_full_walk.txt`) · +`compare_backups_album.py` · `find_benassi2.py` · `check_*` детекторы жанров. + +## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 12 (2026-09-27) — ИСТОРИЧЕСКОЕ (частично ОТКАЧЕНО) + +> 🔴 **ВНИМАНИЕ:** п.3 ниже («Косяк `new_track` → 1379 строк») в сессии 13 **ОТКАЧЕН** как дефект — +> сброс сквозной нумерации многодисковых изданий. См. сессию 13. ``` APPLY-PLAN.csv 7861 строка md5 0e6ff0e05bf0353b4c40c8c05e574e9b @@ -107,30 +211,30 @@ old_38= 0 → '' !Rock&Folk/ЗАХАР МАЙ/2011 - Казань против Детектор потерь: `field == "track" and old_38 непусто and new_apply_plan пусто` в самом DIFF. Скрипт `/tmp/restore_lost_track.py`. Бэкап `APPLY-PLAN.csv.bak-trackrestore-20260927-143257`. -**3. Косяк `new_track` — «номер ни к чему не привязан» → чинится 1379 строк.** +**3. ~~Косяк `new_track` — «номер ни к чему не привязан» → чинится 1379 строк.~~** +> 🔴 **ОТКАЧЕНО в сессии 13.** Класс A (269 строк, «ряд сдвинут») уничтожал сквозную +> нумерацию многодисковых изданий. Не применять. См. сессию 13. + Alex указал на `Алиса в Стране Чудес (Диск2)/04` = `track 17` при 13 файлах в папке (ряд 14..26 = нумерация многодискового издания). Правило Alex: > «если там есть порядковый номер в альбоме — то ты его оставляешь» > «если он ни к чему не привязан то убираешь» Реализация (`/tmp/fix_track_bugs.py`, записано с `--apply`): -| Класс | Строк | Действие | -|---|---|---| -| A. Ряд **сдвинут** (диск N многодискового издания), полный без дыр, смещение ≠ 0 | 269 | пересчитать `1..N`: `_offset_` = `min(nums) - 1` | -| B. В папке **нет полного ряда** | 1107 | номер из имени файла (`^\d{1,3}\s*[-._)]?\s+`, 1..300), иначе **пусто** | -| C. Номер есть, но `new` уже верный | 3 | — | +| Класс | Строк | Действие | Статус | +|---|---|---|---| +| A. Ряд **сдвинут** (диск N многодискового издания), полный без дыр, смещение ≠ 0 | 269 | пересчитать `1..N` | 🔴 **ОТКАЧЕНО — дефект** | +| B. В папке **нет полного ряда** | 1107 | номер из имени файла (`^\d{1,3}\s*[-._)]?\s+`, 1..300), иначе **пусто** | ⚠️ не применён | +| C. Номер есть, но `new` уже верный | 3 | — | — | -Примеры класса A: `Алиса (Диск2)` 14..26 → 1..13; `Трое из Простоквашино (Диск 2)` 10..20 → 1..11; -`Принц и нищий (Диск 2)` 8..14 → 1..7; `Малыш и Карлсон (Диск 2)` 4..6 → 1..3. -Бэкап `APPLY-PLAN.csv.bak-trackfix-20260927-143611`. +Примеры класса A (все **откачены**): `Алиса (Диск2)` 14..26; `Трое из Простоквашино (Диск 2)` 10..20; +`Принц и нищий (Диск 2)` 8..14; `Малыш и Карлсон (Диск 2)` 4..6. +Бэкап `APPLY-PLAN.csv.bak-trackfix-20260927-143611` — **источник откатa**, не удалять. -⏳ **ОСТАЛОСЬ 51 папка с рваной нумерацией — ЖДЁТ ПРАВИЛА ALEX.** -Номера не «сдвинуты» (ряд с дырами или дублями), фикс их не тронул: -- `!Radio/t 2009-07 c6Or_NICK` — 43 файла, 1..36 подряд, потом дыры (нет 37-39, 41-47, 49-59) + хвост 60-63; -- `!Radio/x 2008-04` — 59 файлов, номера **дублируются** (`2,2,3,3,5,5,7,7`); -- `!Radio/t 2009-07 c6Or_NICK/base` — 16 файлов, ряд `1,2,3,5,7,8,...` с дырами; -- `!Radio/s 2009-08 Drinkin Summer`, `.../рап` — в папке 1 файл с номером. -Вопрос задан: сбрасывать номер или пересчитывать `1..N` по алфавиту — **ответа нет**. +⏳ **51 папка с рваной нумерацией** (`!Radio/t 2009-07 c6Or_NICK` дыры 37-59 + хвост 60-63; +`!Radio/x 2008-04` дубли `2,2,3,3,5,5`; `base` дыры; `Drinkin Summer`, `рап` — 1 файл с номером) +— **ЖДЁТ ПРАВИЛА ALEX.** Но: после того как выяснилось, что эти папки = подборки `!Radio` +(не альбомы), вопрос переформулирован — стоит ли вообще трогать номера в подборках. ### 🔴 Метод-уроки сессии 12 (Alex ловил на каждом) @@ -151,11 +255,19 @@ Alex указал на `Алиса в Стране Чудес (Диск2)/04` = по mp3 (7861), поэтому эти форматы в него не попали by design. - `НЕТ-В-38` (41) = есть в новом плане, не было в 38-м. -### 📌 Что делать дальше (порядок приоритета) +### 📌 Что делать дальше (порядок приоритета, обновлён в сессии 13) -1. Получить от Alex правило для 51 папки с рваной нумерацией → применить тем же скриптом. -2. Перегенерировать `DIFF-new-vs-38.csv` (`python3 diff_new_vs_38.py > /tmp/log 2>&1`). -3. Применение плана к файлам — **отдельное «go»** от Alex, через `tagwriter:v2` +1. **Ответить на 8 вопросов по `album`** (см. «❓ ВОПРОСЫ К ALEX» в сессии 13) — особенно #4 + (4 разных формата `album` внутри `!Radio`) и спор про `Benassi Bros`. +2. **Найти источник правила «`album` = имя корня папки»** — в бэкапах `APPLY-PLAN.csv.*` его НЕТ. + Проверить `tag-inventory3.csv`, `APPLY-PLAN-RAW.csv`, `archive/*`, `milestones/music-csv-2026-09-26/`. +3. **Дожать класс «жанр унаследован от `cur_genre`»** — `Enigma/Sleep` 13 строк `General Folk` + (Enigma = нью-эйдж/эмбиент, не фолк). Тот же корень дефекта, что и 26 исправленных строк. +4. **Решить судьбу 51 папки с рваной нумерацией** — но сначала: стоит ли вообще трогать + номера в подборках `!Radio`. +5. Перегенерировать `DIFF-new-vs-38.csv` после любых правок плана + (`python3 diff_new_vs_38.py > /tmp/log 2>&1`). +6. Применение плана к файлам — **отдельное «go»** от Alex, через `tagwriter:v2` (см. `[[personal/plans/tagwriter-container]]`). ## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 11 (2026-09-27) — ИСТОРИЧЕСКОЕ diff --git a/personal/tech/id3-tag-repair.md b/personal/tech/id3-tag-repair.md index 47b9bdb7..2b2d0dc5 100755 --- a/personal/tech/id3-tag-repair.md +++ b/personal/tech/id3-tag-repair.md @@ -34,6 +34,11 @@ related: > и `file_lines` в один список, `field` первым + `scope` вторым; правка `key=` в двух > раздельных циклах бесполезна); **D11** (`new_track` теряется при пустом `cur_track`); > **D12** (`new_track` «ни к чему не привязан» → сдвинутый ряд пересчитать, иначе убрать). +> +> ⚠️ **Обновлён до v1.7.0 (сессия 13):** **D12 ОТКАЧЕН** — см. **D12-R** ниже (сквозная +> нумерация многодисковых изданий НЕ трогать). Добавлены **D13** (жанр унаследован от +> `cur_genre` без проверки), **D14** (статус «привязан/сирота» для `track`), **D15** (поиск +> по бэкапам: фильтр `.csv` отсекает `APPLY-PLAN.csv.bak-*`). ## 📁 Артефакты проекта @@ -429,7 +434,8 @@ lost = [r for r in rows if r["field"] == "track" Если `old_38` было числом, а `new_apply_plan` пусто — это **потеря**, а не правка. Восстановление: перенести значение из `old_38` (`/tmp/restore_lost_track.py`). -### 🔴 D12 — `new_track` «ни к чему не привязан» → пересчитать или убрать +### 🔴 D12 — ~~`new_track` «ни к чему не привязан» → пересчитать или убрать~~ +> ## ⛔ ОТКАЧЕНО в сессии 13 — НЕ ПРИМЕНЯТЬ. См. **D12-R** ниже. Правило Alex (сессия 12): > «если там есть порядковый номер в альбоме — то ты его оставляешь» @@ -464,6 +470,82 @@ lost = [r for r in rows if r["field"] == "track" `/tmp/restore_lost_track.py`, `/tmp/audit_track_seq.py`, `/tmp/classify_track.py`. См. `personal/plans/music-artist-albumartist-repair.md` § «🟢 СЕССИЯ 12». +### 🔴 D12-R — СКВОЗНАЯ нумерация многодисковых изданий НЕ ТРОГАТЬ (сессия 13) + +**Класс A из D12 был дефектом и откачен.** Сброс `14..26 → 1..13` в +`!Детские/Сказки/Алиса в Стране Чудес (Диск2)` **уничтожал верный сквозной номер трека** +многодискового издания. Alex: «ты нахуя сквозную нумерацию двухдисковую — ВЕРНУЮ БЛЯДЬ +В 38 csv — сбросил блядь??» + +**Правило:** папка с суффиксом `(Диск2)` / `CD2` / `Disc 2` / `_2` и рядом последовательных +номеров, продолжающих предыдущий диск → **нумерация сквозная, оставлять как есть**. + +Издания, найденные в библиотеке (эталон для проверки): +`Алиса в Стране Чудес (Диск2)` 14..26 · `Трое из Простоквашино (Диск 2)` 10..20 · +`Принц и нищий (Диск 2)` 8..14 · `Малыш и Карлсон (Диск 2)` 4..6 · +`Остров сокровищ (Диск 2)` 5..6 · `Али-Баба и сорок разбойников_2` 8..14 · +`Final Fantasy VI - Balance and Ruin/MP3/Disc 2..5` (18..34 / 35..50 / 51..60 / 61..74) · +`Rammstein/1999 Live Aus Berlin CD2` 13..18 · `Би-2/Нечетный Воин/2005 .../CD1,CD2` · +`Неголубой огонёк НГ 2004/CD1,CD2`. + +**Осталось в силе из D12 (не откачено):** критерий «номер ни к чему не привязан» относится +к **подборкам** (`!Radio/*`, `!Retro`), а не к дискам альбомов. Класс B (1107 строк) — +**не применялся**, применять только после явного правила Alex. + +Откат: `/tmp/rollback_track_fix.py`, источник `APPLY-PLAN.csv.bak-trackfix-20260927-143611` +(этот бэкап — **состояние ДО дефекта**, не удалять). + +### 🔴 D13 — жанр унаследован от `cur_genre` без проверки принадлежности (сессия 13) + +План копирует `cur_genre` через свой словарь и **не проверяет, относится ли жанр к этому +артисту/альбому**. Из-за этого всплывают абсурды. Все 26 найденных строк имели `cur_genre`, +совпадающий с мусорным `new_genre` — значение **не выдумано планом**, оно было в теге. + +| Было (`cur_genre`) | Стало | Строк | Где | +|---|---|---|---| +| `Детская Музыка` | `Детские` → **`Rock`** | 20+1 | `!Rock&Folk/Несчастный Случай/День радио` (весь альбом), `!Retro/Несчастный случай - Радио (как бы).mp3` | +| `Psychedelic Rock` / `Psychedelic` | → **`Rock`** | 1+1 | `!Retro/Колибри - Желтый лист осенний.mp3`, `!Rock&Folk/Агата Кристи/Триллер(Радио).mp3` | +| `Folk` | → **`Electronic`** | 3 | `IM/Unreleased/Other/IM - Other Track's` | +| `Blues` | → **`Electronic`** | 1 | `IM Vs. The Prodigy - Supervized Firestarter` | + +**Не трогать:** 51 строка `Psychedelic` у Infected Mushroom — верный жанр (IM = психоделик-транс). +Там же план **исправляет** опечатку `Psychadelic` → `Psychedelic` — это плюс. + +**Метод детекции:** `collections.Counter(new_genre)` по артисту/папке + вывод `cur_genre` рядом. +Подозрительно: жанр, встречающийся у одного файла из сотни (`Детские` 1 из 231 в `!Retro`), +или жанр, несовместимый с артистом. + +Скрипты: `/tmp/check_neschast.py`, `check_denradio.py`, `check_kolibri.py`, `check_folk.py`, +`check_imfolio_all.py`, `check_im_blues.py` + фиксеры `fix_nesg.py`, `fix_denradio.py`, +`fix_psy.py`, `fix_imfolk.py`, `fix_imblues.py`. + +### 🔴 D14 — статус `track`: «привязан» vs «сирота» (сессия 13) + +Для оценки объёма правок `track` папки делятся по критерию: **полный ли ряд `1..N`**. +- **привязан** — `sorted(nums) == range(1, len(nums)+1)` → 371 папка, номер рабочий, не трогать; +- **сирота** — иначе → 1907 строк в 184 папках. + +⚠️ «Сирота» **не равно** «надо чистить»: сироты = подборки `!Radio/*`, `!Retro`, `Sbornik_Novij_Rok` +(там номер = порядковый номер в сборнике, напр. `023_Бумбокс`). Судьба сирот — **вопрос к Alex**, +автоочистка запрещена. + +Скрипт: `/tmp/classify_track.py`. + +### 🔴 D15 — поиск по бэкапам: фильтр `.csv` отсекает `APPLY-PLAN.csv.bak-*` (сессия 13) + +`glob(f"{B}/APPLY-PLAN.csv*")` + условие `p.endswith(".csv")` **выбрасывает все бэкапы**, +потому что имя `APPLY-PLAN.csv.bak-before-radiofix-20260927-140315` заканчивается на дату, +а не на `.csv`. Первый прогон дал «файлов: 1» вместо 45, и я построил вывод на неполных данных. + +**Правильно:** фильтровать по началу имени или не фильтровать расширение, а проверять +наличие колонки (`"new_album" in rows[0]`). Скрипт `/tmp/find_benassi2.py` (рабочая версия) +и `/tmp/compare_backups_album.py`. + +**Питфолл восприятия:** в диффе `old_38 = ''` означает «в 38-м прогоне поле было пустым», +а **не** «значение потеряно». Alex увидел `'' -> 'КняZz'`, прочитал как «альбом слетел» и +потребовал откат. Отчёт нужно подавать так, чтобы `''` не читалось как потеря — например +`<пусто в 38-м> -> 'КняZz'`. + ### 🔴 D10 — секции полноты в хвосте диффа читаются как мусор `НЕТ-В-38` (41) + `ТОЛЬКО-В-38` (85) идут после правок и выглядят как сломанный хвост файла.