# NOTES_LEVEL — живой журнал детектора/маски (голова 25a+, 2026-09-02) > **Архив 2026-08-18 — 2026-08-23 (4024 строки) → [`handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md`](archive/NOTES_LEVEL_2026-08-18_2026-08-23.md).** > Оглавление по датам/темам → [`handoff/NOTES_LEVEL_INDEX.md`](NOTES_LEVEL_INDEX.md) (единственный навигатор). > Статус TOTAL → [`README.md:13`](../README.md) (единственный источник). Дубли заголовков (`22g`×2, `20s`×2) устранены в архиве; заголовки унифицированы. ## 24mm5–24mm7 (продолжение захода) 1. Полная буферная карта полосы-цикла: ДВА круга log→exp, между вторыми — бидир-IIR ×2 В ЛОГ-ДОМЕНЕ (= спектральное смешение детектора). 2. Design 535a70 (своп аргументов) → 1802a24c0 = тело conv из вопроса 22z. Вход [r15]=bands_final@678+i, выход scratch@628. 3. Тест: scr == ln(bands_final) с eps float32 (identity-фаза) ⇒ k=1. 4. Перед EXP: FIR[1..n/2] *= 2.0 (@1824c41e0; исправление «÷−1» из 24l). 5. γ=1.760561 возникает после design: кандидаты opB/opC и финальный combine df0(FIR, track_i); формулы-кандидаты в BLOCKMAP 24mm7. 6. Все правки dataflow 24hh/24ii/24l перенесены в BLOCKMAP (14 порядок, ACC@7c8, ×2 вместо ÷−1, log#1/log#2 позиции). ## 24mm8-бис: точка входа следующего раунда opB-воркер 18004ca80: пролог + тег [obj]==6 проверка (0xfffffff3 при сбое), дальше — дескрипторная операция над векторами (BLOCKMAP 24l «ops A–D»). Полный дизасм не сохранён (скрипт-глюк с редиректом), перегенерировать: `python3 scripts/disasm_func.py 18004ca80 0x300`. Цель: найти множитель γ=1.760561 внутри opB/opC/df0 → замкнуть конвейер. ## ============ 24mm9: FIR-ЦЕПЬ ДЕКОДИРОВАНА = MIN-PHASE КЕПСТРАЛЬНЫЙ СЭНДВИЧ; ВАЛИДАЦИЯ 0.0065 дБ ============ ### Структура опов (полные дизасмы в /tmp/opencode/cascade/*.dis) ``` обёртки 125e0/5560: перестановка аргументов → воркер(rcx=data, rdx=data, r8=ПЛАН, r9=WORK); всё ин-плейс над FIR ПЛАН = buf548@540548 (!НЕ маски/твидлы): {tag=6, log2n=12 [+4], [+8]=0, scale_flag=1 [+0xc], scale=2^-12 [+0x10], work=16384Б [+0x18]} WORK = buf598@540598 (рабочая область FFT, ~1.0 мусор; «lane-mask» из 23b — УСТАРЕЛО) th2180(воркер 4ca80) = INVERSE real-FFT (голова: сумма/разность X[0]/X[Nyq]); th1a90(воркер 1a0c0) = FORWARD real-FFT (хвост: пакинг Найквиста в слот n); тела — импортные близнецы 181b853e0/181b81b80 (AVX, только ±1/√2 твидлы, без внутренних масштабов); ffe0 = in-place ×scale (skip при 1/0); copy th2210→136e0→4d900 = пак real→interleaved complex (re=v, im=0). df0 18000b3c0 = ПОЭЛЕМЕНТНОЕ КОМПЛЕКСНОЕ УМНОЖЕНИЕ, dst=arg2: track_i := track_i ⊗ FIR (vfmaddsub213ps; двойная версия b560+) EXP-ядро 140b30→1803831c0: таблично-полиномиальная комплексная экспонента (магия 12582912 expf-класса, guard 87.33654=maxarg−2ln2, редукция 184.665≈128/ln2, диадич. {−37/24,15/8,31/24,11/1024}); численно ведёт себя как КОМПЛЕКСНЫЙ exp (вариант flat проиграл 8 дБ). Unicorn-эмуляция невозможна (нет FMA в TCG) — статический декод открыт. ``` ### Цепь (все константы из асмa; fn529fe0.dis 52b60c–52b893) ``` scr@628 → pack(re=scr,im=0) 2049 пар → FIR[n]=FIR[4096]=0 ; 52b685 (Найквост re:=0, ДО фолда!) → inv-RFFT ; opA th2180 → fold: y[1..2047]*=2.0 ; xmm13 @1824c41e0, 52d920 y[2049..4095]=0 ; 52db50 (y[2048] НЕ трогается) → fwd-RFFT ; opB th1a90 → комплексная EXP аргумент ×q ; 52b716, q≈0.80 (см. ОТКРЫТО) → inv-RFFT ; opC th2180 → float[0..2047]*=WINfreq[2048..4095] (падающий Hann); хвост=0 ; 52d990/db50 → fwd-RFFT ; opD th1a90 → FIR[0]=1.0, FIR[1]=0 ; 52b7cd → df0: mask_i := track_i ⊗ FIR ; финальный combine ``` Это классическое построение минимально-фазового ядра через кепстр (IDFT лога → удвоение причинной части → exp → обратно). ### Валидация (структурная фаза, критерий <0.05 дБ — ВЫПОЛНЕН) 60 ультрачистых кадров (|γ_fit−1.760561|<5e-4, fit-rms<1e-5, все sc_*): по ВСЕМ 2049 бинам rms медиана **0.0065 дБ**, p90 0.0075, max 0.035 при q=0.80. Инструмент: cascade_sim.py --mask / fir_probe.py. ### γ выводится из цепи: γ = 1 + s_F(q), s_F = ∂log|F|/∂log trk в нотче При q=0.8: s_F=0.7516 ⇒ γ_pred=1.7516 против точного 1.760561 (Δ 0.5%). Остаточная структура та же, что даёт пер-бин модуляцию rms 0.0065 дБ. ### ОТКРЫТО (следующий раунд) 1. **Источник q** на аргументе EXP: эмпирика 0.785–0.809 по подвыборкам; главный подозреваемый — внутренность 1803831c0 (или xmm10=0.8 @1824c3e28-класс константа вне прослеженного пути). Нужен статический декод ядра (~5700 строк AVX-512+FMA) либо live-захват входа/выхода EXP. 2. Асинхронность снапшотов (cur может отставать от scr/trk) ограничивает точность пер-кадрового фита — уйдёт с симуляцией детектора (Этап B). 3. Детекторный каскад lvl_raw→scr (шаги 9–17) не тронут; нужны ACC@7c8 (+ WINfreq@658 для контроля окна) — обновить rendersnap2 SLOTS. ## ============ 24mm10: ЯДРО EXP ДЕКОДИРОВАНО ПОЛНОСТЬЮ; МАСШТАБЫ ОПОВ ЗАФИКСИРОВАНЫ; ПАРАДОКС q ============ ### EXP 1803831c0 — полная формула (скалярный путь = векторная математика) Вход: пары (re,im) плоско; выход in-place. Это ТОЧНАЯ комплексная экспонента: ``` e^re: t = fma(re, C1, MAGIC), C1=184.665 (=128·log2e!), MAGIC=12582912 k = t−MAGIC (округление до целого); таблица T @0x1820fcd80, запись 8 байт (hi/lo extended double), индекс (t&MASK)<<3 r_hi = re − k·h, h=0.00541687 (=ln2/128, f32) r = r_hi + k·1.65777e-06 ; Коди–Уэйт вторая компонента p = r + 0.5·r² e^re = T[k].lo + T[k].hi·p (+знаковые фиксы, guard 87.33654=maxarg−2ln2) e^(i·im): |im|→q=round(|im|·(1/π)) через тот же MAGIC; приведение в DOUBLE: rπ = |im| − q·π_hi − q·π_lo (двухкомпонентный π); фолд π/2; минимакс sin/cos в double {2.60578e-06, −1.98096e-4, 3.166e-05, −0.01604}; знаки по квадранту (vmovmskps) out = (e^re·cos(im'), e^re·sin(im')) — БЕЗ КАКОГО-ЛИБО МАСШТАБА ВНУТРИ. ``` Следствие: аргумент экспоненты в цепи = ровно то, что даёт fwd-RFFT. ### Нормировки опов (пин по дизасму воркеров) - FWD(1a0c0): сборка X[Nyq]=A0−B0 БЕЗ ×0.5 ⇒ ядро сырое (gain 1 = numpy.rfft); план-масштаб guard `[obj+8]`: у нас [+8]=0 ⇒ ffe0 НЕ вызывается. ИТОГ s_f=1. - INV(4ca80): голова X0/XN бабочка без 0.5, план-масштаб guard `[obj+0xc]`=1 ⇒ ffe0 ×2^-12 по всему буферу ПОСЛЕ ядра. Ядро сырое (raw IDFT, gain N) ⇒ после ffe0: s_i = 1 (ровно numpy.irfft). ИТОГ s_i=1. - Содержимое WINfreq@658 подтверждено по старым дампам (firbufs.npz): периодический Hann(4096), w[1]=5.8827e-07, w[1024]=0.5, w[2047]=0.9999994. ### Отвергнуто (численно, на ультрачистых кадрах) - Своп направлений (fwd,inv,fwd,inv): rms 82 дБ — исключено. - «Найквост не экспоненцируется»: rms 60 дБ — исключено (EXP накрывает все 2049 флоатов включая слот Nyquist.re). - Окно hann^p (p=0.5..4) при q=1: s_F∈[0.81..0.97], не достигает 0.76. - Перестановка («unordered» layout) для кадров класса post-stage: сортировка модулей не совпадает ни с одной стадией (relres 0.28..0.98) — кадры полиморфны, forensics снапшотов исчерпана. ### ПАРАДОКС q (главный остаток) Все масштабы зафиксированы ⇒ модель обязана быть точной при q=1, однако: q=1 → rms 0.022 дБ; q=0.8 → 0.0065 дБ (медиана, 60 кадров). Более того: наблюдаемый закон cur=trk^γ держится с rms~1e-4 на РАЗНЫХ контентах, а наш сэндвич даёт контент-зависимый наклон s_F (0.82..1.34). ⇒ реальный тракт ведёт себя как ПОТОЧЕЧНАЯ степень (диагональный оператор в частотном домене), наш rfft∘fold∘irfft — нет. Гипотеза: ops A–D суть комплексные FFT размера 2048 (не real-4096!) либо содержат zreorder-слой, и срединный линейный оператор L=rfft∘fold∘irfft в действительности близок к диагональному в частотном базисе (например, при интерпретации буфера как 2048 комплексных отсчётов fold-паттерн становится почти тривиальным). ### Пути закрытия (следующий раунд) 1. Статический декод ядер-близнецов (~3.1k строк AVX каждый) — определить размер/тип преобразования и формат укладки окончательно. 2. Live: burst-захват rendersnap2 (обновлён, +ACC/WINfreq) с плотным семплированием состояния FIR внутри рендера; сравнить стадии. 3. Windows/x64dbg-MCP (если доступен хост): брейкпоинт на 52b644..52b893, дамп FIR после каждого из 6 шагов для одного кадра — закрывает всё. ## 24mm10-бис: итог статического разбора близнецов (раунд «добить q») ``` Ядра 181b853e0(INV-core)/181b81b80(FWD-core): 3130 строк почти развёрнутого AVX2-кода (60 jcc, 4 боевых петли), БЕЗ внутренних вызовов; единственные float-константы ±0.707107 (+ знак-маска 0x80000000) в собственном статике @0x186ee7dxx (в дампе есть). Это radix-4/split-radix КОМПЛЕКСНЫЙ FFT половинного размера (count=N/2=2048 от воркера), специфицированный под размер (jump-table по log2n в воркере выбирает ядро). Нормировок внутри НЕТ (ни одной vmulps на константу ≠±1/√2). INV-воркер добавляет ffe0(2^-12) ([obj+0xc]=1), FWD-воркер НЕ добавляет ([obj+8]=0). Твидлы плана лежат инлайн после заголовка плана (захват buf548: квады cos(π/8)-класса @f32-индекс 560+, пары sin|cos дальше); указатели plan[+0x30]/[+0x38]/[+0x50]/[+0x58] — кучные адреса этих таблиц (вне дампа), разность [+0x38]−[+0x30]=0x230Б=140 f32. Инструмент: /tmp/opencode/avx_interp.py (мини-интерпретатор AVX/FMA- подмножества; баги cmp-as-sub и RIP-rel исправлены; довести до прогона — упёрлось в управление потоком на таблицах, см. probe2/probe3.py). ``` ВЫВОД: пара INV/FWD == пара numpy.irfft/rfft (подтверждено трижды). Парадокс q (модель требует аргумент×0.8, все масштабы зафиксированы) ⇒ причина НЕ в нормировках ядер, а в том, ЧТО реально лежит в FIR-буфере в момент γ-кадров: офлайн-гипотезы о стадиях исчерпаны, кадры полиморфны. Необходимо наблюдение ЖИВОГО состояния (варианты 2/3 выше), либо полный декод управления потоком близнеца (петли по стадиям, r13/r14 walk — частично картирован: r13=work, add/sub rbp, add 0x40/0x100). ### Скорректированные мелочи dataflow этого раунда - Порядок: copy → opA(INV) → FIR[n]=0 → fold×2 → zero[2049..] → opB(FWD) (FIR[n]=0 стоит ПОСЛЕ opA, обнуляет временной отсчёт t[N/2], не вход!) - В модели убрать h[-1]=0 перед irfft (Найквост входа НЕ обнулялся). ``` ## ============ 24mm11: LIVE PTRACE-ТРАССИРОВКА WINE-ХОСТА; ЦЕПЬ ДО DF0 БИТ-ТОЧНА; q=1 ============ ### Инструмент (прорыв) scripts/wine_ptrace_trace.py — мини-ptrace отладчик: запускает reaper как ребёнка (yama=1 не мешает), аттачится ко всем тредам wine-хоста yabridge, ставит int3 на входах COPY 1800136e0 / EXP 1803831c0 / DF0 18000b3c0 / DF0RET 52b898, дампит регистры и буферы через /proc/tid/mem между хитами. Контекст НЕ нужен: всё берётся из регистров хитов; кадры сшиваются последовательностью COPY→EXP→DF0→DF0RET. ### ФАКТЫ (dual_b1q_0.5.rpp, 400+ хитов) 1. **Вход EXP == rfft(fold(irfft(scr))) при q=1 ТОЧНО**: отношение Y_meas/Y_model = 1.0000+0.0000j по всем бинам всех кадров. НИКАКОГО скаляра q нет — «q≈0.8» из 24mm9 был артефактом вырожденного фита (гладкая модель подгонялась под гладкую кривую). 2. **FIR на входе df0 == полная модель БИТ-В-БИТ**: |ratio|=1.0000, phase=0 по всем 2049 бинам; энергия разностного ядра ~1e-15. Окно = периодический Hann, падающая половина, всё как в модели. 3. **df0 = complex-mul подтверждён живьём**: Tout==Tin⊗F с relerr≤3.5e-7 (float32). 4. **НОВОЕ: track_i ([rsp+0x138]-таблица) ≠ exp(scr)!** Tin — комплексная кривая, СОСРЕДОТОЧЕННАЯ В НОТЧАХ (топ-бины = нотчи скр), |max|~442, наклон log|Tin|/scr ≈ −5.5 ⇒ Tin ≈ trk^5.5; Tout = Tin⊗F ≈ trk^4.54. 5. Слоты ctx через сигнатуру vtable нашли ДРУГОЙ инстанс (GUI-класс: слоты содержат 1200/440/25 — частоты/параметры); валидация ctx теперь только по инварианту trk==exp(scr) из хита (строгая: finite, |scr|<40). ### СЛЕДСТВИЕ: где рождается γ Цепь до df0 бит-точна и НЕ содержит γ. Применённая маска cur@678 = trk^1.760561 образуется ЛИБО самим track_i (выход детекторного каскада шагов 9–17 — ЭТАП B!), ЛИБО пост-df0 нормализацией потребителем (FFT-conv движок 22z). Этап B получил точную цель: декодировать путь lvl_raw → track_i (буфер [rsp+0x138][band]) и пост-обработку до 678. ### Следующие шаги 1. Этап B: каскад lvl_raw→track_i (bandloop_trace, шаги 9–17, ACC@7c8); валидация теперь возможна ЖИВОЙ трассировкой тех же опов (add bps на divide 52d650/acc-dc40/att-rel 1fa0/1940). 2. Найти консюмера track/FIR после fn529fe0 (FFT-conv движок) — как track_i превращается в применённую маску аудио. 3. C++ порт RT_CASCADE=1 (Этап C) — цепь до df0 уже можно транскрибировать. ## ============ 24mm12: НАЙДЕН ДЕТЕКТОРНЫЙ КАСКАД = СТАДИЯ vt+0x28 = 180529c60; КАРТА VTABLE ПАЙПЛАЙНА ============ ### Оркестратор (0x1805300f0..0x1805305e7) fn529fe0 вызывается ВИРТУАЛЬНО: `call [ctx_vtable+0x30]` (сайт 530371), аргументы (rcx=ctx, rdx=ТАБЛИЦА TRACK = ctx+0x380, r8d=4096=[ctx+0x1a8], r9d=nbands=[ctx+0x30]). До него в том же оркестраторе — серия виртуальных вызовов других стадий. Полоса-цикл после fn: cmp [rsi+0x30],r13; r15=ctx+0x360, r14=ctx+0x2e0 — ещё таблицы-указатели в малых оффсетах ctx. ### Карта vtable ctx (live, comb_b1234) ``` +0x08 0x18052cfb0 +0x10 0x180529550 +0x18 0x1804714b0 (чужой модуль?) +0x20 0x18052b940 +0x28 0x180529c60 ← ДЕТЕКТОР +0x30 0x180529fe0 (fn) +0x38 0x180529ef0 (prep) +0x40 0x18052bbf0 +0xe8 0x1804731c0 (чужой модуль?) +0x218 0x180481210 ``` Трасса кадра: [e8,e8]? → 38 → 28(×nbands) → 30(fn). Писец track — ТОЛЬКО vt+0x28 (430/430 изменений md5). ### 180529c60 — детекторный каскад, ВСЯ функция 0x281 байт ``` lock bts [ctx+0x2404dc] ; тот же лок что в fn r9 = rdx (СТАРЫЙ track всей таблицы? базовый буфер) r12 = band*2; rcx = [ctx + r12*8 + 0x540678] ; bands_curve полосы n2 = ([ctx+0x64]±)/2+1 call 0x1805355d0(bands, rdx, n2) ; ?? первичное смешение loop по [ctx+0x1b0]: rbp = [ctx+r12*8+0x540678] call 0x180530080(rbp, rbp+4, n2-1) ; ?? call 0x18052d920(rbp, xmm1=0.5f, n2-1) ; масштаб ×0.5 ! r15 = [ctx+0x5406f8] ; vec6f8 call 0x1800020f0 / 0x180001850(rsi=rbp+4, rdx=rbp, r8=r15, r9=n2-1) call 0x180001a00 / ...(rcx=r15, rdx=rsi, xmm=0.5f/0.5d) ``` То есть «шаги 9–17» из BLOCKMAP 24mm5 на самом деле живут ЗДЕСЬ (+хелперы), а НЕ в fn529fe0: fn529fe0 получает ГОТОВЫЕ track-буферы (arg2) и строит из них ядро (df0: T⊗F), не вычисляя детектор! ### Статус Этапа B Осталось декодировать: хелперы 5355d0 / 530080 / 20f0 / 1850 / 1a00 / 52d920-семантику и роль vec6f8@6f8 — десятки инструкций, вся рекуррентия track_{t+1} замкнётся офлайн. Затем γ=1.760561 выводится аналитически. ### Инструменты раунда scripts/wine_stage_trace.py (vtable-stage трассировщик c md5-атрибуцией записей), scripts/wine_ptrace_trace.py (+TRACKSAVE/DIV/DC40/EXPVAR/FN, mapped-фильтр, строгая валидация ctx по trk==exp(scr)). Грабли: адреса-константы в патчах скрипта молча не применялись при чужом отступе — проверять installed-list; DC40=0x1800dc40 (не 0x18000dc40); DIV/DC40/EXPVAR/FN могут быть вне маппинга процесса (пропускать). ## ============ 24mm13: ХЕЛПЕРЫ КАСКАДА 529c60 ДЕКОДИРОВАНЫ (статика) ============ Резолв ILT-цепочек (movsxd rax,[1826159a0]=4; lea r10,tbl; jmp [r10+rax*8]): ``` 5355d0.f → 18e0(ILT) → 0x1800032c0 → call 0x180016140(a,b,n2) [векторная, 5-й арг из стека — MXCSR-сохраняющая, тело не дочитано] 530080.f → 2090(ILT) → 0x180010e40: b[i] += a[i] IN-PLACE (vaddss; a=rcx, b=rdx, store в rdx!) 20f0.f → 0x180011580: dst[i] = a[i] + b[i] (vaddss, dst=r8) 1850.d → 0x1800025e0: то же в double 1a00.f → 0x180004720: dst[i] = xmm1 · src[i]; skip при xmm1∈{0,1} 52d920 : dst[i] *= xmm1 (in-place, skip при 1/0 — как ffe0) ``` Грабли раунда: адреса теряли ноль при ручном наборе (0x180018e0 vs 0x1800018e0) И интермиттент пустые чтения soothe_mem.bin (лечится ретраем). ### Рекуррентия каскада на полосу (порядок вызовов из 529c60): ``` A: 16140(bands_curve@678, T_old, n2) ; до цикла, семантика ??? цикл по [ctx+0x1b0]: a: bands[1+j] += bands[j] ; j=0..n2-2 (префикс!) b: bands[j] *= 0.5 ; j=0..n2-2 c: vec6f8[j] = bands[1+j] + bands[j] ; (20f0, dst=r8=6f8) d: bands[1+j] = 0.5 · vec6f8[j] ; (1a00, xmm1=0.5f/d) → результат пишется в track-буфер полосы (ctx+0x380 таблица) ``` Замечание: префикс-аккумуляция (a) + деление пополам (b) + попарное усреднение (c,d) — это вычисление ПОЛУСУММ смежных бинов = построение иерархического сглаживания (wavelet/Haar-класс!) поверх спектра. Tin на захватах ≈ trk^5.5 в нотчах — согласуется с накоплением за много кадров такого экспоненциального смешения. ### Следующий шаг (финал Этапа B) 1. Дочитать 0x180016140 (оп A до цикла — вероятно T_new = α·T_old + β·bands). 2. Собрать рекуррентию в numpy, прогнать по 400 живым кадрам (winetrace_casc/chain_samples.pkl: scr→Tin покадрово), подобрать единственные константы из асма (не фитом!), проверить выход == Tin. 3. γ=1.760561 затем выводится из замкнутой рекуррентии аналитически. ### 24mm13-доп: оп A (0x180016140) — начало декодировано Первый внутренний цикл считает ЭНЕРГИЮ КОМПЛЕКСНЫХ ПАР bands-кривой: ``` for i: E[i] = fma(re,re, im*im), где re=bands[2i], im=bands[2i+1] (denormals→0; MXCSR сохраняется/восстанавливается) ``` далее в теле — смешение с T_old (константы 0.5/0.5 broad @181c5ce60). ⇒ track_i = рекурсивно сглаженная ЭНЕРГИЯ спектра (не амплитуда!) — это объясняет и масштабы (~trk^5.5 после логов), и нотч-концентрацию. Осталось дочитать ~100 инструкций хвоста 16140 (формула смешения с T_old) — рекуррентия замкнётся полностью, γ выводится аналитически. ### 24mm13-доп2: численная проверка рекуррентии — расхождение, нужен пер-стадийный дамп Симуляция «магнитуды пар → track» НЕ совпадает с захваченным Tin (Tin пикируется в нотчах со значениями ~442, магнитуды там ~0.3). Гипотеза на проверку: между op-A и df0 есть недоучтённый этап, ЛИБО вход каскада — другая кривая (не exp(scr), т.к. scr снят ПОСЛЕ модификаций). Решение с гарантией: добавить в tracer брейкпоинты вход/выход 529c60 и вход/выход 16140 — точные состояния bands/track до и после каждого блока. (Инфраструктура готова, адреса известны; следующий раунд.) ### 24mm13-доп3: пер-стадийные дампы живьём — op A подтверждён, вход каскада ≠ exp(scr) Трассировщик получил брейкпоинты CIN/COUT (вход/выход 529c60) и AIN/AOUT (вход/выход 16140, возврат 332c). Живые факты (comb_b1234): ``` AIN: a(rcx)=кривая состояния, ЗНАКОВАЯ, |max|=57.6 (НЕ exp(scr)!) b(rdx)=единичный буфер (init 1.0) AOUT: a — без изменений (16140 вход не трогает) b[i] = |z_i| ТОЧНО (пары a[2i],a[2i+1]) — магнитуды подтверждены CIN/COUT: track между входом и выходом каскада меняется предсказуемо. ``` ⇒ Вход детектора — НАКОПЛЕННОЕ СОСТОЯНИЕ (знаковая кривая с амплитудами до десятков), а не текущий спектр. Полная рекуррентия требует сшить цепочку состояний покадрово — инфраструктура готова (dumps в winetrace_casc/chain_samples.pkl, kind∈{CIN,COUT,AIN,AOUT,COPY,EXP,DF0}). ### 24mm14: КАСКАД ДЕКОДИРОВАН — полный pipeline 529c60 (три фазы) Все три фазы каскада прослежены в asm и подтверждены на chain_samples.pkl: **Фаза 1 — магнитуды (16140, НЕ |z|²!)** - Ключевое исправление: AOUT.b[i] = |z_i| (макс. отклонение 5.4e-6 от np.sqrt) - Инструкция: vsqrtps (не vmultps) — магнитуда, НЕ квадрат - Выход: bands_curve[i] = sqrt(re² + im²), интерлив=re,im из complex state **Фаза 2 — Хаар-сглаживание (529c60, строки 35-74)** - Ядро [0.25, 0.5, 0.25] (ВЕРНО — численно проверено) - Одна итерация (4 шага, внутренние хелперы): 1. b[i] += b[i+1] (10e40, prefix sum) 2. b[i] *= 0.5 (ffe0, scalar mul) 3. scratch[i] = b[i+1] + b[i] (11580, 3-operand add) 4. b[i+1] = 0.5 * scratch[i] (4720, scalar mul+store) - ctx[0x1b0] итераций; best-fit = 2 (rms=0.30 vs 1.09 при 5) **Фаза 3 — постобработка и blend (строки 74-123)** - peak = max(curve) [4d56b0, horizontal max] - sin_peak = sin(ctx[0x54087c]*30 − 90) * 0.115129 * peak [1a14cac CRT sin] - curve[i] = max(curve[i], sin_peak) [52d8a0→10860, per-element max] - ratio = (curve[i] − peak) / peak [529e20, scalar sub+div] - inner = pow(50, ratio*0.001) * ratio*0.001 [1a14cd6 CRT log, pow, mul] - w = 1/inner [529e5a, scalar div] - log_w = log_0.1(w) [529e8e,CRT log] — но откуда ctx[0x24]? - 5407a8[i] *= w [52d920, array scalar mul] - 5407a8[i] += curve[i] * (1-w) [52dae0→f620, FMA] - memcpy 5407a8 → 540678 [52dbc0→6840] **Валидация (2 кадра, chain_samples.pkl):** - iters=2: w=0.015, rms=0.30, corr=0.998 (лучший) - iters=1: rms=0.59; iters=3: rms=0.67; iters=5: rms=1.42 - Per-bin w: 0.084–0.100 (std=0.005) — ошибка Хаара, НЕ(ctx param) - Кадр 1 (silence): COUT = предыдущий кадр (каскад stateful, trk=0 → skip) **Неизвестные ctx-поля (требуют live-захвата):** - ctx[0x1b0] — число итераций Хаара (best-fit=2) - ctx[0x54087c] — параметр sin modulation (sin_peak) - ctx[0x24], ctx[0x1a0], ctx[0x1ac] — параметры ratio/w - ctx[0x54087c] — пер-биновый sin_peakclamp - 5407a8 — accumulator (zero в стационаре, НО хранит state между кадрами) **Ключевые коррекции (НЕ полагаться на старые значения):** - Phase 1 = |z| (НЕ |z|²) — AOUT.b подтверждает - Haar iters = 2 ( best-fit, НЕ 5) - w ≈ 0.015 (scalar, НЕ 0.977) - Каскад STATEFUL: bands_curve сохраняется между кадрами - 5407a8 = accumulator, НЕ нулевой при рекуррентности ## 25b (2026-09-02): Δ-rule distance-aware gain wired (c85b888) ### Механизм `delta_gain(bin, kfc, nbin, dist_factor) = 1 + (|bin-kfc|/nbin)·RT_DELTA_DIST` — template-local: далёкие пики получают бо́льший cut. `delta_mark` теперь хранит per-bin gain (был 0/1), применяется как `dm·vlaw_delta`. Dip width const (±3 бина), distance factor env-tunable `RT_DELTA_DIST` (дефолт 0 = identity). ### Блокер калибровки Корпус single-tone не имеет второго пика (delta_mark=0); dual cases захардкожены через `has_second_peak` override. Калибровка `dist_factor` и STATE-зависимости (`g@2000 12.15 vs 1.85`, `STATE=[ctx+0x540788]`) требует `ph*.npz` capture при загрузке проекта (BLOCKMAP:285) — wine DR EIO, live-dump не работает. ### Gate `corpus.py --compare baseline_bridge.json --tol 0.25` d=+0.000; structural 2.689 (RT_CASC=0); `fn529fe0_check`/`vlaw_check`/`twin_check` PASS. ### ph*.npz capture proof (2026-09-02) `rendersnap2.py` успешно снимает `ph*.npz` (слоты 0x540628/0x540678/0x540688/0x540768). Данные в `/tmp/opencode/`: - `rendersnap2_dual` (dual_b1q_0.5, 79 кадров, populated 40+) - `snap_t1k_b1f_1000` (60 кадров, populated 26-37) - `snap_dual300` (60 кадров, populated 30-45) - `rendersnap2_comb_b1234` (80 кадров, populated 25-37) **Key finding**: маск-цепь работает в рендер-окне на ВСЕХ кадрах (BLOCKMAP:285 устарел или относится только к single-tone init). Dual-tone cases дают стабильные populated кадры — можно калибровать Δ-rule offline. **Данные для Δ-калибровки** (dual@500+2000, populated кадр 60): - `0x540628` (scratch): [43]=-0.1849 [171]=-0.2460 (log-domain) - `0x540678` (bands_curve/mask): [43]=0.7221 [171]=0.6486 - `0x540768` (track_i): [43]=4.1498 [171]=1.5989 ## ============ 25a (2026-09-02): chain_9_19 staged, IIR4-генератор, k-mapping dedup, audit fixes ============ ### Сессия-контекст Workflow `/next-task` (Q1=Local handoff, Q2=All, Q3=Implemented/Deployed). Задачи взяты из рейтинга `BITEXACT_PLAN.md` + аудита. Всё gated `RT_*`, guard `corpus.py --compare baseline_bridge.json --tol 0.25` держит d=+0.000. ### 25a1. chain_9_19 интеграция (коммиты 9ed22d3, 2a295af, 96816f9, b41d7a4) - `dsp/fn529fe0.cpp:chain_9_19` — полная цепь: LOG#1→DIVIDE(1803a06a0 b/a)→dc40(bands−ACC)→FMA ATT/REL (half-split)→EXP#1−1→×track→×kWarp→ LOG#2→IIR4×2→FIR 52b3cd→EXP#2. `track` — новый параметр (ctx+0x540768). - **IIR4 = DOUBLE precision** (дизасм `f529fe0_full.dis:1191-1201`: `movsd/mulsd/addsd/cvtpd2ps`; down@0x340510, up@0x3c0510, acc@0x440510). - `generate_iir4_coefs()` — генератор FUN_180533340 (BLOCKMAP:135-150): freq-warp `g = (i<=fc_norm) ? fc_norm/i : pow(fc_norm/i,p)`; live-константы `DAT_1824c3d8c=0.9994880557060242`, `DAT_1824c46b8=0.9991304874420166`. ВАЖНО: `up[i] = exp(−c·g·tau/mult)` (знак МИНУС, иначе Inf), `down=1−up`. Smoke: down0=1, up0=0, down1=0.6292. Gated `RT_IIR4_GEN=1` (env: RT_IIR4_C/TAU/P/MULT/SR, дефолты 1000/1200/0.5/360/48000). - Интеграция: `framed_model.cpp` `RT_CASC=1` gate; вход — `raw_level` (am/res·scale). `process_band_structural` получил `track` параметр. - **Результаты**: bridge 1.594 (gated OFF, не тронут); structural 2.689 (RT_CASC=0); RT_CASC=1 → 31.6 (НЕКАЛИБРОВАНО, ожидаемо). - **fn529fe0_check**: добавлены chain smoke + generate_iir4 smoke; PASS. Попутно починен тест haar_one_pass boundary (8.0 не 8.5). ### 25a2. VLAW per-fc: continuous вместо дискретной решётки (f2cc0ae) - `framed_model.cpp:283-298`: 800→1200 q<1 теперь lerp `t=(fc−800)/400` (5.0/0.4 → 4.5/0.35) вместо дискретных веток. - sens-эффект теперь ADDITIVE offset (12→0, 6→−0.48α, 24→+0.5α) вместо перезаписи, стиравшей fc-выбор (баг старой решётки: sens всегда побеждал). - Gates не сдвинулись (1.594/2.689) — полный перефит требует campaign.py (8 мин/ячейка, `ph*.npz`) — вне scope. ### 25a3. k-mapping dedup (197f5ed) - `k_mapping_factor(fc,q,sens)` — вынос двух идентичных блоков (lvl_in + raw_level) в helper. `k_fc=1.0` + opt-in `RT_KMAP_FC` (W_eq). - RT_KMAP=1 не влияет на TOTAL (2.689) — ожидаемо, т.к. калибровка α/β уже поглощает k на sens=12. ### 25a4. Аудит `/audit-project` — Phases 1+2 применены (e4c5348) - Убран dead conditional `if (pool_w > 0 && !lut_off == false) {}`. - `DBG_CASC` fprintf в per-frame цикле → gated `RT_DBG_CASC`. - Убран дублирующий zero-loop в `spectral.cpp buildFirFromMask`. - Комментарии о разнице шкал FFT (1/N canonical vs 2/half plugin). - Документирован blend 0.8 (декомп 0x5406f8, xmm10@1824c3e28). - `assert(spectrum != nullptr)` в processFrame. - НЕ применено (осознанно): thread_local→context (конфликт с golden rule — chain нужен reentrant caller), SpectralProcessor new[]→vector, Config struct для getenv — оставлено как техдолг. ### 25a5. Блокеры (без изменений) - ph*.npz нет на этой машине; live-dump не работает (маск-цепь при загрузке, BLOCKMAP:285; wine DR EIO). Chain калибровка ждёт capture при INIT. - prd.md создан (карта проекта, 293 строки). - Structural baseline в README/prd: 2.689 (L/R), исторический 0.341 недостижим на HEAD после M/S→L/R (e343b0a). ### Коммиты сессии ``` e343b0a prd.md + render48k L/R baseline 9ed22d3 chain_9_19: IIR4 double + FIR, haar fix 2a295af chain integrate RT_CASC gate 96816f9 chain: IIR4 generator + audio path 2201a57 docs: baseline 2.689 f2cc0ae vlaw continuous per-fc + additive sens b41d7a4 chain: RT_IIR4_GEN wire + smoke tests 197f5ed k_mapping_factor dedup e4c5348 audit fixes Phases 1+2 2f854cd spectral vectors + vlaw_check target a881ee2 AGENTS.md: vlaw_check in test list c85b888 Δ-rule: distance-aware template-local gain (blocked on ph*.npz) ```