soundcraft · git:20261026.5706b6a · 2026-10-26 · sha256 4021522bdb703ce6

soundcraft git:20261026.5706b6aA

Immutable. This exact content never changes and is served at /api/v1/blob/4021522bdb703ce6.

---
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 built-in and CLAP/VST3/AU plug-ins, automation, recording, bounce/export, video
  and surround — and every action is a command 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/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` (shallow clone of the release tag). The installed `/Applications/SoundCraft.app` reports version `0.13.1` (`CFBundleShortVersionString`, `CFBundleVersion`, `SoundCraftVersion`), its `Info.plist` records `SoundCraftBuildCommit` = the same full SHA, and its binary prints `SoundCraft 0.13.1`, so the bundle matches this source exactly. **Verify against your installed version** if the ArtCraft Launcher has auto-updated since. Everything below was measured statically from this tree: no build, no GUI launch, no audio device was exercised.

## 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 crates plus three entry points — the desktop app, `soundcraft-cli`, and a WebAssembly build (`Cargo.toml` workspace members, `crates/`, `apps/`). It is **pre-alpha**: the repo's own generated report counts 397/512 target menu leaves implemented by the engine (77.5 %, `docs/parity-checklist.md`) and the alpha gate still lists failing workflows — interface recording, MIDI recording, session interchange (`docs/roadmap.md` "Alpha gate"). Source: `github.com/storytold/soundcraft` (organisation `storytold`).

## Capabilities (verified)

### The command layer is the API (start here)

- **482 engine command specs** at this tag, from the 31 production modules registered by `command_specs()` (33 `.rs` files in the directory: those 31 modules, `mod.rs` and the `more_util` helper; a 32nd module, `panic_probe`, is `#[cfg(test)]` inside `mod.rs`) (`crates/engine/src/cmd/mod.rs` `command_specs`).
  - Breakdown: 387 literal `cmd!` ids (including 9 `cmd!(view …)` calls) + 94 generated by four local helper macros — `toggle!` 17, `flag!` 61, `radio!` 6, `option!` 10 — + 1 direct `CommandSpec` (`mix.insert_state`) = 482. 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, engine 6, markers 6, midi 6, video 3, audiosuite 1, app 1.
  - **Changed since v0.12.1 (480):** +`clip.auto_rename` (new module `crates/engine/src/cmd/auto_rename.rs`) and +`edit.strip_silence_preview` (`crates/engine/src/cmd/edit.rs`). No id was removed. The v0.12.1 count of 480 stands as previously measured; 482 is the true count here.
  - Each spec carries id, label, menu path, shortcut, params doc, enabled reason, journal/undo flags and a run function (`crates/engine/src/cmd/mod.rs` `CommandSpec`). The helper macros live in `options.rs` (`toggle!`), `view_more.rs` (`flag!`, `radio!`) and `setup_more.rs` (`option!`); counting textual `cmd!(` occurrences (394 at v0.12.1, 397 here) overcounts macro-definition/doc lines and misses the 94 generated ids — derive the count from the id literals, not from `cmd!(`.
