# AGENTS.md — runbook for AI agents working in this repo > **Статус → `README.md:13` (единственный источник TOTAL).** Этот файл — только runbook: сборка, метрика, env-флаги, tooling hazard. > Полный журнал — `handoff/NOTES_LEVEL_INDEX.md` → `handoff/NOTES_LEVEL.md` (живая голова) + `handoff/archive/`. > Карта метода — `handoff/BLOCKMAP_529fe0.md`. ЭТОТ ФАЙЛ ЧИТАЙ ПЕРВЫМ. ## Золотое правило (обязательно) 1. **Цель — bit-exact реверс кода**, НЕ эмпирическая подгонка кривых. Каждый параметр должен иметь источник (decomp адрес / live-таблица), а не быть подогнанным числом. Если берёшь эмпирику — явно пометь и занеси в «осталось». 2. Источник истины — **декомпиляция** (`decomp_funs.txt`, `decomp_funs2.txt`, `handoff/nls_dasm/*.dis`) и **live-снимки** (`/tmp/snap_rt.bin`). Обновлять заметки при любом новом декоде. 3. Команды через `rtk` (токен-фильтр), включая цепочки `&&`. ## Сборка и тесты (канон) ```bash # собрать библиотеку + framed_test (bridge, 44.1k) + render48k (структурная цепь, 48k/4096) cmake -S dsp -B dsp/build cmake --build dsp/build --target framed_test render48k # bridge-рендер (N=2048, hop=512, SR 44100, sqrt-Hann OLA) # формат полосы: fc,q,sens,level_scale (одна полоса в comma-форме) ./dsp/build/framed_test /home/m/soothe-bt/tone1kq.wav /tmp/o.wav 678.7611083984375,0.99999785,12,600 # многополосный: через отдельные аргументы fc q sens scale (см. framed_test.cpp) # структурный рендер (внутренняя сетка 48000/4096: resample -> FUN_180529fe0 chain -> resample) ./dsp/build/render48k /home/m/soothe-bt/tone1kq.wav /tmp/o48.wav 1000,0.99999785,12 # корпусные прогонки (62 случая, честная 24-bit метрика) python3 scripts/corpus.py # bridge + guard --compare python3 scripts/corpus_structural.py # структурная цепь python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json # отдельные модули (bit-exact черные проверки) cmake --build dsp/build --target twin_check tables_check fftconv_check vlog_check leveltrack_check levelpath_check fn529fe0_check exp2_check ./dsp/build/twin_check # float-parity twin-резонатора ``` > **TOOLING HAZARD**: cmake пропускает пересборку при изменении исходника в ту же секунду — > параметрические свипы молча гоняют STALE бинарь. Протокол: `touch` исходника перед сборкой + > проверять свежесть mtime бинаря (пример: /tmp/sweep_fresh.py паттерн в NOTES_LEVEL:21a). ### Метрика (tone-cmp, Goertzel stead-state) Сравнивать **тримкнутый** выход (длина = длина входа) с референсом `*_ref.wav` / `t1kq_b1f_*`: ```python # ref: t1kq_b1f_678.7611083984375.wav (или t1kq_b1f_1000.wav и т.д. по fc полосы) # вход: tone1kq.wav (тон 1000 Гц) import wave, numpy as np def load(p): w=wave.open(p,'rb'); n=w.getnframes(); d=w.readframes(n) return np.frombuffer(d,dtype=np.int16).astype(np.float64).reshape(-1,w.getnchannels()).mean(1)/32768 def ta(x,f,sr=44100): x=x[-int(0.75*sr):]; t=np.arange(len(x))/sr; w=2*np.pi*f return np.hypot(2*np.sum(x*np.cos(w*t))/len(x), 2*np.sum(x*np.sin(w*t))/len(x)) def db(a): return 20*np.log10(max(a,1e-9)) inp=load('/home/m/soothe-bt/tone1kq.wav') out=load('/tmp/o.wav') ref=load('/home/m/soothe-bt/t1kq_b1f_678.7611083984375.wav') # fc полосы = 678.76 err = db(ta(out,1000)/ta(ref,1000)) # err в dB, цель <0.1 ``` Тестовый корпус (`/home/m/soothe-bt/`, вне git): `tone1kq.wav`(вход), `t1kq_b1f_.wav` (рефы), `comb.wav`/`comb_ref.wav` (мультиполосный). ## Структура ключевых файлов - `dsp/framed_model.{cpp,hpp}` — C++ порт mask-apply цепи (ГЛАВНЫЙ файл). - `dsp/render48k.cpp` — пайплайн 48000/4096 (resample → chain → resample). - `dsp/twin.{cpp,hpp}` — twin FUN_180535880 (float-parity). - `scripts/corpus.py` / `corpus_structural.py` — харнессы + guard `--compare`. - `handoff/NOTES_LEVEL.md` — живой журнал (голова 24mm5+, хвост → `archive/`). - `handoff/NOTES_LEVEL_INDEX.md` — оглавление журнала. - `handoff/BLOCKMAP_529fe0.md` — блок-карта FUN_180529fe0. - `handoff/nls_dasm/` — 134 дизассембла. ## Env-флаги экспериментов (актуальные) | флаг | действие | |------|----------| | `RT_VLAW=1` | закон применённой стадии: `mask=10^(−(α·ln1p(lvl/β)+c)/20)` | | `RT_SYN=1` | STFT БЕЗ синтез-окна (найденный слой плагина) | | `RT_WIN=0/1/2` | окно анализа: sym-hann / periodic / rect | | `RT_NOWARP=1` | отключить warp-модуляцию маски | | `RT_NOIIR3=1` | отключить IIR3 ×2 | | `RT_IIR12=0` | отключить частотные IIR1/2 (КРИТИЧНО с RT_VLAW — иначе размывают дипы) | | `RT_DUMP_BIN=` (+`RT_DUMP_FRAME=N`) | дамп тракта бина N: am/res/lvl_raw/band_level/prewarp/w | | `RT_VDBG=1` | stderr-печать vlaw-вычислений | | `RT_FAITHFUL=1` | faithful-цепь `dsp/fnfaith.cpp` (детекторный каскад) | | `RT_FIRCONV=1/3` | FIR-применение (1=complex-mul, 3=`1.019·mask^1.8345`) | Полный набор dual-решения: `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0`. ## Инструменты | скрипт | назначение | |--------|-----------| | `scripts/rendersnap2.py [cap] [outdir]` | мягкий STOP-снаппер (RENDER_FILE из rpp!) | | `scripts/campaign.py ` | ячейка параметризации (~8 мин) | | `scripts/disasm_func.py [len]` | capstone-дизасм с RIP-константами | | `scripts/iat_name.py` | рантайм-резолв импортов (SIGSTOP!) | | `scripts/probe_states.py` / `probe_mem.py` / `dump_dispatch.py` | live state/память/таблицы | | `scripts/hunt2.py` | перебор ctx-инстансов | | `scripts/scan_pairs.py`, `scan_lutsub.py` | диагностика памяти | Датасеты: `/tmp/opencode/sc_{q,sens,qmap,k,f,d}*` + `tract_*` + `*.pkl/.npz` (см. `NOTES_LEVEL_INDEX.md`). Открытые пробелы → `BITEXACT_PLAN.md`. ## Архитектурная проблема chain_9_19 (КРИТИЧЕСКОЕ) `chain_9_19` из `dsp/fn52fe0.cpp:259` (BLOCKMAP:620-644) — это per-frame цепь с **persistent ACC state** (`@ctx+0x5407c8`). Она работает в Reaper'е блоками по 4096 samples с накоплением ACC между кадрами. **Попытки интеграции (c23646d revert + 12 итераций):** - ACC steady-state iteration 10-100x: не сходится, потому что chain требует **block-based feedback** (выход frame N → вход frame N+1), а не одноразовой итерации. - Frame-based вызов с `casc_curve` (cascade output ~0.0004): chain output = 0 (неправильный масштаб input). - Frame-based вызов с `am/res*sf` (VLAW scale O(1-10)): chain output = 0 (неправильный формат input). - `f6f8_` (IIR1 out buffer) не был инициализирован → segfault. **Критический пробел: НЕИЗВЕСТЕН формат I/O chain_9_19:** 1. Какой масштаб input? (casc_curve ~0.0004 vs VLAW scale O(1-10) vs что-то другое) 2. Что представляет output? (mask? level curve? log-domain vs linear?) 3. Как ACC (track) взаимодействует с chain в рамках одного кадра? **Правильный путь:** 1. **Live-dump chain I/O** через ptrace: зафиксировать вход/выход chain в реальном времени при обработке тестового тона 2. **Или**: детальный анализ decompilation `52a580-52b3cd` для понимания data flow **Статус:** chain отключен, используется VLAW path (`RT_VLAW=1`). Target `0.314→<0.05` требует **live-dump chain I/O** или полной транскрипции data flow из decompilation. **Live-dump chain (ptrace):** chain вызывается в рантайме (DIV#0-7 на `0x1803a06a0`): - Input: `a` = bands_curve (VLAW output, min=0, max=17.6, mean=0.048) - `b` = tmp6f8 (step 9b accumulation, min=0, max=0.8, mean=0.8) - 8 DIV hits на одном кадре (dual-band: 2 bands × 4 iterations?) **Для полного live-dump нужно:** breakpoints на всех шагах 9-19 (LOG#1, DIVIDE, dc40, FMA, EXP#1, array-mul, kWarp, LOG#2, IIR4×2, EXP#2). --- ## Чистая работа - Не коммитить: `*.bin`, `*.wav/rpp`, `*.log`, `dsp/build/`, `ghidra-proj/`, `dl/lib/bin/include/`, `regions*/`. См. `.gitignore`. - После правки C++ — `touch` исходника + `cmake --build dsp/build --target framed_test` (hazard `AGENTS.md:66`) + `python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25`.