[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/tech/id3-tag-repair.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 18:07:04 +06:00
1 parent 23b4acc8cc
commit 23a5da004b
2 files changed
+245 -3

No files matched your search

@@ -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: «**Где список который ты готовил с удалением скобок**»
+136
View File
@@ -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`. Все три — из-за того, что правило