soundcraft · git:20261026.76415fe · 2026-10-26 · sha256 9fe30940e6f2a4eb
soundcraft git:20261026.76415feA
Immutable. This exact content never changes and is served at /api/v1/blob/9fe30940e6f2a4eb.
---
name: soundcraft
description: >-
SoundCraft is an open-source, pure-Rust digital audio workstation (a clean-room Pro Tools-style
DAW) for macOS, Windows, Linux, FreeBSD and the browser: multitrack audio and MIDI editing, a
full mixer with 30 built-in plug-ins plus CLAP/VST3/AU hosting, automation, recording,
bounce/export, video and surround — and its 482 engine commands are reachable from the CLI, a
JSON control channel and an MCP server. Trigger this skill whenever the user mentions SoundCraft
(or `.scraft`/`.scraftz`/`.scgrp`, `soundcraft-cli`, its control channel or MCP server, or "the
SoundCraft app"), asks what it can or cannot do, which audio/session/MIDI formats it reads or
writes, its keyboard shortcuts, or how to launch, script, automate, inspect or troubleshoot it —
and equally when they never name it but describe the job ("edit this multitrack session", "bounce
a mix to WAV", "record takes from my interface", "convert audio", "drive a DAW from an agent").
Do not infer SoundCraft behaviour from Pro Tools knowledge: read this first.
---
# SoundCraft
**Audit basis:** this checkout is tag `v0.13.1`, commit `01b39850ad16e84b45904ba07bd5cec7cf96c4b7` (single-commit shallow clone; the release commit is dated 2026-10-25). The installed `/Applications/SoundCraft.app` reports version `0.13.1`, its `Info.plist` records `SoundCraftBuildCommit` = the same full SHA, and its binary prints `SoundCraft 0.13.1`, so the bundle matches this source. **Verify against your installed version** if the ArtCraft Launcher has auto-updated since. The claim→evidence table with tiers and counting method is `evidence.md` next to this file.
## Overview
SoundCraft is an open-source, pure-Rust digital audio workstation: a clean-room reimplementation of the Avid Pro Tools workflow (recording, editing, mixing, automation, bounce) that also runs in a browser (`README.md`, `docs/target-app-parity.md`). The parity target is Pro Tools Ultimate 2026.4.1. It is a Cargo workspace of 14 layered crates plus three entry points — the desktop app, `soundcraft-cli`, and a WebAssembly build (`Cargo.toml`, `docs/architecture.md`). It is **pre-alpha**: the repo's measured report claims 397/512 menu leaves implemented by the engine and 482/512 (94.1 %) with UI-layer aliases, and estimates ~40 % readiness. The alpha gate lists interface recording (per-channel routing, one duplex clock, latency compensation — device/rate/buffer choice itself exists), MIDI recording (no device layer at all) and session interchange as failing (`README.md`, `ROADMAP.md`, `docs/roadmap.md`, `docs/gaps.md`, `docs/parity-checklist.md`). Source: `github.com/storytold/soundcraft` (organisation `storytold`).
## Capabilities (verified)
### The command layer is the API (start here)
- **482 engine command specs** at this tag, all in the 31 modules registered by `command_specs()` (33 files under `crates/engine/src/cmd/`). Each spec carries id, label, menu path, shortcut, params doc, enabled reason and run function (`crates/engine/src/cmd/mod.rs` `command_specs`, `cmd!` macro; `crates/engine/src/cmd/plugin_state.rs` holds the one direct `CommandSpec`, `mix.insert_state`).
- Breakdown: 481 ids come from literal `cmd!` invocations plus four local helper macros — `toggle!` (17), `flag!` (61), `radio!` (6), `option!` (10) — and 1 is the direct struct. By prefix: view 96, edit 94, track 66, event 35, options 35, clip 33, mix 29, setup 20, file 17, transport 13, session 11, automation 10, midi 6, engine 6, markers 6, video 3, audiosuite 1, app 1.
- **Previous skill:** its count of 480 was correct for v0.12.1; this tag is **+2** — `clip.auto_rename` (new module `crates/engine/src/cmd/auto_rename.rs`) and `edit.strip_silence_preview` (new query spec in `crates/engine/src/cmd/edit.rs`). Verified by id-set comparison against the v0.12.1 tree; nothing was removed.
- **67 UI-layer commands** (windows, dialogs, `ui.dock`, `ui.theme`, `ui.audio_device`, `ui.set`, `window.*`) live in `crates/ui-egui/src/menus.rs` (`UI_COMMANDS`), not in the engine registry; **+1** vs v0.12.1 (`view.scroll_into_view`, "Scroll Into View"). The desktop control channel appends them to `engine.commands`, so a running app reports **549 ids** (482 + 67, no overlap); `soundcraft-cli commands` and headless MCP report **482**.
- **CLI** (`soundcraft-cli`, 10 subcommands; `apps/soundcraft-cli/src/main.rs` module doc): `info FILE`; `convert IN OUT [--bit-depth 16|24|32f] [--rate HZ] [--normalize] [--dither | --no-dither]`; `run [--in S.scraft | --demo] [--cmd ID[=JSON]]… [--bounce OUT] [--start T --end T] [--save S.scraft] [--inspect]`; `script FILE [--in S | --demo] [--save S] [--bounce OUT] [--start T --end T] [--keep-going]` (one `ID JSON` per line, `#` comments); `commands [FILTER]`; `describe ID`; `app [--port P] METHOD [JSON]` (default port 7979 or `SOUNDCRAFT_CONTROL_PORT`); `mcp [--connect PORT] [--demo] [--automation-read-root DIR]… [--automation-write-root DIR]…`; `parity [--write PATH]`; `plugins`. `--version` prints `SoundCraft CLI <ver>`.
- Every subcommand validates its options: an unknown `--flag`, or a value-taking flag with no value, exits 1 instead of silently running without it (`check_options`). `--dither`/`--no-dither` are new; without them `convert` dithers only when the output loses resolution (fewer bits, a float/lossy source, or resampled/normalized samples) (`auto_dither`, `parse_dither`).
- `run`/`script` `--bounce` with no `--start`/`--end` bounces the **whole session**, not whatever selection the commands left (`finish`). `--save` writes with `replace: true`.
- `app` shorthand: a dotted command id is wrapped as `engine.execute`; accepted control methods stay methods. Caveat: `ui.scroll` and `ui.zoom` are **not** in that whitelist (`is_control_method`), so `soundcraft-cli app … ui.scroll/ui.zoom` is misrouted as a command and fails; send those two over the raw TCP channel. UI commands such as `ui.dock`/`ui.theme`/`ui.audio_device` work through `app` because the app's dispatcher handles UI commands.
- **MCP over stdio** (`crates/automation/src/mcp.rs`): protocol `2025-06-18` (accepts `2025-03-26`, `2024-11-05`); **17 tools** (unchanged). Core: `list_commands`, `execute`, `batch`, `inspect_session`, `new_session`, `open_session`, `save_session`, `import_audio`, `bounce_mix`, `parity`. App-only (need `soundcraft --control` + `mcp --connect`): `screenshot`, `ui_inspect`, `ui_set`, `menu_invoke`, `click`, `drag`, `key`. Resources: `soundcraft://session` (full inspect JSON) and `soundcraft://commands`.
- `batch` runs up to **1000** calls (`MAX_BATCH_CALLS`); a larger array is rejected before anything runs. Any failed call makes the tool result `isError: true`; `keep_going` still runs the rest and lists every result.
- Tool schemas are a subset: `new_session`/`open_session`/`save_session`/`import_audio`/`bounce_mix` forward their arguments unchanged to the engine command, so extra engine params (`source`, `dither`, `fold_down`, `new_tracks`, `paths`, `replace`) work. Verify with `describe`.
- **Desktop control channel**: `soundcraft --control <port>` or `SOUNDCRAFT_CONTROL_PORT`; newline-delimited JSON over TCP on `127.0.0.1`, one reply per line, and `SOUNDCRAFT_CONTROL_PORT=<port>` printed on start (`apps/soundcraft/src/control_server.rs`). At most 16 connections; request lines are bounded (~85 MiB, sized for a 64 MiB `mix.insert_state` blob). **17 canonical methods** (`crates/ui-egui/src/control.rs` `handle`): `engine.execute` (alias `command`), `engine.commands`, `engine.parity`, `session.inspect` (alias `document.inspect`), `ui.inspect`, `ui.menu.list`, `ui.menu.invoke`, `ui.set`, `ui.move`, `ui.click`, `ui.drag`, `ui.scroll`, `ui.zoom`, `ui.key`, `ui.text`, `ui.screenshot`, `app.quit`. `ui.dock`, `ui.dock.reset`, `ui.theme` and `ui.audio_device` are **UI commands**, invoked through `engine.execute`, not top-level methods.
- `ui.screenshot {path?}` returns `{path,width,height}` or `{png_base64,width,height}`; it needs a presented frame and fails with "no frame was presented (is the window visible?)".
- Offscreen UI, no window: `cargo run -p soundcraft-ui-egui --example ui_shot -- out.png [script.jsonl] [--blank] [--size WxH] [--system-theme light|dark|none]`; script lines are `{"method","params"}` control requests, `{"shot":"x.png"}` or `{"steps":n}` (`crates/ui-egui/examples/ui_shot.rs`).
- **Path confinement is MCP-only**: `--automation-read-root` / `--automation-write-root` confine every path a command reads or writes for the in-process MCP server; **each flag may be given more than once** (all roots of a kind apply), a kind left out grants no access of that kind, roots must exist, and `--connect` together with roots is refused (`apps/soundcraft-cli/src/automation_args.rs`, `crates/engine/src/file_access.rs`, `docs/mcp.md`). The desktop control channel has no equivalent confinement.
- **Command gating you can rely on:** a command whose `enabled` check fails still runs when the params name targets (`track`, `tracks`, `clip`, `clips`, `sources`, `start`, `end`, `at`) — the command validates them itself (`crates/engine/src/lib.rs` `execute`). While recording, `session.new/open/close/revert` and `app.quit` are refused with "stop recording first". A panicking command returns `EngineError::Internal` and restores the document, revision, dirty flag and side effects (clipboard, messages, transport requests, last selection).
### Editing and arrangement
- Edit window with tracks/clips/waveforms; Mix window (also as a separate native OS window through `window.mix_separate`); movable/dockable panels Tracks/Groups, Clips, MIDI Editor, Memory Locations, Undo History with `ui.dock` / `ui.dock.reset`. Panel ids are `tracks`, `clips`, `midi`, `memory`, `history` plus the protected document panel `canvas`; operations `open`, `close`, `activate`, `float` (finite positive `rect`), `move` (`target` + `zone: tab|center|left|right|top|bottom|above|below`), and the advanced `moveFloating`, `resizeSplit`, `setStackOpen`, `resizeStack`; layouts persist in UI preferences (`crates/ui-egui/src/docking.rs`, `docs/control-protocol.md` "Panel docking"). Docking now runs on the shared `craft_ui::docking` `DockCommand`/`UiDock` vocabulary.
- Five edit modes Shuffle, Slip, Spot, Grid, Grid-Relative; seven tools Zoom, Trim, Selector, Grabber, Scrubber, Pencil, Smart (`crates/model/src/session.rs` `enum EditMode`, `enum Tool`).
- Fades/crossfades in five shapes Linear, EqualPower, SCurve, Exponential, Logarithmic (`crates/model/src/clip.rs` `enum FadeShape`); `edit.fades_create` refuses a `shape` that is not one of those ids **including a non-string value**; cut/copy/paste/duplicate/repeat/shift/insert-silence/separate (selection, grid, transients)/heal/consolidate/strip-silence/nudge; `edit.tab` (Tab to Transient, `back`/`extend`), `view.zoom_toggle`; loop, quantize-to-grid, clip gain and gain envelope, clip effects, edit locks, colour, ratings (`crates/engine/src/cmd/edit.rs`, `edit_more.rs`, `clip.rs`, `clip_more.rs`, `tab.rs`, `zoom_toggle.rs`).
- Clip looping is a command pair: `clip.loop {clips?, count?, length?, fill?}` (count includes the clip; `length` is the whole looped clip with the last loop cut short; `fill` loops to the next clip or the session end; max 1000 loops) and `clip.unloop {clips?, mode?: remove|flatten}` (`crates/engine/src/cmd/clip.rs`, `clip_more.rs`). `fill` is new at this tag.
- Clip naming on separation: `clip.auto_rename {clips?, name?, number?, zeros?, suffix?}` — "Clip List › Auto Rename" — sets the naming scheme the pieces created by later cuts take (one number sequence for all chosen clips; no `name` clears the scheme). Schemes live in the session as `auto_names` and travel with copied/pasted clips across sessions (`crates/engine/src/cmd/auto_rename.rs`, `crates/model/src/session.rs` `AutoName`, `prune_auto_names`).
- `edit.strip_silence {threshold_db?, min_length_ms?, pad_before_ms?, pad_after_ms?, mode?: strip|extract|separate, name?, number?, zeros?, suffix?}`: strip removes silence, extract removes the audio and keeps the silence, separate cuts at the silence edges and removes nothing; with a `name` the clips left in the selection are numbered. `edit.strip_silence_preview` (query) returns the same ranges without changing anything (what the Strip Silence window draws) (`crates/engine/src/cmd/edit.rs`, `crates/engine/src/io.rs` `strip_silence`, `StripNames`).
- Playlists with comping lanes, clip groups (`.scgrp`), Clip List, track folders, VCA masters, track freeze/commit/bounce, file-based track presets (`track.save_preset`/`track.load_preset`, JSON) and Import Session Data between SoundCraft sessions (`file.import_session_data`) (`crates/engine/src/cmd/track.rs`, `track_more.rs`, `clip_list.rs`, `setup_more.rs`; `crates/engine/src/clip_group_file.rs`). `track.commit` keeps the source track's exact `ChannelFormat` and refuses formats wider than the renderer's 16 channels (`crates/engine/src/io.rs` `commit_track`, `docs/commit-multichannel.md`).
- Commands Keyboard Focus single-key editing in the Edit window (active only when `options.keyboard_focus` is `commands`): A/S trim start/end, D/G fade to start/end, F create fades, B separate, X/C/V cut/copy/paste, Z undo (Shift+Z redo), N insertion-follows-playback, P/; move selection by track, L/' previous/next clip boundary, E zoom toggle, 0/O timeline↔selection, [/] play selection / play timeline, 1–5 zoom presets, comma/period/M/slash nudge; plain R/T zoom out/in are "Commands Focus style" keys outside that map (`crates/ui-egui/src/shortcuts.rs` `commands_focus`; `crates/engine/src/cmd/options.rs` `options.keyboard_focus`).
- Undo/redo for document commands, an undo history window, and `engine.history` returning `{undo, can_redo}` (`crates/engine/src/cmd/edit.rs`, `query.rs`).
### Mixing, automation and plug-ins
- 10 inserts and 10 sends per track, busses, aux inputs, master faders, VCAs, routing folders, object/bed routing, solo modes, mute/solo groups, phase invert, input trim, pre/post sends, automatic plug-in delay compensation (master faders included); aux/folder strips render in dependency order even for chains deeper than eight (`crates/model/src/mixer.rs` `INSERT_SLOTS`/`SEND_SLOTS`; `crates/mix/`; `docs/aux-routing-order.md`). Strip mute automation is evaluated per render block (`docs/strip-mute-automation.md`).
- **30 built-in plug-ins** in `REGISTRY`: 27 processors and 3 instruments (`subtractive_synth`, `drum_synth`, `sampler`). New at this tag: `auto_pan`, `ring_mod`, `subharmonic` (previous tag: 27 total). `engine.plugins` / `soundcraft-cli plugins` list them with ids and params (`crates/dsp/src/plugins/mod.rs` `REGISTRY`, `instantiate`; `crates/engine/src/cmd/query.rs`). `docs/plugin-parity.md` now agrees (27 processors + 3 instruments).
- Offline AudioSuite processing via one command, `audiosuite.process {process, params?, clips?, tracks?, start?, end?}`: any built-in plug-in id plus `normalize|reverse|invert|gain|duplicate|time_stretch|pitch_shift|varispeed|speed_ramp|signal_generator` (`speed_ramp` is new: `change`, `selection` fit/extend, `fades`). It renders a new source and replaces the clip, keeping fades; `signal_generator` fills a range with a new clip. Renders that would exceed the offline/render budgets are refused with an error naming the maximum length — not truncated silently (`crates/engine/src/cmd/audiosuite.rs`, `crates/engine/src/io.rs` `refuse_longer`, `render_limit`).
- Automation: lanes for volume, pan, mute, send level/pan/mute and any insert parameter (`volume|pan|pan2|mute|send_a_level|send_a_pan|send_a_mute|plugin:a:<param>`); seven modes Off/Read/Touch/Latch/Touch-Latch/Write/Trim; `automation.set_point`, `write_range`, `clear`, `thin`, `thin_all`, `write_to_current`, `write_to_all`, `copy_to_send`, `volume_to_clip_gain`, `clip_gain_to_volume`, plus `edit.automation_coalesce_volume_to_clip_gain` / `edit.automation_coalesce_clip_gain_to_volume` (`crates/model/src/automation.rs` `AutoParam`, `AutomationMode`; `crates/engine/src/cmd/mix.rs`, `edit_more.rs`).
- Third-party hosting in-process: CLAP, VST3 and macOS Audio Units; instances are created, loaded and destroyed off the audio thread; state is stored in the session. Discovery: `engine.clap_plugins {rescan?}`, `engine.vst3_plugins {rescan?}`; ids `clap:<id>`, `vst3:<class id hex>`, `au:<type>:<subtype>:<manufacturer>`. **There is no Audio Unit discovery command** — the UI scans AUs at menu time (`crates/clap-host/src/lib.rs`, `vst3-host/src/lib.rs`, `au-host/src/lib.rs`; `crates/engine/src/cmd/clap.rs`, `vst3.rs`).
- Plug-in editors: CLAP opens the plug-in's own floating window (host-owned embedded window only on Windows); VST3 editors on macOS and Windows (Linux X11 not hosted); Audio Units never (no Cocoa view hosting). On a signed macOS build third-party VST3 is refused because the bundle carries only `com.apple.security.device.audio-input` (`docs/plugin-parity.md`; `crates/vst3-host/src/lib.rs`; `packaging/macos/entitlements.plist`, verified on the installed bundle).
- Plug-in presets are partly UI-only: `mix.insert_params {preset?}` labels the recall, while user preset **files** are JSON (`{params: {id: value}}`) under `<settings>/Presets/<plugin id, sanitised>/<name>.json`, read and written by the panel menu (`preset_menu`) in `crates/ui-egui/src/panels.rs` — there is no engine command to enumerate them. For a hosted plug-in's opaque state use `mix.insert_state {track, slot, state: base64|null}` — not undoable and not journaled (`crates/engine/src/cmd/plugin_state.rs`).
### Recording and audio devices
- Record arm, input monitoring, punch on selection or on the fly, pre/post-roll, loop recording with a playlist per take, destructive and track-punch modes (`crates/playback/src/record.rs`, `crates/engine/src/io.rs` `plan_takes`/`add_recording`, `crates/engine/src/cmd/options.rs`).
- Takes **stream to disk** while recording: one WAV per take, the header rewritten every second, so a crash leaves a playable file; after stop the take is read back into RAM as a session source (`crates/playback/src/record.rs` module doc, `TakeWriter::write_header`; `crates/engine/src/io.rs` `add_recording`). `docs/gaps.md` G-06 still says takes sit in RAM — that is stale.
- Device selection is real: `ui.audio_device {system?, output?, input?, sample_rate?, buffer_size?, list?}` changes the audio system/device/rate/buffer through `AudioConfig`, reopens the player and applies to the recorder (sample rate 8 000–768 000, buffer 16–8192; `list` returns the systems and their devices). The choice is kept with UI preferences, not in the session. **`setup.playback_engine` is a different, legacy engine command**: it stores `engine.device`/`engine.buffer_size` in the session and playback never reads them. ASIO is behind the opt-in `asio` build feature (`crates/ui-egui/src/windows.rs` `audio_device_command`, `crates/ui-egui/src/lib.rs` `apply_audio_config`; `crates/playback/src/devices.rs` `AudioConfig`; `crates/playback/Cargo.toml`).
- Autosave runs on its own thread after changes into one folder per session (`session.scraft`, `origin`, `lock`, `recording.json`), owner-only (`0700`) on Unix, with crash recovery at launch; `Backups/` beside a session keeps the newest ten timestamped copies of the `.scraft` on overwrite (`crates/engine/src/autosave.rs`; `crates/engine/src/io.rs` `back_up`).
### MIDI, notation, instruments and time
- Instrument and MIDI tracks, piano-roll editor with velocity lane and event list, note add/edit/delete/move, quantize, input quantize, step input (`event.step_input`), transpose, real-time properties, MIDI track offsets, retrospective record (`crates/engine/src/cmd/midi.rs`, `event.rs`, `event_more.rs`; `crates/ui-egui/src/midi_editor.rs`).
- `mix.instrument {track?, plugin, params?}` sets an instrument track's instrument (built-in, `clap:`, `vst3:` or `au:` id; `params` sets built-in parameters, clamped, and unknown fields are refused) (`crates/engine/src/cmd/instrument.rs`).
- Standard MIDI File: reads formats 0 and 1 fully and format 2 best-effort (5 000 000 events / 4096 tracks caps); **always writes format 1**, includes controllers, and honours the `tracks` filter — a filter that names no MIDI track is an error ("none of the requested tracks hold MIDI"); an empty export writes zero tracks and reads back as zero (`crates/midi/src/smf.rs`; `crates/engine/src/io.rs` `export_midi`). `docs/file-format-parity.md` still says controllers/filter are missing — it is stale.
- `mix.sampler_load {track?, path, params?}` / `mix.sampler_clear`: decode an audio file onto an instrument track's built-in Sampler (root, key range, loop mode) (`crates/engine/src/cmd/sampler.rs`).
- Score Editor, MusicXML export for notation programs (`file.export_sibelius`, `file.send_to_sibelius`), printable SVG score (`file.print_score`) (`crates/engine/src/score.rs`; `crates/engine/src/cmd/file.rs`).
- Monophonic audio-to-MIDI (`edit.copy_audio_as_midi`, `track.extract_midi`) and chord detection to markers (`event.extract_chords`) (`crates/engine/src/cmd/audio_midi.rs`, `event_more.rs`, `track_more.rs`).
- Five timebases Bars|Beats, Min:Secs, Timecode (drop-frame included), Feet+Frames, Samples; tempo and meter maps with linear/parabolic/S-curve ramps; key signatures; Identify Beat; Beat Detective; Elastic Audio warping (`clip.elastic_properties`, `clip.remove_warp`) (`crates/time/src/format.rs` `TimeFormat`; `crates/engine/src/cmd/event_more.rs`, `clip.rs`).
- **No MIDI hardware I/O at all**: no CoreMIDI/ALSA/WinMM layer and no MIDI-device dependency. `setup.midi_input_devices`, `setup.midi_input_filter`, `setup.midi_studio` and `setup.midi_beat_clock` only store settings (`docs/gaps.md` G-02; no MIDI-device crate in `Cargo.toml`).
### Audio and session formats
- **Read:** WAV (PCM 8/16/24/32, float 32/64, `WAVE_FORMAT_EXTENSIBLE`, RF64/BW64, BWF `bext`, unfinalised recording WAVs) and AIFF/AIFF-C natively; FLAC, MP3, Ogg Vorbis, AAC/ALAC in MP4/M4A/M4B, CAF, Matroska and ADPCM/A-law/µ-law via symphonia. Format is detected from bytes first, extension second (`crates/audio-io/src/lib.rs` module doc, `detect_format`, `detect_by_ext`).
- **Write:** `wav, aif, aiff, flac` only (`ENCODE_EXTENSIONS`), with optional TPDF dither; WAV switches to RF64 above 4 GiB; AIFF-C writes `fl32`; FLAC uses the built-in encoder; float formats keep finite samples above full scale instead of clipping (`crates/audio-io/src/lib.rs` `encode`, `encode_wav_rf64`, `FileEncoder`; `crates/engine/src/cmd/file.rs`). No MP3/AAC/Ogg/Opus export.
- Waveform overviews are generated in memory (`crates/audio-io/src/peaks.rs`), not cached as `.wfm` files.
- **Native session `.scraft`**: pretty-printed serde JSON with `format_version` **1**; a newer version is refused; `from_json` sanitises ids/names/positions and then validates — duplicate ids or an exhausted id space are **refused** (new at this tag), so a hand-edited session with colliding ids will not open. Audio that only exists in memory is written as 32-bit float WAV into a sibling `Audio Files/` folder; the session is saved via a temp file plus rename; an overwrite copies the old file to `Backups/` first (`crates/model/src/session.rs` `FORMAT_VERSION`, `from_json`, `sanitize`, `validate`; `crates/engine/src/io.rs` `save_session`, `write_session`, `replace_file`, `back_up`). There is no `deny_unknown_fields`, so unknown JSON keys are ignored by serde and dropped when the app next saves — use an external sidecar for your own metadata.
- `.scraftz` is the **browser** bundle (zip of the `.scraft` plus `Audio Files/`; `BUNDLE_EXTENSION`, `session_bundle`, `open_session_bytes`, 10 000-entry / 1 GiB caps; a save that would exceed them is refused because the bundle could not be opened again). The desktop app's open dialog, positional-file handler and `session.open` do **not** open it: the desktop routes `.scraft` to a session and everything else to audio/MIDI import (`apps/soundcraft/src/main.rs` `open_path`; `crates/engine/src/io.rs`).
- Stored media paths are resolved by `craft_docpath::resolve_absolute`: a stored path is followed only when local or on the session's own network share; relative `Audio Files/` paths resolve against the session folder; a still-relative result is refused (`crates/engine/src/io.rs` `stored_path`; shared library `craft-libs`, pinned rev `16fb3d46e1eaa0d455465e4e66a3ac7b17b55bd3` in `Cargo.toml`).
- `.scgrp` clip groups are JSON plus a `<name> Audio/` folder of 32-bit float WAVs (`crates/engine/src/clip_group_file.rs`).
- One session per running app; `session.new`/`open`/`close` replace it; there are no session tabs. `session.save` with a bare name appends `.scraft`; saving over another existing file needs `replace: true` (`session_file`, `replaces`).
### Export, bounce and delivery
- `file.bounce_mix {path, format?: wav|aiff|flac, bit_depth?: 16|24|32f, start?, end?, source?: main|bus name, normalize?, dither?, fold_down?: stereo}` streams the render to disk and returns `{path, bytes, seconds, peak_db, lufs, true_peak_db}`; `dither` defaults true, `normalize` false, an omitted range means the edit selection else the whole content, and `source` must name an existing bus (`crates/engine/src/cmd/file.rs` `bounce`/`bounce_job`; `crates/engine/src/io.rs` `Bounce`; `crates/dsp/src/meter.rs`).
- `file.bounce_stems {dir, format?, bit_depth?, start?, end?, dither?, fold_down?}` writes one file per unmuted, active audio/instrument/MIDI track and returns `{files}` (`dither` is new). `file.export_clips {dir, clips?, format?}` writes selected clips as **WAV only** — `io::export_clips` uses the default encoder and ignores `format`, and clips too long for one render are refused. `file.export_midi {path, tracks?}`, `file.export_session_text {path?, replace?}` (refuses to overwrite an existing file without `replace: true`), `file.export_selected_tracks_as_session {path, tracks?, replace?}`, `file.export_sibelius`/`file.send_to_sibelius` (MusicXML), `file.print_score` (SVG), `file.export_clip_groups` (`.scgrp`) (`crates/engine/src/cmd/file.rs`; `crates/engine/src/io.rs` `bounce_stems_with`, `export_clips`, `export_midi`).
- `session.save_template {path, replace?}` writes a clip-stripped `.scraft`; there is no template browser or "new from template" command (`session.new` only takes `template: blank|demo`) (`crates/engine/src/cmd/file.rs`, `docs/gaps.md` G-16).
### Video, surround and platforms
- Video track with thumbnails, Video window with optional timecode burn-in, Video Universe strip, `video.online`, `video.offset`, `video.list`; pure-Rust decoding of H.264 (Baseline/Main/High, 8-bit 4:2:0, CAVLC/CABAC), Apple ProRes (422 Proxy/LT/Standard/HQ, 4444/XQ) and Motion JPEG. HEVC, AV1 and DNx movies are recognised by the demuxer but frames return "unsupported" — there is no decoder for them (`crates/video/src/lib.rs`, `crates/engine/src/cmd/video.rs`, `crates/ui-egui/src/video_window.rs`). The bitstream and ISO-BMFF readers are re-exports of `craft_media` from craft-libs.
- Surround/immersive mixing: 34 main/bus format choices from Mono, Stereo, LCR, Quad, LCRS, 5.0, 5.1, 6.0, 6.1, 7.0/7.1 (incl. SDDS), 5.0.2/5.1.2/5.0.4/5.1.4/7.0.2/7.1.2/7.0.4/7.1.4/7.0.6/7.1.6/9.0.4/9.1.4/9.0.6/9.1.6 to 1st–7th Order Ambisonics; `setup.main_format`; surround panners with x/y/z, divergence, centre and LFE (`mix.surround_pan`, `mix.send_surround_pan`); `track.object` for object/bed routing; Renderer window with speaker levels (`crates/model/src/mixer.rs` `ChannelFormat::ALL`; `crates/engine/src/cmd/surround.rs`, `track_more.rs`, `crates/ui-egui/src/extra_windows.rs`).
- Native builds for macOS (universal, signed/notarized, min macOS 11.0), Windows x64/x86/arm64 (DirectX 12 default; `WGPU_BACKEND` to troubleshoot), Linux (AppImage, deb, rpm, Flatpak, tar.gz, riscv64 tarball), FreeBSD x86_64/aarch64, and WebAssembly (`README.md` "Downloads"; `packaging/`). The web build has no TCP control channel and no filesystem: it opens/saves through browser file pickers, saves sessions as one `.scraftz` download, and accepts `?webgl`/`?empty` (`apps/soundcraft-web/src/main.rs`).
### Counts and changes at v0.13.1
- **Re-measured at this tag:** 482 engine command specs (was 480), 67 UI commands (was 66), 549 desktop ids (was 546), 30 built-in plug-ins (was 27), 10 CLI subcommands, 17 MCP tools, 17 control methods (2 aliases), 4 write extensions, `FORMAT_VERSION = 1`, `MAX_BATCH_CALLS = 1000`, 512-line menu catalog. `docs/parity-checklist.md` reports 397/512 engine leaves; ROADMAP's 482/512 app-level figure is a different measure from the 482 commands — do not conflate them.
- **Added:** 3 plug-ins (`auto_pan`, `ring_mod`, `subharmonic`); 2 commands (`clip.auto_rename`, `edit.strip_silence_preview`); 1 UI command (`view.scroll_into_view`); `clip.loop` gained `fill`; `edit.strip_silence` gained `mode` and the naming params; `audiosuite.process` gained `speed_ramp`; `file.bounce_stems` gained `dither`; `file.export_session_text` gained `replace`; `convert` gained `--dither`/`--no-dither` and now rejects unknown options and missing values; MCP roots may be repeated; the session loader rejects duplicate ids; a panicking command restores engine side effects; offline renders are budgeted and refused instead of truncated.
- **Behaviour changes to re-check in your build:** `edit.fades_create` now errors on a shape that is not a string (it used to fall back to Equal Power); `convert` dithers only when the destination loses resolution; `--bounce` without a range covers the whole session.
## Known limitations and not-implemented
Do not assign these; each is absent or partial at v0.13.1 (repo docs plus code checks cited):
- **MIDI hardware I/O is absent.** No recording from a keyboard, no live playing through instruments, no MIDI out, clock or MTC; `setup.midi_*` stores names/flags only (`docs/gaps.md` G-02; no MIDI-device dependency in `Cargo.toml`).
- **No Pro Tools interchange.** No `.ptx`/`.ptf`, AAF, OMF or Media Composer session import/export; the nearest bridge is `file.import_session_data` between SoundCraft `.scraft` sessions (`docs/gaps.md` G-03).
- **Audio-interface depth.** Input and output are separate cpal streams; the mix runs in fixed 512-sample blocks; there is no per-channel hardware routing, no single duplex clock, no latency reporting or record-offset compensation. Device/rate/buffer choice *does* exist (`ui.audio_device`), but its macOS behaviour is **uncertain — see `docs/hardware-parity.md`** ("not yet verified on macOS"); ASIO only behind the opt-in feature; `setup.playback_engine` and `setup.disk_allocation` values are stored but unused by playback (`docs/gaps.md` G-01; `crates/playback/src/lib.rs` `BLOCK = 512`; `crates/engine/src/io.rs` `plan_takes`).
- **All audio sources are decoded whole into RAM.** No disk streaming for playback; takes stream while recording but are decoded into the source pool when the take ends. Long sessions are memory-bound; bounces themselves do stream to disk (`crates/engine/src/io.rs` `decode_source`/`add_source`/`add_recording`; `docs/gaps.md` G-06).
- **Plug-in limits.** AAX is not and likely never will be hosted (Avid SDK/PACE, owner decision). The signed macOS build refuses third-party VST3 without `com.apple.security.cs.disable-library-validation` (#153, not in this tag). AU editor views are not hosted; CLAP embedded editors exist only on Windows; VST3 editors not on Linux. No external sidechain/key input, no ARA, no out-of-process sandboxing; a crashing plug-in can kill the app (`docs/plugin-parity.md`, `docs/gaps.md` G-05/G-13; `packaging/macos/entitlements.plist`).
- **No compressed export** (MP3/AAC/M4A/Ogg/Opus) even though those decode on import (`docs/gaps.md` G-21; `ENCODE_EXTENSIONS`).
- **Video is decode-only and limited.** No HEVC/AV1/DNxHD/DNxHR decode, no video export or bounce-with-video, no external timecode (`docs/gaps.md` G-17; `crates/video/src/lib.rs`).
- **No immersive delivery.** No ADM BWF, Dolby Atmos renderer integration or binaural; surround mixing itself exists (the `Atmos*` channel formats are layout names only) (`docs/gaps.md` G-12).
- **No control surfaces (HUI/MCU/EUCON) and no external sync (MTC/LTC/video reference).** `setup.peripherals`, `setup.external_timecode_offset`, `setup.video_sync_offset` and `setup.midi_beat_clock` store settings only (`docs/gaps.md` G-10/G-11; `crates/engine/src/cmd/setup_more.rs`; no such code in `crates/`).
- **English-only UI.** No string catalog and no translations; no RTL or complex-script shaping. A Hangul fallback font is appended for Korean names (macOS `AppleSDGothicNeo`, Windows Malgun, Linux Noto CJK Korean face) with glyph tests, so Korean text renders; there is no explicit fallback for other scripts and no bundled font (`crates/ui-egui/src/fonts.rs`; `docs/localization-parity.md` is stale here).
- **Session management is thin.** `session.save_template` and track presets exist, but there is no template browser, no batch rename, and the Workspace window is a one-folder file list with no search, previews or audition; `window.workspace_presets`/`_sounds` just open that same list (`crates/ui-egui/src/windows.rs` `workspace`; `docs/gaps.md` G-16).
- **No speech-to-text** (settings only) and **no fidelity harness** against Pro Tools (`docs/gaps.md` G-07/G-18; `crates/engine/src/cmd/setup_more.rs` `setup.transcription_settings`).
- **`.scraftz` does not open on the desktop**; it is a browser bundle (see Session format). `soundcraft-cli info` also treats only `.scraft` as a session; a `.scraftz` path is probed as audio and fails.
- **Parity is uneven by area.** The generated report counts 397/512 menu leaves implemented by the engine (Window 1/52, AudioSuite 0/28); the UI layer aliases many of those, giving the repo's 482/512 app-level claim. AudioSuite catalog leaves all map to the single `audiosuite.process` command, so "menu present" is not per-item behaviour (`docs/parity-checklist.md`; `crates/ui-egui/src/menus.rs` `AUDIOSUITE`).
- **Some shortcuts collide** in the registry (e.g. `Cmd+Alt+L` on both `clip.loop` and `edit.move_selection_left`; `Space` on play, stop and toggle). Bind to command ids; treat the shortcut strings as hints and `Setup › Keyboard Shortcuts` (`window.shortcuts`) as the live list.
- **The docs lag the code in several places**: `docs/architecture.md` still says there is no device/buffer selection, no ASIO and that recordings sit in memory; `docs/gaps.md` G-06 repeats the takes-in-RAM line; `README.md`/`ROADMAP.md` headlines still list "choosing an audio interface and buffer size" as missing while G-01 marks it done; `docs/file-format-parity.md` says track presets don't exist and that MIDI export drops controllers/filter (both implemented); `docs/localization-parity.md` denies the Hangul fallback; `crates/vst3-host/src/lib.rs`'s header still lists `IMessage`/`IAttributeList` as unhosted while `crates/vst3-host/src/ffi.rs` implements them. Trust the code paths cited here, then `soundcraft-cli describe`.
## How to operate
### Launch on macOS
- GUI binary `/Applications/SoundCraft.app/Contents/MacOS/SoundCraft`; `open -a SoundCraft`, or `open -a SoundCraft --args --demo`. A terminal launch with `--control` prints the control port on stdout.
- App flags parsed in `apps/soundcraft/src/main.rs`: `--demo` / `--sample`, `--no-audio` (no output player; use for UI test runs — it does **not** disable recording input), `--control <port>` (`0` = pick a free port and print it), `--version`/`-V`, and positional files: `.scraft` opens as a session, `.mid`/`.midi`/`.smf` imports MIDI, anything else imports as audio in the background (so a `.scraftz` positional is treated as audio and fails).
- Environment: `SOUNDCRAFT_CONTROL_PORT`, `SOUNDCRAFT_CONFIG_DIR` (nonempty = the complete settings directory; empty disables settings), `SOUNDCRAFT_NO_PREFS` (no prefs/log file — recommended for agent test runs), `SOUNDCRAFT_NO_SYSTEM_FONTS`, `RUST_LOG` (`apps/soundcraft/src/config.rs`, `crates/ui-egui/src/fonts.rs`, `apps/soundcraft/src/logging.rs`).
- Settings on macOS: `~/Library/Application Support/SoundCraft/` — `ui.json`, `Autosave/`, `Presets/`, `logs/soundcraft.log` (rotated to `.1` and `.2` at launch, 16 MiB each). A `portable.txt` beside the executable switches to `<exe dir>/SoundCraftData` when writable (`apps/soundcraft/src/config.rs`).
- `soundcraft-cli` is **not inside the app bundle** (and was not on `PATH` on the machine this skill was written on): install `soundcraft-cli-<ver>-macos-universal.zip` from the release, or build with `cargo run --release -p soundcraft-cli -- …`. Check both versions before mixing them.
### Document and window model
One session per app; `session.new`/`session.open`/`session.close` replace the open document. Programmatic session switches **do not prompt to save** (only the UI dialogs do) — save first. Edit and Mix are separate views with independent panel layouts and docking workspaces; `window.mix_separate` puts Mix in its own native window. Specialist windows (Transport, Big Counter, MIDI Editor, Score, Renderer, Video, Clip Effects, I/O Setup, Playback Engine, Keyboard Shortcuts, …) are separate hosts. There are no session tabs.
### Positions, references and ranges
- Positions: a JSON integer (or float) is **samples**; `{"seconds": 2.5}` is seconds; a string is parsed in the current main-counter format then all formats, optionally prefixed with the exact id `bars_beats:`, `min_secs:`, `timecode:`, `feet_frames:` or `samples:` (e.g. `"bars_beats:5|1|000"`, `"min_secs:0:12.500"`, `"timecode:00:00:10:00"`). CLI `--start`/`--end` parse a bare decimal as seconds. Values clamp to ±`MAX_POSITION` (`crates/engine/src/cmd/mod.rs` `position_param`; `crates/time/src/format.rs` `parse_position`; `apps/soundcraft-cli/src/main.rs` `time_arg`).
- Ranges: `start` + `end`, or `start` + `length`; `start` alone is a point range; both omitted = the edit selection. Tracks by id or name (exact, then case-insensitive); clips by id; buses by case-insensitive name; repeated ids in a `tracks` array are de-duplicated (`crates/engine/src/cmd/mod.rs` `range_param`, `tracks_param`; `crates/model/src/session.rs` `track_by_name`, `bus_by_name`).
### Open, import, save, export
- New/open: `session.new {name?, sample_rate?, bit_depth?: 24, template?: blank|demo}`; `session.open {path}` → `.scraft`, returns `{name, tracks, missing}` and never fails on missing media (it warns); `session.save {path?, replace?}` (no path and never saved = error), `session.save_as {path, replace?}`, `session.save_copy`, `session.save_template`, `session.revert`, `session.close` (`crates/engine/src/cmd/file.rs`; `crates/engine/src/io.rs` `open_session`, `save_session`).
- Import: `file.import_audio {path | paths: [..], track?, at?, new_tracks?}`, `file.import_midi {path, at?}`, `file.import_video {path, at?}`, `file.import_clip_groups {path, track?, at?}`, `file.import_session_data {path, tracks?}` (`crates/engine/src/cmd/file.rs`, `setup_more.rs`). Audio imports decode off the UI thread.
- Export/bounce: see "Export, bounce and delivery" for parameters. Always read the returned `path`/`bytes`/levels; a completed call does not prove the audio is non-silent.
- Audio device: `ui.audio_device {"list":true}` returns `{config, device, systems}`; `{"input":"…","buffer_size":256}` applies and reopens the devices. Save before changing devices if third-party plug-in state matters — the change rebuilds the player and its plug-in instances, and state is only captured into the session on save/bounce (`crates/ui-egui/src/lib.rs` `apply_audio_config`/`capture_plugin_states`).
### Keyboard shortcuts (verified in code)
Shortcuts are fields of the command specs (`shortcut`), dispatched by matching a key + modifiers against them (`crates/ui-egui/src/shortcuts.rs`); `Setup › Keyboard Shortcuts` (`window.shortcuts`) renders the live table. The ones worth trusting: Space play/stop/toggle; Shift+Space half speed; Cmd+Space record; F1–F4 edit modes (Alt+1–4, Shift+F4 snap, ` also); F5–F10 tools; Enter new memory location; Cmd+= Mix/Edit; Cmd+Ctrl+S search; Cmd+U Strip Silence; Cmd+Alt+L Clip Loop; Cmd+F create fades; Cmd+E separate; Cmd+D duplicate; Cmd+T trim to selection; Cmd+H heal; Cmd+M mute clips; Cmd+Z undo, Cmd+Shift+Z redo; Cmd+Shift+K export clips; Cmd+Alt+B bounce mix; Cmd+P print score; Cmd+1/3/4/5/9 Transport/Big Counter/Automation/Memory Locations/Video; Cmd+Ctrl+1–5 zoom presets; Cmd+Alt+0–5 clip rating; Cmd+W close window; Cmd+Ctrl+W hide floating windows; Alt+Shift+C track commit; Alt+Shift+I import session data. The Tab key runs `edit.tab` (Alt goes back, Shift extends) and jumps to transients only while `options.tab_to_transient` is on — Cmd+Alt+Tab toggles that mode.
### Recipes that work at this tag
```sh
# Headless: demo session → set a level → insert an EQ → bounce → print the session JSON.
soundcraft-cli run --demo \
--cmd 'mix.volume={"track":"Kick","db":-6}' \
--cmd 'mix.insert={"track":"Bass","plugin":"eq_7band"}' \
--bounce mix.wav --inspect
# Live app: control channel on a free port, no output device.
/Applications/SoundCraft.app/Contents/MacOS/SoundCraft --demo --no-audio --control 7801 &
soundcraft-cli app --port 7801 mix.new_bus '{"name":"Verb"}'
soundcraft-cli app --port 7801 mix.send '{"track":"Bass","bus":"Verb","level_db":-12}'
soundcraft-cli app --port 7801 automation.write_range '{"track":"Bass","param":"volume","start":0,"end":96000,"value":-6}'
soundcraft-cli app --port 7801 file.bounce_mix '{"path":"mix.wav","source":"Verb"}'
soundcraft-cli app --port 7801 ui.screenshot '{"path":"shot.png"}'
# Give an MCP client a headless, confined DAW.
claude mcp add soundcraft -- soundcraft-cli mcp --demo \
--automation-read-root ~/Sessions --automation-write-root ~/Bounces
```
The CLI demo session contains audio tracks Kick, Snare, Hats, Bass, Pad, instrument tracks Keys and Lead, an aux return Verb and a Master (`crates/engine/src/demo.rs`). `mix.insert` takes a built-in id from `engine.plugins` (or a `clap:`/`vst3:`/`au:` id); `mix.send` needs the bus to exist (`mix.new_bus` first). All positions in these examples are samples.
### Command ids you will actually call
All verified present at this tag; `describe` each before relying on its params.
| Area | Ids |
|---|---|
| Session | `session.new`, `session.open`, `session.save`, `session.save_as`, `session.save_copy`, `session.save_template`, `session.revert`, `session.close`, `session.inspect`, `session.audio_health`, `session.format_time` |
| Import / export | `file.import_audio`, `file.import_midi`, `file.import_video`, `file.import_clip_groups`, `file.import_session_data`, `file.get_info`, `file.bounce_mix`, `file.bounce_stems`, `file.export_clips`, `file.export_midi`, `file.export_session_text`, `file.export_selected_tracks_as_session`, `file.export_sibelius`, `file.send_to_sibelius`, `file.print_score`, `file.export_clip_groups` |
| Tracks | `track.new`, `track.duplicate`, `track.group`, `track.commit`, `track.bounce`, `track.save_preset`, `track.load_preset`, `track.extract_midi`, `track.object`, `track.set_record_to_input_only` |
| Editing | `edit.separate`, `edit.duplicate`, `edit.consolidate`, `edit.heal`, `edit.strip_silence`, `edit.strip_silence_preview`, `edit.fades_create`, `edit.tab`, `edit.undo`, `edit.redo`, `edit.select_all`, `edit.clear`, `edit.mute_clips` |
| Clips | `clip.loop`, `clip.unloop`, `clip.auto_rename`, `clip.rename`, `clip.gain`, `clip.group`, `clip.ungroup`, `clip.quantize_to_grid`, `clip.elastic_properties`, `clip.rating`, `clip.edit_lock` |
| Mixer | `mix.volume`, `mix.pan`, `mix.trim`, `mix.mute`, `mix.solo`, `mix.record_arm`, `mix.input_monitor`, `mix.new_bus`, `mix.send`, `mix.insert`, `mix.insert_remove`, `mix.insert_bypass`, `mix.insert_param`, `mix.insert_params`, `mix.insert_state`, `mix.instrument`, `mix.sampler_load` |
| Automation | `automation.set_point`, `automation.write_range`, `automation.clear`, `automation.thin`, `automation.volume_to_clip_gain` |
| Transport / markers | `transport.play`, `transport.stop`, `transport.toggle`, `transport.record`, `transport.rtz`, `transport.go_to_end`, `markers.add`, `markers.edit`, `markers.delete`, `markers.recall` |
| Options / queries | `options.loop_record`, `options.quickpunch`, `options.trackpunch`, `options.tab_to_transient`, `options.keyboard_focus`, `options.delay_compensation`, `engine.plugins`, `engine.clap_plugins`, `engine.vst3_plugins`, `engine.commands`, `engine.history`, `engine.parity` |
| Processing / video / setup | `audiosuite.process`, `video.online`, `video.offset`, `video.list`, `setup.main_format`, `setup.playback_engine`, `setup.io`, `setup.peripherals` |
### Discover the build you are talking to
`soundcraft-cli commands <filter>` lists ids/labels/menu paths; `soundcraft-cli describe ID` prints the authoritative parameter doc and enabled state; over the control channel use `engine.commands {filter}` and `session.inspect`; over MCP use `list_commands` and `inspect_session`. Labels, menu paths and shortcuts can change between releases — bind to **ids**.
## Working with an AI agent
Legitimate, evidence-checkable work:
- **Prepare and convert inputs.** `soundcraft-cli info FILE` probes audio (format, sample format, rate, channels, frames, seconds, BWF) or summarises a `.scraft`. `soundcraft-cli convert IN OUT [--bit-depth 16|24|32f] [--rate HZ] [--normalize] [--dither|--no-dither]` lands assets in WAV/AIFF/FLAC (output extension decides; anything else is refused). This is the safe path before importing exotic sources.
- **Drive it headlessly** with `soundcraft-cli run` / `script` / `mcp`, or bridge MCP to a running app with `--connect`. Prefer `batch` for many edits (cap 1000, rejected up front when larger), then `inspect_session` and `session.audio_health` to verify. Most document commands are undoable; queries, view/session settings and the explicitly non-journaled `mix.insert_state` are not.
- **Read and repair `.scraft`.** It is pretty JSON with `format_version: 1`: you may parse, diff and patch it, but never bump the version, do not add private top-level keys (serde drops unknown fields on the next save), and keep every id unique — `validate()` now refuses a session with duplicate ids or a used-up id space. Keep `Audio Files/` paths relative to the session folder; media outside the folder is subject to the stored-path rule. Never edit a session while the app or CLI has it open; prefer `session.open` → change → `session.save` over hand-editing.
- **Compose mix work from commands.** `mix.new_bus` before `mix.send`; `mix.insert {"plugin":"eq_7band"}` then `mix.insert_params`/`mix.insert_param`; `mix.volume`/`pan`/`trim`; `automation.write_range` or `set_point`; `file.bounce_mix … source:"Verb"` to bounce a bus. `engine.plugins` lists built-ins; `engine.clap_plugins`/`engine.vst3_plugins` list hosted plug-ins (AU has no query — know the fourcc id or use the UI). User plug-in presets are plain JSON files under `<settings>/Presets/<plugin id>/`.
- **Verify exports.** `file.bounce_mix` returns `bytes`, `seconds`, `peak_db`, `lufs`, `true_peak_db`; check the file exists, has plausible duration and non-silent levels, then re-probe with `soundcraft-cli info`. For a stem set, check `files` count and each file.
- **Automate UI acceptance** with the control channel and its UI commands (`ui.screenshot`, `ui.click`, `ui.drag`, `ui.scroll`, `ui.zoom`, `ui.key`, `ui.text`, `ui.menu.invoke`, `ui.inspect`, plus `ui.dock` through `engine.execute`). Run with `--no-audio` and an isolated `SOUNDCRAFT_CONFIG_DIR`; never arm a track, enable input monitoring or start recording in a smoke test (`--no-audio` disables output, not input). Use the offscreen `ui_shot` example when no window can be shown.
- **What not to attempt:** driving the GUI blind (inspect first); promising AAF/OMF/`.ptx`, MIDI-hardware recording/playback, MP3/AAC export, ADM/Atmos delivery or video export; opening `.scraftz` on the desktop; assuming `setup.playback_engine` changes the device (use `ui.audio_device`); assuming `setup.disk_allocation` moves recorded takes; expecting Audio Unit discovery or AU editors; reading/copying Pro Tools resources — SoundCraft is clean-room and its names come only from black-box observation (`AGENTS.md`).
## Verification protocol
SoundCraft is pre-alpha and its docs lag the code in several places. For any real task:
1. **Round-trip on a throwaway session first**: `session.new` → one edit → `session.save` to a scratch folder → `session.open` it back → `file.bounce_mix` → confirm the returned levels are non-silent and the file exists. Do this before touching a user's session.
2. **Never trust a silent export**: a bounce can write a file that is empty or misaligned; compare `peak_db`/`lufs`/`true_peak_db` with expectations and re-probe the output with `soundcraft-cli info`. Offline processors (AudioSuite, clip-gain render, clip export, track commit) refuse over-long work with an error instead of truncating — read the error, do not retry blindly.
3. **Back up and version per session**: copy the whole folder (`.scraft` + `Audio Files/`), not just the JSON; remember `Backups/` only receives a copy when an existing session file is overwritten and its text changed.
4. **One session at a time, isolated profile**: `session.new`/`open`/`close` replace the document without a prompt; run agents with `SOUNDCRAFT_CONFIG_DIR` pointing at a fresh folder so autosave/recovery and presets do not mix with the user's work.
5. **Update apps only between projects**: the Launcher auto-updates SoundCraft; this skill is pinned to v0.13.1. Check `--version` and, when in doubt, the bundle's `SoundCraftBuildCommit`, before relying on a format or parameter detail.
6. **Inspect, don't guess**: `soundcraft-cli describe ID` and `engine.commands` give the authoritative schema of the build you are talking to; `session.audio_health` checks loaded media, sample-rate mismatches and clip bounds without touching files.
## Sources & evidence
- Repo: https://github.com/storytold/soundcraft — inspected at tag **v0.13.1**, commit `01b39850ad16e84b45904ba07bd5cec7cf96c4b7`, dated 2026-10-25, on 2026-10-26. Installed app `/Applications/SoundCraft.app` reports `0.13.1`; `Info.plist` `SoundCraftBuildCommit` is the same full SHA; `CFBundleIdentifier` `ai.storyteller.soundcraft`; `LSMinimumSystemVersion` 11.0; binary `--version` prints `SoundCraft 0.13.1`; its only hardened-runtime entitlement is `com.apple.security.device.audio-input`. The bundle contains only `SoundCraft`, not `soundcraft-cli`.
- Read: `README.md`, `ROADMAP.md`, `AGENTS.md`, `CLAUDE.md`, `Cargo.toml`, `Cargo.lock`, `apps/soundcraft/src/{main.rs,config.rs,control_server.rs,logging.rs,native_menu.rs}`, `apps/soundcraft-cli/src/{main.rs,automation_args.rs}`, `apps/soundcraft-web/src/main.rs`, `crates/engine/src/{lib.rs,io.rs,autosave.rs,clip_group_file.rs,score.rs,file_access.rs,inspect.rs,demo.rs}`, `crates/engine/src/cmd/*.rs` (all 33 files; `mod.rs` `command_specs`/`cmd!`, `auto_rename`, `edit`, `edit_more`, `file`, `track`, `track_more`, `mix`, `clip`, `clip_more`, `clip_list`, `event`, `event_more`, `options`, `transport`, `health`, `instrument`, `sampler`, `surround`, `audio_midi`, `midi`, `query`, `tab`, `zoom_toggle`, `clap`, `vst3`, `plugin_state`, `setup_more`, `view`, `view_more`, `audiosuite`, `video`, `more_util`), `crates/model/src/{session.rs,mixer.rs,clip.rs,automation.rs}`, `crates/time/src/{format.rs,tempo.rs,timecode.rs}`, `crates/audio-io/src/{lib.rs,peaks.rs,wav.rs,aiff.rs,flac.rs}`, `crates/dsp/src/{plugins/mod.rs,offline.rs,meter.rs}`, `crates/midi/src/smf.rs`, `crates/playback/src/{record.rs,devices.rs,lib.rs}`, `crates/video/src/{lib.rs,movie.rs,convert.rs}`, `crates/mix/src/lib.rs`, `crates/automation/src/mcp.rs`, `crates/{clap-host,vst3-host,au-host}/src/lib.rs`, `crates/ui-egui/src/{control.rs,menus.rs,shortcuts.rs,fonts.rs,docking.rs,mix_window.rs,video_window.rs,extra_windows.rs,windows.rs,panels.rs,palette.rs,lib.rs}`, `crates/ui-egui/examples/ui_shot.rs`, `crates/engine/tests/it/{long_renders,commit_multichannel}.rs`, `packaging/macos/{entitlements.plist,package.sh}`.
- Docs read: `docs/{architecture,control-protocol,mcp,gaps,roadmap,target-app-parity,parity-checklist,file-format-parity,plugin-parity,hardware-parity,localization-parity,ui-parity,aux-routing-order,commit-multichannel,strip-mute-automation}.md`; catalog `crates/engine/catalog/menus.txt` (512 lines). Shared library consulted: `storytold/craft-libs` (`craft-docpath`, `craft-media`, `craft-ui`), rev `16fb3d46e1eaa0d455465e4e66a3ac7b17b55bd3` in `Cargo.toml`.
- Counts recomputed from source at this pin: 482 engine specs (481 literal/helper ids + 1 direct struct; 31 modules, 33 files), 67 UI commands (549 desktop ids, no overlap), 17 MCP tools, 17 control methods (2 aliases), 10 CLI subcommands, 30 plug-ins (27 processors + 3 instruments), 4 write extensions, `FORMAT_VERSION = 1`, `MAX_BATCH_CALLS = 1000`, 512-leaf menu catalog, 34 channel formats, 7 automation modes, 5 timebases, 5 fade shapes, 5 edit modes, 7 tools. The v0.12.1→v0.13.1 deltas were confirmed by comparing id sets and specs against the same paths at tag v0.12.1. Menu-parity 397/512 (engine) and 482/512 (app, ~40 % readiness) are the repo's measured/estimated claims, not re-run here (no build).
- Evidence table: `evidence.md` next to this file.