- **67 UI commands** (windows, dialogs, `ui.dock`, `ui.dock.reset`, `ui.theme`, `ui.audio_device`, `ui.set`, `window.*`, `view.mix_section`, `view.scroll_into_view`) live in `crates/ui-egui/src/menus.rs` (`UI_COMMANDS`), not in the engine registry. The desktop control channel appends them to `engine.commands`, so a running app reports **549 ids**; `soundcraft-cli commands` and the headless MCP server report **482**. No id overlaps the two sets. **Changed since v0.12.1:** 66 → 67 (`view.scroll_into_view` added).
- **CLI** (`soundcraft-cli`, 10 subcommands + `--version`/`--help`; `apps/soundcraft-cli/src/main.rs` module doc and dispatch): `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>`.
  - **New at v0.13.1:** every flag also accepts `--flag=value` (`arg_value`); `run`, `script` and `convert` now **reject unknown options and missing values** instead of silently ignoring them (`check_options`), so a typo is an error; `--cmd=ID=JSON` works inline; `--bounce` with no `--start`/`--end` bounces the **whole session** (`0`..`content_end`), not the leftover edit selection (`finish`); `convert` dithers only when resolution is lost (fewer bits than the source, a float/lossy source, or resampled/normalized output) unless `--dither`/`--no-dither` says otherwise (`auto_dither`).
  - `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 from v0.12.1. Core: `list_commands`, `execute`, `batch`, `inspect_session`, `new_session`, `open_session`, `save_session`, `import_audio`, `bounce_mix`, `parity`. App-only (need the desktop app plus `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`); larger arrays are 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`). **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?)".
  - Two UI commands refuse a direct call and name the replacement: `ui.new_tracks_dialog` ("call `track.new` with parameters") and `ui.bounce_dialog` ("call `file.bounce_mix` with a path"); both otherwise only open a dialog from the menu (`crates/ui-egui/src/menus.rs` `run_ui_command`).
  - 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 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 repeated** and a path inside any root of its kind is allowed (v0.13.1, #874), a kind left out grants no access of that kind, roots must exist, and `--connect` 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.

### 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 `tracks`, `clips`, `midi`, `memory`, `history`; operations `open`, `close`, `activate`, `float`, `move`; move zones `tab|center|left|right|top|bottom|above|below`) (`crates/ui-egui/src/docking.rs`, `docs/control-protocol.md` "Panel docking").
- 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`); cut/copy/paste/duplicate/repeat/shift/insert-silence/separate (selection, grid, transients)/heal/consolidate/strip-silence/nudge; Tab to Transient (`edit.tab`); Zoom Toggle (`view.zoom_toggle`); loop, quantize-to-grid, clip gain and gain envelope, clip effects, edit locks, colour, ratings (`crates/engine/src/cmd/{edit,edit_more,clip,clip_more,tab,zoom_toggle}.rs`).
- **Strip Silence has three modes** at this tag: `edit.strip_silence {threshold_db?, min_length_ms?, pad_before_ms?, pad_after_ms?, mode?: strip|extract|separate, name?, number?, zeros?, suffix?}` — strip removes the silence, extract removes the audio and keeps the silence, separate only cuts at the silence edges; the rename fields name the clips left in the selection (`crates/engine/src/cmd/edit.rs`; `crates/engine/src/io.rs` `StripMode`, `StripNames`). `edit.strip_silence_preview` (query) returns the padded ranges it would keep per track and changes nothing — this is what the Strip Silence window draws as rectangles (`crates/engine/src/io.rs` `StripScan`, `strip_silence_ranges`).
- **Clip Looping**: `clip.loop {clips?, count?, length?, fill?}` — `count` counts the clip plus its loops, `length` is the whole looped clip with the last loop cut short, `fill` loops to the next clip or the session end (`crates/engine/src/cmd/clip.rs`); `clip.unloop {mode?: remove|flatten}` (`crates/engine/src/cmd/clip_more.rs`).
- **Clip List › Auto Rename** (new): `clip.auto_rename {clips?, name?, number?: 0..999999999, zeros?: 0..9, suffix?}` sets the names that editing gives the clips it separates from the selected clips — the piece after each cut is `name` + zeros + an incrementing number + `suffix`, one sequence per clip; the schemes live in the session (`auto_names`) and survive save/reopen and `file.import_session_data` (`crates/engine/src/cmd/auto_rename.rs`, `crates/model/src/session.rs` `AutoName`, `crates/engine/src/edit.rs` `separate_at_if`).
- 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 with `"format": "soundcraft-track-preset"`) and Import Session Data between SoundCraft sessions (`file.import_session_data`) (`crates/engine/src/cmd/{track,track_more,clip_list,setup_more}.rs`; `crates/engine/src/clip_group_file.rs`).
- **`track.commit` keeps the track's exact `ChannelFormat`**, surround/height/ambisonic included, up to the renderer's 16 channels; wider formats are rejected before rendering, a failing track rolls back the whole multi-track commit (`crates/engine/src/io.rs` `commit_track`; `crates/mix/src/lib.rs` `MAX_CHANNELS = 16`, `render_track_pre_fader`; `docs/commit-multichannel.md`).
- Commands Keyboard Focus single-key editing in the Edit window: A/S trim start/end, D/G fade to start/end, F create fades, B separate, X/C/V cut/copy/paste, Z undo, N insertion-follows-playback, P/; move selection by track, L/' next/previous clip boundary, E zoom toggle, R/T zoom out/in, 1–5 zoom presets, comma/period/M/slash nudge; `options.keyboard_focus {commands|clips|groups|none}` (`crates/ui-egui/src/shortcuts.rs` `commands_focus`; `crates/engine/src/cmd/options.rs`).
- Undo/redo for document commands, an undo history window, and `engine.history` returning `{undo, can_redo}` (`crates/engine/src/cmd/edit.rs`, `query.rs`; `docs/architecture.md`).

### 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 (`crates/model/src/mixer.rs` `INSERT_SLOTS`/`SEND_SLOTS`; `docs/architecture.md` "Audio"). Aux and Folder strips are now ordered by **exact acyclic routing depth** (was capped at 8, which could run a consumer before its producer); a cyclic session keeps the old bounded calculation (`docs/aux-routing-order.md`).
- **30 built-in plug-ins** in `REGISTRY`: 27 processors + 3 instruments (`is_instrument: true` on the synth, drum synth and sampler). **Changed since v0.12.1 (27 = 24 + 3):** added `auto_pan` (LFO/envelope panning of a stereo track's mono sum), `ring_mod` (ring/frequency modulation, resonators; now also the AudioSuite Sci-Fi item) and `subharmonic` (octave-down bass). `engine.plugins` / `soundcraft-cli plugins` list them with ids and params (`crates/dsp/src/plugins/mod.rs` `REGISTRY`; `crates/engine/src/cmd/query.rs`). Docs now agree with the code (`docs/plugin-parity.md` 27 + 3).
- 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` — **10 named processes**, was 9. `speed_ramp` (the Vari-Fi item) takes `change` (0 slow to a stop, 1 speed up from one), `selection` (0 fit to the clip's length, 1 extend to 150 %) and `fades` (`crates/engine/src/cmd/audiosuite.rs`; `crates/dsp/src/offline.rs` `speed_ramp`). It renders a new source and replaces the clip, keeping fades; signal_generator fills a range with a new clip.
- 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`, `volume_to_clip_gain`, plus `edit.automation_coalesce_volume_to_clip_gain` / `edit.automation_coalesce_clip_gain_to_volume` (`crates/model/src/automation.rs`, `crates/engine/src/cmd/mix.rs`, `edit_more.rs`).
  - **Mute automation is evaluated per sample inside a render block** on audio, instrument, Aux and Folder strips (≥ 0.5 silences the strip and its pre-fader send copy); a mute and unmute inside one block both take effect (`crates/mix/src/lib.rs`, `docs/strip-mute-automation.md`).
  - Coalescing clip gain to a volume lane now **keeps an existing lane**: it pins the level just outside the clip and adds the clip gain on top of the lane's own points, so neighbouring segments keep their shape (`crates/engine/src/cmd/mix.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,vst3-host,au-host}/src/lib.rs`; `crates/engine/src/cmd/{clap,vst3}.rs`; `crates/ui-egui/src/mix_window.rs` `soundcraft_au_host::scan`).
  - The VST3 host now creates `IMessage`/`IAttributeList` objects, so plug-ins whose controller and processor talk over `IConnectionPoint::notify` load their content (`crates/vst3-host/src/lib.rs`).
- Plug-in editors: CLAP opens the plug-in's own floating window (hosted embedded Windows only), VST3 editors on macOS and Windows, Audio Units never (no Cocoa view hosting); on the signed macOS build third-party VST3 is refused because the bundle lacks the library-validation entitlement — still true at this tag (`docs/plugin-parity.md`; `packaging/macos/entitlements.plist` has only `com.apple.security.device.audio-input`; `docs/gaps.md`). Plug-ins run in-process, so a crashing plug-in can take the app down.
- Plug-in presets are partly UI-only: `mix.insert_params {preset?}` labels the recall, while user preset **files** are saved/recalled by the panel menu in `crates/ui-egui/src/panels.rs` (no engine command to enumerate preset files). For a hosted plug-in's opaque state use `mix.insert_state {track, slot: 0..9|"a".."j"|"instrument", state: base64|null}` — not undoable and not journaled (`crates/engine/src/cmd/plugin_state.rs`). The UI stores editor changes as they happen and on save/bounce (`crates/ui-egui/src/lib.rs` `capture_plugin_states`, `store_editor_changes`).

### 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`/`place_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 decoded into RAM as a session source (`crates/playback/src/record.rs` module doc, `TakeWriter::write_header`; `crates/engine/src/io.rs`). A take whose punch misses the audio leaves the session untouched.
- **Track input routing exists**: `track.input {"input":"In 3"}` sets the track's hardware input; `plan_takes` takes the first channel from the `In N` name and records `track.channels()` consecutive device channels, wrapping at the device's channel count (`crates/engine/src/cmd/track.rs` `route`/`parse_route`; `crates/engine/src/io.rs` `plan_takes`). What is missing is an input/output matrix with per-channel gain/delay and latency reporting — see limitations.
- 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 on its next open; it refuses while recording (`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/src/record.rs` `Recorder::open_with`). 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/playback/Cargo.toml`).
- Autosave on its own thread after changes, one folder per session, plus 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,event,event_more}.rs`; `crates/ui-egui/src/midi_editor.rs`).
- Standard MIDI File: reads formats 0 and 1 fully and format 2 best-effort; **always writes format 1**; exported controllers are included and the `tracks` filter is honoured. A lone conductor track is written only when it carries global data, and an empty sequence writes a zero-track file that reads back empty; overlapping same-pitch notes are trimmed so the reader's off/on pairing cannot swap their ends (`crates/midi/src/smf.rs`; `crates/engine/src/io.rs` `export_midi`).
- `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,event_more,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/`, `crates/engine/src/cmd/{event_more,clip}.rs`). Absolute grid lines (Seconds/Frames/Samples) are now computed from one rounded multiple instead of accumulating rounding (`crates/time/src/grid.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` and `setup.midi_beat_clock` only store settings (`docs/gaps.md` G-02; no MIDI-device crate anywhere in the workspace manifests).

### 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 0.6. Format is detected from bytes first, extension second (`crates/audio-io/src/lib.rs` module doc, `detect_format`; `Cargo.toml`).
- **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 (`crates/audio-io/src/lib.rs` `encode`, `FileEncoder`, `encode_wav_rf64`). Float32 WAV and AIFF-C `fl32` now **keep finite samples above full scale** instead of clipping them, mapping NaN to 0 and infinities to ±1 (`crates/audio-io/src/pcm.rs` `sanitize_float`; `crates/audio-io/src/aiff.rs` `fl32`). 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 and repairs a bad sample rate or tempo map, then **rejects** duplicate ids in any namespace and an exhausted id space (`crates/model/src/session.rs` `FORMAT_VERSION`, `from_json`, `sanitize`, `validate`). 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/engine/src/io.rs` `save_session`, `write_session`, `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.
  - Sessions written by 0.13.1 can carry fields an older build does not know: the session `auto_names` map and each clip's `auto_name` key. Serde ignores unknown fields and drops them on the next save, so a downgrade does not fail — it silently loses the Auto Rename schemes. Do not assume downgrade is lossless; keep the newer app for a session it wrote.
- `.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). The writer now enforces the same limits as the reader, so a saved bundle always reopens. 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` `open_session`). Convert/unpack it on the web side or refuse it.
- 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` `crates/docpath`, workspace pin 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.

