- Add RT_CASC, RT_IIR4_GEN, RT_IIR4_C/TAU/P/MULT/SR, RT_FIR, RT_FIR_Q, RT_KMAP_FC, RT_DELTA_DIST, RT_DELTA_STATE, RT_DBG_CASC to env flags table - Update chain_9_19 section: WIRED+GATED (was CRITICAL) - Add capture proof section: rendersnap2.py + /tmp/opencode/ datasets - Remove outdated PTRACE live-dump hazards (superseded by /proc/pid/mem capture)
134 lines
9.6 KiB
Markdown
134 lines
9.6 KiB
Markdown
# 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 vlaw_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`) |
|
||
| `RT_CASC=1` | цепь 9–19 (`chain_9_19`, IIR4×2 double + FIR min-phase, gated) |
|
||
| `RT_IIR4_GEN=1` | генератор IIR4 (FUN_180533340) vs proxy `kIIR_A1/B1` |
|
||
| `RT_IIR4_C/TAU/P/MULT/SR` | параметры генератора IIR4 (1000/1200/0.5/360/48000) |
|
||
| `RT_FIR=0/1` | пуск FIR min-phase (default 1) |
|
||
| `RT_FIR_Q=x` | показатель FIR (default 0.8002203702926636, live `.rdata`) |
|
||
| `RT_KMAP_FC=1` | fc-фактор k-mapping через W_eq (default off) |
|
||
| `RT_DELTA_DIST=x` | Δ distance factor (template-local gain, default 0=flat) |
|
||
| `RT_DELTA_STATE=1` | STATE-dependent Δ (опен-ин, wired, empty) |
|
||
| `RT_DBG_CASC=1` | gate DBG_CASC fprintf (per-frame spam otherwise) |
|
||
|
||
Полный набор 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 (WIRED, GATED)
|
||
|
||
`chain_9_19` из `dsp/fn52fe0.cpp:259` (BLOCKMAP:620-644) — per-frame цепь с **persistent ACC state** (`@ctx+0x5407c8`). Интегрирована в `framed_model.cpp:process_band_structural` через `RT_CASC=1`. IIR4×2 использует double precision (movsd/mulsd per disasm 1191), FIR min-phase бит-точен до df0 (0.0065 dB). Генератор IIR4 (`generate_iir4_coefs`, FUN_180533340) env-gated `RT_IIR4_GEN`.
|
||
|
||
**Статус:** chain wired и gated (`RT_CASC=0` default = canon untouched). Все unit checks PASS, corpus gate d=+0.000. Калибровка I/O format требует `ph*.npz` capture (rendersnap2.py) — capture proof получен (2026-09-02).
|
||
|
||
**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?)
|
||
|
||
**Capture proof (2026-09-02):** `rendersnap2.py` снимает `ph*.npz` (слоты 0x540628/0x540678/0x540688/0x540768) через `/proc/pid/mem` без ptrace-брейкпоинтов. Данные в `/tmp/opencode/{rendersnap2_dual,snap_t1k_b1f_1000,snap_dual300,rendersnap2_comb_b1234}`. Маск-цепь работает в рендер-окне (BLOCKMAP:285 устарел).
|
||
|
||
---
|
||
|
||
## Чистая работа
|
||
- Не коммитить: `*.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`.
|