daw-smoke
Real-DAW (REAPER) functional smoke for reload/editor/format-adapter changes — opt-in, scoped, headless-safe, zero-pollution
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Real-DAW (REAPER) functional smoke for reload/editor/format-adapter changes — opt-in, scoped, headless-safe, zero-pollution
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Build, edit, validate, explain, render, import, or integrate Pulp timeline projects through the CLI, MCP tools, or C++ SDK. Use for sequencers, arrangements, clips or notes, tempo and meter maps, automation, takes and comps, freeze, capture-to-timeline workflows, durable journals, project persistence, DAWproject import, Standard MIDI File import and export, and agent-driven timeline operations.
Pulp in the browser — the WAM v2 and WebCLAP adapters, the wasm runtime, the Skia/WebGL2 browser window host, and the WebGPU (emdawnwebgpu) GPU-audio lane. Covers what does and does not compile to wasm, the worklet-thread constraints (no std::thread, no fetch, no navigator.gpu), the non-realtime tick, memory growth, how to PROVE a GPU lane actually ran rather than silently falling back to CPU, and the silent-failure traps that make a wasm build "work" while producing no audio or no pixels.
Local and cloud CI for Pulp — validate branches, create PRs, merge on green. Handles "push to main", "ship this", "run CI", "check PR", and "list PRs".
Stand up a fast, cached, isolated, disposable macOS CI lane on Tart — layered golden VM images, ephemeral per-job GitHub Actions runners, host-mounted caches, and a reusable per-repo vm-image manifest. Use when setting up VM-based CI for Pulp or generalizing it to another repo, building/refreshing golden images, wiring ephemeral runners, or debugging the VM lane.
Pulp timeline transport, immutable compiled playback programs, bounded arrangement audio rendering, block-level publication latches, stable shells, and ProcessContext projection.
Load, run, and test VST3 / AU / CLAP / LV2 plugins from Pulp code. Use when working on `core/host/` (scanner, plugin_slot, signal_graph), when adding a new format backend, when wiring a plugin into a SignalGraph, or when writing an integration test that needs a real plug-in binary.
| name | daw-smoke |
| description | Real-DAW (REAPER) functional smoke for reload/editor/format-adapter changes — opt-in, scoped, headless-safe, zero-pollution |
Use this when you need proof that a reload / editor / format-adapter behavior
actually works inside a host, beyond what auval / pluginval /
clap-validator give (those prove scan+load, not functional behavior).
Harness: tools/testing/daw-smoke/reaper_smoke.py (+ insert_and_float.lua and
sequence_loop_seek.lua). The scraper + mode dispatch are unit-tested with no
REAPER in tools/testing/daw-smoke/test_reaper_smoke.py.
Full rules: docs/guides/daw-smoke.md. CLAUDE.md has the one-paragraph policy.
--mode, default reload)reload — the original flow. Seed watched DSP variant A, insert+float the
FX, copy variant B over the watched path, scrape swapped DSP / reload rejected.live-plugin-swap — a live plugin-INSTANCE swap inside a Pulp host that
hosts another plugin in a SignalGraph. Insert+float the host FX, write a swap
request to --watched-swap-request, then scrape [live-swap] committed (PASS)
vs live plugin swap refused (FAIL). The success marker is NOT logged by the
core swap path — SignalGraph::prepare_swap logs ONLY refusals. The host plugin
must emit [live-swap] committed itself from its
NodeLiveSwapPolicy::on_instance_swapped observer (that is the seam the smoke
scrapes and the headless test exercises). The DAW-free, CI-runnable mirror is
test/test_signal_graph_live_swap_continuity.cpp, which drives the same
stage + prepare_swap commit through process() and asserts sample continuity
(no dropout/xrun) across the swap block for every hosted format (VST3/AU/CLAP/
LV2). Use the REAPER mode as the local-only in-host confirmation.sequence-loop-seek — set a loop region on the REAPER timeline, play, and
perform scripted seeks (into/out of the loop and across the wrap) against a
plugin that embeds a sequence. sequence_loop_seek.lua drives the transport
via a deferred pump (GetSet_LoopTimeRange + GetSetRepeat + OnPlayButton +
SetEditCurPos seeks), handshaking FX_SHOWN→SEEKS_DONE through the status
file. The plugin emits per-block markers [seq-loop] blk host_qn=.. seq_qn=.. active=.. jump=.. dropout=..; the scraper (analyze_seq_loop_log) asserts the
sequence read position TRACKED the host playhead within --pos-tolerance-qn on
every block — a free-running counter that ignores the host jump is caught as
drift → FAIL. PASS requires >=1 wrap AND >=1 seek, note activity, and no
dropout; otherwise INCONCLUSIVE (a SKIP/INCONCLUSIVE is never a PASS). This is
the harness half of Phase-2 DoD Proof #2 — the full proof also needs the
embedded-sequence plugin (Gate-5/6) and a real REAPER run. The scraper is
unit-tested with synthetic logs (incl. a negative drift/dropout case), so the
parse logic is proven without REAPER.ReaperSession): fresh portable
dir, temp scan path, pre-warm scan, scripted insert+float, guaranteed teardown.
They differ only in what they SEED, the TRIGGER they fire once the FX is shown,
and how they VERIFY the captured log. Add a mode by writing seed/trigger/verify
around ReaperSession, not by duplicating the launch plumbing.render_to_png capture PASSES but you're not sure it
works in a host. It usually doesn't tell the whole story — see the gotcha below.~/.config/pulp/daw-smoke.toml; a
ship gate only when enabled + gate + the diff hits the allowlist. NOT every
build. DAW-Smoke: skip reason="..." trailer bypasses a single commit.parameter contract differs) while every headless capture passed. Only REAPER surfaced it (fixed
4a6e048f4: the contract gate now excludes an adapter-synthesized bypass). If you
change reload/editor/adapter code, a real-DAW smoke is the check that catches this.screencapture returns BLACK frames in an agent/SSH context (no Screen-
Recording TCC grant). Do NOT verify visually via screencapture. Verify by scraping
the plugin reload log from REAPER's captured stdout (swapped DSP = applied,
reload rejected + reject-diff = fail, loaded initial logic = it loaded). Use
render_to_png (e.g. examples/hot-reload-morph capture) for human-viewable visuals.pkill -9 -x REAPER stragglers, timeouts, and an INCONCLUSIVE outcome — never a
spurious FAIL. A STATUS: None/no-FX-load run is INCONCLUSIVE, not PASS.reaper.ini vstpath/clap_path), never ~/Library/Audio/Plug-Ins. AU can't be
scanned from a custom path, so --format au installs to the real Components folder
and MUST uninstall on exit. Cleanup ALWAYS runs (finally): kill REAPER, rm temp
dirs, uninstall AU. Prefer VST3/CLAP — the reload path is format-agnostic (shared
editor pump), so they validate the same code the owner runs as AU in Logic.~/.config/pulp/secrets/reaper-license.txt,
personal non-commercial) — never commit, echo, or bake into a TartCI golden. Local
Macs (m1/m3/m5) are already serialized; local is the primary lane.