livepilot 1.20.3 → 1.21.1

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,295 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.21.1 — Audit-response patch: experiment-commit safety + doc hygiene + lockfile (April 24 2026)
4
+
5
+ Small patch release responding to an external audit of v1.21.0 performed
6
+ the same day it shipped. The audit surfaced one real safety bug
7
+ (commit_experiment status allowlist was an exclusion list when the
8
+ intent was an inclusion list) plus several doc-consistency drifts.
9
+ No new features. No API changes beyond the tightened commit_experiment
10
+ contract. v1.21.0 callers already doing the right thing
11
+ (run_experiment → commit the ranked winner) continue to work unchanged.
12
+
13
+ ### P1 — commit_experiment only accepts `status='evaluated'`
14
+
15
+ Pre-fix (v1.21.0 and all prior versions with commit_experiment), the
16
+ status check was an EXCLUSION list:
17
+
18
+ ```python
19
+ if target.status in ("rejected", "analytical", "failed"):
20
+ return {"error": ...}
21
+ ```
22
+
23
+ Blocks 3 statuses; implicitly allows the other 6 — including `pending`,
24
+ `running`, `discarded`, and `interesting_but_failed`. Those branches
25
+ can't be ranked by `compare_experiments()`, but `commit_experiment`
26
+ would accept them as long as a compiled plan was attached. The code's
27
+ own inline comment already described the correct contract ("only
28
+ status='evaluated' branches are ranking candidates"); the implementation
29
+ had the wrong polarity. The fix flips it to an INCLUSION check:
30
+
31
+ ```python
32
+ if target.status != "evaluated":
33
+ return {"error": ...}
34
+ ```
35
+
36
+ Error message updated to enumerate all 9 possible statuses and explain
37
+ which state each represents (pending/running = not yet evaluated;
38
+ rejected/analytical/failed = classifier exclusions; committed =
39
+ already committed; discarded = explicitly thrown out;
40
+ interesting_but_failed = exploration-audit only).
41
+
42
+ Why this matters: v1.21.0's `commit_experiment` ledger writer records
43
+ every commit into `SessionLedger` where anti-repetition filters read
44
+ it. Without the tighter status check, a caller bypassing the ranking
45
+ layer could pollute the ledger with entries the system explicitly
46
+ classified as non-winners — degrading anti-repetition signal quality.
47
+
48
+ **Regression tests added (4)** in `tests/test_commit_experiment_ledger.py`:
49
+ `test_commit_on_pending_branch_rejects`,
50
+ `test_commit_on_running_branch_rejects`,
51
+ `test_commit_on_discarded_branch_rejects`,
52
+ `test_commit_on_interesting_but_failed_branch_rejects`. All FAILED
53
+ pre-fix (reproducing the audit's finding), all PASS post-fix.
54
+
55
+ ### P1 — package-lock.json bumped 1.17.5 → 1.21.1
56
+
57
+ Lockfile's root `.version` and `.packages[""].version` had been stale
58
+ at 1.17.5 since before v1.18. `npm publish` doesn't read these fields
59
+ (it reads package.json), so the npm registry was always correct — but
60
+ the repo-local lockfile misled local `npm install` workflows and any
61
+ release-check tooling that compared package.json vs lockfile. Fixed by
62
+ surgical replace on the 2 stale strings; no dependency tree
63
+ regeneration (keeps dep versions identical to v1.21.0).
64
+
65
+ ### P2 — Analyzer tool count: 32/33 → 38 (actual)
66
+
67
+ README.md previously said "32 spectral/analyzer tools" in one place
68
+ and "38 analyzer tools" in another — inconsistent within the same file.
69
+ `docs/M4L_BRIDGE.md` said "33 MCP tools in the analyzer domain" in two
70
+ places. Actual `@mcp.tool` count in `mcp_server/tools/analyzer.py` is
71
+ **38** (grep-verified). All stale 32/33 refs corrected to 38.
72
+
73
+ ### P2 — Reversibility language hedge
74
+
75
+ README's header NOTE block said "Everything is reversible with undo,"
76
+ which is too strong. Live-session mutations (clips, devices, mixer,
77
+ arrangement) do route through Ableton's undo stack and are reversible
78
+ — but Splice downloads, memory/ledger writes, installer actions, atlas
79
+ scans, and filesystem writes persist beyond undo. Hedged the language
80
+ to reflect this.
81
+
82
+ ### Deferred to v1.22
83
+
84
+ Audit-surfaced items that aren't patch-release material:
85
+
86
+ - **Atlas statistics reconciliation.** Docs claim "1305 devices / 120
87
+ enriched / 641 pack-indexed" across 9 description fields, but the
88
+ shipped `device_atlas.json` has 5264 devices and 135 entries with
89
+ `.enriched` truthy. The "1305" number appears to be a long-stale
90
+ cargo-culted count. Requires deciding whether `.devices` contains
91
+ duplicates, what the canonical "enriched" definition is, and
92
+ whether to restructure atlas JSON or fix the readers that look for
93
+ `.meta.version`. v1.22 scope.
94
+ - **`sync_metadata` expansion** to check package-lock.json project
95
+ version, semantic-move count via `registry.count()`, and
96
+ analyzer-tool count via grep on `analyzer.py`. Would convert this
97
+ entire class of drift into CI failures.
98
+ - **Dev-install path** for local contributors hitting missing
99
+ `soundfile` / `scipy` / `pretty_midi` / `pytest_asyncio` deps during
100
+ bare-python local runs. CI has these installed via requirements.txt.
101
+
102
+ ### Credits
103
+
104
+ External audit performed same day v1.21.0 shipped. Findings
105
+ file-linked and reproducible. Response time: ~2 hours from audit
106
+ receipt to v1.21.1 patch shipping.
107
+
108
+ ### Scope stats
109
+
110
+ - 1 code fix (`mcp_server/experiment/tools.py` — commit_experiment status check)
111
+ - 4 new regression tests (all initially FAILING pre-fix to reproduce
112
+ the audit, all PASSING post-fix)
113
+ - 3 doc corrections (README.md × 2, docs/M4L_BRIDGE.md × 2, plus the
114
+ reversibility hedge)
115
+ - 15 version-string sites + `.amxd` binary patch (2 bytes) +
116
+ package-lock.json (2 version fields)
117
+ - Test suite: 3120 → 3124 pass (+4). Zero regressions.
118
+
119
+ ---
120
+
121
+ ## 1.21.0 — Consolidation: experiment ledger + preset library + record-readiness + reader audit (April 24 2026)
122
+
123
+ Consolidation release closing five items from the v1.20 plan §12 non-goals
124
+ that were tractable in a 2-session scope. Adds 1 new semantic move + 1
125
+ major-tool auto-ledger-write + minimal preset-library infrastructure +
126
+ a store-purpose audit that turns the v1.20 director-SKILL fix into a
127
+ codebase-wide invariant, all gated behind a test-first wire-format
128
+ parity extension that caught 1 pre-v1.20 drift bug and closed the
129
+ audit window.
130
+
131
+ Registry: 43 → 44 moves. Test suite: 3043 → 3118 pass (+75 across all
132
+ chunks). Wire-format parity suite: 10 → 44 scenarios. Zero regressions.
133
+
134
+ ### New semantic moves (1)
135
+
136
+ - **`configure_record_readiness`** (performance family, low risk, protects
137
+ `signal_integrity=0.7`)
138
+ - seed_args: `{track_index, armed, exclusive?}`
139
+ - Non-exclusive: single step `set_track_arm(track_index, arm=armed)` — note
140
+ the wire-format key is `arm`, not `armed` (the MCP tool accepts `armed`
141
+ as an ergonomic kwarg but renames to `arm` before send_command; the
142
+ remote_command backend bypasses that rename).
143
+ - Exclusive (`exclusive=True` + `armed=True`): N+1 step plan —
144
+ `set_track_arm(arm=False)` for every other regular track, then
145
+ `set_track_arm(target, arm=True)` for the target. Emulates Ableton's
146
+ exclusive-arm mode manually because `song.exclusive_arm` has no
147
+ Python setter in Live 12.4 (pre-existing v1.20.3 Remote Script bug
148
+ surfaced during v1.21 live-test pre-flight — see plan correction #6).
149
+ Requires `session_info.tracks` (automatically built by
150
+ `apply_semantic_move`).
151
+ - Rejects `exclusive=True + armed=False` (contradictory), missing `armed`
152
+ seed_arg, and negative `track_index` (return tracks can't be armed
153
+ per Ableton's handler at `remote_script/LivePilot/tracks.py:261`).
154
+ - Closes the one tech_debt entry seeded during v1.20 live test 6.
155
+
156
+ ### Experiment-engine ledger writer
157
+
158
+ - **`commit_experiment`** now writes to `SessionLedger` after a successful
159
+ commit, mirroring v1.20's `apply_semantic_move` pattern (commit `0b3489b`).
160
+ Anti-repetition filters downstream now see experiment commits the same
161
+ way they see direct semantic-move applies.
162
+ - `engine` tag reflects branch SOURCE (not escalation success): `"composer"`
163
+ when `target.seed.source == "composer"`, else `"experiment"`. A composer-
164
+ sourced branch that fell back to scaffold execution is still
165
+ `engine=composer` — the escalation-success detail lives in
166
+ `target.evaluation["composer_escalation"]`.
167
+ - `move_class` via `_infer_move_family(target)`: `seed.family` when set,
168
+ else inspect first compiled_plan step's tool via `_TOOL_TO_FAMILY`
169
+ lookup, else default `"mix"`.
170
+ - `actions` = one entry per ok row in `commit_result["execution_log"]`
171
+ (the router's actual execution record — captures the post-escalation
172
+ plan when composer escalation fired).
173
+ - Response gains `ledger_entry_id` field (same pattern as
174
+ `apply_semantic_move`). Best-effort — a ledger-write exception is logged
175
+ and swallowed so the commit never fails on a bookkeeping path.
176
+
177
+ ### Minimal affordance preset library
178
+
179
+ - New path `mcp_server/affordances/` with loader (`presets.py`), schema
180
+ validator (`_schema.py`), and 3 seed YAML files:
181
+ - `reverb.yaml` — preset `dub-cathedral` (Basic Channel-adjacent huge
182
+ space; Decay 0.85, Room 0.95, Dry/Wet 0.40, Predelay 0.45, Diffusion 0.80)
183
+ - `delay.yaml` — preset `ping-pong-dub` (dotted 8th, feedback 0.45,
184
+ HP+LP filtered)
185
+ - `auto-filter.yaml` — preset `slow-sweep` (LP type, bar-long LFO,
186
+ moderate resonance)
187
+ - `configure_device` compiler gained optional `preset` + `device_slug`
188
+ seed_args (additive — v1.20 callers unaffected). Merge: preset resolves
189
+ first, explicit `param_overrides` win on per-key conflict (last-write-wins).
190
+ v1.21 requires explicit `device_slug` when `preset` is used; v1.22
191
+ adds class_name → slug auto-inference.
192
+ - `livepilot-creative-director/references/phase-6-execution.md` §Affordance-
193
+ preset resolution rewritten to document the live import path and the
194
+ three dispatch patterns (preset reference, preset + explicit override,
195
+ fallback to Python resolve).
196
+
197
+ ### Wire-format parity retroactive gate
198
+
199
+ - `tests/test_compiler_wire_format_parity.py` extended from 10 to 44
200
+ scenarios (10 v1.20 + 33 pre-v1.20 + 1 new `configure_record_readiness`).
201
+ Gate surfaced **1 pre-v1.20 drift pattern** affecting 7 moves:
202
+ `create_chaos_modulator`, `create_feedback_resonator`,
203
+ `create_wavefolder_effect`, `create_bitcrusher_effect`,
204
+ `create_karplus_string`, `create_stochastic_texture`, `create_fdn_reverb`
205
+ all shipped with a `find_and_load_device` plan_template emitting
206
+ `{"query": "Wonder X"}` but no `track_index`. The `remote_command`
207
+ backend bypasses MCP normalization, so Ableton's handler threw
208
+ `KeyError` on `params["track_index"]` — broken at runtime since
209
+ pre-v1.20, caught now.
210
+ - Fix: `device_creation_compilers.py::_compile_device_creation` threads
211
+ `track_index` from `seed_args` into `find_and_load_device` steps.
212
+ Plan templates updated from `{"query": ...}` to `{"device_name": ...}`
213
+ across all 7 Device Forge moves.
214
+ - Variant-B' consideration (>3 drift bugs → pause v1.21, ship patch):
215
+ all 7 failures were one pattern in one file, fixable in one commit.
216
+ Path B (inline fix as part of Task 1.1.5a) taken — single-pattern
217
+ single-file fixes are exactly what Task 1.1.5a was written for.
218
+ See `docs/plans/v1.21-impl-status.md` §3 for the decision log.
219
+
220
+ ### Store-purpose reader audit
221
+
222
+ - Every file in `mcp_server/` that imports `SessionLedger` or calls
223
+ `memory_list` / `get_session_memory` now carries a
224
+ `# store_purpose: <purpose>` comment naming its intent.
225
+ - Allowed purposes (closed set): `writer`, `anti_repetition`,
226
+ `audit_readonly`, `technique_library`, `session_observations`,
227
+ `escape_hatch_log`, `mcp_tool_definition`.
228
+ - **`tests/test_ledger_readers.py`** new (5 test cases): enforces the
229
+ annotation contract at CI, AND guards against any file annotated
230
+ `anti_repetition` also calling `memory_list` — which would be the
231
+ latent v1.20 store-confusion bug (director SKILL originally pointed
232
+ at `memory_list` for recency; `memory_list` reads the persistent
233
+ technique library, not the action ledger).
234
+ - Audit was clean across the entire `mcp_server` tree — no anti-repetition
235
+ caller was found reading the wrong store. The audit is purely
236
+ documentation contracts enforced at CI.
237
+
238
+ ### Plan corrections applied during execution (6, logged in impl-status doc)
239
+
240
+ Documented for future release planners in `docs/plans/v1.21-impl-status.md`:
241
+
242
+ 1. **Baseline drift** — v1.20.2 and v1.20.3 shipped between plan-write and
243
+ execution; all version-string literals in the plan needed `1.20.1 →
244
+ 1.20.3` remapping.
245
+ 2. **Wire-format drift in device_creation** — plan_templates emitted
246
+ `{"query": ...}`; handler requires `{"track_index", "device_name"}`.
247
+ 3. **`set_track_arm` wire format** — plan §3.1 used `{"armed": ...}`;
248
+ handler reads `params["arm"]`. MCP tool renames `armed → arm` before
249
+ send_command; remote_command backend bypasses the rename.
250
+ 4. **`set_exclusive_arm` shape** — plan §3.1 assumed a per-track signature
251
+ `(track_index=...)`; handler is a global mode toggle taking
252
+ `{"enabled": bool}`.
253
+ 5. **Return-track arm rejection** — plan's test expected negative
254
+ `track_index` to succeed; handler raises `ValueError: "Cannot arm a
255
+ return track"`. Corrected test pins compile-time rejection.
256
+ 6. **`set_exclusive_arm` is broken in Live 12.4 LOM** — live-test pre-flight
257
+ (2026-04-24) surfaced that `song.exclusive_arm` is a property WITHOUT a
258
+ Python setter in Live 12.4's Remote Script API. The v1.20.3 handler's
259
+ direct assignment `song.exclusive_arm = bool(...)` errors with
260
+ "property of 'Song' object has no setter". v1.21's
261
+ `configure_record_readiness` exclusive-mode now emulates the behavior
262
+ manually (disarm-all-others + arm-target loop) — correctness is
263
+ identical, works in any Live version. The broken v1.20.3
264
+ `set_exclusive_arm` handler is LEFT AS-IS for potential future fix
265
+ (when/if Ableton re-exposes the setter, or someone finds a LOM method
266
+ alternative); it's tracked as an independent bug in the Remote Script.
267
+
268
+ ### Non-goals (deferred)
269
+
270
+ - **Hard cutover of the escape hatch.** v1.22+ target, conditional on
271
+ zero `tech_debt` log entries accumulating in one month of production.
272
+ - **Standard / Deep preset catalog.** v1.22+. Minimal library from v1.21
273
+ proves the loader; catalog fills in after real-usage signal.
274
+ - **class_name → slug auto-inference** for the preset library. v1.22+.
275
+ - **Pre-v1.20 move compiler refactors** beyond the 1 drift fix. v1.22+.
276
+ - **New move families beyond the canonical 7.** The performance family
277
+ already exists; `configure_record_readiness` joins it.
278
+ - **Taste-graph integration for preset ranking.** v1.22+ once the catalog
279
+ exists.
280
+
281
+ ### Scope stats
282
+
283
+ - 7 atomic commits on `v1.21.0-dev` (fix + gate + 4 feature + refactor + release)
284
+ - 28 files changed, +1933 / -63 lines
285
+ - 3 new test files (commit_experiment_ledger, performance_moves,
286
+ affordance_presets, ledger_readers) + 1 doc (impl-status)
287
+ - 1 new package (`mcp_server/affordances/`) + 3 YAML seed files
288
+ - 15 version-string sites bumped + `.amxd` binary patch (2 bytes)
289
+ - 10 files annotated with `# store_purpose:`
290
+
291
+ ---
292
+
3
293
  ## 1.20.3 — Automated analyzer pre-flight (April 24 2026)
4
294
 
5
295
  Micro-release closing one class of operator error that broke the v1.20.1
package/README.md CHANGED
@@ -25,7 +25,9 @@
25
25
  > [!NOTE]
26
26
  > LivePilot works with **any MCP client** — Claude Code, Claude Desktop, Cursor, VS Code, Windsurf.
27
27
  > All tools execute on Ableton's main thread through the official Live Object Model API.
28
- > Everything is reversible with undo.
28
+ > Live-session mutations (clips, devices, mixer, arrangement) route through Ableton's undo stack.
29
+ > Side effects that touch state outside the Live project — Splice downloads, memory/ledger writes,
30
+ > installer actions, atlas scans, filesystem writes — persist beyond undo.
29
31
 
30
32
  <br>
31
33
 
@@ -38,12 +40,12 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
38
40
  | Layer | What it provides |
39
41
  |-------|-----------------|
40
42
  | **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 |
43
+ | **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 |
44
+ | **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 |
45
+ | **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 |
46
+ | **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
47
  | **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 |
48
+ | **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. **44 semantic moves** (v1.21) — 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
49
 
48
50
  <br>
49
51
 
@@ -101,9 +103,9 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
101
103
 
102
104
  **MCP Server** (`mcp_server/`) — Python FastMCP server. Validates inputs, routes commands to the Remote Script over TCP, manages the M4L bridge, runs the atlas, sample engine, composer, and all intelligence engines. This is what your AI client connects to.
103
105
 
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.
106
+ **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 38 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
107
 
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.
108
+ **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
109
 
108
110
  **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
111
 
@@ -133,7 +135,7 @@ Learns your production preferences across sessions. Tracks which move families y
133
135
 
134
136
  ### Semantic Moves — Musical Actions, Not Parameters
135
137
 
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.
138
+ **44 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
139
 
138
140
  ### Wonder Mode — Stuck-Rescue Workflow
139
141
 
@@ -177,7 +179,7 @@ Every engine follows: **measure before → act → measure after → compare**.
177
179
 
178
180
  <br>
179
181
 
180
- ### Core (210 tools)
182
+ ### Core Ableton Control — highlights
181
183
 
182
184
  | Domain | # | What it covers |
183
185
  |--------|:-:|----------------|
@@ -200,12 +202,12 @@ Every engine follows: **measure before → act → measure after → compare**.
200
202
 
201
203
  <br>
202
204
 
203
- ### M4L Bridge — 30 tools `[optional]`
205
+ ### M4L Bridge — 38 analyzer tools `[optional]`, 31 bridge commands
204
206
 
205
- The M4L Analyzer sits on the master track. UDP 9880 carries spectral data to the server. OSC 9881 sends commands back.
207
+ 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
208
 
207
209
  > [!TIP]
208
- > Most tools work without the analyzer — it adds 32 spectral/analyzer tools (frequency, loudness, perception) and closes the feedback loop.
210
+ > Most tools work without the analyzer — it adds 38 spectral/analyzer tools (frequency, loudness, perception, Simpler, warp) and closes the feedback loop.
209
211
 
210
212
  ```
211
213
  SPECTRAL ─────── 9-band frequency decomposition (sub_low → air)
@@ -228,36 +230,43 @@ WARP ─────────── get / add / move / remove markers
228
230
 
229
231
  <br>
230
232
 
231
- ### Device Atlas — 6 tools
233
+ ### Device Atlas — 10 tools
232
234
 
233
235
  The atlas is an in-memory indexed database of Ableton's entire device library.
234
236
 
235
237
  ```
236
238
  1305 devices total
237
239
  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
240
+ 47 with aesthetic-tagged signature_techniques
241
+ 683 drum kits mapped with note assignments
242
+ 7 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack
243
+ 146 technique cross-references across 58 devices (reverse-index)
240
244
  ```
241
245
 
242
246
  ```
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
247
+ atlas_search Search devices by name, category, or tag
248
+ atlas_device_info Full enriched profile for a single device
249
+ atlas_suggest Suggest devices for a musical intent (e.g., "warm pad")
250
+ atlas_chain_suggest Build a device chain from a genre, artist, or purpose
251
+ atlas_compare Compare two devices side-by-side
252
+ atlas_describe_chain Free-text describe-a-chain ("granular pad like Tim Hecker")
253
+ atlas_techniques_for_device Reverse-lookup: what techniques reference this device?
254
+ atlas_pack_info Inspect a single Ableton pack — devices + enrichment coverage
255
+ scan_full_library Scan what's actually installed on this machine
256
+ reload_atlas Hot-reload the atlas after adding enrichments
249
257
  ```
250
258
 
251
259
  <br>
252
260
 
253
- ### Sample Engine — 6 tools
261
+ ### Sample Engine — 23 tools
254
262
 
255
- Three-source sample intelligence with critic-driven fitness scoring.
263
+ Three-source sample intelligence with critic-driven fitness scoring, plus deep Splice integration (catalog search, preview, collections, preset downloads).
256
264
 
257
265
  ```
258
266
  SOURCES ─────────── BrowserSource (Ableton's built-in library)
259
267
  SpliceSource (local Splice catalog via SQLite)
260
268
  FilesystemSource (user-specified directories)
269
+ Splice LIVE (gRPC + GraphQL for the full catalog)
261
270
 
262
271
  CRITICS ─────────── key fitness · tempo fitness · spectral match
263
272
  genre alignment · mood alignment · technical quality
@@ -265,15 +274,35 @@ CRITICS ─────────── key fitness · tempo fitness · spectr
265
274
  TECHNIQUES ─────── 29 processing recipes:
266
275
  Surgeon (precise, transparent) vs.
267
276
  Alchemist (experimental, transformative)
277
+
278
+ PLAN-AWARE ─────── Ableton Live plan 100 samples/day (no credit drain)
279
+ Sounds+/Creator CREDIT_HARD_FLOOR=5 safety gate
280
+ Free samples bypass both gates
268
281
  ```
269
282
 
270
283
  ```
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
284
+ Sample analysis & planning
285
+ analyze_sample Build complete SampleProfile (material, key, BPM, spectral)
286
+ search_samples Multi-source search with critic scoring
287
+ evaluate_sample_fit Score a candidate sample against session context
288
+ suggest_sample_technique Recommend processing technique for a sample
289
+ plan_sample_workflow Full processing pipeline: warp + slice + effects
290
+ plan_slice_workflow Slice-specific workflow for breaks / drum loops
291
+ get_sample_opportunities Surface sample-friendly spots in the session
292
+
293
+ Splice LIVE (catalog, collections, presets)
294
+ get_splice_credits Plan + remaining credits + daily quota state
295
+ splice_catalog_hunt Query the full Splice catalog (gRPC)
296
+ splice_download_sample Plan-aware download (credit floor + quota check)
297
+ splice_preview_sample Zero-cost audition via PreviewURL
298
+ splice_describe_sound Natural-language search via Splice GraphQL
299
+ splice_generate_variation Find catalog samples similar to a given UUID
300
+ splice_list_collections Enumerate user's Likes / bass / keys folders
301
+ splice_search_in_collection / add_to_collection / remove_from_collection / create_collection
302
+ splice_list_presets Purchased instrument presets
303
+ splice_preset_info · splice_download_preset
304
+ splice_pack_info Per-pack metadata
305
+ splice_http_diagnose Debug the Splice HTTPS bridge
277
306
  ```
278
307
 
279
308
  <br>
@@ -461,12 +490,12 @@ livepilot --install
461
490
  </details>
462
491
 
463
492
  <details>
464
- <summary><strong>3. M4L Analyzer (optional — adds 27 tools)</strong></summary>
493
+ <summary><strong>3. M4L Analyzer (optional — adds 38 tools)</strong></summary>
465
494
 
466
495
  Drag `LivePilot_Analyzer.amxd` onto the master track for real-time spectral analysis.
467
- The `--setup` wizard and Desktop Extension do this automatically.
496
+ 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
497
 
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.
498
+ > **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
499
 
471
500
  </details>
472
501
 
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.3";
37
+ var VERSION = "1.21.1";
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.3"
2
+ __version__ = "1.21.1"
@@ -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
+ ]