Locked decisions
These are the locked decisions behind Pit’s design — collected here so a contributor (or future-you) can see the shape of the project without trawling old planning conversations. Each links to where the decision actually lives (a feature page, a concept doc, or an ADR).
Don’t re-litigate any of these casually. If a decision genuinely needs reopening, propose it explicitly — preferably with a new ADR — and update this page when it resolves.
Engine + audio
- CLAP only for plugins in v1.0; VST3 + AU as v1.x or v2.0 — see Plugin hosting
- First-party Rust SDKs for VST3 + AU are v2.0+ initiatives
- Plugin sandboxing (out-of-process) is a hard requirement — currently violated, scheduled v0.7.0 — ADR-0002
- Realtime paths allocation-free — see Engine architecture and CLAUDE.md
- Engine consumes whole patch graph —
Plan::build→ topo-sort → allocation-freePlan::processper block — ADR-0006, Accepted - Audio I/O on Windows: ASIO default when a vendor driver is detected AND input + output are the same device; WASAPI Exclusive default otherwise (including any split-I/O configuration — ASIO is single-device by design). WASAPI Shared as fallback when Exclusive fails. macOS uses CoreAudio. Linux uses ALSA → JACK → PipeWire fallback. Separate input/output devices fully supported via WASAPI — see Latency budget (default flipped from “WASAPI Exclusive first” — refined 2026-05-28 during v0.6.0 refinement of #8)
- No VST2 support, ever — Steinberg deprecated 2018, SDK not available to new licensees
- MIDI device identity = midir’s opaque platform port id, name as replug fallback — not
(vendor_id, product_id)as originally locked in #2 refinement: midir exposes no vendor/product IDs on any platform, so that decision was unimplementable as written. Port ids are stable on CoreMIDI (macOS); the display-name fallback covers replug on platforms with unstable ids. Names remain display-only + the open key. (Amended 2026-07-03 while shipping stardust-pit#2 in PR #118.) - Device change decision tree: identity → rebind, sample-rate/buffer-size → rebuild —
engine_rebind_routingswaps cpal/midir I/O under the live Plan (no plugin reloads, held voices intact); anything that resizes buffers or reactivates plugins goes through a fullengine_start_from_patchrebuild. On rebind failure the previous device stays (or is restored) active — never silent-fail, never silence. (Shipped 2026-07-03, stardust-pit#1 / PR #118.) - Panic resets every controller the engine touches, not just notes — sustain-off, per-tracked-voice note-offs + poly-aftertouch-clear, all-notes-off, pitch-bend center, mod-wheel 0, channel-pressure 0, all 16 channels, within one audio block. (Shipped 2026-07-03, stardust-pit#3 / PR #118.)
- Source kinds imply MIDI event classes — a sustain-pedal source accepts only CC 64, expression CC 11, mod wheel CC 1, pitch wheel pitch-bend, pads notes, keyboards everything; binding filters (channel / note / CC range) narrow further. Prevents note-doubling when a pedal and a keyboard feed the same instrument. (Locked while shipping #2, 2026-07-03.)
- Device identity lives on the rig component; node-level
hardwareBindingceases to exist — rig basics pulled forward from v0.10.0 to v0.6.0 as rig-lite (stardust-pit#122): configure a device once in Setup → Rig, patch source nodes reference the component (rigComponentId) — that reference is the node’s identity, not a binding override. There is no fallback model: a v2→v3 raw-JSON migration auto-converts #2-era per-nodehardwareBindingblobs into rig components (one per distinct device+channel), points the nodes at them, and deletes the field. A source node with no component assigned is silent (UI flags it), not any-device omni. #2’s engine matching/filter/rebind plumbing is reused underneath, resolving component bindings instead of node blobs. (Direction locked 2026-07-06 during post-#118 review; fallback model dropped and details locked 2026-07-06 during #122 refinement.) - Engine opens the union of rig-bound devices, session-wide — MIDI input set derives from the rig (every device any component is bound to), not from the active patch. Patch switches never touch MIDI I/O; rebind (#1 path) fires only on rig edits and device connect/disconnect. Learn mode temporarily opens all connected inputs, then restores the rig-derived set. Supersedes #121’s draft “derive from the active patch’s bindings”. (Locked 2026-07-06 during #122 refinement.)
- Sine synth →
instrument.testtone— hidden from catalog, diagnostic-only. Default built-in instrument becomes the bundled GM piano SFZ in v0.14.0. (Shipped 2026-07-03 in v0.6.0 via stardust-pit#9:stardust.patch+stardust.showschemas bumped to v2 with a v1→v2 rename migration; Settings → “Run engine self-test” is the only user surface. Engine topo lifts MIDI sources before any consumer in the same block — needed for the single-block end-to-end self-test render.) - Native SFZ player as graph node, not standalone CLAP (sforzando exists for users wanting CLAP) — see v0.14.0 roadmap
- Bundled GM piano SFZ (~3 MB) in install bundle so first-launch isn’t silent
Data model
- All patches are references to library entries (no inline-vs-ref dichotomy). Library entries have
scope: "show" | "global". Show file bundles snapshots of global entries on share — see Patch library - Orphan handling: deleted library entry → refs freeze the last graph as
orphan.snapshot, banner + reattach/save-new/keep options - Patch reference overrides: Basic (name, notes, tempo, transpose, trim, color, tags) + Advanced (MIDI channel offset, bus routing, plugin params, FX bypass, on-enter/exit triggers, custom CSS). Tempo override = Advanced.
- Graph edits to shared patches trigger merge UI with per-instance update/keep/three-way-merge dialog
- Show metadata structured as Production (this run) / Source (the work) / Distribution (sharing). Source includes Productions list for revivals — see Show metadata model
- Schema-versioned everything — ADR-0003
- All shipping data formats use semver with explicit migration paths
File format
.stardustshow/bundle (folder with extension) — not single JSON. Containsshow.json,libraries/,assets/audio,assets/images,assets/samples,thumbnails/. Opt-in zip export for sharing — see Patch library and Backing tracks- Migration scheduled v0.13.0 (when backing tracks force the issue anyway)
UI shell
- Three modes: Setup / Program / Perform — see concept doc
- Settings is a floating Window, not a fourth mode
- Plugin GUIs dock in the bottom panel of the patch editor by default, with per-plugin pop-out to a floating Window. User’s dock-vs-float preference persists per plugin instance — see Plugin hosting (decision reversed from earlier “floating only” — refined 2026-05-28 during v0.6.0 refinement of #6)
- New Show wizard is a Modal — see New Show wizard
- Splash is pre-shell screen (separate from the three modes)
- Native menu bar for File/Edit/View/Window/Help — mode switches are NOT in the menu bar
- Song page renders in patch-canvas area when a song is selected in outline (tabs: Settings / Click / Backing / Patches)
- No hard-forced Live widgets — user places everything; layout templates seed sensible starting layouts
- Confirm-patch-changes-during-Live: default off (MainStage style instant), opt-in confirm available
- Layout: global default + per-song override (cascading)
Widget customization
- Every widget has a config inspector; detailed specs refined in v0.11.0 spec session — see Widget catalog
- MIDI widgets reactive in Live (keys/pads/wheels/pedals reflect hardware state)
- Conductor cam: USB webcam only v1.0; RTSP/NDI/virtual-webcam v2.0+ — see Conductor cam
- Engine monitor: includes CPU/RAM/xrun/peak/MIDI activity/plugin status/crash count/latency/uptime. Excludes thermal pressure + raw temp (cross-platform fragility) — see Engine monitor
- Clock widget: pause + splits (manual / auto-from-songs / auto-from-cuepoints)
Click track + transport + backing tracks
- Click track editor: no bake step — data-driven, always-live — see Click track editor
- SMF Type 1 export/import for DAW interop (tempo + bar markers + cue points; vamps/repeats via our marker naming)
- Tempo per song + per bar (no master-show BPM)
- Backing tracks decouple from patches — tracks on songs, transport independent, patches optionally interact — see Backing tracks
- Vamp interaction: vamp regions in click track; transport loops; footswitch signals end-of-vamp
- MIDI clock sync: song-level (not per-track)
- MIDI recording + audio bounce in v0.13.0; piano roll editor in v1.x
Balance tool
- LUFS via
ebur128crate — see Balance tool - Offline render (silent measurement)
- Velocity-normalized at v80 for v1.0; velocity-curve at v40/v80/v120 for v1.x
- True Peak + LRA + Integrated LUFS reported per patch
Plugin missing handling
- Warning icons on patch name + plugin node + greyed-out missing node
- “Find this plugin” link via CLAP
plugin.urlmetadata - Graph node never removed when plugin missing — preserved for when plugin returns
- Show plugin requirements auto-computed and surfaced in pre-show validation (v0.8.0)
Extension API
- Hybrid TypeScript + WASM for v0.15.0 — TS for UI/importers/commands, WASM for compute-heavy — see Extension API
- Realtime WASM extensions v2.0+
- Sandboxed by default — no native module plugins
- Stream Deck support ships as bundled example extension
Sync + ecosystem (post-v1.0)
- CRDT (Automerge) for sync + collaboration — works offline, P2P-capable, server-optional — see Marketplace architecture
- Local-first non-negotiable — every cloud feature is additive, never gates the app
- MoR for payments: Lemon Squeezy or Polar.sh (do not build payment infra from scratch)
- Self-hostable marketplace server — open source aligns with AGPL ethos
- No mandatory accounts ever — anonymous downloads stay available
- Cloud provider stack (provisional): Cloudflare R2 + Fly.io + Neon Postgres + Clerk/Ory + Plausible + GlitchTip — see Infrastructure choices. Architecture is provider-agnostic; can swap to AWS/GCP if needed.
Tooling
- Bun, not npm for stardust-pit (uses
@tauri-apps/cliJS, notcargo-tauri) - GitHub Projects v2 for kanban tracking — https://github.com/orgs/StardustMT/projects/1
- GitHub Actions for CI (basic PR CI in v0.6.0, soak tests in v0.7.0, visual regression in v0.10.0, release pipeline in v0.15.0)
- No
Co-Authored-By: Claudefooter in commits. Ever. - Storybook stays as design-iteration surface — real screens in
src/src/screens/*.tsx,.stories.tsxwraps with fixture data - Storybook-first for UI features — CLAUDE.md rule
- Placeholder icons in
stardust-pit/src-tauri/icons/— don’t touch until real branding - Pre-feature refinement + post-feature review sessions per CLAUDE.md
- Accessibility is a hard requirement — WCAG 2.2 AA, full keyboard nav, screen reader, focus indicators, reduced-motion
Crate organization
- Stay flat Cargo workspace — group by naming convention (
stardust-audio-*etc.). Revisit if past ~15 crates — ADR-0008
What’s NOT in v1.0
- AU plugin hosting (tentatively v1.0 if scope allows; defer otherwise)
- Multi-channel audio input via Dante/AVB (USB multitrack only)
- Cloud sync, marketplace, collaboration (all v2.0+)
- Sheets app (post-Pit-v1)
- Piano roll editor (v1.x or v2.0)
- Pure-Rust VST3 host (v1.x with C++ shim is fine; pure-Rust is v2.0)
- Realtime WASM extensions (v2.0+)
- IP/RTSP/NDI conductor cam (v2.0+)
- DMX / lighting (revisit if Show Control unlocks demand)
- Velocity-curve balance (v0.12.0 does velocity-normalized; curve in v1.x)
- Show Control as full theatre brain (v2.0+) — see Show Control vision
- Full DAW MIDI import (v1.x or v2.0)
Related
- Pit roadmap — what ships and in what order
- ADRs — fully-argued decision records