xtc/docs/IDEA.md
2026-10-26 22:51:25 +01:00

723 lines
30 KiB
Markdown

# Project Brief: A Modern, Lightweight, energyXT-Inspired DAW
**Document purpose:** This file is the authoritative project brief and implementation guide for an AI coding agent working inside an IDE.
**Working project name:** `xtc` (temporary; keep the project name configurable and do not hard-code it into public APIs).
**Primary goal:** Build a modern, reliable, lightweight digital audio workstation inspired by the workflow and modular philosophy of energyXT. This is a new implementation, not a clone of energyXT's proprietary code or assets.
**Architecture decision:** **B — Modular engine first.** The sequencer, instruments, effects, mixers, audio inputs, and outputs are components in one unified processing graph. The default user experience should still be a simple sequencer-first workspace; users can open the modular view when they need to inspect or edit signal flow.
**Language decision:**
- **100% C for the audio engine and core domain logic.**
- **FLTK with C++ for the graphical user interface only.**
- The GUI talks to the core through a small, documented C API.
- Do not introduce C++ into the audio engine or core library.
- Keep dependencies small, prefer open-source components, and avoid unnecessary frameworks.
---
## 1. Instructions to the AI coding agent
Treat this document as the project contract. Follow it in every implementation task.
1. Read this file and the existing project before making changes.
2. Inspect the current build system, source tree, and tests. Do not assume the repository is empty.
3. Implement work in small, buildable increments. Each increment must compile and have relevant tests before proceeding.
4. Prefer complete, coherent file updates over partial snippets. Do not silently remove existing functionality.
5. Before editing, state briefly what you intend to change and which files are affected. Then make the changes.
6. Do not invent APIs, FLTK features, audio backend capabilities, or dependency behavior. Check installed headers and official documentation when uncertain.
7. Keep C source compatible with **C99** initially. Use C11 features only when explicitly justified and supported by the selected compiler/toolchain.
8. Use C snake_case identifiers. Use clear names; avoid cryptic one-letter names except conventional loop counters.
9. Validate external input, lengths, indexes, counts, enum values, and allocation sizes. Handle errors explicitly.
10. Do not hide failures, silently clamp invalid data, or continue with partially initialized objects.
11. Avoid unnecessary dependencies, generated-code systems, global mutable state, and elaborate abstractions.
12. Never perform audio-thread-unsafe operations from the real-time callback. See the real-time rules below.
13. Keep the project usable on Linux first. Do not make Linux-specific details leak into the public core API.
14. Add or update tests and documentation with each feature.
15. At the end of a task, report: files changed, behavior added, build/test commands run, results, known limitations, and the next sensible step.
16. If a requirement is ambiguous, choose the smallest safe implementation consistent with this document and explain the assumption. Ask a question only if the ambiguity blocks safe progress.
17. Do not claim that a feature works unless it has been implemented and tested. Clearly distinguish a prototype, stub, and finished feature.
18. Never copy energyXT source code, proprietary resources, UI artwork, or undocumented internal implementation. Use energyXT only as inspiration for high-level workflow ideas.
### Definition of done for every task
A task is complete only when:
- the code builds with warnings enabled;
- relevant tests pass;
- error paths are considered;
- resource ownership and cleanup are clear;
- documentation is updated where behavior or APIs changed;
- no unrelated functionality was removed;
- the agent reports any untested behavior honestly.
---
## 2. Product principles
The application should feel like a small, responsive music instrument rather than a general-purpose content-creation suite.
### Core principles
- **Simple by default, powerful when opened up.**
- A single unified component graph underlies the application.
- The sequencer, mixer, instruments, effects, audio I/O, and utility processors use a common node model.
- Common operations should require few clicks and avoid modal dialogs when practical.
- The modular graph should expose real signal flow, not be a decorative diagram.
- The project must remain understandable to a human developer.
- Audio reliability is more important than visual effects or feature count.
- Favor predictable behavior over clever implicit behavior.
- Use non-destructive editing where practical.
- Avoid feature bloat. Every feature must justify its complexity.
- Save files should be versioned and recoverable.
- The application should remain useful at every development milestone.
### Explicit non-goals for the first release
Do not initially implement:
- video, notation, collaboration, cloud accounts, or online services;
- an integrated marketplace;
- scripting as a requirement for basic operation;
- dozens of bundled instruments or effects;
- advanced surround/spatial audio;
- distributed processing;
- a complicated theme system;
- an elaborate plugin sandbox;
- automatic online update services;
- a proprietary project format that cannot be inspected or migrated.
These may be considered later, but they must not delay the first reliable recording and playback workflow.
---
## 3. High-level architecture
Use distinct layers with narrow interfaces.
```text
+------------------------------------------------------------+
| FLTK / C++ GUI |
| Main window, transport, sequencer, mixer, modular editor, |
| dialogs, custom drawing, keyboard and mouse interaction |
+----------------------------+-------------------------------+
|
documented C API
|
+----------------------------v-------------------------------+
| C application core |
| Project model, commands/undo, transport state, persistence, |
| parameter model, asset references, validation |
+----------------------------+-------------------------------+
|
real-time-safe bridge
|
+----------------------------v-------------------------------+
| C audio engine |
| Graph compilation, node processing, scheduling, buffers, |
| transport sample position, MIDI/event delivery, metering |
+----------------------------+-------------------------------+
|
backend abstraction
|
+----------------------------v-------------------------------+
| Audio/MIDI backend(s): start with JACK-compatible audio |
| and ALSA MIDI on Linux; add backends only when justified |
+------------------------------------------------------------+
```
The C core and audio engine may be separate libraries or clearly separated modules within one C library. Keep their interfaces explicit even if they initially compile into one library.
### Suggested repository layout
Adapt this to an existing repository instead of replacing it blindly.
```text
xtlite/
├── README.md
├── LICENSE
├── Makefile
├── docs/
│ ├── ARCHITECTURE.md
│ ├── AUDIO_ENGINE.md
│ ├── PROJECT_FORMAT.md
│ ├── GUI.md
│ ├── ROADMAP.md
│ └── decisions/
│ ├── 0001-modular-engine-first.md
│ ├── 0002-c-core-fltk-gui.md
│ └── 0003-realtime-thread-rules.md
├── include/
│ └── xtlite/
│ ├── xtlite.h
│ ├── xtlite_types.h
│ ├── xtlite_project.h
│ ├── xtlite_engine.h
│ └── xtlite_version.h
├── src/
│ ├── core/
│ ├── project/
│ ├── graph/
│ ├── engine/
│ ├── backend/
│ ├── midi/
│ ├── dsp/
│ ├── persistence/
│ ├── util/
│ └── gui/
│ ├── main.cpp
│ ├── main_window.cpp
│ ├── main_window.hpp
│ ├── transport_widget.cpp
│ ├── sequencer_widget.cpp
│ ├── mixer_widget.cpp
│ └── graph_widget.cpp
├── tests/
│ ├── unit/
│ ├── integration/
│ └── fixtures/
├── examples/
└── build/ # generated; do not commit
```
Keep C and C++ files clearly separated. C headers exposed to C++ must use `extern "C"` guards.
---
## 4. Language and API boundary
### C code owns
- project and track data;
- graph nodes, ports, and connections;
- audio processing and DSP;
- transport and sample-position calculations;
- MIDI event representation and scheduling;
- parameter values and automation evaluation;
- project serialization and validation;
- undoable core commands where they affect domain state;
- backend-independent engine state;
- error codes and diagnostic messages.
### C++/FLTK code owns
- windows and widgets;
- layout and drawing;
- mouse and keyboard interaction;
- selection and view state;
- GUI-only drag previews;
- user-facing dialogs and menus;
- conversion between user gestures and C API commands.
### Boundary rules
- Public C headers must compile from both C and C++.
- Do not expose C++ classes, STL containers, exceptions, or FLTK types through the C API.
- Prefer opaque handles for objects whose internal layout must remain private.
- Use explicit integer error codes and documented output parameters.
- Define ownership for every pointer: borrowed, caller-owned, or transferred.
- Do not return pointers into mutable engine memory unless their lifetime is guaranteed.
- Do not let GUI widgets mutate internal engine structures directly.
- Avoid a public API that mirrors every internal struct field. Provide stable operations instead.
Example API shape (illustrative only; refine after reviewing the actual codebase):
```c
#ifndef XTLITE_H
#define XTLITE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct xt_app xt_app;
typedef enum {
XT_OK = 0,
XT_ERR_INVALID_ARGUMENT,
XT_ERR_OUT_OF_MEMORY,
XT_ERR_NOT_FOUND,
XT_ERR_INVALID_STATE,
XT_ERR_IO,
XT_ERR_UNSUPPORTED
} xt_result;
xt_result xt_app_create(xt_app **out_app);
void xt_app_destroy(xt_app *app);
xt_result xt_transport_play(xt_app *app);
xt_result xt_transport_stop(xt_app *app);
#ifdef __cplusplus
}
#endif
#endif
```
Do not implement this exact API blindly if the existing design calls for a better interface. Keep it small and consistent.
---
## 5. Modular processing graph — primary design decision
**Decision B: Modular engine first.** The graph is the common representation of signal processing and routing. The sequencer is a source of time-based events and clips, not a separate audio system disconnected from the graph.
### Node categories
Start with a small set:
- audio input;
- audio output;
- audio clip player;
- MIDI input;
- MIDI clip/event player;
- instrument;
- audio effect;
- gain/pan;
- mixer;
- metering/analyzer;
- utility nodes such as channel mapping.
Do not build every node at once. First prove the graph with a test tone, gain node, and output node.
### Ports and connections
Each node has a stable identifier, a node type, and declared ports. A port has:
- a unique identifier within its node;
- a direction (input or output);
- a signal/data type (audio, MIDI/event, or control);
- a channel layout or event format where applicable;
- a documented sample/block processing contract.
Validate connections before accepting them:
- source and destination exist;
- source port is an output and destination is an input;
- signal types are compatible;
- channel layouts can be connected or explicitly converted;
- the connection does not violate graph rules;
- identifiers and connection counts are valid.
### Cycles and feedback
For the first working engine, reject cycles in the audio-processing graph. A directed acyclic graph is easier to schedule safely and predictably. Report a useful error to the user. Do not silently break a cycle.
Feedback support can be considered later using explicit delay/feedback nodes and a defined block-boundary model. Never introduce arbitrary graph cycles without a correct scheduling design.
### Graph compilation
Compile the editable graph into an execution plan outside the real-time thread:
1. validate nodes, ports, and edges;
2. identify source nodes and reachable output nodes;
3. detect cycles;
4. topologically sort processing nodes;
5. determine channel layouts and required buffers;
6. allocate buffers and prepare node state;
7. publish an immutable, ready-to-run plan.
The audio callback processes the prepared plan. It must not perform graph discovery, sorting, dynamic allocation, file loading, or plugin creation.
### Graph replacement during playback
The editor must not modify the graph currently being traversed by the audio thread. Prepare a new plan off-thread and publish it through a bounded real-time-safe mechanism. Define how the old plan is retired only after the audio thread can no longer access it. Do not use a naive pointer swap followed by immediate free.
Possible approaches include a carefully designed single-producer/single-consumer handoff, epochs, or another proven reclamation scheme. Choose one, document it, and test it under stress. Keep the initial design as simple as safety allows.
### Sequencer integration
The sequencer stores timeline content and emits events to the graph at the correct sample positions. A clip player or instrument node consumes those events. The GUI timeline is a view/editor for project data; it is not the audio scheduler.
---
## 6. Real-time audio requirements
These rules are mandatory.
### Audio callback must not
- allocate or free memory;
- acquire blocking mutexes or wait on condition variables;
- perform file, network, or console I/O;
- log using potentially blocking facilities;
- create or destroy GUI objects;
- load plugins, sample files, or project files;
- resize containers or buffers;
- perform unbounded work based on arbitrary user input;
- wait for the GUI or another thread.
### Audio callback should
- process a bounded number of frames and events;
- use preallocated buffers;
- use predictable algorithms;
- avoid unnecessary copying;
- detect invalid runtime states and recover safely;
- update meters through a bounded, non-blocking communication path;
- process silence safely when a source is unavailable;
- handle backend buffer sizes and channel counts within documented limits.
Do not assume all backends use a fixed block size unless the backend contract guarantees it. Establish maximum supported frame/event counts and define what happens when limits are exceeded.
### Thread ownership
Document which thread owns each mutable resource:
- GUI thread: widgets and GUI state;
- control/project thread: editable project state, file operations, graph compilation;
- audio thread: active processing plan and real-time DSP state;
- backend callbacks: follow the backend's documented threading model.
Avoid sharing mutable structs across threads without an explicit synchronization strategy. Atomics are not automatically safe for every use: verify that the selected atomic operations are lock-free where required and use correct memory ordering.
### Timing
Use audio sample position as the authoritative playback clock. Represent timeline positions with an integer or rational scheme that avoids accumulated floating-point drift. Convert musical time (bars/beats/ticks) to sample positions using tempo and meter maps. Define behavior at tempo changes before implementing advanced tempo automation.
---
## 7. Audio backend strategy
Start on Linux and keep the backend replaceable.
1. Define a small backend interface in C.
2. Implement one backend first, selected after checking the target system and installed development packages. JACK-compatible audio is a reasonable first target on Linux.
3. Test device enumeration, start/stop, sample rate, buffer size, channel count, disconnects, and shutdown.
4. Keep backend-specific headers out of the public API.
5. Add another backend only after the engine works and tests pass.
Do not assume PipeWire-native, JACK compatibility, ALSA PCM, and desktop audio routing are interchangeable. Document the chosen backend's setup and limitations.
The engine should also have an offline/test mode that processes buffers without physical audio hardware. This makes unit and integration testing repeatable.
---
## 8. MIDI, parameters, automation, and DSP
### MIDI/events
- Use a compact, well-defined event representation.
- Store event time relative to a project timeline and convert it to sample offsets for a processing block.
- Validate event sizes and event counts.
- Handle note-on, note-off, velocity, channel, controller, pitch bend, and tempo/transport events as needed.
- Ensure all held notes can be released on stop, panic, seek, device loss, or project teardown.
- Avoid unbounded event queues.
### Parameters
Every automatable control should have:
- a stable parameter ID;
- a type and valid range;
- a default value;
- a documented unit and mapping;
- a safe update mechanism;
- optional automation and MIDI mapping.
Do not silently clamp invalid serialized values. Validate project data and return an error or apply a clearly documented migration rule. UI controls may constrain user input, but core validation must remain authoritative.
### Automation
Implement automation only after transport, graph processing, and basic parameter updates work. Define interpolation types explicitly (initially step and linear are enough). Automation evaluation must be deterministic and bounded.
### DSP
Start with a few well-tested processors:
- gain;
- pan;
- simple meter/peak detector;
- test oscillator;
- basic delay only after buffer/lifetime handling is sound.
Keep DSP algorithms in C and independently testable. Use appropriate numerical safeguards, including finite-value checks where needed. Avoid premature SIMD optimization until profiling shows a real bottleneck.
---
## 9. FLTK/C++ GUI guidelines
The GUI is a thin client of the C API, not the owner of the engine.
### Main views
1. **Sequencer:** tracks, audio/MIDI clips, timeline ruler, playhead, selection, zoom and horizontal scrolling.
2. **Mixer:** channels, faders, pan, mute/solo, inserts/sends when supported, and meters.
3. **Modular graph:** nodes, ports, connections, zoom/pan, selection, and connection validation feedback.
4. **Transport:** play, stop, record, tempo, meter, loop range, and current position.
5. **Project/file dialogs:** create, open, save, import audio, and manage missing assets.
The default workspace should emphasize the sequencer and transport. The graph view is an advanced but first-class view of the same underlying project.
### GUI implementation
- Use FLTK for conventional widgets and application windows.
- Use custom-drawn widgets for the timeline, piano roll, mixer strips, waveform display, and graph canvas where needed.
- Keep drawing code separate from project mutation.
- Use GUI timers or the toolkit's appropriate event mechanism for refreshing meters; never poll from the audio callback or block the GUI waiting for audio.
- Avoid redrawing the entire application when only a small region changes.
- Support keyboard shortcuts and predictable focus behavior.
- Make selection, dragging, snapping, zooming, and scrolling consistent across editors.
- Never call FLTK APIs from the audio thread.
- Do not hold GUI locks while calling potentially blocking engine operations.
- Keep C++ simple: use RAII for GUI resources, clear ownership, and small classes. Do not force the C core into C++ abstractions.
### Visual direction
Aim for a compact, dark, high-contrast interface with restrained decoration. Prioritize legible waveforms, clip boundaries, transport state, and meter levels. Avoid excessive gradients, animations, nested panels, and modal configuration windows.
Do not copy energyXT's artwork, exact visual assets, or proprietary UI resources. Recreate the general principles of compactness, direct manipulation, and modular routing in an original design.
---
## 10. Project format and persistence
The project format must be versioned from its first release.
### Requirements
- Include a format version and application version.
- Store stable IDs for tracks, nodes, ports where necessary, clips, parameters, and connections.
- Save relative asset paths when possible.
- Define behavior for missing audio files.
- Validate every field before allocating large amounts of memory.
- Handle truncated, malformed, unsupported, or corrupted files gracefully.
- Never execute code while loading a project.
- Save atomically: write a temporary file, flush as appropriate, and rename it into place when safe.
- Preserve unknown optional fields when practical, or explicitly document when they are discarded.
- Include migration functions when the format changes.
- Keep project serialization outside the real-time thread.
Choose a simple, well-supported format after considering the dependency and robustness trade-offs. A small, documented text format can help inspect projects, but large binary audio data should generally live in separate assets rather than be embedded in a text document. Do not add a serialization dependency without a clear reason.
---
## 11. Undo/redo and editing commands
Use explicit commands for user-visible edits, such as:
- create/delete/move/resize clip;
- create/delete/rename track;
- change parameter;
- add/remove node;
- connect/disconnect ports;
- change tempo or meter.
A command should validate its inputs and have a well-defined inverse or stored previous state. Avoid designing a generic event-sourcing framework before it is needed.
The initial implementation may begin without a complete undo stack, but design edit operations so undo/redo can be added without rewriting all editors. Never make an edit partially visible if it fails validation.
---
## 12. Build, coding standards, and dependencies
### Build system
Use a straightforward Makefile unless the existing repository has a sound reason to use another build system.
The build must:
- compile C with the selected C standard and warnings enabled;
- compile C++ GUI sources with the standard supported by the chosen FLTK version;
- link the GUI with FLTK and the C core;
- link the required audio backend;
- provide `all`, `clean`, `test`, and a debug build target;
- allow compiler and installation-prefix overrides;
- avoid embedding machine-specific paths;
- separate generated objects and binaries into a build directory.
Use `pkg-config` where supported by the installed dependencies. Check actual package names on the target distribution rather than hard-coding unverified names.
### C style
- Use C99 initially.
- Use `snake_case`.
- Prefer explicit structs and functions over clever macros.
- Keep functions focused.
- Validate pointers and sizes at API boundaries.
- Check allocation and I/O results.
- Avoid variable-length arrays for untrusted sizes.
- Make ownership and cleanup obvious.
- Use `const` where appropriate.
- Do not suppress warnings merely to get a clean build.
### C++ GUI style
- Keep C++ confined to the GUI and any unavoidable C++-only third-party wrapper.
- Prefer small classes with explicit ownership.
- Use RAII for GUI-side resources.
- Avoid exceptions crossing the C API boundary.
- Catch exceptions at the GUI's top-level boundary if any dependency can throw.
- Never pass STL types across the C API.
### Dependencies
Before adding a dependency:
1. identify the exact feature it provides;
2. check license and maintenance status;
3. determine whether the existing code can provide the functionality simply and safely;
4. document the dependency and its version constraints;
5. keep it optional if the feature is optional.
Prefer open-source dependencies with clear licensing. Do not claim the whole application is license-compatible until all direct and bundled dependencies have been reviewed.
---
## 13. Testing and diagnostics
### Unit tests
Test at least:
- graph validation and topological sorting;
- invalid ports and incompatible connections;
- cycle detection;
- parameter validation;
- sample-position and tempo conversion;
- project serialization/deserialization;
- malformed project files and missing assets;
- node lifecycle and buffer bounds;
- DSP output for known inputs;
- command execution and undo/redo when implemented.
### Integration tests
Test:
- test oscillator → gain → output processing;
- offline rendering with deterministic output;
- graph replacement during active processing;
- start/stop and repeated initialization/cleanup;
- project save/load round trips;
- missing audio assets;
- MIDI note start/stop and panic;
- backend disconnect/reconnect where supported.
### Tools
Where available, use:
- compiler warnings;
- AddressSanitizer and UndefinedBehaviorSanitizer in non-real-time test builds;
- static analysis;
- a leak checker;
- stress tests for graph edits and shutdown;
- audio loopback or offline render comparisons.
Sanitizers and diagnostic logging should not be enabled in production real-time callbacks unless their real-time implications have been specifically evaluated.
---
## 14. Development roadmap and acceptance criteria
Implement phases in order. Do not jump ahead merely because a later feature is exciting.
### Phase 0 — Repository and design baseline
- Inspect the repository and available toolchain.
- Create or update architecture documentation and the Makefile.
- Verify C compiler, C++ compiler, FLTK, audio development headers, and test tools.
- Record dependency and licensing decisions.
- Add a minimal application that starts and exits cleanly.
**Acceptance:** clean build on the target Linux environment; no audio engine assumptions hidden in GUI code.
### Phase 1 — C core and offline processing
- Create the C library/API.
- Define node, port, connection, and graph data structures.
- Implement graph validation and topological sorting.
- Implement preallocated audio buffers.
- Add oscillator, gain, meter, and output test nodes.
- Implement deterministic offline block processing.
**Acceptance:** automated test proves oscillator → gain → meter/output produces expected samples without hardware.
### Phase 2 — Real-time backend
- Add the first Linux audio backend.
- Implement start/stop, device configuration, and error handling.
- Ensure callback rules are followed.
- Add safe control-to-audio communication.
- Stress-test shutdown and graph replacement.
**Acceptance:** stable playback for a sustained test; no known callback allocations or blocking calls; failures are reported clearly.
### Phase 3 — FLTK shell
- Create main window, menus, transport, status area, and view switching.
- Call the C API through the public header.
- Add an audio engine status indicator.
- Implement clean startup and shutdown.
**Acceptance:** GUI can start/stop the engine, report backend errors, and close without leaks or hangs.
### Phase 4 — Modular graph editor
- Draw nodes and ports.
- Add/select/move nodes.
- Create/delete connections.
- Display invalid connection feedback.
- Submit graph edits through validated C API commands.
**Acceptance:** graph shown by the GUI matches the graph used by the engine; invalid connections are rejected without corrupting active playback.
### Phase 5 — Project and sequencer foundation
- Add project persistence and stable IDs.
- Add tracks and clip objects.
- Implement timeline rendering, selection, moving, trimming, zoom, and scrolling.
- Add transport/sample-position synchronization.
**Acceptance:** save/load a project with clips and reproduce the same arrangement.
### Phase 6 — Audio clips and recording
- Add audio file import and clip playback.
- Add recording through a safe producer/consumer buffer path.
- Keep file I/O outside the audio callback.
- Add missing-asset reporting.
**Acceptance:** record, save, reopen, and play a basic audio project without callback file I/O.
### Phase 7 — MIDI and a simple instrument
- Add MIDI input and clip events.
- Add a basic internal synthesizer or test instrument.
- Handle note-off, panic, seeking, and stop correctly.
- Add a minimal piano-roll editor only after event scheduling works.
**Acceptance:** record or draw MIDI notes, play them in time, save the project, and reopen it.
### Phase 8 — Mixer, automation, and editing polish
- Add mixer channels, gain/pan, meters, mute/solo.
- Add automation lanes and interpolation.
- Add a bounded undo/redo system for common edits.
- Improve keyboard interaction and workflow.
**Acceptance:** basic composition and mixing can be completed without editing project files manually.
### Phase 9 — Extensibility and release preparation
- Define and document the native node/instrument/effect API.
- Evaluate external plugin hosting only after core lifecycle handling is robust.
- Review project format migration, crash behavior, performance, and licensing.
- Package the application and document Linux setup.
**Acceptance:** reproducible release build, tested install/uninstall, clear limitations, and complete user/developer documentation.
---
## 15. Initial implementation task for the agent
Start with **Phase 0 only**.
1. Inspect the current repository and report its state.
2. Identify the available C/C++ compilers, FLTK version, audio backend development packages, `pkg-config`, and test tools.
3. Do not install packages or modify system configuration without permission.
4. Propose the smallest initial source tree compatible with this brief.
5. Add or update the Makefile and documentation only as needed.
6. Create a minimal program that builds and exits cleanly; do not yet implement a full audio engine or GUI.
7. Build it and run its tests.
8. Report exact commands, results, assumptions, and any missing dependencies.
After Phase 0 is reviewed, continue with Phase 1. Keep each phase independently buildable and testable.
---
## 16. Long-term quality bar
The project succeeds if a musician can open it quickly, connect components without fighting the interface, record and arrange audio/MIDI, and make music without needing to understand every detail of the engine.
The implementation succeeds if a developer can read the C code, understand ownership and thread boundaries, test components independently, and add functionality without turning the application into an unmaintainable framework.
**Guiding rule:** build a small, coherent system whose components compose well. Do not build a large DAW and then attempt to simplify it afterward.