livepilot 1.20.2 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,227 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.21.0 — Consolidation: experiment ledger + preset library + record-readiness + reader audit (April 24 2026)
4
+
5
+ Consolidation release closing five items from the v1.20 plan §12 non-goals
6
+ that were tractable in a 2-session scope. Adds 1 new semantic move + 1
7
+ major-tool auto-ledger-write + minimal preset-library infrastructure +
8
+ a store-purpose audit that turns the v1.20 director-SKILL fix into a
9
+ codebase-wide invariant, all gated behind a test-first wire-format
10
+ parity extension that caught 1 pre-v1.20 drift bug and closed the
11
+ audit window.
12
+
13
+ Registry: 43 → 44 moves. Test suite: 3043 → 3118 pass (+75 across all
14
+ chunks). Wire-format parity suite: 10 → 44 scenarios. Zero regressions.
15
+
16
+ ### New semantic moves (1)
17
+
18
+ - **`configure_record_readiness`** (performance family, low risk, protects
19
+ `signal_integrity=0.7`)
20
+ - seed_args: `{track_index, armed, exclusive?}`
21
+ - Non-exclusive: single step `set_track_arm(track_index, arm=armed)` — note
22
+ the wire-format key is `arm`, not `armed` (the MCP tool accepts `armed`
23
+ as an ergonomic kwarg but renames to `arm` before send_command; the
24
+ remote_command backend bypasses that rename).
25
+ - Exclusive (`exclusive=True` + `armed=True`): N+1 step plan —
26
+ `set_track_arm(arm=False)` for every other regular track, then
27
+ `set_track_arm(target, arm=True)` for the target. Emulates Ableton's
28
+ exclusive-arm mode manually because `song.exclusive_arm` has no
29
+ Python setter in Live 12.4 (pre-existing v1.20.3 Remote Script bug
30
+ surfaced during v1.21 live-test pre-flight — see plan correction #6).
31
+ Requires `session_info.tracks` (automatically built by
32
+ `apply_semantic_move`).
33
+ - Rejects `exclusive=True + armed=False` (contradictory), missing `armed`
34
+ seed_arg, and negative `track_index` (return tracks can't be armed
35
+ per Ableton's handler at `remote_script/LivePilot/tracks.py:261`).
36
+ - Closes the one tech_debt entry seeded during v1.20 live test 6.
37
+
38
+ ### Experiment-engine ledger writer
39
+
40
+ - **`commit_experiment`** now writes to `SessionLedger` after a successful
41
+ commit, mirroring v1.20's `apply_semantic_move` pattern (commit `0b3489b`).
42
+ Anti-repetition filters downstream now see experiment commits the same
43
+ way they see direct semantic-move applies.
44
+ - `engine` tag reflects branch SOURCE (not escalation success): `"composer"`
45
+ when `target.seed.source == "composer"`, else `"experiment"`. A composer-
46
+ sourced branch that fell back to scaffold execution is still
47
+ `engine=composer` — the escalation-success detail lives in
48
+ `target.evaluation["composer_escalation"]`.
49
+ - `move_class` via `_infer_move_family(target)`: `seed.family` when set,
50
+ else inspect first compiled_plan step's tool via `_TOOL_TO_FAMILY`
51
+ lookup, else default `"mix"`.
52
+ - `actions` = one entry per ok row in `commit_result["execution_log"]`
53
+ (the router's actual execution record — captures the post-escalation
54
+ plan when composer escalation fired).
55
+ - Response gains `ledger_entry_id` field (same pattern as
56
+ `apply_semantic_move`). Best-effort — a ledger-write exception is logged
57
+ and swallowed so the commit never fails on a bookkeeping path.
58
+
59
+ ### Minimal affordance preset library
60
+
61
+ - New path `mcp_server/affordances/` with loader (`presets.py`), schema
62
+ validator (`_schema.py`), and 3 seed YAML files:
63
+ - `reverb.yaml` — preset `dub-cathedral` (Basic Channel-adjacent huge
64
+ space; Decay 0.85, Room 0.95, Dry/Wet 0.40, Predelay 0.45, Diffusion 0.80)
65
+ - `delay.yaml` — preset `ping-pong-dub` (dotted 8th, feedback 0.45,
66
+ HP+LP filtered)
67
+ - `auto-filter.yaml` — preset `slow-sweep` (LP type, bar-long LFO,
68
+ moderate resonance)
69
+ - `configure_device` compiler gained optional `preset` + `device_slug`
70
+ seed_args (additive — v1.20 callers unaffected). Merge: preset resolves
71
+ first, explicit `param_overrides` win on per-key conflict (last-write-wins).
72
+ v1.21 requires explicit `device_slug` when `preset` is used; v1.22
73
+ adds class_name → slug auto-inference.
74
+ - `livepilot-creative-director/references/phase-6-execution.md` §Affordance-
75
+ preset resolution rewritten to document the live import path and the
76
+ three dispatch patterns (preset reference, preset + explicit override,
77
+ fallback to Python resolve).
78
+
79
+ ### Wire-format parity retroactive gate
80
+
81
+ - `tests/test_compiler_wire_format_parity.py` extended from 10 to 44
82
+ scenarios (10 v1.20 + 33 pre-v1.20 + 1 new `configure_record_readiness`).
83
+ Gate surfaced **1 pre-v1.20 drift pattern** affecting 7 moves:
84
+ `create_chaos_modulator`, `create_feedback_resonator`,
85
+ `create_wavefolder_effect`, `create_bitcrusher_effect`,
86
+ `create_karplus_string`, `create_stochastic_texture`, `create_fdn_reverb`
87
+ all shipped with a `find_and_load_device` plan_template emitting
88
+ `{"query": "Wonder X"}` but no `track_index`. The `remote_command`
89
+ backend bypasses MCP normalization, so Ableton's handler threw
90
+ `KeyError` on `params["track_index"]` — broken at runtime since
91
+ pre-v1.20, caught now.
92
+ - Fix: `device_creation_compilers.py::_compile_device_creation` threads
93
+ `track_index` from `seed_args` into `find_and_load_device` steps.
94
+ Plan templates updated from `{"query": ...}` to `{"device_name": ...}`
95
+ across all 7 Device Forge moves.
96
+ - Variant-B' consideration (>3 drift bugs → pause v1.21, ship patch):
97
+ all 7 failures were one pattern in one file, fixable in one commit.
98
+ Path B (inline fix as part of Task 1.1.5a) taken — single-pattern
99
+ single-file fixes are exactly what Task 1.1.5a was written for.
100
+ See `docs/plans/v1.21-impl-status.md` §3 for the decision log.
101
+
102
+ ### Store-purpose reader audit
103
+
104
+ - Every file in `mcp_server/` that imports `SessionLedger` or calls
105
+ `memory_list` / `get_session_memory` now carries a
106
+ `# store_purpose: <purpose>` comment naming its intent.
107
+ - Allowed purposes (closed set): `writer`, `anti_repetition`,
108
+ `audit_readonly`, `technique_library`, `session_observations`,
109
+ `escape_hatch_log`, `mcp_tool_definition`.
110
+ - **`tests/test_ledger_readers.py`** new (5 test cases): enforces the
111
+ annotation contract at CI, AND guards against any file annotated
112
+ `anti_repetition` also calling `memory_list` — which would be the
113
+ latent v1.20 store-confusion bug (director SKILL originally pointed
114
+ at `memory_list` for recency; `memory_list` reads the persistent
115
+ technique library, not the action ledger).
116
+ - Audit was clean across the entire `mcp_server` tree — no anti-repetition
117
+ caller was found reading the wrong store. The audit is purely
118
+ documentation contracts enforced at CI.
119
+
120
+ ### Plan corrections applied during execution (6, logged in impl-status doc)
121
+
122
+ Documented for future release planners in `docs/plans/v1.21-impl-status.md`:
123
+
124
+ 1. **Baseline drift** — v1.20.2 and v1.20.3 shipped between plan-write and
125
+ execution; all version-string literals in the plan needed `1.20.1 →
126
+ 1.20.3` remapping.
127
+ 2. **Wire-format drift in device_creation** — plan_templates emitted
128
+ `{"query": ...}`; handler requires `{"track_index", "device_name"}`.
129
+ 3. **`set_track_arm` wire format** — plan §3.1 used `{"armed": ...}`;
130
+ handler reads `params["arm"]`. MCP tool renames `armed → arm` before
131
+ send_command; remote_command backend bypasses the rename.
132
+ 4. **`set_exclusive_arm` shape** — plan §3.1 assumed a per-track signature
133
+ `(track_index=...)`; handler is a global mode toggle taking
134
+ `{"enabled": bool}`.
135
+ 5. **Return-track arm rejection** — plan's test expected negative
136
+ `track_index` to succeed; handler raises `ValueError: "Cannot arm a
137
+ return track"`. Corrected test pins compile-time rejection.
138
+ 6. **`set_exclusive_arm` is broken in Live 12.4 LOM** — live-test pre-flight
139
+ (2026-04-24) surfaced that `song.exclusive_arm` is a property WITHOUT a
140
+ Python setter in Live 12.4's Remote Script API. The v1.20.3 handler's
141
+ direct assignment `song.exclusive_arm = bool(...)` errors with
142
+ "property of 'Song' object has no setter". v1.21's
143
+ `configure_record_readiness` exclusive-mode now emulates the behavior
144
+ manually (disarm-all-others + arm-target loop) — correctness is
145
+ identical, works in any Live version. The broken v1.20.3
146
+ `set_exclusive_arm` handler is LEFT AS-IS for potential future fix
147
+ (when/if Ableton re-exposes the setter, or someone finds a LOM method
148
+ alternative); it's tracked as an independent bug in the Remote Script.
149
+
150
+ ### Non-goals (deferred)
151
+
152
+ - **Hard cutover of the escape hatch.** v1.22+ target, conditional on
153
+ zero `tech_debt` log entries accumulating in one month of production.
154
+ - **Standard / Deep preset catalog.** v1.22+. Minimal library from v1.21
155
+ proves the loader; catalog fills in after real-usage signal.
156
+ - **class_name → slug auto-inference** for the preset library. v1.22+.
157
+ - **Pre-v1.20 move compiler refactors** beyond the 1 drift fix. v1.22+.
158
+ - **New move families beyond the canonical 7.** The performance family
159
+ already exists; `configure_record_readiness` joins it.
160
+ - **Taste-graph integration for preset ranking.** v1.22+ once the catalog
161
+ exists.
162
+
163
+ ### Scope stats
164
+
165
+ - 7 atomic commits on `v1.21.0-dev` (fix + gate + 4 feature + refactor + release)
166
+ - 28 files changed, +1933 / -63 lines
167
+ - 3 new test files (commit_experiment_ledger, performance_moves,
168
+ affordance_presets, ledger_readers) + 1 doc (impl-status)
169
+ - 1 new package (`mcp_server/affordances/`) + 3 YAML seed files
170
+ - 15 version-string sites bumped + `.amxd` binary patch (2 bytes)
171
+ - 10 files annotated with `# store_purpose:`
172
+
173
+ ---
174
+
175
+ ## 1.20.3 — Automated analyzer pre-flight (April 24 2026)
176
+
177
+ Micro-release closing one class of operator error that broke the v1.20.1
178
+ five-project live-test campaign: the LLM operator had a clear memory
179
+ instruction to load `LivePilot_Analyzer` on master at the start of a
180
+ fresh Ableton session but missed it in 5 of 5 projects — producing
181
+ basic mixes instead of the intended mix-polish outcomes because every
182
+ analyzer-gated move (`tighten_low_end`, `sculpt_midrange`,
183
+ `balance_stereo_image`, etc.) silently degraded. Fixed forward with a
184
+ new idempotent pre-flight tool + Director skill wiring.
185
+
186
+ ### Added
187
+
188
+ **New tool: `ensure_analyzer_on_master`** (`mcp_server/tools/analyzer.py`).
189
+ Idempotent pre-flight that loads `LivePilot_Analyzer.amxd` on master
190
+ when missing, no-ops when already loaded. Returns one of:
191
+ - `already_loaded` (with `is_last_on_master`, `duplicate_count`)
192
+ - `loaded` (first-time load from Ableton browser)
193
+ - `install_required` (device not in browser — actionable hint points at
194
+ `install_m4l_device`)
195
+ - `failed` (any other error)
196
+
197
+ Post-load report surfaces the CLAUDE.md invariant "LivePilot_Analyzer
198
+ must be LAST on master" via `is_last_on_master: bool` and warns when
199
+ violated. Duplicate-count warning covers the edge case of multiple
200
+ analyzers on the master chain.
201
+
202
+ Safe to call every turn — subsequent calls short-circuit via one
203
+ `get_master_track` read. Tool count: 429 → 430. 6 new contract tests
204
+ covering already-loaded, missing-loads, install-required,
205
+ duplicate-handling, is-last warning, and two-call idempotence.
206
+
207
+ ### Changed
208
+
209
+ **Director Phase 1** (`livepilot-creative-director/SKILL.md`). Added
210
+ `ensure_analyzer_on_master` at the top of the "Ground" reads as a
211
+ REQUIRED call, ahead of `get_session_info`. Wording explicitly connects
212
+ the step to the failure it prevents so future agents don't rationalize
213
+ skipping it: "Skipping it is how the v1.20.1 live-test campaign
214
+ produced basic mixes — the analyzer-gated moves degrade silently when
215
+ there's no master spectrum to read."
216
+
217
+ ### Notes
218
+
219
+ No breaking changes. Calling code that assumed the analyzer was loaded
220
+ continues to work; the new tool adds an explicit pre-flight path.
221
+ `install_m4l_device` contract unchanged.
222
+
223
+ ---
224
+
3
225
  ## 1.20.2 — 5 bugs + 1 race condition from the live-test campaign (April 24 2026)
4
226
 
5
227
  Patch release fixing every issue surfaced during the v1.20.1 five-project
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  <p align="center">
19
19
  An agentic production system for Ableton Live 12.<br>
20
- 429 tools. 53 domains. Device atlas. Plan-aware Splice integration. Auto-composition. Spectral perception. Technique memory. Drum-rack pad builder. Live dead-device detection.
20
+ 430 tools. 53 domains. Device atlas. Plan-aware Splice integration. Auto-composition. Spectral perception. Technique memory. Drum-rack pad builder. Live dead-device detection.
21
21
  </p>
22
22
 
23
23
  <br>
@@ -38,12 +38,12 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
38
38
  | Layer | What it provides |
39
39
  |-------|-----------------|
40
40
  | **Deterministic Tools** | Direct control: transport, tracks, clips, notes, devices, scenes, mixing, arrangement, browser, automation |
41
- | **Device Atlas** | Knowledge of every device in Ableton's library — 1305 devices indexed by name, URI, category, tag, genre, and pack (641 pack-indexed). 120 enriched with sonic intelligence (47 with aesthetic-tagged `signature_techniques`). 683 drum kits mapped. **Free-text `atlas_describe_chain`** ("a granular pad like Tim Hecker") and **reverse-lookup `atlas_techniques_for_device`** (what techniques reference this device?) added in v1.17 |
42
- | **Concept Surface** | **New in v1.17.** Two reference files let the LLM's training translate into LivePilot: `artist-vocabularies.md` maps ~25 producers (Villalobos, Hawtin, Basic Channel, Gas, Basinski, Hecker, Aphex, Autechre, Dilla, Burial, Henke, Daft Punk, …) to `reach_for` / `avoid` / `key_techniques`; `genre-vocabularies.md` maps 15 genres to tempo / kick / bass / percussion / harmonic / texture / devices. 146 cross-references between devices and techniques. The LLM reads "sound like Gas" and gets a concrete device chain, not guesswork |
43
- | **Sample Engine** | Three-source sample intelligence — Ableton's browser, your filesystem, and Splice's catalog (plan-aware: Ableton Live plan uses daily quota, Sounds+/Creator uses credits, free samples bypass both). 6 fitness critics. 29 processing techniques. Collections, presets, preview-URL audition, **LIVE Describe-a-Sound + Variations via Splice GraphQL (v1.17)** |
44
- | **Spectral Perception** | Real-time ears via M4L — **9-band FFT (v1.17+, sub_low split at 20-60 Hz)**, RMS/peak metering, Krumhansl-Schmuckler key detection, pitch tracking, FluCoMa mel/chroma/onset. Closes the feedback loop so the AI hears its own changes |
41
+ | **Device Atlas** | Knowledge of every device in Ableton's library — 1305 devices indexed 7 ways (by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack 641 pack-indexed). 120 enriched with sonic intelligence (47 with aesthetic-tagged `signature_techniques`). 683 drum kits mapped. Free-text `atlas_describe_chain` ("a granular pad like Tim Hecker") and reverse-lookup `atlas_techniques_for_device` cross-reference 146 techniques across 58 devices |
42
+ | **Concept Surface** | Two reference files let the LLM's training translate into LivePilot: `artist-vocabularies.md` maps ~25 producers (Villalobos, Hawtin, Basic Channel, Gas, Basinski, Hecker, Aphex, Autechre, Dilla, Burial, Henke, Daft Punk, …) to `reach_for` / `avoid` / `key_techniques`; `genre-vocabularies.md` maps 15 genres to tempo / kick / bass / percussion / harmonic / texture / devices. The LLM reads "sound like Gas" and gets a concrete device chain, not guesswork |
43
+ | **Sample Engine** | Three-source sample intelligence — Ableton's browser, your filesystem, and Splice's catalog (plan-aware: Ableton Live plan uses daily quota, Sounds+/Creator uses credits, free samples bypass both). 6 fitness critics. 29 processing techniques. Collections, presets, preview-URL audition, LIVE Describe-a-Sound + Variations via Splice GraphQL |
44
+ | **Spectral Perception** | Real-time ears via M4L — 9-band FFT (with sub_low split at 20-60 Hz for kick fundamentals), RMS/peak metering, Krumhansl-Schmuckler key detection, pitch tracking, FluCoMa mel/chroma/onset. Auto-loaded via `ensure_analyzer_on_master` (v1.20.3) no more silently-degraded mix moves from forgotten analyzer |
45
45
  | **Technique Memory** | Persistent library of production decisions. Save a beat pattern, device chain, or mix template. Recall by mood, genre, or texture across sessions |
46
- | **Creative Intelligence** | A dozen engines that understand song identity, learn your taste, diagnose stuck sessions, generate creative options, and evaluate results before claiming success |
46
+ | **Creative Intelligence** | 12 engines on top of the tools: SongBrain, Taste Graph, Wonder Mode, Mix/Sound-Design/Transition/Reference/Translation engines, Hook Hunter, Stuckness Detector, Session Continuity, Preview Studio. **43 semantic moves** (v1.20) — musical intents like "tighten the low end" or "make kick and bass lock" that compile into tool sequences with risk levels and target dimensions |
47
47
 
48
48
  <br>
49
49
 
@@ -80,7 +80,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
80
80
  │ └─────────────────┼──────────────────┘ │
81
81
  │ ▼ │
82
82
  │ ┌─────────────────┐ │
83
- │ │ 429 MCP Tools │ │
83
+ │ │ 430 MCP Tools │ │
84
84
  │ │ 53 domains │ │
85
85
  │ └────────┬────────┘ │
86
86
  │ │ │
@@ -103,7 +103,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
103
103
 
104
104
  **M4L Bridge** (`m4l_device/`) — Optional Max for Live Audio Effect on the master track. Provides deep LOM access through Max's LiveAPI that the ControlSurface API can't reach. UDP 9880 (M4L to server) carries spectral data and LiveAPI responses. OSC 9881 (server to M4L) sends commands. The 32 spectral/analyzer tools strictly require the bridge; device and sample tools that call the bridge also have graceful fallbacks, so core functionality works without it. Backed by 31 bridge commands for hidden parameters, Simpler internals, warp markers, display values, and Simpler warp / Compressor sidechain writes that live on child objects Python can't reach.
105
105
 
106
- **Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 1305 devices with browser URIs, 120 enriched with YAML sonic intelligence profiles (mood, genre, texture, recommended chains). 6 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre. The AI never hallucinates a device name or preset — it always resolves against the atlas first.
106
+ **Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 1305 devices with browser URIs, 120 enriched with YAML sonic intelligence profiles (mood, genre, texture, recommended chains). 7 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack (641 devices mapped to their source pack). Reverse-index `device_techniques_index.json` powers `atlas_techniques_for_device` (146 cross-references across 58 devices). The AI never hallucinates a device name or preset — it always resolves against the atlas first.
107
107
 
108
108
  **Sample Engine** (`mcp_server/sample_engine/`) — Searches three sources simultaneously: BrowserSource (Ableton's library), SpliceSource (local Splice catalog via SQLite), FilesystemSource (user directories). Every result passes through a 6-critic fitness battery (key, tempo, spectral, genre, mood, technical). 29 processing techniques (Surgeon precision vs. Alchemist experimentation). Builds complete sample processing plans with warp, slice, and effect recommendations.
109
109
 
@@ -121,7 +121,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
121
121
 
122
122
  ## The Intelligence Layer
123
123
 
124
- 12 engines sit on top of the 429 tools. They give the AI musical judgment, not just musical execution.
124
+ 12 engines sit on top of the 430 tools. They give the AI musical judgment, not just musical execution.
125
125
 
126
126
  ### SongBrain — What the Song Is
127
127
 
@@ -133,7 +133,7 @@ Learns your production preferences across sessions. Tracks which move families y
133
133
 
134
134
  ### Semantic Moves — Musical Actions, Not Parameters
135
135
 
136
- 26+ high-level intents ("add contrast," "tighten the low end," "build tension") that compile into tool sequences. Each move carries a risk level, target dimensions, and protection thresholds. The AI knows what it's risking with every action.
136
+ **43 high-level intents** across 7 families (mix, arrangement, transition, sound_design, performance, device_creation, sample) — "add contrast," "tighten the low end," "make kick and bass lock," "sample vocal ghost," "destroy then rebuild." Each move compiles into a concrete tool sequence with risk level, target dimensions, and protection thresholds. Analyzer-gated moves (`tighten_low_end`, `make_kick_bass_lock`) mark their spectrum pre-reads as optional so the plan continues even when the analyzer isn't available. The AI knows what it's risking with every action.
137
137
 
138
138
  ### Wonder Mode — Stuck-Rescue Workflow
139
139
 
@@ -173,11 +173,11 @@ Every engine follows: **measure before → act → measure after → compare**.
173
173
 
174
174
  ## Tools
175
175
 
176
- 429 tools across 53 domains. Highlights below — [full catalog here](docs/manual/tool-catalog.md).
176
+ 430 tools across 53 domains. Highlights below — [full catalog here](docs/manual/tool-catalog.md).
177
177
 
178
178
  <br>
179
179
 
180
- ### Core (210 tools)
180
+ ### Core Ableton Control — highlights
181
181
 
182
182
  | Domain | # | What it covers |
183
183
  |--------|:-:|----------------|
@@ -200,12 +200,12 @@ Every engine follows: **measure before → act → measure after → compare**.
200
200
 
201
201
  <br>
202
202
 
203
- ### M4L Bridge — 30 tools `[optional]`
203
+ ### M4L Bridge — 38 analyzer tools `[optional]`, 31 bridge commands
204
204
 
205
- The M4L Analyzer sits on the master track. UDP 9880 carries spectral data to the server. OSC 9881 sends commands back.
205
+ The M4L Analyzer sits on the master track. UDP 9880 carries spectral data to the server. OSC 9881 sends commands back. The `ensure_analyzer_on_master` pre-flight (v1.20.3) loads the analyzer idempotently on first use — call it once at session start and forget about it.
206
206
 
207
207
  > [!TIP]
208
- > Most tools work without the analyzer — it adds 32 spectral/analyzer tools (frequency, loudness, perception) and closes the feedback loop.
208
+ > Most tools work without the analyzer — it adds 38 spectral/analyzer tools (frequency, loudness, perception, Simpler, warp) and closes the feedback loop.
209
209
 
210
210
  ```
211
211
  SPECTRAL ─────── 9-band frequency decomposition (sub_low → air)
@@ -228,36 +228,43 @@ WARP ─────────── get / add / move / remove markers
228
228
 
229
229
  <br>
230
230
 
231
- ### Device Atlas — 6 tools
231
+ ### Device Atlas — 10 tools
232
232
 
233
233
  The atlas is an in-memory indexed database of Ableton's entire device library.
234
234
 
235
235
  ```
236
236
  1305 devices total
237
237
  120 enriched with sonic intelligence (mood, genre, texture, chains)
238
- 683 drum kits mapped with note assignments
239
- 6 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre
238
+ 47 with aesthetic-tagged signature_techniques
239
+ 683 drum kits mapped with note assignments
240
+ 7 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack
241
+ 146 technique cross-references across 58 devices (reverse-index)
240
242
  ```
241
243
 
242
244
  ```
243
- atlas_search Search devices by name, category, or tag
244
- atlas_suggest Suggest devices for a musical intent (e.g., "warm pad")
245
- atlas_chain Build a device chain from a genre or purpose
246
- atlas_compare Compare two devices side-by-side
247
- atlas_detail Get full enriched profile for a device
248
- atlas_library_scan Scan what's actually installed on this machine
245
+ atlas_search Search devices by name, category, or tag
246
+ atlas_device_info Full enriched profile for a single device
247
+ atlas_suggest Suggest devices for a musical intent (e.g., "warm pad")
248
+ atlas_chain_suggest Build a device chain from a genre, artist, or purpose
249
+ atlas_compare Compare two devices side-by-side
250
+ atlas_describe_chain Free-text describe-a-chain ("granular pad like Tim Hecker")
251
+ atlas_techniques_for_device Reverse-lookup: what techniques reference this device?
252
+ atlas_pack_info Inspect a single Ableton pack — devices + enrichment coverage
253
+ scan_full_library Scan what's actually installed on this machine
254
+ reload_atlas Hot-reload the atlas after adding enrichments
249
255
  ```
250
256
 
251
257
  <br>
252
258
 
253
- ### Sample Engine — 6 tools
259
+ ### Sample Engine — 23 tools
254
260
 
255
- Three-source sample intelligence with critic-driven fitness scoring.
261
+ Three-source sample intelligence with critic-driven fitness scoring, plus deep Splice integration (catalog search, preview, collections, preset downloads).
256
262
 
257
263
  ```
258
264
  SOURCES ─────────── BrowserSource (Ableton's built-in library)
259
265
  SpliceSource (local Splice catalog via SQLite)
260
266
  FilesystemSource (user-specified directories)
267
+ Splice LIVE (gRPC + GraphQL for the full catalog)
261
268
 
262
269
  CRITICS ─────────── key fitness · tempo fitness · spectral match
263
270
  genre alignment · mood alignment · technical quality
@@ -265,15 +272,35 @@ CRITICS ─────────── key fitness · tempo fitness · spectr
265
272
  TECHNIQUES ─────── 29 processing recipes:
266
273
  Surgeon (precise, transparent) vs.
267
274
  Alchemist (experimental, transformative)
275
+
276
+ PLAN-AWARE ─────── Ableton Live plan 100 samples/day (no credit drain)
277
+ Sounds+/Creator CREDIT_HARD_FLOOR=5 safety gate
278
+ Free samples bypass both gates
268
279
  ```
269
280
 
270
281
  ```
271
- analyze_sample Build a complete SampleProfile (material, key, BPM, spectral)
272
- search_samples Multi-source search with critic scoring
273
- suggest_sample_move Recommend processing technique for a sample
274
- build_sample_plan Full processing pipeline: warp + slice + effects
275
- list_sample_techniques Browse the 29-technique library
276
- get_sample_technique Get detailed recipe for a specific technique
282
+ Sample analysis & planning
283
+ analyze_sample Build complete SampleProfile (material, key, BPM, spectral)
284
+ search_samples Multi-source search with critic scoring
285
+ evaluate_sample_fit Score a candidate sample against session context
286
+ suggest_sample_technique Recommend processing technique for a sample
287
+ plan_sample_workflow Full processing pipeline: warp + slice + effects
288
+ plan_slice_workflow Slice-specific workflow for breaks / drum loops
289
+ get_sample_opportunities Surface sample-friendly spots in the session
290
+
291
+ Splice LIVE (catalog, collections, presets)
292
+ get_splice_credits Plan + remaining credits + daily quota state
293
+ splice_catalog_hunt Query the full Splice catalog (gRPC)
294
+ splice_download_sample Plan-aware download (credit floor + quota check)
295
+ splice_preview_sample Zero-cost audition via PreviewURL
296
+ splice_describe_sound Natural-language search via Splice GraphQL
297
+ splice_generate_variation Find catalog samples similar to a given UUID
298
+ splice_list_collections Enumerate user's Likes / bass / keys folders
299
+ splice_search_in_collection / add_to_collection / remove_from_collection / create_collection
300
+ splice_list_presets Purchased instrument presets
301
+ splice_preset_info · splice_download_preset
302
+ splice_pack_info Per-pack metadata
303
+ splice_http_diagnose Debug the Splice HTTPS bridge
277
304
  ```
278
305
 
279
306
  <br>
@@ -362,7 +389,7 @@ The V2 intelligence layer. These tools analyze, diagnose, plan, evaluate, and le
362
389
  | Creative Constraints | 5 | constraint activation, reference-inspired variants |
363
390
  | Preview Studio | 5 | variant creation, preview rendering, comparison, commit |
364
391
 
365
- > **[View all 429 tools →](docs/manual/tool-catalog.md)**
392
+ > **[View all 430 tools →](docs/manual/tool-catalog.md)**
366
393
 
367
394
  <br>
368
395
 
@@ -461,12 +488,12 @@ livepilot --install
461
488
  </details>
462
489
 
463
490
  <details>
464
- <summary><strong>3. M4L Analyzer (optional — adds 27 tools)</strong></summary>
491
+ <summary><strong>3. M4L Analyzer (optional — adds 38 tools)</strong></summary>
465
492
 
466
493
  Drag `LivePilot_Analyzer.amxd` onto the master track for real-time spectral analysis.
467
- The `--setup` wizard and Desktop Extension do this automatically.
494
+ The `--setup` wizard and Desktop Extension do this automatically. From v1.20.3, your AI client can also call `ensure_analyzer_on_master` — an idempotent pre-flight that loads the device if missing and no-ops otherwise. The Creative Director skill does this on every session's Phase 1 ground read so you can't forget.
468
495
 
469
- > **Important:** The Analyzer must be the LAST device on the master track — after all effects (EQ, Compressor, Utility) so it reads the final output signal.
496
+ > **Important:** The Analyzer must be the LAST device on the master track — after all effects (EQ, Compressor, Utility) so it reads the final output signal. The pre-flight tool reports `is_last_on_master: bool` and warns if the invariant is broken.
470
497
 
471
498
  </details>
472
499
 
@@ -589,7 +616,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for architecture details, code guidelines
589
616
 
590
617
  | Document | What's inside |
591
618
  |----------|---------------|
592
- | [Manual](docs/manual/index.md) | Complete reference: architecture, all 429 tools, workflows |
619
+ | [Manual](docs/manual/index.md) | Complete reference: architecture, all 430 tools, workflows |
593
620
  | [Intelligence Layer](docs/manual/intelligence.md) | How the 12 engines connect — conductor, moves, preview, evaluation |
594
621
  | [Device Atlas](docs/manual/device-atlas.md) | 1305 devices indexed — search, suggest, chain building |
595
622
  | [Samples & Slicing](docs/manual/samples.md) | 3-source search, fitness critics, slice workflows |
Binary file
@@ -34,7 +34,7 @@ outlets = 2; // 0: to udpsend (responses), 1: to buffer~/status
34
34
  // Single source of truth for the bridge version — bumped alongside the
35
35
  // rest of the release manifest. Surfaced in the UI via messnamed("livepilot_version", ...)
36
36
  // so the frozen .amxd visibly reports which build it was last exported from.
37
- var VERSION = "1.20.2";
37
+ var VERSION = "1.21.0";
38
38
 
39
39
  // ── State ──────────────────────────────────────────────────────────────────
40
40
 
@@ -1,2 +1,2 @@
1
1
  """LivePilot MCP Server — bridges MCP protocol to Ableton Live."""
2
- __version__ = "1.20.2"
2
+ __version__ = "1.21.0"
@@ -0,0 +1,31 @@
1
+ """Affordance-preset library — named presets per Ableton device class.
2
+
3
+ v1.21 (minimal): infrastructure + 3 seed presets proving the resolver
4
+ pattern:
5
+ - reverb.yaml → preset: dub-cathedral
6
+ - delay.yaml → preset: ping-pong-dub
7
+ - auto-filter.yaml → preset: slow-sweep
8
+
9
+ v1.22 (planned): full catalog coverage, class_name → slug inference,
10
+ taste-graph integration for preset ranking.
11
+
12
+ Public API (re-exported for ``from mcp_server.affordances import ...``):
13
+ resolve_preset(device_slug, preset_name) -> dict[str, number|bool] | None
14
+ list_devices() -> list[str]
15
+ list_presets(device_slug) -> list[str]
16
+ get_preset_metadata(device_slug, preset_name) -> dict | None
17
+ """
18
+
19
+ from .presets import (
20
+ resolve_preset,
21
+ list_devices,
22
+ list_presets,
23
+ get_preset_metadata,
24
+ )
25
+
26
+ __all__ = [
27
+ "resolve_preset",
28
+ "list_devices",
29
+ "list_presets",
30
+ "get_preset_metadata",
31
+ ]
@@ -0,0 +1,143 @@
1
+ """Schema validator for affordance-device preset YAML files.
2
+
3
+ Enforces at test-time that every seed preset file is well-formed. The
4
+ runtime loader (``presets.py``) is tolerant of missing optional fields
5
+ so the product never crashes on a bad preset; this validator is the
6
+ pre-ship gate run by ``tests/test_affordance_presets.py``.
7
+
8
+ Schema:
9
+ device_slug: str # REQUIRED — must match filename stem
10
+ device_class_name: str # REQUIRED — Ableton's class_name
11
+ presets: # REQUIRED — dict[name → PresetRecord]
12
+ <name>:
13
+ description: str # REQUIRED — human-readable
14
+ param_overrides: dict # REQUIRED — {name: number|bool|int}, ≥1 entry
15
+ risk_notes: str # optional
16
+ suggested_pairings: list[str] # optional
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ import yaml
25
+
26
+
27
+ _REQUIRED_TOPLEVEL = {"device_slug", "device_class_name", "presets"}
28
+ _REQUIRED_PER_PRESET = {"description", "param_overrides"}
29
+ _OPTIONAL_PER_PRESET = {"risk_notes", "suggested_pairings"}
30
+
31
+
32
+ def validate_preset_file(path: Path) -> list[str]:
33
+ """Return a list of validation errors. Empty list = valid.
34
+
35
+ Errors are human-readable one-liners suitable for test failure output.
36
+ """
37
+ errors: list[str] = []
38
+
39
+ try:
40
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
41
+ except yaml.YAMLError as exc:
42
+ return [f"{path.name}: YAML parse error — {exc}"]
43
+
44
+ if not isinstance(data, dict):
45
+ return [
46
+ f"{path.name}: top-level must be a mapping, "
47
+ f"got {type(data).__name__}"
48
+ ]
49
+
50
+ missing = _REQUIRED_TOPLEVEL - set(data.keys())
51
+ if missing:
52
+ errors.append(
53
+ f"{path.name}: missing required top-level fields {sorted(missing)}"
54
+ )
55
+
56
+ slug = data.get("device_slug")
57
+ if slug is not None and not isinstance(slug, str):
58
+ errors.append(f"{path.name}: device_slug must be a string")
59
+ elif slug and slug != path.stem:
60
+ errors.append(
61
+ f"{path.name}: device_slug {slug!r} must match "
62
+ f"filename stem {path.stem!r}"
63
+ )
64
+
65
+ class_name = data.get("device_class_name")
66
+ if class_name is not None and not isinstance(class_name, str):
67
+ errors.append(f"{path.name}: device_class_name must be a string")
68
+
69
+ presets = data.get("presets")
70
+ if presets is not None:
71
+ if not isinstance(presets, dict):
72
+ errors.append(f"{path.name}: presets must be a mapping")
73
+ elif not presets:
74
+ errors.append(f"{path.name}: presets dict is empty")
75
+ else:
76
+ for preset_name, preset in presets.items():
77
+ errors.extend(_validate_preset(path.name, preset_name, preset))
78
+
79
+ return errors
80
+
81
+
82
+ def _validate_preset(filename: str, name: str, preset: Any) -> list[str]:
83
+ errors: list[str] = []
84
+ if not isinstance(preset, dict):
85
+ return [f"{filename}: preset {name!r} must be a mapping"]
86
+
87
+ missing = _REQUIRED_PER_PRESET - set(preset.keys())
88
+ if missing:
89
+ errors.append(
90
+ f"{filename}: preset {name!r} missing required fields {sorted(missing)}"
91
+ )
92
+
93
+ description = preset.get("description")
94
+ if description is not None and not isinstance(description, str):
95
+ errors.append(f"{filename}: preset {name!r} description must be a string")
96
+
97
+ overrides = preset.get("param_overrides")
98
+ if overrides is not None:
99
+ if not isinstance(overrides, dict):
100
+ errors.append(
101
+ f"{filename}: preset {name!r} param_overrides must be a dict"
102
+ )
103
+ elif not overrides:
104
+ errors.append(
105
+ f"{filename}: preset {name!r} param_overrides is empty"
106
+ )
107
+ else:
108
+ for k, v in overrides.items():
109
+ if not isinstance(k, str):
110
+ errors.append(
111
+ f"{filename}: preset {name!r} param_overrides key "
112
+ f"{k!r} must be a string"
113
+ )
114
+ if not isinstance(v, (int, float, bool)):
115
+ errors.append(
116
+ f"{filename}: preset {name!r} param_overrides value for "
117
+ f"{k!r} must be number|bool, got {type(v).__name__}"
118
+ )
119
+
120
+ pairings = preset.get("suggested_pairings")
121
+ if pairings is not None:
122
+ if not isinstance(pairings, list):
123
+ errors.append(
124
+ f"{filename}: preset {name!r} suggested_pairings must be a list"
125
+ )
126
+ elif not all(isinstance(p, str) for p in pairings):
127
+ errors.append(
128
+ f"{filename}: preset {name!r} suggested_pairings entries "
129
+ f"must be strings"
130
+ )
131
+
132
+ risk_notes = preset.get("risk_notes")
133
+ if risk_notes is not None and not isinstance(risk_notes, str):
134
+ errors.append(f"{filename}: preset {name!r} risk_notes must be a string")
135
+
136
+ extras = set(preset.keys()) - _REQUIRED_PER_PRESET - _OPTIONAL_PER_PRESET
137
+ if extras:
138
+ errors.append(
139
+ f"{filename}: preset {name!r} has unknown fields {sorted(extras)} "
140
+ f"(allowed: {sorted(_REQUIRED_PER_PRESET | _OPTIONAL_PER_PRESET)})"
141
+ )
142
+
143
+ return errors