### 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, 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 at this tag** (`crates/engine/src/cmd/file.rs`; `crates/engine/src/io.rs` `bounce_stems_with`).
- `file.export_clips {dir, clips?, format?: wav}` writes clips as **WAV only** — the spec now documents `wav` and `io::export_clips` uses the default encoder. `file.export_midi {path, tracks?}`, `file.export_session_text {path?, replace?: false}` (**`replace` is new**: an existing file is now refused without `replace: true`), `file.export_selected_tracks_as_session {path, tracks?, replace?}`, `file.export_sibelius`/`file.send_to_sibelius` (MusicXML `.musicxml`), `file.print_score` (SVG), `file.export_clip_groups` (`.scgrp`) (`crates/engine/src/cmd/file.rs`; `crates/engine/src/io.rs` `export_clips`, `replaces`).
- `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), Apple ProRes (422 Proxy/LT/Standard/HQ, 4444/XQ) and Motion JPEG (`crates/video/src/lib.rs`, `crates/engine/src/cmd/video.rs`, `crates/ui-egui/src/video_window.rs`). HEVC, AV1 and DNx movies are recognised by the demuxer but frames return "unsupported". At this tag the container/bitstream layers are **shared craft-libs code** (`craft_media::isobmff`, `craft_media::bitstream`) rather than in-repo copies; H.264 and ProRes remain in-repo (`crates/video/src/lib.rs` module doc; `Cargo.toml`).
- 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; `track.object` for object/bed routing; Renderer window with speaker levels (`crates/model/src/mixer.rs` `ChannelFormat`, `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, 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`).

### What moved at v0.13.1

- **Automation surface:** 482 engine commands (was 480), 67 UI commands (was 66 → 549 desktop ids), 17 MCP tools (unchanged), 17 control methods (unchanged), 10 CLI subcommands (unchanged, flags changed below), 30 built-in plug-ins (was 27), 10 AudioSuite process names (was 9), 4 write extensions (unchanged).
- **CLI:** strict option checking, `--flag=value`, `--cmd=ID=JSON`, `convert` dither policy, repeatable automation roots, `--bounce` default range = whole session.
- **Engine commands:** `clip.auto_rename` (new module), `edit.strip_silence_preview` (new), `edit.strip_silence` modes + rename, `clip.loop` gains `fill`/`length`/`count` docs, `file.bounce_stems` gains `dither`, `file.export_session_text` gains `replace`.
- **Audio engine fixes** (behaviour, no new ids): exact acyclic aux/folder routing order; per-sample mute automation inside a block; master volume automation uses the block's true last sample; failed insert containment excluded from aux/folder/master latency; Split into Mono keeps each source channel; a bus bounce through an aux is latency-compensated; master insert PDC; mono device playback averages both sides; NaN/infinity map to silence; Float32 file headroom preserved (`docs/roadmap.md` progress log, `docs/gaps.md` G-04).
- **Import path:** the desktop app decodes an imported file on a worker thread and hands it to `file.import_audio` (`crates/ui-egui/src/import.rs`, `crates/engine/src/io.rs` `decode_import`/`offer_import`); the engine still ends up with the whole file in RAM.
- **Session model:** `auto_names` map + `Clip::auto_name`, `AutoName` sanitisation (≤ 9 zeros, ≤ 999 999 999 start), load-time id `validate()`, and a `TrackHeight` deserialiser that still accepts the legacy bare `"Fit"` string (drawn as Medium).
- **Shared library pins moved:** craft-libs `16fb3d46e1eaa0d455465e4e66a3ac7b17b55bd3` for `craft-docpath`, `craft-ui` and the new `craft-media`; symphonia 0.6, cpal 0.18, chrono added (`Cargo.toml`).

## Known limitations and not-implemented

Do not assign these; each is absent or partial at v0.13.1 (code checks plus repo docs 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 any manifest).
- **No Pro Tools interchange.** No `.ptx`/`.ptf`, AAF, OMF or Media Composer session import/export; the parity report still lists "Export > Selected Tracks as New AAF/OMF" and "…Media Composer Compatible Session" as unimplemented; the nearest bridge is `file.import_session_data` between SoundCraft `.scraft` sessions (`docs/parity-checklist.md`; `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 duplex clock, no mixer-side I/O matrix, no latency reporting and no record-offset compensation. Device/input/rate/buffer choice is real through `ui.audio_device`, but `docs/hardware-parity.md` still marks macOS verification as pending, and `docs/roadmap.md`'s alpha-gate row is **stale** on two points: it says "no input device choice" (the recorder honours `AudioConfig.input`) and "takes held in RAM until stop" (the take writer streams each take to a WAV file while recording) (`crates/playback/src/{lib,devices,record}.rs`; `crates/ui-egui/src/lib.rs` `ensure_input`).
- **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`; `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, still absent from `packaging/macos/entitlements.plist`). AU editors are not hosted and AUs have no discovery command; CLAP embedded editors exist only on Windows; VST3 editors only macOS/Windows. 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).
- **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/movie.rs` `Codec::Unsupported`).
- **No immersive delivery.** No ADM BWF, Dolby Atmos renderer integration or binaural; surround mixing itself exists (`docs/gaps.md` G-12; no ADM/Dolby/binaural code in `crates/`).
- **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`).
- **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), so Korean text renders; there is no explicit fallback for other scripts and no bundled font. The localization doc still says there is no Hangul fallback — stale (`crates/ui-egui/src/fonts.rs`; `docs/localization-parity.md`).
- **Session management is thin.** `session.save_template`, track presets and Clip List Auto Rename 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, View 92/112, File 22/30) (`docs/parity-checklist.md`, the repo's report — not re-run here). AudioSuite catalog leaves all map to the single `audiosuite.process` command, so "menu present" is not per-item behaviour (`crates/ui-egui/src/menus.rs` `AUDIOSUITE`).
- **Docs still lag the code** in these places: `docs/roadmap.md` alpha gate says "no input device choice" (input choice works); `docs/gaps.md` G-16 lists track-preset recall UI as missing (recall commands exist, the browser does not); `docs/localization-parity.md` denies the Hangul fallback. 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` (built-in demo session), `--no-audio` (no output player; the input is only opened alongside the player, so no device is opened either — still never arm or record in a smoke test), `--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 on a worker thread (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,logging}.rs`, `crates/ui-egui/src/fonts.rs`).
- Settings on macOS: `~/Library/Application Support/SoundCraft/` — `ui.json`, `Autosave/`, `Presets/`, `logs/soundcraft.log` (rotated 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**: 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; `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, 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 (`crates/engine/src/cmd/mod.rs` `position_param`; `crates/time/src/format.rs` `parse_position`; `apps/soundcraft-cli/src/main.rs` `time_arg`). Negative timecode/feet-frames positions now round-trip exactly.
- 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 (`crates/engine/src/cmd/mod.rs` `range_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,setup_more}.rs`).
- 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. It refuses while recording; stop first. Save before changing devices: the change rebuilds the player and its plug-in instances.

### Keyboard shortcuts (verified in code)

Space play/stop; Shift+Space half-speed; Cmd+Space or F12 record; F1–F4 edit modes (hold F4 with F1–F3 for Grid/Snap; Alt+1–4 also work); F5–F10 tools Zoom/Trim/Selector/Grabber/Scrubber/Pencil; Enter new memory location; Cmd+= Mix/Edit; Cmd+Ctrl+S search; Tab to transient (Alt+Tab back, Shift+Tab extends); Cmd+Shift+N new tracks; Cmd+S/O/N save/open/new; Cmd+E separate; Cmd+D duplicate; Cmd+U strip silence; Cmd+Alt+L loop clip; Cmd+Z undo; Cmd+Shift+K export clips; Cmd+Alt+B bounce mix; Cmd+P print score; Cmd+1 Transport, Cmd+3 Big Counter, Cmd+4 Automation, Cmd+5 Memory Locations, Cmd+9 Video; Alt+Shift+C track commit; Cmd+Alt+[ / ] waveform zoom; Cmd+Ctrl+1–5 zoom presets; Cmd+Alt+0–5 clip rating; Cmd+W close window; Cmd+Ctrl+W hide floating windows (`crates/ui-egui/src/shortcuts.rs`; command specs in `crates/engine/src/cmd/`). The in-app **Setup › Keyboard Shortcuts** window (`window.shortcuts`) is the live list.

### 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"}'

# Named clips from Strip Silence, then preview the ranges without touching the session.
soundcraft-cli run --in song.scraft --cmd 'edit.strip_silence_preview={"tracks":["Vox"]}'
soundcraft-cli run --in song.scraft \
  --cmd 'edit.strip_silence={"tracks":["Vox"],"mode":"strip","name":"VOX","number":1,"zeros":2}' \
  --save song.scraft --inspect

# Give an MCP client a headless DAW confined to two read folders and one output folder.
claude mcp add soundcraft -- soundcraft-cli mcp --demo \
  --automation-read-root ./project --automation-read-root ./samples \
  --automation-write-root ./out
```

`mix.insert` takes a built-in id from `engine.plugins` (or a `clap:`/`vst3:`/`au:` id); `mix.send` needs the bus to exist. All positions in these examples are samples.

### 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), 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, and do not add private top-level keys (serde drops unknown fields on the next save). Keep ids unique per namespace and `next_id` below `u64::MAX`, or the load is rejected. Keep `Audio Files/` paths relative to the session folder. 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).
- **Name clips from a batch edit.** `clip.auto_rename` before a cut/Strip Silence run gives every resulting piece a numbered name; `edit.strip_silence`'s `name`/`number`/`zeros`/`suffix` do the same inside that command.
- **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`, `view.scroll_into_view`, 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. 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 a few 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`.
3. **Back up and version per session**: copy the whole folder (`.scraft` + `Audio Files/`), not just the JSON; `Backups/` only receives a copy when an existing session file is overwritten and its text changed, and keeps the newest ten.
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. Opening a session saved by a newer build in an older one silently drops the fields it does not know (Auto Rename schemes today), so keep the newer app for sessions it wrote.
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`, release commit dated 2026-10-26. Installed app `/Applications/SoundCraft.app` reports `0.13.1`; `Info.plist` `SoundCraftBuildCommit` is the same full SHA; `CFBundleIdentifier` `ai.storyteller.soundcraft`; binary `--version` prints `SoundCraft 0.13.1`. The bundle contains only `SoundCraft`, not `soundcraft-cli`.
- Read: `README.md`, `ROADMAP.md`, `AGENTS.md`, `Cargo.toml`, `apps/soundcraft/src/{main.rs,config.rs,control_server.rs,logging.rs}`, `apps/soundcraft-cli/src/{main.rs,automation_args.rs}`, `apps/soundcraft-web/src/main.rs`, `crates/engine/src/{io.rs,autosave.rs,clip_group_file.rs,score.rs,file_access.rs,edit.rs,lib.rs}`, `crates/engine/src/cmd/*.rs` (all 33 files; `mod.rs` `command_specs`/`cmd!`, `file`, `track`, `track_more`, `mix`, `edit`, `edit_more`, `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`, `clip`, `clip_more`, `clip_list`, `auto_rename`, `audiosuite`, `video`, `more_util`), `crates/model/src/{session.rs,mixer.rs,clip.rs,automation.rs,track.rs}`, `crates/time/src/{format.rs,grid.rs,tempo.rs,timecode.rs}`, `crates/audio-io/src/{lib.rs,pcm.rs,wav.rs,peaks.rs}`, `crates/dsp/src/{plugins/mod.rs,plugins/auto_pan.rs,offline.rs,meter.rs}`, `crates/midi/src/smf.rs`, `crates/mix/src/lib.rs`, `crates/playback/src/{record.rs,devices.rs,lib.rs}`, `crates/video/src/{lib.rs,movie.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,appearance.rs,import.rs,mix_window.rs,video_window.rs,extra_windows.rs,windows.rs,panels.rs,lib.rs}`, `crates/ui-egui/examples/ui_shot.rs`, `packaging/macos/{entitlements.plist,package.sh}`, `packaging/linux/package.sh`.
- Docs read: `docs/{architecture,aux-routing-order,commit-multichannel,control-protocol,mcp,gaps,roadmap,target-app-parity,parity-checklist,file-format-parity,plugin-parity,hardware-parity,localization-parity,ui-parity,strip-mute-automation}.md`; catalog `crates/engine/catalog/menus.txt` (512 lines). Shared library pins from `Cargo.toml`: `storytold/craft-libs` rev `16fb3d46e1eaa0d455465e4e66a3ac7b17b55bd3` (`craft-docpath`, `craft-ui`, `craft-media`).
- Counts recomputed from source at this pin: 482 engine specs (387 literal `cmd!` + 94 helper-macro ids + 1 direct struct), 67 UI commands (549 desktop ids), 31 registered production modules (+1 test-only) across 33 files, 17 MCP tools / 2 resources / protocol `2025-06-18`, 17 control methods (2 aliases), 10 CLI subcommands, 30 built-in plug-ins (27 + 3), 10 AudioSuite process names, 34 channel formats, 4 write extensions, `FORMAT_VERSION = 1`, `MAX_BATCH_CALLS = 1000`, `INSERT_SLOTS = SEND_SLOTS = 10`, `mix::MAX_CHANNELS = 16`, 512-leaf menu catalog. Menu-parity 397/512 is the repo's generated report, not re-run here (no build).
- Change attribution: every "since v0.12.1" statement was derived by diffing this tree against a read-only fetch of tag `v0.12.1` (`e85a4801e9029c9a1f5ce536b11be65aea832ed8`, the previous skill's pin) and re-extracting ids with the same script — 480 → 482 ids, 27 → 30 plug-ins, 66 → 67 UI commands.
- Evidence table: `evidence.md` next to this file.