[
 {
  "id": "T01",
  "level": "macro",
  "section": "Песня и текст",
  "rule": "Держите число спетых слогов адаптации в пределах ±5 % от числа спетых слогов оригинала, считая гласные в том performance-тексте, который gen2 реально отправляет, и превышайте потолок только по версионированному director-решению.",
  "why": "Все три версии поются с одной скоростью (191–205 слог/мин), поэтому лишние слоги превращаются в хронометраж 1:1: оригинал 645 слогов → вокал до 188,56 с, Taras 620 → 193,94 с, наш спетый текст 762–770 → 239,4 с (+45 с, ratio 1,18), а скилл запрещает обязательный процент расширения, но не ставит потолка.",
  "evidence": [
   "measurements/summary.json: syllables 645/620/762, syllablesPerMin 205.2/191.8/191.0, sungSpan end 188.56/193.94/239.4; film.sungTextSyllables 770, proseScriptSyllables 847",
   "measurements/container-gates-film-vs-targ.json B01 (645→762, ratio 1.181, fail) и B02 (188.56→239.4, ratio 1.27, fail)",
   "lyrics/lyrics-alignment.md §3 (итоги по секциям); lyrics/lyrics-songcraft.md §1 («length is a text-volume problem»)",
   "scripts/gen2.py:64 duration = int(os.environ.get('DUR','240')); .agents/skills/song-clone-ua/SKILL.md L238-240 «нет обязательного процента расширения»"
  ],
  "placement": [
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2 «Бюджет строки» (L238-240) → глобальный потолок ±5 % от спетых слогов оригинала рядом с запретом обязательного расширения",
   "tools/song_budget.py (новый) → вызов из scripts/gen2.py song_fields() (L55) на файле LYRICS → fail выше +5 % без SONG_BUDGET_DECISION",
   "references/adapt-phase-contract.md → шаг 4 «Песенный текст», колонка проверки (L27) → строка «слоги спетого текста vs оригинал, ±5 %»",
   "AGENTS.md → правило 18 → одна фраза: бюджет и часы задаёт оригинальная песня"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T02",
  "level": "macro",
  "section": "Песня и текст",
  "rule": "Не тратьте в секции больше 1,15× слогов её оригинала, возвращайте перерасход сокращением в другой секции так, чтобы дрейф к CTA вернулся к ≤3 % хронометража, и сохраняйте профиль плотности оригинала — его самая быстрая секция остаётся самой быстрой.",
  "why": "Taras занимает +18,2 с на сцене скана и возвращает их (финал +5,4 с), а наш дрейф растёт монотонно до +50,9 с, причём самую быструю секцию оригинала (коллапс, 4,24 слог/с) мы растянули сильнее всего (1,62×), сведя плотность к плоским 2,99–3,44 слог/с.",
  "evidence": [
   "lyrics/lyrics-alignment.md §6 (drift): Taras 0/+5.2/…/+18.2 на карточке 23/…/+5.4; наш +2.5/+5.1/+12.4/+21.8/+31.6/+40.6/+50.9",
   "lyrics/lyrics-songcraft.md §1 (секции): наставница 118 слог/38.2 с · 131/34.9 · 149/49.2; результаты 88/27.2 · 86/28.0 · 127/37.0",
   "measurements/phase1-digest.md L209 (коллапс оригинала 4.24 слог/с против наших плоских 2.99-3.44); film/asr.json 28.4-35.5 — карточка 10 (targ 24.7-31.0, 4.6 с) стала тремя фразами 7.1 с",
   "song-v02/musical-phrasing-plan-v08.json densityAndDuration (intentSeconds 230, reviewPlanningBandSeconds [220,245] — без исходных величин)"
  ],
  "placement": [
   "song-v02/musical-phrasing-plan-vNN.json → structuralDevelopment[] → поля sourceSyllables/sourceSeconds/newSyllables/plannedSeconds/sourceSylPerSec/plannedSylPerSec/offsettingCut на строку",
   "tools/song_budget.py → --sections → fail секции > 1.15× без названного offsettingCut и WARN, если дрейф не вернулся к CTA",
   "references/adapt-phase-contract.md → шаг 4 и шаг 7 «song→visual timing map» → обязательная кривая дрейфа с возвратом к часам оригинала",
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2 «Длина против смысла» (L253-259) → «спринт остаётся спринтом»"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T03",
  "level": "macro",
  "section": "Песня и текст",
  "rule": "Пишите строку как одну музыкальную фразу на одном дыхании и держите общее число спетых фраз в пределах 0,55–0,70 от числа дыхательных фраз оригинала, сливая соседние карточки вместо привязки строки к sourceCaptionId.",
  "why": "У оригинала 75 дыхательных фраз, у Taras 43 (0,57×, медиана 14 слогов) со слияниями 1+2, 9-11, 53-55, а у нас 65 строк по карточкам развернулись в 98 запланированных фраз, из-за чего карточка 10 (4,6 с) поётся тремя фразами 28,4–35,5 с.",
  "evidence": [
   "measurements/summary.json asrPhrases 75/43/70; song-v02/lyrics-phrases-v06.json — 98 запланированных фраз при 65 строках",
   "lyrics/lyrics-alignment.md §7 (слияния 1+2, 9-11, 15+16, 42-44, 53-55, 59-61, 63+64)",
   "film/asr.json 28.4-30.5 / 30.5-32.8 / 32.8-35.5 против targ/asr.json 24.7-31.0",
   "tools/targ_film_prepare.py:94 'captionAnchors': h['actualSongLineAnchors'] — всё ключуется к карточке 1:1"
  ],
  "placement": [
   ".agents/skills/song-clone-ua/SKILL.md → «Что считать строкой» (L176-177) и Стадия 2 (L248-251) → целевое число фраз = дыхательные фразы оригинала × 0.55-0.70 с записанным выводом",
   "references/adapt-phase-contract.md → шаг 4 output lyrics_uk.txt → одна строка = одно дыхание, не одна карточка",
   "tools/targ_film_prepare.py → captionAnchors (L94) → потреблять спетые фразы, а не sourceCaptionId",
   "tools/song_budget.py → --phrases → WARN на фразы < 8 и > 18 слогов"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T04",
  "level": "macro",
  "section": "Песня и текст",
  "rule": "Покрывайте функции оригинала (речевой акт каждого бита), а не все его мысли: каждая защищённая функция-разворот получает свою фразу с тем же речевым актом в бюджете source × 1,15, а текстуру резать разрешено.",
  "why": "Правило 18 «сохранить полную продающую логику» исполнено как 65/65 замапленных мыслей с нулём сокращений и добавленным продуктовым текстом, тогда как Taras выбрасывает 4 карточки и сжимает 20, но держит все 17 функций, включая оба императива («зупинись, усе це робить тільки гірше» 114,2–117,7 и «Решту викинь» 125,4–127,2), которых у нас нет вовсе.",
  "evidence": [
   "runs/difference-dna-v1/lyrics/lyrics-alignment.md: наш баланс 41 дословно / 14 перефразировано / 9 расширено / 1 добавлено / 0 выброшено против Taras 38/20/2/4",
   "coverage-map-v06.json coverage.allThoughtsMapped (полнота 65/65 как единственный критерий)",
   "targ/asr.json 105.1-108.0 «stop, all of that is making it worse» и 116.4-118.8 «Throw the rest aside»; taras/asr.json 114.2-117.7, 125.4-127.2; film/asr.json 132.2-136.7 и 146.2-150.8 — оба бита отсутствуют",
   "lyrics/lyrics-advertorial.md §2 (все 17 функций есть во всех трёх версиях); lyrics/lyrics-songcraft.md §2.9"
  ],
  "placement": [
   "runs/<run>/adaptation-map-vNN.json → functions[] (id, sourceCards, speechAct, protected, budgetSyl = source × 1.15) + textureCuts[] → замена coverage.allThoughtsMapped на allFunctionsMapped + protectedFunctionsIntact",
   "references/adapt-phase-contract.md → шаг 3 validation «все исходные функции покрыты» → таблица функций вместо списка мыслей",
   "AGENTS.md → правило 18 / references/owner-generation-rules.md L48-61 → одна фраза: покрытие функций, не слов",
   "tools/song_budget.py → --functions → fail при потере защищённой функции"
  ],
  "statusKind": "conflict",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T05",
  "level": "micro",
  "section": "Песня и текст",
  "rule": "Сохраняйте столько же числовых якорей, сколько в оригинале, словами-числительными и без замены на «кілька», и не давайте собственных имён ролям, которых оригинал не называет.",
  "why": "Оригинал несёт 12 числовых якорей, Taras 11 («Через три тижні» 131,7; «На шостому тижні» 144,5; «десять років без тонального» 150,7), а у нас 11 токенов, из которых два — «кілька» (155,5 и 194,1): таймлайн результатов исчез, зато добавлено имя «Ірина» (105,4–108,7) там, где и оригинал, и Taras оставляют наставницу безымянной.",
  "evidence": [
   "film/asr.json 155.5-157.6 «Минуло кілька тижнів», 194.1-198.6 «через кілька місць», 101.6-108.7 «Там Ірина»; taras/asr.json 131.7-136.4, 144.5-148.9, 150.7-155.2, 100.1-104.6 (безымянная «вона»)",
   "lyrics/lyrics-songcraft.md §2.6 и §2.11; lyrics/lyrics-alignment.md карточки 28, 40, 45, 47-48, 51",
   "tools/song_stress.py L36 tokens() отбрасывает цифры; scripts/lines_check.py L17-21 norm() знает только 16/34/60",
   "runs/<run>/localization-v01.json «Original Korean mentor → mature Ukrainian peer Iryna55»"
  ],
  "placement": [
   "references/adapt-phase-contract.md → шаг 3 adaptation-map → numericAnchors[] (source number, preserve/replace, reason, product-truth fact ID) и таблица ролей с полем namedInSource",
   "tools/song_budget.py → --numbers --names → fail на «кілька» там, где карта содержит число, и на спетое имя роли с namedInSource=false",
   "references/localization-and-resolution-rules.md → region gate (L13-21) → фраза: именование не является локализацией",
   "scripts/lines_check.py → norm() (L17-21) → расширить карту чисел"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T06",
  "level": "micro",
  "section": "Песня и текст",
  "rule": "Повторяйте опорные слова и коллбэки оригинала одним и тем же украинским словом не менее чем в N−1 из N вхождений и держите число продуктовых токенов в спетом тексте не выше числа сюжетных мотивов.",
  "why": "Taras держит 5 из 7 движков повтора («Потім» ×4 на 16,7/48,1/66,4/155,2, «важить» ×2 на 178,0–188,7) при ≈6 продуктовых токенах против ≈16 сюжетных, а у нас «Потім» ноль, параллель «її думка / моя нарешті» сломана, и продуктовых токенов ≈20 («шия» ×10, «догляд» ×4, «крем» ×5, «go pure» ×2) против ≈8 сюжетных.",
  "evidence": [
   "taras/asr.json: «Потім» на 16.7, 48.1, 66.4, 155.2; «сказала» ×4; 178.0-184.0 / 184.0-188.7 «важить» ×2",
   "film/asr.json: «Потім» 0, «сказала» 1 (217.9); 217.9-226.7 «Її думка — не головна / Бо нарешті я ціную те, що думаю сама»",
   "song-v02/stress/lyrics-with-stress-v04.txt (пересчёт): шия-формы 10, догляд 4, крем 5, go pure 2",
   "lyrics/lyrics-songcraft.md §2.8 (таблица мотивов)"
  ],
  "placement": [
   "references/adapt-phase-contract.md → шаг 3 adaptation-map → motifs[] (source word, occurrences, positions, adapted word, требование ≥ N−1 одинаковых)",
   "tools/song_budget.py → --motifs → отчёт по каждому мотиву и ratio product/story, fail если продуктовых токенов больше сюжетных",
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2 «Рифма, созвучия и повторы» (L251) → сделать проверяемым пунктом"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T07",
  "level": "micro",
  "section": "Песня и текст",
  "rule": "Ведите повествование в простом прошедшем времени и бытовой лексике регистра «жінка розповідає подрузі», держа на 10 фраз не более 2 тире, 1 пары кавычек и 0 двоеточий перед прямой речью, как в карточках оригинала без знаков.",
  "why": "У нас 21 тире, 8 «» и 8 двоеточий на 98 фраз плюс настоящее время в нарративе («Стою, дивлюся на крем» 48,9; «Кажу, креми для обличчя» 123,4), тогда как Taras идёт с 0 тире и 0 кавычек в прошедшем времени («Я стояла там у суботу» 35,4; «Спитала, а ти чим?» 108,3), а печатно-рекламные слова стоят ровно там, где оригинал говорит бытово («Я пішла до авто» 80,7 против «Я дійшла до машини і просто сиділа» 79,4).",
  "evidence": [
   "song-v02/stress/lyrics-with-stress-v04.txt (пересчёт): 21 тире, 8 «, 8 двоеточий на 98 фраз; Taras 0/0/0",
   "film/asr.json 35.5-38.8 «Була дітям опорою», 48.9-50.8, 75.0-80.7, 80.7-87.2, 123.4-126.2, 150.8-155.5",
   "taras/asr.json 35.4-40.9, 72.2-79.4, 79.4-83.6, 108.3-111.5, 127.2-131.7",
   "lyrics/lyrics-songcraft.md §2.4 (регистр) и §2.5 (время, пунктуация)"
  ],
  "placement": [
   "references/song-register-uk.md (новый, рядом с references/pronunciation-traps.md) → список запрещённых печатно-рекламных слов с заменами → расширяется по продукту",
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2, абзац рядом с «Ловушки произношения» (L275-276) → «Ловушки регистра»",
   "tools/song_budget.py → --register --punctuation → WARN на настоящее время вне «» и без хабитуального маркера, fail выше бюджета знаков",
   "references/adapt-phase-contract.md → шаг 4 validation → строка про регистр и пунктуацию"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T08",
  "level": "micro",
  "section": "Песня и текст",
  "rule": "Открывайте фразу ударным слогом не позже второго (не более одного затакта, ≤10 % фраз), заканчивайте её знаменательным словом с предпоследним или предпредпоследним ударением и убирайте клитические кластеры («й»+согласный, «та ж», «із с-») и длинные слова с конца строки.",
  "why": "10 из наших 98 фраз открываются двумя и более безударными затактами («У суботу зайшла», «Результат у кожної свій»), окончания masc/fem/dact у нас 16/42/7 против 9/23/11 у Taras, который открывает на Я/Вона/Потім и держит длинные слова в середине фразы («називається» 100,1–104,6), а наши слушательные аудиты слышали «но́ву» вместо «нову́».",
  "evidence": [
   "song-v02/stress/lyrics-with-stress-v04.txt: «У суботу зайшла до крамниці.», «Із сусіднього ряду — голос.», «Результат у кожної свій.», «Посилання внизу.»",
   "lyrics/lyrics-songcraft.md §1 (фразовые окончания 9/23/11 против 16/42/7) и §2.1-2.2",
   "film/asr.json 53.5-56.0 и 127.0-132.2 («перепробувала» в конце строки дважды), 117.7-123.2 «користуєшся», 211.4-215.5 «Та ж жінка», 233.7-236.2 «ім'я й телефон лиши»",
   "taras/asr.json 100.1-104.6, 125.4-127.2 («Решту викинь»); references/ukrainian-stress-contract.md L30, L69-73 (клитики)"
  ],
  "placement": [
   "tools/song_stress.py → validate() (парсер акутов, L36 tokens; классификация клитик L102-107) → возвращать warnings[]: затакты ≥ 2, длинное финальное слово, кластеры",
   "scripts/gen2.py → stress_fields() (L101-108) → записывать WARN в <tag>-stress-request-audit.json; нерешённые WARN требуют решения до покупки",
   "references/ukrainian-stress-contract.md → «Stress changes the composition» (L41-51) → breath boundary становится обязательным полем каждой фразы",
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2 чеклист → тест затакта и конца строки"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T09",
  "level": "macro",
  "section": "Песня и текст",
  "rule": "Пойте CTA одним вдохом в бюджете CTA оригинала (не более +10 % слогов и не дольше по секундам), одним предложением с одной ссылкой, а инструкции формы и оговорки уводите в экранный текст.",
  "why": "Оригинал поёт CTA 15 слогами за 5,9–7,0 с (182,6–188,6), Taras — 14 слогами за 4,0 с (189,9–193,9), а мы 40–42 слога за 12,7 с (226,7–239,4), причём внутрь мелодии попали дисклеймер «Результат у кожної свій» и инструкция лид-формы «У формі — ім'я й телефон лиши».",
  "evidence": [
   "targ/asr.json 181.6-188.6 (ledgers/targ-seg4.json targ-S42, слова 182.64-188.56); taras/asr.json 189.9-193.9; film/asr.json 226.7-230.0 / 230.0-233.7 / 233.7-236.2 / 236.2-239.4",
   "measurements/container-gates-film-vs-targ.json B13 (15→40 слогов, fail) и B14 (7.0→12.68 с, fail)",
   "lyrics/lyrics-alignment.md строки 63-65 (слоги O/T/U 6/8/15, 4/6/16, 4/0/11); lyrics/lyrics-advertorial.md §6",
   "ledgers/film-seg4.json film-S36 226.57-235.2 (указание на банку) и film-S37 235.2-239.7"
  ],
  "placement": [
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2 «Бюджет строки» → бюджет CTA = CTA оригинала × 1.1 и ≤ его секунд, одно предложение",
   "references/adapt-phase-contract.md → шаг 4 «слышимый CTA» → «CTA ≤ слогов оригинала +10 %, один вдох»",
   "tools/song_budget.py → --cta → fail при превышении слогов/секунд и при фразе с placement ≠ sung в спетом тексте",
   "tools/targ_film_build.py → блок энд-карты (L338-340) → инструкция формы и оговорка рендерятся как экранный текст"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "songwriter",
   "passport",
   "harness",
   "editor"
  ]
 },
 {
  "id": "T10",
  "level": "macro",
  "section": "Музыка и подача",
  "rule": "Просите у генератора контейнер оригинала (клок фраз, регистр подачи, дуга энергии) и делайте совпадение тональности и темпа совещательным критерием под версионированным director-решением, сохраняя обязательную загрузку своего оригинала.",
  "why": "Любимый владельцем Taras — не музыкальный клон (A minor против F major, лучшая транспозиция 10 полутонов, chroma-DTW 0,0771 при reversed-базе 0,0775, темп 152/76 против 103,4), а наш настоящий клон (транспозиция 0, DTW 0,0518) даёт самую плоскую подачу из трёх; critic_music держит tempo и intro_mode в KEY и забраковал бы песню класса Taras, поэтому смена требует владельческого решения по правилу 16 (song-clone-ua как обязательный клон-скилл) при сохранении правил 2 и 16 о загрузке своего оригинала.",
  "evidence": [
   "measurements/music-similarity.json: taras chromaDtwCostVsOriginal 0.0771 при reversed baseline 0.0775, bestTransposition {10, 0.0687}; film 0.0518 / 0.0572 / {0, 0.0518}",
   "measurements/summary.json: keyTop F major 0.78 / A minor 0.83 / C-F major; tempoLibrosa 103.4/152/103.4; taras tempoCandidates 152/78.3/103.4",
   "scripts/critic_music.py L23-24 (KEY = timbre, cohere, intro_mode, intro_start), L199-202 (tempo = 1−|Δbpm|/6)",
   "song-v02/jobs/targ_r2_v6_cover03-request.json (audioWeight 0.85, styleWeight 0.65, weirdness 0.2); song-v02/style-v04.txt «Closely follow uploaded original timbre»"
  ],
  "placement": [
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 3 STYLE-шаблон (L278-293) → вместо жёстких «<BPM> bpm, <key>» — «phrase clock of the original, spoken-sung storytelling, groove по решению», AW/SW/WC с письменной причиной",
   "scripts/critic_music.py → KEY (L24) → вывести tempo/intro_mode из KEY, когда director-decision-cover-vNN.json фиксирует осознанную смену грува",
   "generation_contracts.md → шаг 6 (L44) «жанр, подача» → «фразовый ритм, подача, дуга энергии»",
   "references/adapt-phase-contract.md → «Музыкальная проверка» (L67-71) → tempo/key с неопределённостью, метрики контейнера становятся гейтами"
  ],
  "statusKind": "conflict",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T11",
  "level": "macro",
  "section": "Музыка и подача",
  "rule": "Просите в STYLE разговорно-спетое повествование и принимайте дубль по числам оригинала: диапазон P5–P95 не ниже оригинала минус 1 полутон, std громкости вокала не ниже оригинала, доля кадров «в ноте» не выше оригинала плюс 0,03.",
  "why": "Оригинал даёт 0,799 on-note при 14,1 полутона и 5,89 dB разброса, Taras 0,73 / 15,1 / 6,57, а наш — 0,823 / 12,5 / 5,63, то есть самый «спетый» и самый плоский из трёх, потому что STYLE прямо просил «expressive rising/falling melody … never flat spoken prose».",
  "evidence": [
   "measurements/summary.json: vocalOnNoteFraction 0.799/0.73/0.823; rangeP5P95Semitones 14.1/15.1/12.5; vocalRmsDbStd 5.89/6.57/5.63; noteP5P50P95 G3-E4-A4 / A3-C♯4-C5 / A3-D♯4-A4",
   "song-v02/style-v04.txt («expressive rising/falling melody … no spoken prose»)",
   "references/owner-generation-rules.md — раздел advertorial-to-song (подача рассказчицы)"
  ],
  "placement": [
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 3 STYLE-шаблон → «conversational sung storytelling; whisper-level low point in the car, lift at the mirror»",
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 4 таблица приёмки (L299-323) → три числа подачи из audio-dna.json рядом с LUFS/BPM",
   "scripts/critic_music.py → новые совещательные оси (не KEY) → range, rms_std, on_note против оригинала",
   "references/adapt-phase-contract.md → «Музыкальная проверка» → допуски диапазона и подачи относительно оригинала"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T12",
  "level": "macro",
  "section": "Музыка и подача",
  "rule": "Закладывайте в текст и в мастер не меньше воздуха, чем в оригинале (не менее трёх пауз ≥1 с, суммарный отдых не ниже оригинала, доля озвученных кадров не выше оригинала плюс 0,03), и отдавайте каждую паузу бессловесному удержанному кадру с выключенным титром.",
  "why": "Taras отдыхает 19,9 с (пять пауз >1 с на 21,14 / 58,32 / 64,52 / 148,92 / 188,74) и вешает на них свои самые запоминающиеся кадры — пустой дверной проём S07 19,5–22,75, ECU-скан S17 56,25–60,65, удивление→улыбка S35 147–152,5, — оригинал отдыхает 11,1 с, а у нас ноль пауз >1 с, 8,7 с отдыха и 42,8 с непрерывного вокала (81–124 с), так что монтажу физически негде дышать.",
  "evidence": [
   "measurements/summary.json gapsOver1s 1 (91.88) / 5 (21.14, 58.32, 64.52, 148.92, 188.74) / 0; voicedFrameFraction 0.708/0.577/0.787; sungFraction 0.95/0.948/0.994",
   "measurements/container-gates-film-vs-targ.json B04 (паузы ≥1 с, fail) и B05 (самый длинный кусок без паузы, fail)",
   "lyrics/lyrics-songcraft.md §1 (отдых 16/11.1 с · 28/19.9 с · 18/8.7 с) и §2.3",
   "ledgers/taras-seg1.json taras-S07 16.8-22.75 (пустой проём); ledgers/taras-seg2.json taras-S17 56.25-60.65; ledgers/taras-seg4.json taras-S35 147.17-152.33; film — единственная дыра титров 98.9-100.0",
   "song-v02/style-v04.txt «connected breath, no spoken prose»"
  ],
  "placement": [
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2 musical-phrasing-plan → rests[] (source time, beat, reason) + пустая строка/ремарка в lyrics_uk.txt; Стадия 3 STYLE → «leave instrumental breaths between phrases»; Стадия 4 приёмка → замеренные паузы стема",
   "scripts/critic_music.py → совещательные оси rest_gaps и voiced_fraction (не KEY) → сравнение с оригиналом",
   "references/adapt-phase-contract.md → шаг 7 validation → паузы ≥1 с не меньше, чем у оригинала",
   "public-targ-film/evidence/shot-plan.json → restBeats[] → tools/targ_film_prepare.py делает из них бессловесные вставки, tools/targ_film_build.py caption loop (L333-336) не ставит карточку на паузе"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "songwriter",
   "harness",
   "editor"
  ]
 },
 {
  "id": "T13",
  "level": "macro",
  "section": "Монтаж и кадр",
  "rule": "Держите stretchRatio каждой единицы (песенные секунды / секунды исходного плана) не выше 1,25 и суммарный retained/source не выше 1,05: излишек песни тратьте на новую постановку, установочные планы либо укладывайте в тот же допуск, либо выбрасывайте целиком, а экспорт сверяйте с гистограммой оригинала (медиана ≤ ×1,15, планов ≥8 с не больше, планов ≤2 с не меньше 0,7 доли).",
  "why": "Taras держит ритм оригинала при равной длине (медиана 4,17 против 4,3 с, 2 плана ≥8 с против 5), а наши +45 с песни ушли в удержания: медиана 4,83–5,97 с, 8–13 планов ≥8 с, максимум 13,3 с, и 11 из 43 единиц растянуты ≥1,5× (targ-s012 2,36×, targ-s020 2,15×), причём targ-S13 4,07 с → film-S13 6,5 с и targ-S20 3,93 с → film-S18 8,0 с («drags»), тогда как Taras эти же установочные планы просто выбрасывает.",
  "evidence": [
   "measurements/summary.json medianShotSec 4.48/4.17/5.97, p90 8.767/7.083/10.9; measurements/container-budget-*.json shotsOver8s 5/2/8, shotsUnder2s 7/4/1",
   "measurements/container-gates-film-vs-targ.json B06 (медиана, pass) и B07 (планы >8 с, fail)",
   "ledgers (пересчёт по времени): film S16 12.0, S25 11.8, S28 10.4, S27 10.3, S22 9.9; targ S18 10.1, S22 8.8, S29 9.4, S32 8.1; taras S30 8.5, S34 8.0, S38 8.3",
   "film-handoff-v02.json (пересчёт stretchRatio): targ-s012 2.36, s020 2.15, s004 2.08, s028 2.00; targ-S12 31.0-33.0 → film-S12 37.0-41.5; targ-S13 33.0-37.07 → film-S13 41.5-48.0; taras выбрасывает все три (S13 aisle wide 35.5-38.3)",
   "tools/targ_film_prepare.py L109-110 gen_duration = max(4, ceil(duration)), assert ≤ 15; tools/plan_film.py L51"
  ],
  "placement": [
   "tools/targ_film_prepare.py → unit loop после L109 → assert retainedSeconds ≤ min(8, 1.25 × sourceDuration) либо coverageSplit, а для тега establishing — drop-decision",
   "tools/verify_adapt_handoff.py → схема handoff → поля stretchRatio/coverageSplit на каждую единицу до покупки",
   "references/film-quality-meta-rules.md → F02 (L29-38) → ритм выводится из длин планов оригинала, а не из песенного времени",
   "tools/film_rhythm.py (новый) + references/film-quality-meta-rules(-v01).json → новый гейт G08 между G06 и G07 → гистограмма экспорта против оригинала печатается в tools/build_film_report.py, fail блокирует G07"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T14",
  "level": "macro",
  "section": "Монтаж и кадр",
  "rule": "Единицу с удержанием больше 6 с и двумя и более названными в строке реакциями готовьте как минимум тремя постановками (двойной план плюс сингл или POV на каждого реагирующего), а каждую названную реакцию давайте отдельным синглом не шире MCU и не длиннее 4,5 с, срезанным в пределах 0,5 с от ключевого слова и читаемым на прокси 480 px.",
  "why": "Taras покрывает бит сочувствия пятью постановками за 21,4 с (S16 53,8–56,25 → ECU-скан S17 56,25–60,65 → S18 → POV S19 63,4–69,6 → S20), а наш один дубль 12,0 с (film-S16 62,4–74,4) прячет опускание взгляда в «eyes-only ~1,5 с», роняет идентичность и кончается мультяшным испугом, строка «глянула на шию» в WS film-S22 112,5–122,4 не поставлена вовсе, а «здивування» film-S31b 198,7–203,3 играется поверх закрытых глаз.",
  "evidence": [
   "ledgers/taras-seg2.json taras-S16..S20 53.8-75.2 (пять постановок); ledgers/film-seg2.json film-S16 62.433-74.433 («gaze-drop legible only at full resolution», «bun morphs 69-74», «final open-mouth shock reads as fear»)",
   "ledgers/targ-seg1.json targ-S18 47.03-57.10 (исходное удержание 10.07 с, соперница сканирует лицо)",
   "ledgers/film-seg3.json film-S22 112.5-122.4 (9.9 с WS, лирика «глянула на шию» не поставлена); film-S31b 198.7-203.3",
   "measurements/phase1-digest.md film[60,120]: «regenerate S16 as two shorter takes with a locked hairstyle and a real lean/gaze-drop»",
   "public-targ-film/evidence/shot-plan.json adaptedSubevents[].type = facial-reaction/scan/response (одна спецификация на единицу)"
  ],
  "placement": [
   "tools/targ_film_prepare.py → unit loop после gen_duration → coverageSetups[] (двойной план + сингл/POV) и отдельная спецификация на каждую постановку при retainedSeconds > 6 и ≥ 2 лицевых реакциях",
   "handoff schema (film-handoff-vNN.json) → coverageSetups[] с sizeMax на реакцию → потребляется prepare и verify_adapt_handoff",
   "references/film-quality-meta-rules.md → F11 (L115-121) → «названная лирикой реакция получает свой сингл»",
   "public-targ-film/evidence/full-film-review.json → eventChecks → legibleAt480px и |событие − ключевое слово| ≤ 0.5 с; generation_contracts.md шаг 8 validation → счёт постановок ≥ 3"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T15",
  "level": "micro",
  "section": "Монтаж и кадр",
  "rule": "Ставьте на каждый ~30-секундный блок истории не меньше одной бессловесной вставки длиной 1,3–2,5 с, срезанной на спетое существительное, и держите общее число вставок и долю планов не длиннее 2 с не ниже, чем в оригинале.",
  "why": "У оригинала 7 планов ≤2 с и вставки S15/S27/S34/S41, у Taras 4 плана ≤2 с и вставки-подписи (руки с кольцами 15,4–16,8 = 1,4 с; пустой проём 19,5–22,75; CU-панч 115,5–117,5 = 1,7 с; пэкшот 126–130,5), а у нас один план ≤2 с по container-budget и единственная предметная вставка film-S23 122,4–129,2 длиной 6,8 с, поэтому каждую строку несёт удержание лица.",
  "evidence": [
   "measurements/container-budget-*.json inserts 2/2/1, insertSeconds 7.4/5.9/6.8, shotsUnder2s 7/4/1; measurements/container-gates-film-vs-targ.json B09 (вставки без лиц, fail)",
   "ledgers/taras-seg1.json S06 15.4-16.8, S07 16.8-22.75; ledgers/taras-seg3.json S29 115.5-117.5; ledgers/taras-seg4.json S31 126.0-130.5, S32 130.5-132.0",
   "ledgers/targ-seg1.json S15 39.33-42.13; targ-seg3.json S27 100.5-105.1; targ-seg4.json S34 143.0-146.4 (урна + 0.9 с пустого кадра), S41 180.33-182.47 (2.1 с)",
   "ledgers/film-seg2.json film-S15 52-56 (наклон к банкам); film-seg3.json film-S23 122.4-129.2 (6.8 с натюрморт)"
  ],
  "placement": [
   "public-targ-film/evidence/shot-plan.json → adaptedSubevents[].type = object-insert → собственная спецификация ≤ 2.5 с в tools/targ_film_prepare.py, срез на спетое существительное",
   "references/film-quality-meta-rules.md → F02 → «добавить бессловесную вставку там, где адаптированная строка длиннее исходного плана»",
   "tools/film_rhythm.py → гейт G08 (T13) → строки «вставок на минуту ≥ оригинала» и «доля планов ≤ 2 с ≥ 0.7 доли оригинала» в tools/build_film_report.py"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T16",
  "level": "micro",
  "section": "Монтаж и кадр",
  "rule": "Ведите каждый удержанный план длиннее 4 с медленным наездом или дрейфом 3–10 % масштаба (либо сменой мизансцены) и держите долю планов с движением камеры не ниже, чем в оригинале.",
  "why": "51 % планов Taras несут медленный наезд против 21 % у оригинала и 26 % у нас, и именно наши статичные удержания (film-S16 12,0 с, S22 9,9 с, S24 8,6 с, S28 10,4 с, S36 8,6 с) читаются как оживлённые картинки, тогда как длинные планы оригинала либо пушат (S18, S29), либо живут на смене титров.",
  "evidence": [
   "measurements/container-budget-*.json cameraMoveShare 0.213 / 0.51 / 0.261; measurements/container-gates-film-vs-targ.json B10",
   "ledgers (пересчёт камеры): taras 25/47 с движением (S01, S12, S14, S19, S20, S22, S23, S30, S31, S33-S37, S39-S41, S43); targ 10/44; film 12/44",
   "ledgers/film-seg2..4.json статичные удержания S16 12.0 (camera «handheld drift»), S18 8.0 («drags»), S22 9.9, S24 8.6, S28 10.4, S36 8.6",
   "measurements/phase1-digest.md targ[0,50]: «Static frames dominate; the source gets its film feel from a handful of soft pushes (S05, S13, S18)»"
  ],
  "placement": [
   "tools/targ_film_build.py → пер-слотовый GSAP → tl.fromTo('#picNNN',{scale:1},{scale:1.06,duration,ease:'none'},0) для слотов > 4 с без исходного движения (монтажно, без покупки)",
   "tools/targ_film_prepare.py → directions → «camera: slow push-in» для retained > 4 с",
   "references/film-quality-meta-rules.md → F11 (L115-121) → сохранить «камера не заменяет актёрскую игру» и добавить «статичное удержание > 4 с на удлинённой единице — дефект»"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T17",
  "level": "micro",
  "section": "Монтаж и кадр",
  "rule": "Генерируйте дубль с лицом крупностью MS и ближе не дольше 6 с (удержание ≤5 с), не ставьте рядом два дубля с одинаковой исходной композицией и крупностью и проверяйте идентичность первого и последнего кадра каждого дубля.",
  "why": "Идентичность плывёт внутри длинных генераций (film-S16 62,4–74,4 — лицо круглее к 74 с, пучок превращается в распущённый узел; film-S27 162,9–173,2 — «different, younger-looking face» в конце 10,3 с), сбросы того же ракурса телепортируют реквизит (film-S22a>S22b 117,7), а единственное ограничение сегодня — плоские 4–15 с.",
  "evidence": [
   "ledgers/film-seg2.json film-S16 62.4-74.4; ledgers/film-seg3.json film-S27 162.9-173.2, film-S22a>S22b 117.7 («posture/blocking teleport»)",
   "ledgers/taras-seg4.json taras-S38 161-169.3 (блондинка меняет сторону, меняется пучок); ledgers/targ-seg3.json targ-S29 115.5-117 (фон плывёт на наезде)",
   "смежные пары одной крупности: targ 13/42, taras 8/43, film 10/41",
   "tools/targ_film_prepare.py L109-110 gen_duration cap 15 с; tools/film_provider.py L127 4 ≤ duration ≤ 15"
  ],
  "placement": [
   "tools/targ_film_prepare.py → gen_duration (L109-110) → потолок 6 с для единиц с лицом MS и ближе, длинный бит режется на реакцию/смену мизансцены",
   "tools/targ_film_build.py → сборка слотов → отказ от смежных слотов с идентичным sourceComposition (обратный ракурс или один непрерывный клип)",
   "references/film-quality-meta-rules.md → G05 → строка firstLastFrameIdentity на каждый дубль; F07/F11 → смена крупности или ракурса на каждой склейке"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T18",
  "level": "micro",
  "section": "Монтаж и кадр",
  "rule": "Кросс-фейд между состояниями проблемы допускайте только при совпадающей кадровке (голова смещается ≤5 % высоты кадра, та же крупность, ≤0,5 с) — иначе держите кадр и режьте встык, — а каждый спецпереход оригинала воспроизводите ровно один раз там, где он стоит.",
  "why": "Правило 14 «использовать фейды между состояниями проблемы» реализовано в сборке как opacity-кросс-фейды без проверки совпадения кадровки, и это дало четыре ghost-шва (107,7–108,9 «double exposure ~1,2 с», 155,6–155,9, 173,3–173,7 после трёхкадровой вспышки, 198,45–198,7), тогда как Taras идёт 44 склейками встык с одним 0,3 с дизолвом, который сам читается как глитч, а оригинал тратит каждый приём ровно один раз.",
  "evidence": [
   "ledgers/film-seg3.json и film-seg4.json transitionOut/In: 107.7-108.9 (S21a>S21b), 155.6-155.9 (S26a>S26b), 173.3-173.7 (S27>S28), 198.45-198.7 (S31a>S31b); film/sheets/sheet06.jpg кадр 2:53 (двойное лицо)",
   "ledgers/taras-seg3.json taras-S25a>S25b 95.1-95.4 (единственный дизолв, «reads as glitch»); задуманное наложение свадебного призрака S21 75.2-79.85",
   "ledgers/targ-seg1..4.json приёмы по одному разу: бумажная диафрагма 3.37-4.0, вспышка 11.87-12.27, блюр 45.97-46.07, белый 85.6-86.6, морф 98.9-99.7, радиальный зум 155.03-155.43",
   "tools/targ_film_build.py L268-270 (opacity 0.45 с), L276-280 (#intermediate-compare 0.5 с), L251-254 (zoom рендерится как opacity+scale); public-targ-film/evidence/shot-plan.json problemStateTransitionPlan 173.232-173.732 «0.5s crossfade»"
  ],
  "placement": [
   "tools/targ_film_build.py → блок переходов (L251-280) → frame-diff головы между последним кадром A и первым кадром B; при несовпадении — удержание + склейка встык; targ-tr-06 рендерить радиальным зумом, а не блюр-дизолвом",
   "AGENTS.md → правило 14 «Use fades between problem states» → уточнение: фейды только при совпадающей кадровке",
   "references/localization-and-resolution-rules.md → шаг 6 (L41-43) → то же уточнение и проверка обоих концов фейда на кадрах экспорта",
   "references/film-quality-meta-rules.md → F05 (L56-64) + public-targ-film/evidence/full-film-review.json → ghostFrames: 0"
  ],
  "statusKind": "conflict",
  "sourceLenses": [
   "editor",
   "harness",
   "passport"
  ]
 },
 {
  "id": "T19",
  "level": "micro",
  "section": "Монтаж и кадр",
  "rule": "Сажайте склейку и смену выражения на ключевое спетое слово в пределах 0,3–0,5 с, как это делают все три версии, и не ломайте этот механизм при исправлении остального.",
  "why": "Это единственная ритмическая механика, общая для оригинала, Taras и нас (taras «шкода» 9,0–10,5, «Пігментні плями» 88,08, film-S19 «На шиї» 92,3, film-S36 «Посилання» 232,2), и она уже спланирована 217 микрособытиями с targetSongTime, поэтому её легко потерять при пересборке покрытия.",
  "evidence": [
   "ledgers/taras-seg1..4.json: S03 «шкода» 9.0-10.5, S10 «ще не дійшов» 27.4, S16 «почула» 48.2, S23 «Пігментні плями» 87.6, S33 «зупинилась» 135.0",
   "ledgers/film-seg2..4.json: film-S16 57.5 «голос», film-S19 92.3 «На шиї», film-S36 232.2 «Посилання»",
   "public-targ-film/evidence/shot-plan.json: 43 единицы, 217 adaptedSubevents с targetSongTime; measurements/cut-alignment.json cutToWordMedianMs 113/94/173"
  ],
  "placement": [
   "public-targ-film/evidence/shot-plan.json → adaptedSubevents[].targetSongTime → сохранить как есть",
   "public-targ-film/evidence/full-film-review.json → eventChecks → |время события − начало ключевого слова| ≤ 0.5 с как проверка, а не как задача на переделку"
  ],
  "statusKind": "exists",
  "sourceLenses": [
   "editor"
  ]
 },
 {
  "id": "T20",
  "level": "macro",
  "section": "Персонажи, стиль и непрерывность",
  "rule": "Фиксируйте каждому повторяющемуся персонажу один силуэт (форма и цвет причёски, цвет верха, аксессуары) на весь фильм с принятым изображением на каждой спецификации, меняйте его только по сюжетному мотиву и не наследуйте перебросы гардероба самого оригинала.",
  "why": "Taras узнаётся по кардигану и низкому пучку во всех 45 планах, а наша героиня распадается на два силуэта (9 описаний «shoulder-length wavy» против 8 «bun»): film-S20 93,8–101,6 идёт с распущенными волосами между двумя планами с пучком, повторяя переброс targ-S22, а film-S16 69–74 меняет причёску внутри дубля, потому что continuityGroups в problem_quality привязаны к визиту и локации, а не к фильму.",
  "evidence": [
   "ledgers/taras-seg1..4.json: «Heroine wardrobe fully consistent» (низкий пучок + пыльно-розовый кардиган + белая футболка во всех современных планах)",
   "ledgers/film-seg2.json film-S20 93.8-101.6 («hair fully down and no cardigan between two bun-and-cardigan shots»), film-S16 69-74 (пучок морфится внутри дубля)",
   "ledgers/targ-seg3.json: 25 описаний причёски, «five outfits in 50 s» (targ-S22 — источник переброса)",
   "tools/problem_quality.py L48-63 continuityGroups по unitIds (визит/локация), L104, L191"
  ],
  "placement": [
   "tools/problem_quality.py → continuityGroups (L48-63) → добавить silhouetteLock на characterId для всего фильма с принятым изображением в preflight requiredAttachments",
   "tools/targ_film_prepare.py → allowedChanges (L123) → запрет импорта перебросов гардероба/причёски из оригинала",
   "references/film-quality-meta-rules.md → F07 (L75-81) → «каждый визит» расширить до «весь фильм, кроме мотивированных смен»",
   "tools/problem_quality.py → validate_export → число различных описаний причёски/гардероба на персонажа ≤ 2 + мотивированные смены"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T21",
  "level": "macro",
  "section": "Персонажи, стиль и непрерывность",
  "rule": "Рендерите все планы, включая ECU проблемной зоны, панели состояний паспорта и продуктовые вставки, в одной стилизации фильма, привязанной к принятому cast-листу, даже там, где оригинал в этом же месте сам меняет стиль.",
  "why": "У нас пять стилевых швов, и два из них стоят ровно на доказательных планах зоны (film-S19 92,3–93,8 фотореалистичная шея против Pixar-профиля; film-S23 фотореалистичный натюрморт, скопированный с targ-S27), тогда как у Taras и у оригинала по два шва — то есть мы скопировали чужие швы как «верность», и доказательство выглядит другой продукцией.",
  "evidence": [
   "ledgers/film-seg2..3.json film-S15 53-56 (полуреалистичные грудь и руки), film-S19 92.3-93.8, film-S21b 108.3-112.5 (копия targ-S24), film-S23 (копия targ-S27), film-S25/S26a 146-155 (более тяжёлая кожа)",
   "ledgers/taras-seg2.json taras-S17 56.25-60.65; ledgers/taras-seg4.json taras-S31 126-130.5",
   "ledgers/targ-seg2.json targ-S24 86.03-90.33; ledgers/targ-seg3.json targ-S27 100.5-105.1",
   "tools/problem_passport.py L279-288 — проверки изображений состояния только по происхождению (lineage)"
  ],
  "placement": [
   "references/problem-passport-contract.md → states[].image acceptance (L66-72) → stylizationReference {path, sha256} принятого cast-листа и styleConsistent на каждом изображении состояния/рекомендателя/непрерывности",
   "tools/problem_passport.py → проверки изображений состояния (L279-288) → требовать styleConsistent",
   "references/film-quality-meta-rules.md → F09 (L93-104) и F13 (L136-145) → строка стилевой согласованности, производные кропы перерисовываются",
   "tools/targ_film_prepare.py → directions доказательных единиц (L154) → «кожа рисуется в стилизации фильма; без фотореалистичных пор»"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T22",
  "level": "macro",
  "section": "Персонажи, стиль и непрерывность",
  "rule": "Играйте каждый бит результата и комплимента как облегчение, переходящее в радость не позже 1 с после ключевого слова (без сведённых бровей и без руки на зоне в течение ≥2 с чистого показа), и держите амплитуду выражения не выше соответствующего бита оригинала — горе несут мокрые глаза и мелкие движения рта, а не отвисшая челюсть.",
  "why": "У оригинала улыбка приходит через 0,3 с после «almost gone» (targ-S33 138,4 → 140,5), у Taras удивление переходит в улыбку на вокальной паузе 148,92–150,72 (S35 147–152,5), а у нас оба результата открываются испугом (film-S26b 155,7–162,9 — сведённые брови и рука на шее; film-S28 173,7–177,3 — открытый рот и ладони на зоне с 175,5 с), при том что в паспорте слово «выражение» встречается один раз, а смайл/радость/удивление — ни разу, и ледж отмечает «slightly overacted» с первой секунды.",
  "evidence": [
   "ledgers/film-seg3.json film-S26b 155.7-162.9, film-S27 167.5-169 (шок на «Гарна інша»), film-S28 173.7-177.3; film/sheets/sheet06.jpg 02:36-02:42, 02:48-02:49, 02:54-02:59",
   "ledgers/film-seg1.json film-S01 (1.0 с гримаса «slightly overacted»), film-S03 6.0 с «cartoon-wide», film-S07 22.5 (улыбка в камеру «breaks the observational tone»)",
   "ledgers/taras-seg4.json taras-S35 147-152.5 (удивление → улыбка на паузе), taras-S34 139-147; ledgers/targ-seg4.json targ-S32 128.2-136.3 (удивление → смех → рука к груди), targ-S33 136.3-143.0",
   "public-targ-film/evidence/problem-passport.json: expression 1, smile 0, joy 0, surprise 0; tools/targ_film_prepare.py L136-140 directions без валентности"
  ],
  "placement": [
   "references/problem-passport-contract.md → states[] intermediate/resolved и resolved каждого участника → expressionArc {onWord, from, to, maxLagSec: 1.0, unoccludedHoldSec: 2}",
   "tools/problem_quality.py → preflight (L103-106) → требовать expressionArc на каждой единице результата бенефициара",
   "tools/targ_film_prepare.py → directions (L136-140) и criteria.actingMin → expressionValence из класса строки + «restrained: wet eyes, small mouth shapes», overacting: 0",
   "public-targ-film/evidence/full-film-review.json → eventChecks → valenceMatchesLyric; generation_contracts.md шаг 10 → испуг под подписью о результате = fail"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "editor",
   "passport",
   "harness"
  ]
 },
 {
  "id": "T23",
  "level": "macro",
  "section": "Персонажи, стиль и непрерывность",
  "rule": "Снимайте биты «до / промежуточно / после» в одной декорации с одной мизансценой, связав их одной кросс-дневной группой непрерывности, и проверяйте совпадение кадровки по обоим крайним кадрам экспорта, а не по плану.",
  "why": "Прямое сравнение оригинала держится на одинаковой OTS-раскадровке (targ-S31 122,9–128,2 и targ-S33 136,3–143,0 — «Same OTS blocking»), а у нас три разные ванные (S28 белая плитка, S29 мятная стена, S35 арочное золотое зеркало), и запланированный 0,5 с matched crossfade 173,232–173,732 вышел вспышкой и ghost-дизолвом в иначе скадрированный CU.",
  "evidence": [
   "ledgers/targ-seg3.json targ-S31 122.9-128.2 и ledgers/targ-seg4.json targ-S33 136.3-143.0 («Same OTS blocking as S31», прямое сравнение)",
   "ledgers/film-seg4.json: «Three different bathrooms in one thread: S28 white-tile/wooden mirror, S29 mint wall, S35 arched gold mirror»",
   "ledgers/film-seg3.json ghost-дизолвы 155.6-155.9 и 173.3-173.7 с трёхкадровой вспышкой более широкой кадровки",
   "public-targ-film/evidence/shot-plan.json problemStateTransitionPlan intermediate→resolved 173.232-173.732 «0.5s crossfade» (план) против экспорта"
  ],
  "placement": [
   "references/problem-passport-contract.md → continuityGroups → кросс-дневная mirrorCompareGroup (targ-s009/s030/s031/s033/s041) с одним принятым изображением сета",
   "references/localization-and-resolution-rules.md → «Repeatable resolution lock», шаг 6 → «тот же сет + та же мизансцена» и проверка обоих концов фейда на кадрах экспорта",
   "tools/targ_film_build.py → переходы состояний → assert почти идентичной кадровки по обе стороны фейда до его принятия"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "passport",
   "editor",
   "harness"
  ]
 },
 {
  "id": "T24",
  "level": "macro",
  "section": "Проблема, продукт и заявления",
  "rule": "Держите долю времени состояния «до», где проблемная зона читается на масштабе телефона (кадр 360 px), не ниже доли оригинала, а первый читаемый кадр зоны — не позже первого кадра оригинала плюс 5 % хронометража, засчитывая только планы крупностью MCU и ближе с зоной не менее 15 % высоты кадра.",
  "why": "Оба чемпиона продают дефект 59–65 % времени фазы «до», начиная с 0,0 с (targ-S01) и 10,8 с (taras-S04), а у нас 30 % и первый читаемый кадр на 52,0 с, причём линии на film-S09 27–31 «too faint to read at phone size», и это проходит гейты, потому что F13 смотрит только полное разрешение и увеличение, а problemRegionVisible — булево поле на план.",
  "evidence": [
   "ledgers/{targ,taras,film}-seg1..4.json (пересчёт по сетке 0.5 с): фаза «до» с читаемой зоной targ 57.5/88.0 с, taras 48.5/82.5 с, film 28.5/94.5 с; первый «yes» targ-S01 0.0 с, taras-S04 10.8 с, film-S15 52.0 с",
   "measurements/container-gates-film-vs-targ.json B11 (доля времени с видимой проблемой, fail) и B12 (первый читаемый кадр, fail; film firstLegibleShareOfRuntime 0.1126)",
   "ledgers/film-seg1.json film-S01 0-4 «no (neck mostly covered by cardigan collar)», film-S09 27-31 «too faint to read at phone size», film-S14 48-52 «no (neck covered)»; самый длинный провал фазы «до» targ 12.5 с, taras 15.0 с, film 27.0 с",
   "references/film-quality-meta-rules.md F13 («проверять зону в фактическом разрешении и увеличенных деталях» — без масштаба телефона); tools/problem_quality.py L174-178 (булево problemRegionVisible + visibilityReason)"
  ],
  "placement": [
   "references/problem-passport-contract.md → Versioned JSON contract (video-spec, L90-96) → visibilityBudget {beforeShareMinRatioToOriginal: 1.0, firstLegibleSecMax: первый кадр оригинала + 5 % хронометража, minSizeForCount: MCU, zoneMinFrameHeight: 0.15}",
   "tools/problem_quality.py → validate_export (после L177) → считать долю по unitVisualRanges и классу зоны каждой единицы, fail ниже доли оригинала",
   "references/video-dna-contract.md → пошаговый разбор оригинала → поле problemZoneVisible на каждый план оригинала как знаменатель (legibilityProfile в каноническом DNA JSON)",
   "tools/build_film_report.py → раздел сравнения состояний → публиковать сравнение и в 360 px, и увеличенным; AGENTS.md правило 6 → зона видна в первом кадре героини, если она видна в первом кадре оригинала"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "passport",
   "editor",
   "harness"
  ]
 },
 {
  "id": "T25",
  "level": "micro",
  "section": "Проблема, продукт и заявления",
  "rule": "Прописывайте в паспорте на каждое состояние инварианты экспозиции зоны (вырез, причёска, ракурс), которые держат её в кадре в каждом MCU/CU, и проверяйте, что инварианты непрерывности им не противоречат.",
  "why": "Оригинал и Taras кладут дефект на лицо, которое есть в каждом кадре 9:16, а наш паспорт заморозил воротник кардигана и высокий пучок (в тексте паспорта collar, neckline и exposure не встречаются ни разу), так что film-S01 0–4 и film-S14 48–52 идут с закрытой шеей, а единственный открытый портрет S20 93,8–101,6 куплен ценой разрыва идентичности.",
  "evidence": [
   "public-targ-film/evidence/problem-passport.json continuityGroups.oksana-same-shop-visit invariants («All chestnut hair gathered in the same high bun», «Same muted rose cardigan, cream scoop-neck…»); счётчики слов: collar 0, neckline 0, exposure 0",
   "ledgers/film-seg1.json film-S01 0-4, film-S14 48-52; ledgers/film-seg2.json film-S16 56-62.4 «partial: faint neck lines above the collar», film-S20 93.8-101.6 (разрыв идентичности)",
   "доля крупностей CU/MCU/ECU в фазе «до»: taras 50.0/82.5 с = 61 %, targ 18.5/88.0 с = 21 %, film 23.0/94.5 с = 24 %",
   "tools/problem_quality.py L48-63 continuityGroups (инварианты фиксируют внешность, а не открытость зоны)"
  ],
  "placement": [
   "references/problem-passport-contract.md → continuityGroups schema → обязательные zoneExposureInvariants[] для групп бенефициара в состоянии «до», когда зона не на лице",
   "tools/problem_quality.py → validate_plan (L14-64) → требовать zoneExposureInvariants рядом с invariants и проверять, что они не противоречат друг другу",
   "tools/targ_film_prepare.py → directions (~L154) → подставлять zoneExposureInvariants группы в каждый промпт состояния «до»",
   "references/film-quality-meta-rules.md → F07 → добавить вырез/открытость зоны в список непрерывности"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "passport",
   "harness"
  ]
 },
 {
  "id": "T26",
  "level": "micro",
  "section": "Проблема, продукт и заявления",
  "rule": "Ставьте каждый бит, где другой персонаж замечает зону, физически: глаза или камера идут по пути «лицо → зона» с отдельным крупным планом глаз, а в бите близкого человека давайте контакт с зоной вместо жеста издалека.",
  "why": "Taras изолирует скан в ECU 56,25–60,65 («глаза буквально сканируют лоб-щёку-челюсть») и добавляет POV-наезд 63,4–69,6, а ладонь дочери ложится на щёку матери 145–146 («лучший актёрский бит»); у нас скан — статичный двухплан 12,0 с с опусканием взгляда «eyes-only ~1,5 с», строка «глянула на шию» (film-S22 112,5–122,4) не поставлена вовсе, а бит дочери (film-S27 162,9–173,2) идёт без контакта и с испугом на комплимент.",
  "evidence": [
   "ledgers/taras-seg2.json taras-S17 56.25-60.65 и taras-S19 63.4-69.6; ledgers/taras-seg4.json taras-S34 139-147 (ладонь на щеке 145-146)",
   "ledgers/targ-seg2.json targ-S18 47.03-57.10 (соперница сканирует, лицо ≥ 1/3 кадра); ledgers/targ-seg4.json targ-S32 128.2-136.3 (рука к груди)",
   "ledgers/film-seg2.json film-S16 62.4-74.4; ledgers/film-seg3.json film-S22 112.5-122.4 («reads as ordinary eye contact»), film-S27 162.9-173.2; film/sheets/sheet06.jpg 02:48-02:49",
   "public-targ-film/evidence/shot-plan.json plannedReaction для targ-s018/s027/s032 — типовая формулировка без цели взгляда"
  ],
  "placement": [
   "tools/plan_film.py → shot-plan единицы targ-s018 / targ-s027 / targ-s032 → plannedReaction называет источник взгляда, путь «лицо→зона», время и контакт с зоной вместо boilerplate",
   "handoff schema → subevents gazeTarget: zone + физический признак → tools/targ_film_prepare.py подставляет в промпт",
   "references/film-quality-meta-rules.md → F11 → «взгляд на адресата» расширить до «взгляд или касание зоны» для каждой названной лирикой реакции",
   "generation_contracts.md → шаг 10 → проверка читаемости взгляда/контакта на масштабе телефона"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "passport",
   "editor",
   "harness"
  ]
 },
 {
  "id": "T27",
  "level": "micro",
  "section": "Проблема, продукт и заявления",
  "rule": "Давайте каждой рекомендательнице хотя бы один экспортный план крупностью MCU и ближе, не длиннее 3 с, на её строке совета, где её зона результата открыта целиком (для шеи — от ключиц до подбородка, без воротника).",
  "why": "Правило 21 требует у рекомендателя полностью разрешённое состояние, но problem_quality позволяет спрятать каждое её появление «с причиной»: у нас совет идёт в общих планах (film-S21a 101,6–108,3 и film-S22 112,5–122,4, лица ≈1/12 кадра), а на строке «шия гладенька» (film-S21b 108,3–112,5) видна лишь полоска над воротником, тогда как оригинал даёт портрет targ-S24 86,03–90,33 и MCU targ-S28 105,1–109,4, а Taras — MCU 92,7–95,4, OTS 105,5–110,0 и CU-панч 115,5–117,5.",
  "evidence": [
   "ledgers/film-seg2.json film-S21a 101.6-108.3, film-S21b 108.3-112.5 («the smooth neck the lyric sells is only a sliver above the collar»); ledgers/film-seg3.json film-S22 112.5-122.4, film-S24 129.2-137.8",
   "ledgers/taras-seg3.json taras-S25a 92.7-95.4, taras-S27 105.5-110.0, taras-S29 115.5-117.5",
   "ledgers/targ-seg2.json targ-S24 86.03-90.33; ledgers/targ-seg3.json targ-S28 105.1-109.4",
   "tools/problem_quality.py L37 (recommender requiredVisibleState == 'resolved' и больше ничего), L92, L174-178"
  ],
  "placement": [
   "tools/problem_quality.py → validate_plan (L37) → participants[role=recommender] получают proofShot {unitId, exposure: 'unoccluded zone', holdSec ≥ портрета оригинала} и zoneVisibleInAtLeastOneCU: true",
   "tools/problem_quality.py → validate_export (после L177) → fail, если все строки рекомендателя скрыты с причиной",
   "references/problem-passport-contract.md → абзац рекомендателя (L135-141) / AGENTS.md правило 21 → определить «visibly embody» как открытую зону на доказательной строке",
   "tools/targ_film_prepare.py → directions для targ-s024 → открытый воротник, зона в кадре, крупность ≤ MCU"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "editor",
   "passport",
   "harness"
  ]
 },
 {
  "id": "T28",
  "level": "macro",
  "section": "Проблема, продукт и заявления",
  "rule": "Оформляйте замену иностранного авторитета оригинала украинским персонажем как явное версионированное владельческое решение в карте адаптации, сохраняя выводимую часть авторитета — ежедневную знакомость и видимое доказательство результата.",
  "why": "Правило 12 требует украинский состав, а любимый владельцем референс сохраняет кореянку («Кореянка, яку я бачу щодня, за п'ятдесят» 92,3–100,1) и согласует её с буллитом «Корейська формула» на энд-карте, то есть предпочтение владельца и правило 12 расходятся; линза не решает этот конфликт, но доказательный портрет и «бачу щодня» от этничности не зависят.",
  "evidence": [
   "taras/asr.json 92.3-100.1; taras/sheets/sheet07.jpg 03:08-03:13 (буллит «Корейська формула — арбутин і транексамова»)",
   "film/asr.json 101.6-112.5 («Там Ірина — бачу її майже щодня. Їй п'ятдесят п'ять — шия гладенька»); ledgers/targ-seg2.json targ-S24 86-90.3 (портрет наставницы)",
   "AGENTS.md правило 12; references/localization-and-resolution-rules.md — region gate перед кастингом",
   "runs/difference-dna-v1/measurements/lead-observations.md L7 (кореянка в Taras против Ірини у нас)"
  ],
  "placement": [
   "references/localization-and-resolution-rules.md → Region gate → поле mentorAuthorityDecision {kept-foreign | localized, ownerDecisionRef} на адаптацию",
   "generation_contracts.md → шаг 4 adaptation-map → поле решения и перечень сохранённых механизмов авторитета (доказательный портрет, «бачу щодня»)",
   "AGENTS.md → правило 12 → пометка, что эталон владельца отклоняется от него и решение версионируется, а не выводится автоматически"
  ],
  "statusKind": "conflict",
  "sourceLenses": [
   "passport"
  ]
 },
 {
  "id": "T29",
  "level": "macro",
  "section": "Проблема, продукт и заявления",
  "rule": "Раскрывайте упаковку на той же доле хронометража, что оригинал, держите её «загадочным предметом» и пойте название бренда столько же раз и на той же позиционной доле, что оригинал, показывая текст этикетки на экране, а не в мелодии.",
  "why": "Оба чемпиона называют бренд один раз в конце (targ 182,6 с = 96,9 %, taras 189,9 с = 97,8 %), а мы называем его на 58,2 % (139,4 с) и повторно на 94,6 %, вычитывая этикетку 141,6–150,8 (28 слогов) «с near-tearful frown», из-за чего теряются и загадка упаковки, и имя как развязка.",
  "evidence": [
   "lyrics/lyrics-advertorial.md §4: имя спето targ 182.6 с (96.9 %), taras 189.9 с (97.8 %), у нас 139.4 с (58.2 %) и 226.7 с (94.6 %)",
   "measurements/container-budget-*.json brandSungCount 1/1/2, firstProductOnScreenShareOfRuntime 0.563/0.649/0.575; container-gates-film-vs-targ.json B15 (fail), B16 (pass)",
   "ledgers/film-seg3.json film-S25 137.8-149.6 («goPure. Я стала читати:» 139.4-141.6, чтение этикетки 141.6-150.8, «needs a curious rather than grieving face»)",
   "ledgers/targ-seg3.json targ-S29 109.4-118.8 («pink tube / Korean writing, something I'd never tried»); ledgers/taras-seg4.json taras-S30 117.5-126"
  ],
  "placement": [
   "generation_contracts.md → шаг 4 adaptation-map → brandNamingPosition = функция оригинала (CTA) и brandTokenCount ≤ числа оригинала, если бренд не заменяет слово-загадку 1:1",
   ".agents/skills/song-clone-ua/SKILL.md → «Гейт бренда» (L108-137) → бренд поётся один раз в CTA; текст этикетки показывается, а не поётся",
   "scripts/lines_check.py → счёт брендовых токенов и их позиции в спетом тексте против оригинала → fail при превышении",
   "references/problem-passport-contract.md → claimLimits → «текст этикетки и ритуал принадлежат карточке, не лирике»"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "passport",
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T30",
  "level": "micro",
  "section": "Проблема, продукт и заявления",
  "rule": "Композитируйте реальный пэкшот в HyperFrames везде, где этикетка занимает не менее 10 % высоты кадра, и ставьте между передачей продукта и нанесением одну пэкшот-вставку не короче вставки оригинала, проверяя читаемость бренда на кадрах экспорта.",
  "why": "Сгенерированные этикетки искажены во всех трёх видео (targ-S42 вторичная этикетка, taras-S43 184,2–188,4), и именно чистая вставка Taras S31 126,0–130,5 (4,5 с) делает бренд читаемым, тогда как у нас банка в кадре презентёра film-S36 226,6–235,2 — AI-абракадабра за три секунды до чистой карточки.",
  "evidence": [
   "ledgers/film-seg4.json film-S36 226.57-235.2 («jar label is AI-garbled pseudo-text, only lid GOPURE readable»); ledgers/film-seg3.json film-S25 137.8-149.6 («body label only partly legible»)",
   "ledgers/taras-seg4.json taras-S31 126.0-130.5 (читаемый «HANBIT SKINCARE / ARBUTIN 7.0+TXA 4.0 CREAM») против taras-S43 184.2-188.4 (искажённый вертикальный текст)",
   "ledgers/targ-seg3.json targ-S27 100.5-105.1 (натюрморт 4.6 с) и targ-seg4.json targ-S42 (искажённая вторичная этикетка)",
   "tools/targ_film_prepare.py L215-216 — пэкшот прикреплён к генератору для s029/s030/s042"
  ],
  "placement": [
   "tools/plan_film.py → shot-plan единицы targ-s029/s030 → packshotInsert {src: реальный пэкшот, holdSec ≥ вставки оригинала} между передачей и нанесением",
   "tools/targ_film_build.py → слоты с этикеткой → композит реального PNG поверх банки + проверка читаемости на кадрах экспорта",
   "tools/targ_film_prepare.py → роли пэкшота (L215-216) → пометка «этикетку рисует монтаж, не генератор»",
   "references/film-quality-meta-rules.md → F03 (L40-46) и F13 → строка проверки бренда в гейте G06"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "editor",
   "passport",
   "harness"
  ]
 },
 {
  "id": "T31",
  "level": "micro",
  "section": "Проблема, продукт и заявления",
  "rule": "Сохраняйте пятичастную грамматику заявления оригинала (сюжетное число недель, то же существительное проблемы, один допустимый абсолютный глагол, поведенческий якорь сравнения, поведенческое доказательство), называйте проблему одним спетым существительным в бюджете диагноза оригинала и хеджируйте один раз.",
  "why": "Taras копирует все пять частей («Через три тижні… стали світлішими», «На шостому тижні… майже не було», «десять років без тонального») и обходится одним существительным в 18 слогах диагноза с эхом «плями → плями → пігментації», а мы убрали число и якорь, дважды сказали «не так помітні» и дважды «на вигляд», раздув диагноз до 29 слогов против 25 у оригинала и блок результата до 61 слога против 32.",
  "evidence": [
   "lyrics/lyrics-advertorial.md §5 (диагноз: targ 25 слогов / 3 зоны, taras 18 / 1 зона, наш 29 + хедж) и §6 (таблица заявления)",
   "film/asr.json 92.0-98.9 («На шиї — дрібні заломи, / Лінії впоперек. Шкіра на вигляд в'яла»), 160.3, 177.4-183.6",
   "public-targ-film/evidence/problem-passport.json problem: «Coarse crepey neck texture, lax-looking skin and conspicuous transverse neck lines» (три подпризнака)",
   "product JSON permittedUk «Допомагає шкірі шиї виглядати гладкішою та пружнішою»; ledgers/film-seg4.json film-S29 183.6-189 (шарф в ящик — поведенческое доказательство, сохранить)"
  ],
  "placement": [
   "generation_contracts.md → шаг 4 adaptation-map → таблица заявления с пятью слотами на каждую строку результата",
   "references/problem-passport-contract.md → Versioned JSON contract → поля sungProblemNoun и sungResultVerb + claimLimits со списком допустимых абсолютных глаголов из permittedUk и ownerDecision на спетое сюжетное число",
   "tools/problem_passport.py → prompt_context → передавать оба слова в PROBLEM_PASSPORT для gen2",
   "scripts/lines_check.py → проверка строк результата → детектор повторного хеджа («не так помітні», «на вигляд» > 1) и эхо существительного в диагнозе/зеркале/результате"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "passport",
   "harness"
  ]
 },
 {
  "id": "T32",
  "level": "macro",
  "section": "Проблема, продукт и заявления",
  "rule": "Не пойте оговорки и не держите постоянную полосу раскрытия: раскрытие живёт мелкой строкой на энд-карте или полосой не дольше 3 с на первом нанесении, а его место фиксируется полем placement в claimLimits.",
  "why": "У оригинала и Taras ноль спетых дисклеймеров и одна строка-футер на карте, а у нас полоса #fiction-note идёт с 149,63 с до конца (90,1 с = 37,6 % хронометража) поверх сюжетных планов и самой карточки, плюс два спетых дисклеймера в самых продающих местах (148,2 «Без обіцянок чудес» и 230,0 «Результат у кожної свій»); достаточно ли карточной строки для правила 7 о продуктовой правде без выдуманных обещаний — владельческое и юридическое решение.",
  "evidence": [
   "tools/targ_film_build.py L371: #fiction-note .disclosure data-start='149.63' до конца фильма (90.1 с из 239.7 с)",
   "film/sheets/sheet08.jpg — полоса на каждом кадре 03:30-03:59, включая карточку; ledgers/film-seg4.json «bottom third is crowded by both the lyric caption box and the persistent disclaimer strip»",
   "film/asr.json 148.2-150.8 и 230.0-233.7 (спетые оговорки); lyrics/lyrics-advertorial.md §6 — строка disclaimer: none / none",
   "ledgers/taras-seg4.json taras-S44 188.42-194.17 — единственная мелкая строка-футер на карте"
  ],
  "placement": [
   "tools/targ_film_build.py → клип .disclosure (L371) → data-start = начало карточки либо ≤ 3 с на первом нанесении",
   "references/problem-passport-contract.md → claimLimits[].placement (end-card | on-screen-strip | sung, по умолчанию end-card) → читается сборкой и song_budget --cta",
   ".agents/skills/song-clone-ua/SKILL.md → Стадия 2 → «дисклеймеры и условия формы не поются»",
   "scripts/lines_check.py → проверка спетого текста → флаг дисклеймерных фраз и условий формы в лирике"
  ],
  "statusKind": "conflict",
  "sourceLenses": [
   "editor",
   "passport",
   "harness"
  ]
 },
 {
  "id": "T33",
  "level": "micro",
  "section": "Проблема, продукт и заявления",
  "rule": "Защищайте уже работающие биты — нанесение 4–6 с с не менее чем тремя микродействиями и видимой зоной, наезд в зеркало на слове диагноза и крупные планы развязки с круглой вставкой памяти — от перемонтажа при исправлении остального.",
  "why": "Это места, где мы не проигрываем: film-S26a 149,6–155,7 (6,1 с, шея открыта под кремом) честнее нанесения Taras (S32 130,5–132,0, 1,7 с, «too short to sell no sting»), film-S19 89,6–93,8 повторяет наезд targ-S21 67,1–72,2 на слове диагноза, а наша развязка S31–S36 со вставкой 214,9–218,4 близка к targ-s035..s043 1:1, тогда как Taras теряет три бита развязки в текст.",
  "evidence": [
   "ledgers/targ-seg3.json targ-S30 118.8-122.9 (четыре микродействия) и targ-S21 67.1-72.2 (наезд в зеркало, «Melasma» 69.2); ledgers/targ-seg4.json targ-S39 169.9-173.4 (вставка памяти)",
   "ledgers/film-seg3.json film-S26a 149.6-155.7, film-S19 89.6-93.8 («На шиї» 92.0, «the one true camera move… pays off»); ledgers/film-seg4.json film-S33 214.9-218.4",
   "ledgers/taras-seg3.json taras-S23 84.5-90.05 (палец на пятне 87.6-88.3 на «Пігментні плями»); ledgers/taras-seg4.json taras-S32 130.5-132.0 (1.7 с) и заметка сегмента «three of the source's key payoff beats survive only as text»"
  ],
  "placement": [
   "tools/plan_film.py → shot-plan problemStateTransitionPlan.applicationSequence, единица targ-s021, overlays targ-memory-inset → оставить без изменений при любой перепланировке",
   "references/film-quality-meta-rules.md → F05/F11 → пометить эти биты как защищённые от исправлений покрытия и ритма",
   "AGENTS.md → правило 21 → уже покрывает результат в каждом появлении, дополнительной нормы не нужно"
  ],
  "statusKind": "exists",
  "sourceLenses": [
   "passport",
   "editor"
  ]
 },
 {
  "id": "T34",
  "level": "micro",
  "section": "Титры и финал",
  "rule": "Верстайте титры по геометрии оригинала: жирный геометрический гротеск с высотой прописных не ниже доли оригинала, непрозрачный белый скруглённый бокс с центром на 68–72 % высоты кадра, не более 2 строк и не больше слов, чем максимум оригинала, без конечной точки, тире, кавычек и двоеточий, карточка не короче 1,5 с и ни одной карточки на паузе.",
  "why": "У оригинала высота прописных ~5 % при боксе на 68–75 %, у Taras 3,5–4 % при боксе на 68–72 % и ноль конечных точек в 54 карточках, а у нас 23 px на 864 (2,7 %), бокс на 78–88 %, гуманистический шрифт, 49 из 67 карточек с точкой (всего 70 точек, 22 тире, 16 «», 8 двоеточий) и четыре карточки за 5,67 с в film-S34 218,4–224,07.",
  "evidence": [
   "tools/targ_film_build.py L193 (DejaVu Sans Bold), L355 (.caption{bottom:108px; font-size:23px} на 486x864), L142-151 display_caption() сохраняет пунктуацию, L333-336 caption loop",
   "ledgers/taras-seg1..4.json captionStyle: ~3.5-4 % на строку, центр бокса 68-72 % (86 % после 181.6), 1-2 строки, медиана 6 слов, максимум 9, без конечной пунктуации, 14 внутренних запятых на 54 карточки",
   "ledgers/targ-seg1..4.json captionStyle: Poppins-подобный ~5 %, центр бокса 68-75 %, медиана 7 слов, максимум 13, 17 запятых, ноль точек",
   "ledgers/film-seg4.json film-S34 218.4-224.07 (четыре карточки за 5.67 с, в т.ч. «щиро.» 1.0 с и «Її думка — не головна.» 1.0 с)"
  ],
  "placement": [
   "tools/targ_film_build.py → css .caption (L355) и шрифтовой ассет (L193) → размер и центр бокса как доли от containerBudget.captionGeometry, геометрический OFL-гротеск",
   "tools/targ_film_build.py → display_caption() (L142-151) → снимать конечную точку/двоеточие, «», ' — '; caption loop (L333-336) → сливать карточки короче 1.5 с и не ставить карточку на вокальной паузе",
   "tools/caption_lint.py (новый) → прогон по captionAnchors и CSS сборки перед экспортом → fail на нарушение геометрии и пунктуации",
   "references/film-quality-meta-rules.md → F15 (L156-166) → типографская спецификация рядом с требованием синхронности титра и песни"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "editor",
   "harness"
  ]
 },
 {
  "id": "T35",
  "level": "macro",
  "section": "Титры и финал",
  "rule": "Укладывайте финал в бюджет оригинала: план ведущей не длиннее исходного (×1,1) с одним жестом на продуктовой строке, карточка не короче карточки оригинала, не менее 0,5 с карточки после последнего спетого слова и ни одного лирического титра поверх карточки, а строка о собственной ценности заканчивается на лице героини.",
  "why": "Оба чемпиона закрывают ~12 с (targ-S42 6,03 с + targ-S43 5,87 с; taras-S43 4,21 с + taras-S44 5,75 с, где бренд допевается поверх карты), а у нас 8,63 с презентёра с тремя жестами (film-S36 226,57–235,20) и самая короткая карта из трёх (4,5 с) с вокалом до 239,4 и лирическим боксом на ней, при том что sourceCardDuration 5,87 уже записана в плане и никогда не проверяется.",
  "evidence": [
   "ledgers/targ-seg4.json targ-S42 182.47-188.50 (ладонь вниз на «link's below») и targ-S43 188.50-194.37 (вокал кончается 188.56)",
   "ledgers/taras-seg4.json taras-S43 184.21-188.42 (двумя руками, без указаний) и taras-S44 188.42-194.17 (бренд 189.9-193.9 поверх карты, без титра)",
   "ledgers/film-seg4.json film-S36 226.57-235.20 (указание на банку, ладонь вверх, указание вниз 232.2) и film-S37 235.2-239.7; film/sheets/sheet08.jpg 03:47-03:59",
   "public-targ-film/evidence/shot-plan.json endingEditorialOverride.sourceCardDuration 5.87; tools/targ_film_build.py L337 card_start = 235.2; measurements/container-gates-film-vs-targ.json B17 (длительность карточки, fail)"
  ],
  "placement": [
   "public-targ-film/evidence/shot-plan.json → endingEditorialOverride → cardMinSeconds = sourceCardDuration и heroRange ≤ исходного × 1.1",
   "tools/targ_film_build.py → card_start (L337) → выводить из max(sourceCardDuration, конец вокала + 0.5 с); caption loop → отбрасывать карточки со стартом ≥ card_start",
   "references/film-quality-meta-rules.md → F12 (L125-134) / F16 (L170-175) → строка финала: бюджет презентёра, карты и воздуха после вокала",
   "tools/plan_film.py → shot-plan unit targ-s042 → один жест на продуктовой строке, последняя сюжетная строка на лице героини"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "editor",
   "passport",
   "harness"
  ]
 },
 {
  "id": "T36",
  "level": "micro",
  "section": "Титры и финал",
  "rule": "Стройте энд-карту как оригинал: вордмарк, заголовок, 3–4 буллита пользы с подстрочником из permittedUk-фактов, пэкшот, кнопка CTA и одна мелкая строка раскрытия — без лирического бокса и без полосы дисклеймера на карте.",
  "why": "Карта оригинала несёт пользы, тюбик и «LINKS BELOW» (targ-S43 188,5–194,37), у Taras — заголовок, подзаголовок, четыре буллита с подстрочниками, тюбик, кнопку и мелкий футер, а наша карта 4,5 с идёт без буллитов пользы, зато с лирическим боксом и серой полосой в нижней трети.",
  "evidence": [
   "taras/sheets/sheet07.jpg 03:08-03:13: «ЧИСТА ШКІРА. / ВПЕВНЕНА ТИ.», «Щовечірній крем від пігментації.», четыре буллита с подстрочниками, тюбик, «▼ ПОСИЛАННЯ НИЖЧЕ ▼», мелкий футер",
   "ledgers/targ-seg4.json targ-S43 188.5-194.37 (розовая карта, пользы слева, тюбик справа, полоса «LINKS BELOW»)",
   "film/sheets/sheet08.jpg 03:55-03:59: вордмарк goPure, «Догляд за шиєю», банка, зелёная кнопка, строки оффера + лирический бокс + серая полоса",
   "ledgers/film-seg4.json: «End card is 4.5 s … bottom third crowded by lyric box plus the persistent disclaimer strip»"
  ],
  "placement": [
   "tools/targ_film_build.py → HTML/CSS карточки (.brand, h1, .card-cta, .offer, .disclosure) → буллиты пользы из product permittedUk, пропуск .caption клипов после card_start, завершение #fiction-note до карточки",
   "tools/plan_film.py → shot-plan unit targ-s043 plannedAction → явный перечень элементов карточки",
   "generation_contracts.md → шаг 11 → чеклист карточки (вордмарк, заголовок, буллиты, пэкшот, CTA, одна строка раскрытия)"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "passport",
   "editor"
  ]
 },
 {
  "id": "T37",
  "level": "macro",
  "section": "Процесс и harness",
  "rule": "Снимайте с оригинала containerBudget-vNN.json до написания текста (спетые слоги и слоги по секциям, дыхательные фразы, паузы ≥1 с, доля озвученных кадров, диапазон и динамика вокала, гистограмма длин планов, вставки, доля движений камеры и крупностей, доля времени с читаемой зоной и первый читаемый момент, доля раскрытия продукта, брендовые токены, бюджет CTA, длительность карточки, геометрия титров) и пусть каждый гейт пишет sourceValue/adaptedValue/ratio/threshold/derivation.",
  "why": "Все существующие гейты — это полнота N/N (43/43 единицы, 217/217 событий, 65/65 мыслей), а правило 5 и generation_contracts L155-157 требуют «выводить из оригинала», не записывая ни одной его величины как бюджет, поэтому ни один ratio-гейт сегодня невозможен; прототип из этого прогона (containerBudget + 17 гейтов B01-B17) уже показывает 11 провалов.",
  "evidence": [
   "runs/difference-dna-v1/measurements/container-budget-{targ,taras,film}.json (schema adfactory.container-budget/v1) и container-gates-{film,taras}-vs-targ.json (B01-B17 с полями sourceValue/adaptedValue/ratio/threshold/derivation)",
   "runs/video-dna-v1/targ/analysis.json — 43 плана и 65 карточек со временем, но без выведенного блока бюджета",
   "film-handoff-v02.json coverage (mergedSourceMicroshots 0, adaptedSubevents 217); coverage-map-v06.json allThoughtsMapped",
   "references/film-quality-meta-rules-v01.json requiredGateRecordFields — без полей источника/адаптации/ratio"
  ],
  "placement": [
   "tools/video_dna_budget.py (новый) → шаг разбора оригинала → переиспользует runs/difference-dna-v1/measurements/{audio_dna,shots,cut_align,music_sim}.py и пишет containerBudget-vNN.json на каждый канонический оригинал",
   "references/video-dna-contract.md → шаг 6 output column и строка уровня 7 → containerBudget обязателен; references/video-dna-canonical-v01.json → привязка бюджета к оригиналу",
   "generation_contracts.md → шаг 2 output + pipeline.json → containerBudgetRequired / containerBudgetTool",
   "references/film-quality-meta-rules-v01.json → requiredGateRecordFields → добавить sourceValue, adaptedValue, ratio, threshold, derivation в каждую запись гейта"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "harness"
  ]
 },
 {
  "id": "T38",
  "level": "macro",
  "section": "Процесс и harness",
  "rule": "Прогоняйте все бюджетные и линт-гейты на ledger-привязанном performance-тексте, который gen2 реально отправляет, требуйте CONTAINER_BUDGET как обязательную переменную рядом со STRESS_LEDGER, берите DUR по умолчанию из спетого пролёта оригинала и печатайте в отчёте прозовый сценарий и спетый текст с раздельными счётчиками.",
  "why": "Прозовый lyrics_uk-v07.txt содержит 847 слогов и отличается от реально спетого lyrics-with-stress-v04.txt (770) в 62 из 65 строк, а summary.json сперва считал бюджет по прозе; при этом DUR захардкожен в 240 вместо спетого пролёта оригинала (188,56 с), так что измеренный не там бюджет бессмыслен.",
  "evidence": [
   "measurements/summary.json film: sungTextSyllables 770, proseScriptSyllables 847, sungTextSource («differs in 62/65 lines»)",
   "lyrics/lyrics-alignment.md §0 (строка «Ours words + time»)",
   "scripts/gen2.py L64 duration = int(os.environ.get('DUR','240')); L101-108 stress_fields() (проверка actualPromptSha256)",
   "references/owner-approved-adapt-baseline.md L51 («Show the requested lyric and actual accepted performed text separately»)"
  ],
  "placement": [
   "scripts/gen2.py → song_fields() (L55) → вызов tools/song_budget.py на файле LYRICS до POST и запись <tag>-container-request-audit.json",
   "scripts/gen2.py → окружение / .agents/skills/song-clone-ua/SKILL.md Стадия 3 шаблон env (L284-286) → CONTAINER_BUDGET обязателен, DUR по умолчанию = спетый пролёт оригинала",
   "tools/build_adapt_report.py → шаг 8 отчёта ADAPT → два счётчика (проза и спетый текст) рядом",
   "references/adapt-phase-contract.md → шаг 4 output column → «бюджет измерен на спетом тексте»"
  ],
  "statusKind": "strengthen",
  "sourceLenses": [
   "songwriter",
   "harness"
  ]
 },
 {
  "id": "T39",
  "level": "macro",
  "section": "Процесс и harness",
  "rule": "Получив от владельца эталонный рендер, прогоняйте video_dna_budget.py на нём и на текущем лучшем выходе и публикуйте трёхколоночный диф против containerBudget оригинала до новой покупки, выводя пороги только из оригинала, а бренд и проблему эталона фиксируя как наблюдение.",
  "why": "Абзац AGENTS.md об эталоне (L23-30) требует только «посмотреть перед генерацией», и просмотр не породил ни одного правила до этого исследования, тогда как измеренный диф превращает предпочтение в гейты; при этом эталон идёт с другим брендом (HANBIT) и другой проблемой (пигментация), которые не должны протечь в продуктовую правду goPure.",
  "evidence": [
   "AGENTS.md L23-30 и references/approved-targ-adaptation-video.md («watch it before generating», без шага измерения)",
   "runs/difference-dna-v1/measurements/{summary.json,container-budget-*.json,container-gates-*-vs-targ.json} + ledgers + lyrics — первый такой диф",
   "runs/difference-dna-v1/README.md и measurements/lead-observations.md L5 — HANBIT/пигментация зафиксированы как наблюдение, а не как продуктовая правда",
   "tools/build_difference_report.py — существующий инструмент отчёта о различиях"
  ],
  "placement": [
   "references/approved-targ-adaptation-video.md → раздел «What to compare it against» → обязательный измеренный трёхколоночный диф до генерации",
   "AGENTS.md → абзац про эталонный рендер (L23-30) → ссылка на измерение вместо просмотра",
   "generation_contracts.md → шаг 2 output → диф эталона публикуется рядом с containerBudget оригинала",
   "tools/build_difference_report.py → постоянный инструмент прогона → пороги берутся из оригинала, значения эталона идут как наблюдение"
  ],
  "statusKind": "new",
  "sourceLenses": [
   "harness"
  ]
 }
]
