@sdd-flow/CLAUDE.md
Layout — src/core/ libopenmpt playback core (ctk_core) — the single source of audio, transport and introspection; also owns error.{h,c} (CtkStatus/CtkError) and the dependency-free ctk_audio_config.h · src/audio/ src/tui/ src/cli/ shell, all core-backed · third_party/miniaudio · samples/ reference fixtures (DREAMS.IT, SPICINKO.S3M, PANDEMON.MOD, FEELINGS.XM). The custom DSP path (src/loader/ src/engine/ src/mixer/) is gone — deleted by EPIC-013; libopenmpt is the only decoder.
Conventions — never decode raw pattern/effect bytes in the shell: libopenmpt's effect-letter assignment differs from the old ordinal scheme, so render column text from the *_str fields ctk_core already formats
Build & test — ./build.sh (CMake) · ./test.sh (default fast gate) · ./test.sh --asan · ./test.sh --valgrind
Gotchas — Full-song playthrough tests carry the CTest label slow and are EXCLUDED from the default ./test.sh gate (they run 90–400s each); include them with ./test.sh --all or CTK_RUN_SLOW_TESTS=1. Full audible correctness is verified manually by the human, off the gate. · Tests that open a REAL audio device carry the label real_device and are excluded from --valgrind only (the host's PulseAudio backend emits ~53 benign "still reachable" records that are not ctracker's; ASan still covers that path every gate) — re-check that rationale if the host's audio backend ever changes. · libopenmpt's channel VU reads stale immediately after a position jump until the next render, so never trust an instant VU dip after seek/stop/restart.