# fl_control — LLM System Overview > **About the name**: The directory is named `fl_control` for historical reasons, but the system generates **REAPER `.rpp` files**, not FL Studio `.flp` files. There is NO FL Studio support. The name has been retained for backward compatibility with repository URLs and CI/CD pipelines. ## What Is This? `fl_control` is a Python system that generates complete **REAPER `.rpp` projects** from the command line. Given a key, BPM, and emotion, it composes a full reggaetón instrumental with drums, 808 bass, chord progressions (with voice leading), lead melodies (hook-based call-and-response), pads, percussion, and transition FX. The output is a valid `.rpp` file that REAPER opens directly, plus an auto-generated ReaScript (`.py`) for post-processing (plugin loading, mix calibration, rendering). **Target**: REAPER (Windows, v7.x). **Format**: `.rpp` text files. **No DAW dependencies** beyond reading sample files from disk. ## Quick Start ```bash # Install dependencies pip install -r requirements.txt # Generate a song (defaults: 95 BPM, Am key, romantic emotion) python scripts/generate.py --bpm 95 --key Am --output output/song.rpp --seed 42 # Generate with validation python scripts/generate.py --bpm 95 --key Dm --output output/song.rpp --seed 42 --validate # Compose directly (more detailed output) python scripts/compose.py --bpm 99 --key Am --emotion romantic --output output/song.rpp ``` Output: a ready-to-open `.rpp` file at the specified path. ## Architecture ``` ┌───────────────────────────────────────┐ │ CLI Layer │ │ scripts/generate.py (thin wrapper) │ │ scripts/compose.py (full pipeline) │ │ scripts/run_in_reaper.py (ReaScript) │ └──────────────┬────────────────────────┘ │ ┌──────────────────────┼──────────────────────┐ │ ▼ │ ┌────────────────┴──────┐ ┌────────────────────┴──┐ ┌───────┴──────────┐ │ src/composer/ │ │ src/calibrator/ │ │ src/selector/ │ │ ┌──────────────────┐ │ │ ┌────────────────┐ │ │ SampleSelector │ │ │ chords.py │ │ │ │ Calibrator │ │ └────────────────┘ │ │ ChordEngine │ │ │ │ .apply() │ │ │ │ melody_engine.py │ │ │ │ presets.py │ └───────────────────┘ │ │ build_motif() │ │ │ │ VOLUME_PRESETS │ │ │ patterns.py │ │ │ │ PAN_PRESETS │ ┌──────────────────┐ │ │ rhythm.py │ │ │ │ SEND_PRESETS │ │ src/core/ │ │ │ templates.py │ │ │ └────────────────┘ │ schema.py │ │ └──────────────────┘ │ └─────────────────────────────┘ │ SongDefinition │ └───────────┬────────────┘ │ TrackDef │ │ │ ClipDef │ ▼ │ MidiNote │ ┌──────────────────────────┐ │ PluginDef │ │ SongDefinition │◄──────────────────────────────│ SectionDef │ │ ┌────────────────────┐ │ │ PatternDef │ │ │ meta: SongMeta │ │ │ Arrangement... │ │ │ tracks: [TrackDef] │ │ │ CCEvent │ │ │ patterns: [...] │ │ └──────────────────┘ │ │ sections: [...] │ │ │ │ master_plugins │ │ │ └────────────────────┘ │ └───────────┬──────────────┘ │ ┌─────────┼─────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌───────────────┐ ┌─────────────────┐ │ RPPBuilder │ │ ReaScriptGen │ │ Validator │ │ .write() → │ │ generate() → │ │ validate_rpp() │ │ song.rpp │ │ fl_control_ │ │ │ │ │ │ phase2.py │ │ │ └──────────────┘ └───────┬───────┘ └─────────────────┘ │ ▼ ┌──────────────┐ │ REAPER │ │ opens .rpp │ │ runs script │ │ renders WAV │ └──────────────┘ ``` **Pipeline**: CLI → compose `SongDefinition` → Calibrator.apply() (post-processing) → RPPBuilder.write() → `.rpp` file → ReaScriptGenerator.generate() → ReaScript → REAPER execution. ## Data Model All types live in `src/core/schema.py`. Every entity is a Python dataclass. ### SongMeta Song-level metadata. | Field | Type | Default | Description | |-------|------|---------|-------------| | `bpm` | `float` | *(required)* | Tempo 20–999 | | `key` | `str` | *(required)* | Key string, e.g. `"Am"`, `"Dm"`, `"G"` | | `title` | `str` | `""` | Song title | | `ppq` | `int` | `960` | Ticks per quarter note (REAPER default) | | `time_sig_num` | `int` | `4` | Time signature numerator | | `time_sig_den` | `int` | `4` | Time signature denominator | | `calibrate` | `bool` | `True` | Enable post-processing mix calibration | ### MidiNote A single MIDI note event within a clip. | Field | Type | Default | Description | |-------|------|---------|-------------| | `pitch` | `int` | *(required)* | MIDI note number 0–127 (60 = middle C) | | `start` | `float` | *(required)* | Start time in beats from item start | | `duration` | `float` | *(required)* | Duration in beats | | `velocity` | `int` | `64` | Velocity 0–127 | ### CCEvent A MIDI CC event within a clip. | Field | Type | Default | Description | |-------|------|---------|-------------| | `controller` | `int` | *(required)* | CC number (e.g. 11 = Expression) | | `time` | `float` | *(required)* | Position in beats from clip start | | `value` | `int` | *(required)* | CC value 0–127 | ### ClipDef A clip placed on a track — either audio or MIDI. | Field | Type | Default | Description | |-------|------|---------|-------------| | `position` | `float` | *(required)* | Start position in beats | | `length` | `float` | *(required)* | Duration in beats | | `name` | `str` | `""` | Display name | | `audio_path` | `str \| None` | `None` | Absolute path to audio file (audio clips) | | `midi_notes` | `list[MidiNote]` | `[]` | MIDI notes (MIDI clips) | | `midi_cc` | `list[CCEvent]` | `[]` | MIDI CC events | | `loop` | `bool` | `False` | Whether the audio clip loops | | `fade_in` | `float` | `0.0` | Fade-in duration in seconds | | `fade_out` | `float` | `0.0` | Fade-out duration in seconds | | `vol_mult` | `float` | `1.0` | Volume multiplier applied at clip level | Properties: `is_midi` → True when `midi_notes` is non-empty; `is_audio` → True when `audio_path` is not None. ### PluginDef A VST plugin instance on a track. | Field | Type | Default | Description | |-------|------|---------|-------------| | `name` | `str` | *(required)* | Display name (e.g. `"Serum 2"`) | | `path` | `str` | *(required)* | Plugin path/identifier | | `index` | `int` | `0` | Chain position (0 = first) | | `params` | `dict[int, float]` | `{}` | Parameter index → value | | `preset_data` | `list[str] \| None` | `None` | Base64 preset chunks | | `role` | `str` | `""` | Track role for role-aware preset lookup | | `builtin` | `bool` | `False` | True → deferred to ReaScript, not written to .rpp | ### TrackDef A track in the REAPER project. | Field | Type | Default | Description | |-------|------|---------|-------------| | `name` | `str` | *(required)* | Track display name | | `volume` | `float` | `0.85` | 0.0–1.0 (maps to REAPER volume fader) | | `pan` | `float` | `0.0` | -1.0 (left) to 1.0 (right) | | `color` | `int` | `0` | REAPER color index 0–67 | | `clips` | `list[ClipDef]` | `[]` | Audio/MIDI clips | | `plugins` | `list[PluginDef]` | `[]` | VST plugins on this track | | `send_reverb` | `float` | `0.0` | Reverb send level 0.0–1.0 | | `send_delay` | `float` | `0.0` | Delay send level 0.0–1.0 | | `send_level` | `dict[int, float]` | `{}` | Return track index → send level | ### SectionDef A section in the song arrangement. | Field | Type | Default | Description | |-------|------|---------|-------------| | `name` | `str` | *(required)* | Display name (e.g. `"intro"`, `"chorus"`) | | `bars` | `int` | *(required)* | Length in bars | | `energy` | `float` | `0.5` | Energy level 0.0–1.0 | | `velocity_mult` | `float` | `1.0` | Velocity multiplier for all notes in section | | `vol_mult` | `float` | `1.0` | Volume multiplier for tracks in section | ### PatternDef A pattern definition with generator and variation axes. | Field | Type | Default | Description | |-------|------|---------|-------------| | `id` | `int` | *(required)* | Unique pattern ID | | `name` | `str` | *(required)* | Display name | | `instrument` | `str` | *(required)* | Sample/instrument key | | `channel` | `int` | *(required)* | MIDI channel | | `bars` | `int` | *(required)* | Length in bars | | `generator` | `str` | *(required)* | Generator function name | | `velocity_mult` | `float` | `1.0` | Velocity multiplier 0.85–1.1 | | `density` | `float` | `1.0` | Note density 0.0–1.0 | ### ArrangementItemDef An item placed in the arrangement referencing a pattern on a track. | Field | Type | Default | Description | |-------|------|---------|-------------| | `pattern` | `int` | *(required)* | Pattern ID | | `bar` | `float` | *(required)* | Start position in bars | | `bars` | `float` | *(required)* | Length in bars | | `track` | `int` | *(required)* | Track index | ### ArrangementTrack A track in the REAPER arrangement with index and display name. | Field | Type | Default | Description | |-------|------|---------|-------------| | `index` | `int` | *(required)* | Track index | | `name` | `str` | *(required)* | Display name | ### SongDefinition Complete song definition — the source of truth for one `.rpp` file. | Field | Type | Default | Description | |-------|------|---------|-------------| | `meta` | `SongMeta` | *(required)* | Song metadata | | `tracks` | `list[TrackDef]` | `[]` | REAPER tracks with clips and plugins | | `patterns` | `list[PatternDef]` | `[]` | Pattern definitions for arrangement | | `items` | `list[ArrangementItemDef]` | `[]` | Arrangement items referencing patterns | | `progression_name` | `str` | `"i-VII-VI-VII"` | Chord progression label | | `section_template` | `str` | `"standard"` | Section template name | | `samples` | `dict[str, str]` | `{}` | Sample file map (name → filename) | | `sections` | `list[SectionDef]` | `[]` | Section definitions in playback order | | `master_plugins` | `list[str]` | `[]` | Plugin registry keys for master FX chain | Key methods: - `validate()` → `list[str]` — returns list of validation errors (empty = valid) - `length_beats` (property) → `float` — total length computed from all clips - `to_json(indent=2)` → `str` — serialize to JSON via `dataclasses.asdict` ## Module Index | Module | Location | Role | |--------|----------|------| | **schema** | `src/core/schema.py` | All dataclass definitions. Single source of truth for data model. | | **composer** | `src/composer/` | Composition engine: chord progression, melody generation, rhythm patterns, drum analysis | | **reaper_builder** | `src/reaper_builder/` | RPPBuilder: `.rpp` file generation from `SongDefinition`. Plugin registry (~97 entries). VST2/VST3 element builders. Headless render. | | **reaper_scripting** | `src/reaper_scripting/` | ReaScript generation: self-contained Python scripts for REAPER post-processing | | **calibrator** | `src/calibrator/` | Post-processing mix calibration: volume, pan, sends, master chain by track role | | **selector** | `src/selector/` | Sample selection: scores samples by key compatibility, BPM proximity, character | | **validator** | `src/validator/` | `.rpp` output validation | | **CLI scripts** | `scripts/` | `compose.py` (full pipeline), `generate.py` (thin wrapper), `run_in_reaper.py` (ReaScript execution) | ### composer/ sub-modules | File | Role | |------|------| | `chords.py` | `ChordEngine` — emotion-aware chord progressions with voice leading | | `melody_engine.py` | `build_motif()`, `build_call_response()` — hook-based melody generation | | `patterns.py` | Pattern weight tables extracted from real reggaetón tracks | | `rhythm.py` | Dembow rhythm generators (classic, perreo, trápico) | | `templates.py` | RPP template extraction and generation | | `converters.py` | Conversion utilities | | `variation.py` | Pattern variation logic | | `melodic.py` | Melodic pattern generators | | `drum_analyzer.py` | `DrumLoopAnalyzer` — transient detection for sidechain CC generation | | `__init__.py` | Scale intervals, chord type tables, legacy pattern generators | ## Pipeline The complete composition and build pipeline, step by step: ### Phase 1: Composition (`scripts/compose.py`) 1. **Parse CLI args**: `--bpm`, `--key`, `--emotion`, `--inversion`, `--seed`, `--no-calibrate` 2. **Load `SampleSelector`**: Reads `data/sample_index.json` for clap/snare/FX sample selection 3. **Build sections**: 9-section structure (intro → verse → pre-chorus → chorus → verse2 → chorus2 → bridge → final → outro), each with energy level and velocity/volume multipliers 4. **Build tracks** (in order): - `Drumloop` — audio clips cycling between seco/filtrado variants per section - `Perc` — percussion audio loops per section - `808 Bass` — MIDI notes using proven i-iv-i-V harmonic pattern with CC11 sidechain ducking - `Chords` — `ChordEngine.progression()` with voice leading; i-VII-VI-VII progression - `Lead` — `build_motif("hook")` + `build_call_response()` via melody engine - `Clap` — Short audio samples on dembow grid (beats 2.0, 3.5) - `Transition FX` — Audio clips at section boundaries (risers, impacts, sweeps) - `Pad` — Arpeggiated chord tones on eighth notes - `Reverb` / `Delay` — Return tracks with Pro-R 2 and ValhallaDelay 5. **Wire sends**: Return track sends by role from `SEND_LEVELS` 6. **Assemble `SongDefinition`**: Meta + tracks + sections + master_plugins 7. **Calibrate** (unless `--no-calibrate`): `Calibrator.apply(song)` — role-based volume, pan, sends, master chain ### Phase 2: Build (`src/reaper_builder/RPPBuilder`) 1. `RPPBuilder(song, seed).write(path)` serializes to `.rpp`: - Project header (static metadata from ground-truth `test_vst3.rpp`) - Dynamic TEMPO line - Master track with FX chain (Ozone 12 triplet or FabFilter fallback) - Per-track TRACK elements with VOLPAN, AUXRECV (sends) - Per-track FXCHAIN with VST elements (resolved from `PLUGIN_REGISTRY`) - Per-clip ITEM elements with SOURCE (WAVE or MIDI with E-lines) - Built-in plugins (Cockos) deferred to ReaScript insertion ### Phase 3: ReaScript (Phase 2B) `scripts/run_in_reaper.py` generates a self-contained Python ReaScript that REAPER executes: 1. Reads `fl_control_command.json` (command file) 2. Opens the `.rpp` project 3. Executes action pipeline: `add_plugins` → `configure_fx_params` → `verify_fx` → `calibrate` → `render` 4. Writes `fl_control_result.json` (LUFS metrics, FX errors, plugin results) ### Validation `SongDefinition.validate()` checks: BPM range (20–999), key format (regex), unique track names, clips with neither audio nor MIDI. ## Plugin System ### PLUGIN_REGISTRY Located in `src/reaper_builder/__init__.py` (~97 entries). Format: ```python PLUGIN_REGISTRY: dict[str, tuple[str, str, str]] = { "key": ("display_name", "filename", "uid_guid"), } ``` - **key**: Short registry key (e.g. `"Serum_2"`, `"Pro-Q_3"`, `"Decapitator"`) - **display_name**: Full REAPER display name (e.g. `"VST3i: Serum 2 (Xfer Records)"`) - **filename**: File on disk (e.g. `"Serum2.vst3"`, `"Decapitator.dll"`) - **uid_guid**: Unique identifier — VST2 uses ``, VST3 uses `{GUID}` ### ALIAS_MAP Backward compatibility mapping: old key names → new `PLUGIN_REGISTRY` keys. Example: ```python ALIAS_MAP = { "Serum2": "Serum_2", "FabFilter Pro-Q 3": "Pro-Q_3", "Valhalla Delay": "ValhallaDelay", "Pro-Q 3": "Pro-Q_3", } ``` FabFilter plugins map space-separated names to short VST3 keys. SoundToys plugins keep their underscore names. ### PLUGIN_PRESETS Role-aware preset data. Format: ```python PLUGIN_PRESETS: dict[tuple[str, str], list[str]] = { ("Pro-Q_3", ""): [chunk1, chunk2, ...], # default ("Serum_2", "bass"): [chunk1, chunk2, ...], # role-specific } ``` Lookup chain: `(key, role)` → `(key, "")` (default) → `fallback` → `None`. ### REAPER_BUILTINS Set of Cockos native plugin keys (ReaEQ, ReaComp, ReaVerb, etc.). Plugins matching this are deferred to ReaScript insertion (`TrackFX_AddByName`) rather than written to `.rpp` as VST elements. ### Plugin Lookup in RPPBuilder ``` PluginDef.name → ALIAS_MAP.get(name, name) → PLUGIN_REGISTRY.get(resolved) │ ┌───────────────┤ found│ │not found ▼ ▼ Build VST element Fallback: .dll with display_name, with 19 param filename, uid_guid slots ``` ## ReaScript Protocol ### Overview ReaScript is a self-contained Python file generated by `ReaScriptGenerator` that REAPER executes internally. It communicates via JSON files on disk: ``` fl_control_command.json ──read──► ReaScript ──write──► fl_control_result.json ``` ### ReaScriptCommand Defined in `src/reaper_scripting/commands.py`: | Field | Type | Default | Description | |-------|------|---------|-------------| | `version` | `int` | `1` | Protocol version | | `action` | `str \| list[str]` | `"calibrate"` | Single action or pipeline list | | `rpp_path` | `str` | `""` | Path to `.rpp` file to open | | `render_path` | `str` | `""` | Path for rendered WAV output | | `timeout` | `int` | `120` | Polling timeout in seconds | | `track_calibration` | `list[dict]` | `[]` | Volume/pan/send calibration per track | | `plugins_to_add` | `list[dict]` | `[]` | `{"track_name": str, "fx_name": str, "params": {...}}` | ### ReaScriptResult | Field | Type | Default | Description | |-------|------|---------|-------------| | `version` | `int` | `1` | Protocol version | | `status` | `str` | `"ok"` | `"ok"`, `"error"`, or `"timeout"` | | `message` | `str` | `""` | Error or status message | | `lufs` | `float \| None` | `None` | Integrated LUFS | | `integrated_lufs` | `float \| None` | `None` | Integrated LUFS reading | | `short_term_lufs` | `float \| None` | `None` | Short-term LUFS | | `fx_errors` | `list[dict]` | `[]` | FX verification errors | | `tracks_verified` | `int` | `0` | Number of tracks verified | | `added_plugins` | `list[dict]` | `[]` | `{"fx_name", "instance_id", "track_name", "status"}` | ### ProtocolVersionError Raised by `read_result()` when result version doesn't match expected version. ### Key Functions - `write_command(path, cmd)` — Serialize `ReaScriptCommand` to JSON file - `read_result(path, expected_version=1)` — Deserialize `ReaScriptResult` from JSON file. Raises `ProtocolVersionError` on version mismatch. ### Known Actions `add_plugins`, `configure_fx_params`, `verify_fx`, `calibrate`, `render` ### ReaScript JSON Parser Since REAPER's internal Python has no `json` module, the ReaScript includes a hand-rolled JSON parser (~100 lines) that handles strings, integers, floats, booleans, nulls, nested objects, and arrays via string splitting. ## CLI Reference ### scripts/compose.py Main composition pipeline. Builds a full reggaetón track from scratch. | Flag | Type | Default | Description | |------|------|---------|-------------| | `--bpm` | `float` | `99` | Tempo | | `--key` | `str` | `"Am"` | Musical key | | `--output` | `str` | `"output/song.rpp"` | Output `.rpp` path | | `--seed` | `int` | `None` | Random seed for determinism | | `--emotion` | `str` | `"romantic"` | `romantic` / `dark` / `club` / `classic` | | `--inversion` | `str` | `"root"` | `root` / `first` / `second` | | `--no-calibrate` | `flag` | `False` | Skip post-processing mix calibration | ### scripts/generate.py Thin wrapper around `compose.py`. Delegates via `sys.argv` manipulation. | Flag | Type | Default | Description | |------|------|---------|-------------| | `--bpm` | `float` | `95` | Tempo | | `--key` | `str` | `"Am"` | Musical key | | `--output` | `str` | `"output/song.rpp"` | Output `.rpp` path | | `--seed` | `int` | `42` | Random seed | | `--emotion` | `str` | `"romantic"` | Chord emotion | | `--inversion` | `str` | `"root"` | Chord inversion | | `--validate` | `flag` | `False` | Run `validate_rpp_output()` after generation | ### scripts/run_in_reaper.py ReaScript execution CLI. Generates and runs a Phase 2 ReaScript. | Arg/Flag | Type | Default | Description | |----------|------|---------|-------------| | `rpp_path` | positional | *(required)* | Path to `.rpp` file | | `--output`, `-o` | `str` | auto-derived | Rendered WAV output path | | `--timeout` | `int` | `120` | Seconds to poll for result JSON | | `--plugins-config` | `str` | `None` | JSON SongDefinition for deriving plugins_to_add | | `--action` | `str` | `"calibrate"` | Space-separated action pipeline | Example: ```bash python scripts/run_in_reaper.py output/song.rpp --action "add_plugins calibrate render" --timeout 300 ``` ## Conventions From `AGENTS.md`: - **Python**: Type hints on all function signatures - **Dataclasses**: Over dicts for structured data - **Deterministic output**: Seed-based RNG (`random.Random(seed)`), no global random state - **No bare except clauses** - **Testing**: `pytest` only. Unit tests for all new functions. Integration tests for end-to-end flows. - **Architecture**: Separate modules by concern (calibrator, composer, builder, selector, validator) - **Post-processing**: Calibrator.apply() pattern — modify in-place after construction, not inline - **Schema changes**: Must be backward-compatible (new fields get defaults) - **SDD**: All changes follow spec-driven development pipeline (propose → spec → design → tasks → apply → verify) ## How to Extend ### Adding a New Track Role 1. Add role to `TRACK_ACTIVITY` in `scripts/compose.py` — define which sections it plays in 2. Add volume/presets to `src/calibrator/presets.py` (`VOLUME_PRESETS`, `PAN_PRESETS`, `SEND_PRESETS`) 3. Add FX chain to `FX_CHAINS` in `scripts/compose.py` 4. Write a `build__track()` function 5. Add the track to the `tracks` list in `main()` 6. Add calibration role mapping in `Calibrator._resolve_role()` ### Adding a New Plugin to the Registry 1. Scan the plugin in REAPER or extract from a ground-truth `.rpp` 2. Add entry to `PLUGIN_REGISTRY`: `"key": ("display_name", "filename", "uid_guid")` 3. If needed, add preset data to `_PRESETS_FLAT` with the key 4. Add aliases to `ALIAS_MAP` if there are alternate names 5. Use `make_plugin(registry_key, index, role)` to create `PluginDef` instances ### Adding a New CLI Flag 1. Add `parser.add_argument()` in `scripts/compose.py` (and `scripts/generate.py` if wrapping) 2. Thread the flag value through the composition pipeline 3. Pass to relevant builder/track functions ### Adding a New Emotion to ChordEngine 1. Add entry to `EMOTION_PROGRESSIONS` in `src/composer/chords.py` 2. Format: `"name": [(semitone_offset, quality), ...]` for a 4-chord loop 3. Add choice to `--emotion` argparse choices ## Known Limitations - **Hardcoded paths**: Drumloop samples reference `C:\ProgramData\Ableton\...` paths. These must exist on the build machine or the pipeline will skip those clips. - **Environment-specific registry**: `PLUGIN_REGISTRY` was generated from a specific REAPER/plugin installation. Different machines may have different plugin paths or GUIDs. - **Windows only**: REAPER executable path in `render.py` defaults to `C:\Program Files\REAPER (x64)\reaper.exe`. The ReaScript REAPER API calls are Windows-specific. - **No CI**: No automated test/validation pipeline. Tests run manually with `pytest`. - **Single genre**: Only reggaetón is fully implemented. Other genres exist in `knowledge/` as JSON definitions but the main pipeline (`compose.py`) is hardcoded for reggaetón. - **Sample library**: Requires pre-existing sample files in expected directories. No bundled samples. - **ReaScript execution**: `run_in_reaper.py` generates scripts but does NOT automatically launch REAPER. REAPER must be running and monitoring the scripts directory, or the script must be executed manually from within REAPER's action list. ## Further Reading - [CLI Reference](CLI.md) — Complete CLI documentation - [Module: Composer](modules/composer.md) — Composition engine deep-dive - [Module: Reaper Builder](modules/reaper-builder.md) — RPP format and plugin registry - [Module: Reaper Scripting](modules/reaper-scripting.md) — ReaScript protocol - [Module: Calibrator](modules/calibrator.md) — Mix calibration presets - [JSON Schema: Song Definition](schemas/song-definition.json) — Data model schema - [JSON Schema: ReaScript Protocol](schemas/reascript-protocol.json) — Command/result schema