Files
soothe2-re/AGENTS.md
T

140 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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_<fc>.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=<f>` (+`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 <rpp> [cap] [outdir]` | мягкий STOP-снаппер (RENDER_FILE из rpp!) |
| `scripts/campaign.py <base> <fc> <q> <sens> <in> <drives> <out>` | ячейка параметризации (~8 мин) |
| `scripts/disasm_func.py <VA> [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`.