diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index 6c8a84ec..fa020499 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: session18-dop-diff-inventoried-folders-counted-awaiting-apply-go +status: session18-dop-diff-inventoried-folders-counted-genre-origin-found-awaiting-apply-go tags: - music - metadata @@ -24,8 +24,16 @@ related: ## 🟣 СЕССИЯ 18-ДОП (2026-09-27, вечер) — ИНВЕНТАРЬ ДИФФА ПО ФАКТУ, ПОСЛЕ ВОПРОСА ALEX «ГДЕ ДИФ?» > **Триггер:** Alex — «**Где диф последний с 371 правкой альбомов/albumartist который ты готовил?**» -> → затем «**Какие там записи?**». Отвечено из **Zulip DB** (по скиллу `zulip-db-forensics`), -> затем проверено по самим файлам на диске. +> → «**Какие там записи?**» → «**Все папки**» → «**С количеством файлов**» → +> «**Откуда там 7963 правки?**» → «**Откуда там правка жанра то??**» → +> «**Ты только что блядь сказал что в диффе 306 сука файлов! Какие нахуй 4359?!**». +> Отвечено из **Zulip DB** (по скиллу `zulip-db-forensics`), затем проверено по самим файлам на диске. +> +> **Два новых факта сессии (см. разделы ниже):** +> 1. **GENRE в плане — колонки в волне 38 НЕ БЫЛО вообще** → все 4359 = первая запись тега (ср. D3). +> Генератора `new_genre` на диске нет — только `fix_apply_plan.py` (1 правка мусора). +> 2. **306 в диффе НЕТ — 0 ячеек** (literal search). Дифф держит те же скобки как **15 записей** +> (схлопывание D16). Моё изложение склеило 306 (счётный файл) и дифф в одну фразу — дефект изложения. ### 📍 Местонахождение (факт с диска, mtime 2026-09-27 17:41) @@ -158,6 +166,104 @@ ANY field differing 7651 > план — «что реально отличается от целевого». **Две разные базы счёта** (ср. §Число 371 ≠ строки диффа). > Проверочный скрипт: `/tmp/explain_genre.py`. +### 🔴 ОТКУДА ВЗЯЛСЯ GENRE В ПЛАНЕ — КОЛОНКИ В ВОЛНЕ 38 НЕ БЫЛО ВООБЩЕ + +Alex: «**Откуда там правка жанра то??**» → затем «**Откуда там 7963 правки?**» + +**Факт — заголовки двух планов (`csv.DictReader`):** + +``` +tag-fix-plan.csv (волна 38): + path, current_verdict, cur_title, cur_artist, cur_album, + new_title, new_artist, new_album, new_track, action, source + ↑ genre ОТСУТСТВУЕТ + +APPLY-PLAN.csv (текущий, 16 колонок): + path, folder, class, cur_title, cur_artist, cur_albumartist, cur_album, + cur_genre, cur_track, new_title, new_artist, new_albumartist, new_album, + new_genre, new_track, changed ← genre ПОЯВИЛСЯ +``` + +**Следствие (ср. D3):** жанр не писался ни разу за всю историю волн. Любое непустое +`new_genre` в текущем плане = **первая запись тега**. Это и есть все `4359`. + +**Состав — 137 пар `cur_genre → new_genre`, три класса:** + +| файлов | было | стало | класс | +|---:|---|---|---| +| 1136 | *пусто* | `Rock` | заполнение (правило «жанр по артисту/папке», ср. G4) | +| 439 | *пусто* | `Pop` | заполнение | +| 322 | *пусто* | `Детские` | заполнение | +| 252 | *пусто* | `Бардовская песня` | заполнение | +| 237 | *пусто* | `Сказки` | заполнение | +| 182 | `Рок` | `Rock` | нормализация | +| 177 | *пусто* | `Chanson` | заполнение | +| 101 | *пусто* | `Dark Cabaret` | заполнение | +| 94 | `Other` | `Pop` | разрешение по артисту (G3) | +| 82 | `Punk Rock` | `Punk` | нормализация | +| 52 | `Metal` | `Heavy Metal` | нормализация | +| 41 | `Psy Årance` | `Psy-Trance` | омоглиф (G2) | +| 32 | `(17)` | `Rock` | 🔴 **поле прочитано неверно** — `(17)` стоял на 32 файлах ДДТ | +| 30 | `Psy Еrance` | `Psy-Trance` | омоглиф (G2) | +| 23 | `Dance/Club` | `Dance` | нормализация | +| 22 | `Разное` | `Бардовская песня` | 🔴 поле прочитано неверно (Высоцкий) | +| 22 | `Детская Музыка` | `Rock` | нормализация | +| 16 | `Поп` | `Pop` | нормализация | + +**Кто правил `new_genre` в самом плане:** `~/Downloads/music-tags/fix_apply_plan.py` (mtime 13:13) +содержит ровно **одну** genre-правку — мусор `яюElectronic` → `Electronic` (JUNK_FIX), плюс +пересчёт `changed`. 🔴 **Генератора, который построил `new_genre`, на месте НЕТ** — `fix_apply_plan.py` +правит уже готовый план, а не строит его. Скрипт-строитель отсутствует в +`~/Downloads/music-tags/*.py`. + +> 🔑 **МЕТОД-УРОК:** прежде чем объяснять «откуда правка», **сравнивать ЗАГОЛОВКИ обеих версий +> плана**. Отсутствие колонки в предыдущей версии объясняет весь объём поля одним фактом — +> и это надёжнее любых рассуждений о логике заполнения. Проверочный скрипт: `/tmp/where_genre.py`, +> `/tmp/genre_origin.py`. + +### 🔴 306 В ДИФФЕ НЕТ — ПРОВЕРЕНО ПО ЯЧЕЙКАМ (ответ на «Какие нахуй 4359?») + +Alex: «**Ты только что блядь сказал что в диффе 306 сука файлов! Какие нахуй 4359?!**» + +**Он прав: 306 в диффе не существует.** Проверено скриптом `/tmp/find_306.py` по всем ячейкам: + +``` +DIFF-new-vs-38-v2.csv + строк всего 2854 + album-строк 164 + из них old_38 в скобках 15 (12 ПАПКА + 3 ТРЕК) + ячеек, содержащих "306" 0 ← literal search по всем колонкам +``` + +**Где стоят эти 15 строк (схлопывание D16: одна запись папки = N файлов):** + +| scope | old_38 | new_apply_plan | +|---|---|---| +| ПАПКА | `(2001) Infected Mushroom - S Is Here (Yahel Mix)` | `S Is Here (Yahel Mix)` | +| ПАПКА | `(Авария)` | `Москва пьёт пиво` | +| ПАПКА | `(Авария)` | `Старые хиты` | +| ПАПКА | `(zvezda)` | `zvezda` | +| ПАПКА | `(Несчастный Случай)` | `Несчастный Случай` | +| ПАПКА | `(Блатные)` | `Блатные` | +| ПАПКА | `(Из Сказок)` | `Из Сказок` | +| ПАПКА | `(Морские)` | `Морские` | +| ПАПКА | `(Высоцкий - Разное)` | `Разное` | +| ПАПКА | `(Спортивные)` | `Спортивные` | +| ПАПКА | `(Философские)` | `Философские` | +| ПАПКА | `(Вилли Токарев)` | `Вилли Токарев` | +| ТРЕК | `(2001) Infected Mushroom - Wider (Domestic RMX)` | `Wider (Domestic RMX)` | +| ТРЕК | `(2004) Infected Mushroom - Shakawkaw (Vibe Tribe Rmx)` | `Shakawkaw (Vibe Tribe Rmx)` | +| ТРЕК | `(2009) Infected Mushroom - Bust a Move (Infected Remix)` | `Bust a Move (Infected Remix)` | + +`distinct paths` в этих строках — **4** (три IM-файла + пустой path у ПАПКА-строк). + +> 🔴 **ПРАВИЛО ИЗЛОЖЕНИЯ (мой дефект, не дефект данных):** я произнёс «306» и «дифф» +> **в одном ответе**, хотя 306 — счётный файл `ALBUM-FIX-306.csv` (построчный список путей), +> а дифф схлопнул те же 306 в **15 записей**. Alex прочитал это как подмену числа. +> **Отвечая про дифф — называть ТОЛЬКО числа диффа (2854 / 2768 / 164).** +> Числа из `ALBUM-FIX-*.csv` давать отдельным абзацем с явной пометкой «это счётный файл, +> не дифф». Никогда не склеивать два артефакта в одну фразу. + ### 🧾 Список удаления скобок — ГДЕ ЛЕЖИТ (ответ на «Где список с удалением скобок») Alex: «**Где список который ты готовил с удалением скобок**» diff --git a/personal/tech/id3-tag-repair.md b/personal/tech/id3-tag-repair.md index 6ddc5da5..0d599ba3 100755 --- a/personal/tech/id3-tag-repair.md +++ b/personal/tech/id3-tag-repair.md @@ -22,6 +22,7 @@ related: > Зеркало скилла `id3-tag-repair` (категория `media`, `~/.hermes/hermes-whale/skills/media/id3-tag-repair/`). > Скилл — для исполнения, этот док — для контекста проекта и истории. +> **Текущая версия скилла — v2.8.0** (правила D1–D35, W1–W30, A1–A8, G1–G5). > > ⚠️ **Скилл обновлён до v1.4.0** — добавлены правила **D1–D6** (слой диффа планов, > раздел «🔵 Слой ДИФФА планов» ниже). Полные формулировки — в `SKILL.md` скилла. @@ -80,6 +81,22 @@ related: > весь — из-за ПОЯВЛЕНИЯ колонки, `old_38='<КОЛОНКИ НЕ БЫЛО>'`, ср. D3). Инвентарь > актуального диффа — `personal/plans/music-artist-albumartist-repair.md` §СЕССИЯ 18-ДОП. +> ⚠️ **Обновлён до v2.5.0 (сессия 18-доп, вечер):** **D32** — 🔴 **счёт ФАЙЛОВ: дифф завышает, +> правда в ПЛАНЕ.** Развёртка ПАПКА-строки в файлы папки дала `7963` по `genre`; верное число +> `4359` (по `APPLY-PLAN.csv`: `cur_X != new_X`). Причина — файлы с уже правильным тегом тоже +> попадают в развёртку (3393 фантома). Плюс **D33** — отвечая «где список, который ты готовил», +> всегда давать путь + md5 + число строк + пометку ПРОМЕЖУТОЧНЫЕ/ФИНАЛЬНЫЕ значения +> (`ALBUM-FIX-TABLE.csv` промежуточный vs `ALBUM-FIX-306.csv` финальный). +> Разбивка диффа по 633 папкам — §СЕССИЯ 18-ДОП плана. +> +> ⚠️ **Обновлён до v2.6.0 (сессия 18-доп, вечер-2):** **D34** — 🔴 **«откуда взялось поле?» → +> сравнить ЗАГОЛОВКИ обеих версий плана.** В `tag-fix-plan.csv` (волна 38) колонки `genre` +> **не было вообще** → все `4359` по `genre` = первая запись тега (прямое продолжение D3). +> Генератора `new_genre` на диске нет. **D35** — 🔴 **не называть в одном ответе числа двух +> разных артефактов**: «306» (файл `ALBUM-FIX-306.csv`) и дифф — разные артефакты; в диффе +> `306` отсутствует (0 ячеек), те же строки схлопнуты в **15 записей** по D16. +> Мой дефект был в **изложении** — Alex прочитал как подмену числа. + ## 📁 Артефакты проекта **Путь:** `~/Developer/personal-os/tools/music-tags/` @@ -359,6 +376,125 @@ md5 out.csv && python3 gen.py > /tmp/run.log 2>&1 && md5 out.csv # MD5 обя **Скрипт:** `~/Downloads/music-tags/fix_junk_album.py` (dry-run по умолчанию, `--apply` пишет). **Верификация:** `verify_junk_album.py` — перечитывает файл, сверяет контрольные точки. +### 🔴 D32 — счёт ФАЙЛОВ: дифф завышает, правда — в ПЛАНЕ (сессия 18-доп) + +На вопрос «сколько файлов реально меняет `<поле>`» я развернул каждую ПАПКА-строку диффа +в файлы папки и сложил → получил `7963` по `genre`. Alex: «**Чё за нахуй?! В смысле блядь 7963?!!!**» +— и он прав. **Правильное число `4359`.** + +``` +по диффу (НЕВЕРНО) по плану (ВЕРНО) +911 genre-строк 7861 строк всего + 700 ПАПКА -> 7752 файла для каждой строки: cur_X != new_X ? + 211 ТРЕК -> 211 файлов genre 4359 albumartist 7213 album 2003 + объединение = 7963 artist 381 track 344 title 8 + ANY field 7651 +``` + +**Почему дифф завышает:** ПАПКА-строка означает «значение в папке однородно И отличается от +38-го прогона». Это **не** значит, что у каждого файла папки значение отличается от целевого — +файлы, у которых тег **уже равен** целевому, тоже попадают в развёртку. **3393 файла-фантома.** + +🔴 **Правило: две разные базы счёта, не смешивать.** + +| Вопрос | Считать по | Предикат | +|---|---|---| +| «что изменилось против прошлого прогона?» | **диффу** | строки диффа | +| «сколько файлов реально меняется?» | **`APPLY-PLAN.csv`** | `cur_X != new_X` построчно | + +Та же семья, что D8 (`changed` ≠ изменение на диске) и D24 (`new == cur` = pass-through). +**Никогда не разворачивать ПАПКА-строку в список файлов папки и не называть сумму «файлов меняется».** + +**Форма отчёта по папкам (что сработало):** на каждую папку +`правок · файлов в папке · файлов меняется · счётчики по полям`, по алфавиту; корневые папки +считать отдельно от вложенных. Архив — `/tmp/folders_report.txt` (633 папки: **6 корневых** — +`!Pop, Club, Любэ`, `!Retro`, `!Детские`, `!Классика`, `!Рэп`, `z Другое, Enigma, Ленинград` — +и **627 вложенных**). Важно: у `!Rock&Folk` и `!Барды` **нет** корневых строк — они затронуты +только через вложенные папки. + +### 🔴 D33 — «где список, который ты готовил?» → путь + md5 + какой из них ФИНАЛЬНЫЙ + +Alex спрашивал про список снятия скобок дважды; оба раза ответ должен быть **фактом о файле**, +а не описанием. Существовали два артефакта, и было неочевидно, какой главный: + +| Файл | md5 | Строк | Роль | +|---|---|---:|---| +| `~/Downloads/music-tags/ALBUM-FIX-TABLE.csv` | `1597f2716ac5a2881efabaec13755d4c` | **306** | механическое снятие скобок (`folder,cur_album,new_album,path,title`) | +| `~/Downloads/music-tags/ALBUM-FIX-306.csv` | `49ba645462964cc299da7dcffb55ca09` | **306** | + `new_album_was` / `new_album_now` — несёт ПОЗДНЕЙШЕЕ решение | + +**Промежуточный список — ловушка.** В `ALBUM-FIX-TABLE.csv` стоит `(Авария)` → `Авария`, +но ФИНАЛЬНОЕ решение (D27) — `Москва пьёт пиво` / `Дискотека Авария`. Механическое снятие скобок +**неверно** везде, где скобочный мусор лежал ПОВЕРХ уже правильного имени подпапки. + +🔴 **Правило: отвечая «где список», всегда указывать (1) путь, (2) md5 + число строк, +(3) значения ПРОМЕЖУТОЧНЫЕ или ФИНАЛЬНЫЕ — и указывать файл с колонками `was`/`now`, +если значения позже уточнялись.** Таблицу из 306 строк, где 17 пар сводятся к 9 финальным +значениям, нельзя утверждать по одной только форме. + +**Факт для проверки:** `cur_album` начинается с `(` — 306 из 306; значение целиком в скобках — +306 из 306; уникальных путей 306; уникальных пар «папка+было→стало» — 17. +Скрипт: `/tmp/verify_alb_fix.py`. + +### 🔴 D34 — «откуда взялось это поле?» → сравнить ЗАГОЛОВКИ обеих версий плана + +Alex: «**Откуда там правка жанра то??**». Ответ оказался одним фактом: **в волне 38 колонки +`genre` не существовало вообще.** + +``` +tag-fix-plan.csv (волна 38), 11 колонок: + path, current_verdict, cur_title, cur_artist, cur_album, + new_title, new_artist, new_album, new_track, action, source + ↑ genre ОТСУТСТВУЕТ + +APPLY-PLAN.csv (текущий), 16 колонок: + ... cur_genre, ... new_genre, new_track, changed ← genre ПОЯВИЛСЯ +``` + +**Следствие (прямое продолжение D3):** жанр не писался ни разу. Любое непустое `new_genre` = +**первая запись тега** → отсюда весь объём `4359`. + +**Состав — 137 пар `cur_genre → new_genre`:** заполнение пустого (1136 `''`→`Rock`, 439 `''`→`Pop`, +322 `''`→`Детские`, 252 `''`→`Бардовская песня`, 237 `''`→`Сказки`), нормализация (`Рок`→`Rock` 182, +`Punk Rock`→`Punk` 82, `Metal`→`Heavy Metal` 52, `Поп`→`Pop` 16), омоглифы (`Psy Årance`→`Psy-Trance` 41, +`Psy Еrance`→`Psy-Trance` 30 — ср. G2), и **поле прочитано неверно** (`(17)`→`Rock` на 32 файлах ДДТ, +`Разное`→`Бардовская песня` на 22 файлах Высоцкого — ср. G3). + +🔴 **Генератора `new_genre` на диске НЕТ.** `~/Downloads/music-tags/fix_apply_plan.py` содержит +**одну** genre-правку (`яюElectronic`→`Electronic` в `JUNK_FIX`) + пересчёт `changed`. Скрипт +правит готовый план, а не строит его. Аудитор жанра — `rc_gen_check.py`, `fix_klassika.py`, +`fix_radio_format.py` (точечные). + +> 🔑 **МЕТОД-УРОК:** на вопрос «откуда правка в поле X» **первым делом сравнивать заголовки** +> обеих версий плана. Отсутствие колонки объясняет весь объём одним фактом и не требует +> рассуждений о логике заполнения. Дешёвая проверка, снимающая целый класс споров. + +### 🔴 D35 — не называть в одном ответе числа ДВУХ РАЗНЫХ артефактов + +Alex: «**Ты только что блядь сказал что в диффе 306 сука файлов! Какие нахуй 4359?!**» + +**Он прав — 306 в диффе не существует.** Проверено literal-поиском по всем ячейкам +(`/tmp/find_306.py`): + +``` +DIFF-new-vs-38-v2.csv + строк всего 2854 + album-строк 164 + из них old_38 в скобках 15 (12 ПАПКА + 3 ТРЕК) + ячеек, содержащих "306" 0 +``` + +Те же самые 306 файлов дифф держит как **15 записей** — по схлопыванию **D16** (одна запись +папки = N файлов). Разница `306 → 15` — это схлопывание, **не превращение одного числа в другое**. + +🔴 **Мой дефект был в изложении, не в данных:** я сказал «306» и «дифф» в **одном ответе**, +хотя `306` — из счётного файла `ALBUM-FIX-306.csv` (построчные пути), а дифф — отдельный артефакт. +Alex прочитал это как подмену числа, и повторение цифры только усугубляло (см. профиль: повтор +цифры = «не услышал»). + +**Правило:** отвечая про артефакт A, называть **только** числа A. Числа артефакта B давать +отдельным абзацем с явной пометкой («это счётный файл, не дифф»). Никогда не склеивать два +артефакта в одну фразу — читатель свяжет числа, которых вместе не существует. + ## 🔴 Классы порчи `albumartist` (сессия 7, 2026-09-27) Три класса, найденных сплошным поиском по `APPLY-PLAN.csv`. Все три — из-за того, что правило