livepilot 1.21.0 → 1.21.2

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,257 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.21.2 — Second audit-response: atlas reconciliation + manual hygiene + sync_metadata expansion (April 24 2026)
4
+
5
+ Second same-day audit-response patch. After v1.21.1 shipped, the repo
6
+ owner ran another audit and surfaced one code bug (`AtlasManager`
7
+ reported `version="unknown"` because the atlas JSON has `.version` at
8
+ top level, not under `.meta.version`) plus four documentation drifts
9
+ (atlas claims in 11 description fields, stale "32" analyzer counts in
10
+ 3 locations, reversibility overclaim in the manual, `.maxpat` source
11
+ displaying `v1.17.5`), and the meta-observation that `sync_metadata.py`
12
+ had been blind to the entire category of drift that keeps surfacing
13
+ in audit rounds.
14
+
15
+ This patch closes all five findings AND expands `sync_metadata`'s
16
+ scope so future drift of the same classes fails CI instead of requiring
17
+ another manual audit.
18
+
19
+ ### P2 — `AtlasManager.version` now reads top-level `.version`
20
+
21
+ Pre-fix (`mcp_server/atlas/__init__.py:21`):
22
+
23
+ ```python
24
+ self._meta = data.get("meta", {})
25
+ # ...
26
+ @property
27
+ def version(self) -> str:
28
+ return self._meta.get("version", "unknown")
29
+ ```
30
+
31
+ The shipped `device_atlas.json` has `.version = "2.0.0"` at top level,
32
+ not nested under `.meta.version`. So `self._meta` was always `{}` and
33
+ `.version` always returned `"unknown"`. Fixed by reading both locations
34
+ with top-level winning; `.meta.version` preserved as fallback for any
35
+ internal/dev atlas using the older schema. After fix:
36
+ `AtlasManager.version` returns `"2.0.0"` on the shipped atlas.
37
+
38
+ ### P2 — Atlas description claims: 1305 devices → 5264, drop "641 pack-indexed"
39
+
40
+ The "1305 devices" claim was false in every public surface (actual
41
+ shipped JSON has `stats.total_devices = 5264`). The "641 pack-indexed"
42
+ claim was meaningless — the shipped atlas has an empty `.packs` list;
43
+ device-side pack count via `.devices[*].pack` field is 50 across 13
44
+ unique pack names (not 641 or 655).
45
+
46
+ Updated 11 surfaces: `README.md` (5 hits), `AGENTS.md`, `CLAUDE.md`,
47
+ `package.json`, `server.json`, `manifest.json` (2 hits), `marketplace.json`,
48
+ `Codex-plugin/plugin.json`, `claude-plugin/plugin.json`,
49
+ `livepilot-core/SKILL.md`, `livepilot-core/references/overview.md` (2 hits).
50
+
51
+ "120 enriched" kept — defensible as the YAML enrichment file count
52
+ (120 files on disk). The `stats.enriched_devices` value (87) and the
53
+ `.enriched=true` flag count (135) are two other valid definitions; the
54
+ three-way divergence is a schema question for v1.22+ scope, not a
55
+ drift fix here.
56
+
57
+ ### P2 — Manual analyzer-tool count 32 → 38
58
+
59
+ `docs/manual/getting-started.md:256` and `livepilot/skills/livepilot-release/SKILL.md`
60
+ (3 lines) still said "32 spectral/analyzer tools" despite the actual
61
+ `@mcp.tool` count in `analyzer.py` being 38. Corrected.
62
+
63
+ ### P2 — `.maxpat` source label v1.17.5 → v1.21.2
64
+
65
+ `m4l_device/LivePilot_Analyzer.maxpat:1611` had a stale visible version
66
+ label `"text": "v1.17.5"`. The compiled `.amxd` had been binary-patched
67
+ to the correct version across releases, but the source patch Max uses
68
+ when someone opens/rebuilds the device never got updated. Fixed.
69
+
70
+ ### P3 — Manual reversibility language hedge
71
+
72
+ `docs/manual/index.md:31` said "All 430 tools... reversible with undo"
73
+ — the README was hedged in v1.21.1 but the manual missed. Corrected
74
+ with matching language: Live-session mutations route through Ableton's
75
+ undo stack; side effects outside the Live project (Splice downloads,
76
+ memory/ledger writes, installer actions, atlas scans, filesystem writes)
77
+ persist beyond undo.
78
+
79
+ ### `sync_metadata.py` expansion — converts this drift class into CI failures
80
+
81
+ Added to close the audit-memory gap that made v1.21.0, v1.21.1, and
82
+ now v1.21.2 each need a manual sweep:
83
+
84
+ - **`check_lockfile_version(version)`** — asserts `package-lock.json`
85
+ root `.version` matches `package.json`. Would have caught the
86
+ lockfile-stuck-at-1.17.5 drift at CI time from pre-v1.18 onwards.
87
+ - **`PROSE_CLAIM_FILES["semantic move"]`** — enforces every registered
88
+ "N semantic moves" claim matches `registry.count()` (currently 44).
89
+ Would have caught v1.21.0's "43 semantic moves" drift at CI time.
90
+ - **`PROSE_CLAIM_FILES["analyzer tool"]`** — enforces "N analyzer tools"
91
+ claims match `@mcp.tool` count in `analyzer.py` (currently 38).
92
+ Catches plain-form drift; "spectral/analyzer tools" slashed variants
93
+ bypass the current `check_prose_claim` regex's optional-word prefix
94
+ (noted in an inline comment — left for a future patch to broaden).
95
+
96
+ `sync_metadata --check` banner now reports 2 new counts:
97
+ `moves=44, analyzer_tools=38` alongside the previous 6.
98
+
99
+ ### Deferred to v1.22
100
+
101
+ - **`sync_metadata.py` atlas-stats check** — catching drift in
102
+ "5264 devices" or "87 enriched" claims requires either a noun-specific
103
+ regex (generic "device" is too broad — false-positives on every
104
+ historical "5 devices" mention) or a different check strategy.
105
+ Worth doing but nontrivial.
106
+ - **`check_prose_claim` regex broadening** to catch slashed compounds
107
+ like "spectral/analyzer tools". Current regex's `(?:[A-Za-z]+\s+)?`
108
+ prefix misses `spectral/` because the slash breaks the character class.
109
+ - **Atlas JSON schema decision** — whether `stats.enriched_devices`,
110
+ `.enriched=true` flag count, or YAML file count is THE canonical
111
+ "enriched" number. Picking one + renaming fields ends the three-way
112
+ ambiguity.
113
+
114
+ ### Credits
115
+
116
+ Second external audit by the repo owner, performed ~30 min after
117
+ v1.21.1 shipped. Same-day patch #2 ships ~2 hours after audit receipt
118
+ (matches the v1.20.1 / v1.21.1 same-day-hotfix precedent — this is the
119
+ third such same-day patch in 48 hours).
120
+
121
+ ### Scope stats
122
+
123
+ - 1 code fix (`mcp_server/atlas/__init__.py` — `AtlasManager` reads
124
+ top-level `.version`)
125
+ - 1 code expansion (`scripts/sync_metadata.py` — 3 new checks:
126
+ move_count, analyzer_tool_count, lockfile_version)
127
+ - 4 doc corrections (atlas claims across 11 files + analyzer count in
128
+ 4 files + reversibility hedge + .maxpat label)
129
+ - 15 version-string sites + `.amxd` binary patch (2 bytes) +
130
+ package-lock.json (2 fields) bumped 1.21.1 → 1.21.2
131
+ - Test suite: 3124 passed, 1 skipped (unchanged — this is a
132
+ no-regression patch; the AtlasManager fix makes a previously-
133
+ untested code path produce the right answer).
134
+
135
+ ---
136
+
137
+ ## 1.21.1 — Audit-response patch: experiment-commit safety + doc hygiene + lockfile (April 24 2026)
138
+
139
+ Small patch release responding to an external audit of v1.21.0 performed
140
+ the same day it shipped. The audit surfaced one real safety bug
141
+ (commit_experiment status allowlist was an exclusion list when the
142
+ intent was an inclusion list) plus several doc-consistency drifts.
143
+ No new features. No API changes beyond the tightened commit_experiment
144
+ contract. v1.21.0 callers already doing the right thing
145
+ (run_experiment → commit the ranked winner) continue to work unchanged.
146
+
147
+ ### P1 — commit_experiment only accepts `status='evaluated'`
148
+
149
+ Pre-fix (v1.21.0 and all prior versions with commit_experiment), the
150
+ status check was an EXCLUSION list:
151
+
152
+ ```python
153
+ if target.status in ("rejected", "analytical", "failed"):
154
+ return {"error": ...}
155
+ ```
156
+
157
+ Blocks 3 statuses; implicitly allows the other 6 — including `pending`,
158
+ `running`, `discarded`, and `interesting_but_failed`. Those branches
159
+ can't be ranked by `compare_experiments()`, but `commit_experiment`
160
+ would accept them as long as a compiled plan was attached. The code's
161
+ own inline comment already described the correct contract ("only
162
+ status='evaluated' branches are ranking candidates"); the implementation
163
+ had the wrong polarity. The fix flips it to an INCLUSION check:
164
+
165
+ ```python
166
+ if target.status != "evaluated":
167
+ return {"error": ...}
168
+ ```
169
+
170
+ Error message updated to enumerate all 9 possible statuses and explain
171
+ which state each represents (pending/running = not yet evaluated;
172
+ rejected/analytical/failed = classifier exclusions; committed =
173
+ already committed; discarded = explicitly thrown out;
174
+ interesting_but_failed = exploration-audit only).
175
+
176
+ Why this matters: v1.21.0's `commit_experiment` ledger writer records
177
+ every commit into `SessionLedger` where anti-repetition filters read
178
+ it. Without the tighter status check, a caller bypassing the ranking
179
+ layer could pollute the ledger with entries the system explicitly
180
+ classified as non-winners — degrading anti-repetition signal quality.
181
+
182
+ **Regression tests added (4)** in `tests/test_commit_experiment_ledger.py`:
183
+ `test_commit_on_pending_branch_rejects`,
184
+ `test_commit_on_running_branch_rejects`,
185
+ `test_commit_on_discarded_branch_rejects`,
186
+ `test_commit_on_interesting_but_failed_branch_rejects`. All FAILED
187
+ pre-fix (reproducing the audit's finding), all PASS post-fix.
188
+
189
+ ### P1 — package-lock.json bumped 1.17.5 → 1.21.1
190
+
191
+ Lockfile's root `.version` and `.packages[""].version` had been stale
192
+ at 1.17.5 since before v1.18. `npm publish` doesn't read these fields
193
+ (it reads package.json), so the npm registry was always correct — but
194
+ the repo-local lockfile misled local `npm install` workflows and any
195
+ release-check tooling that compared package.json vs lockfile. Fixed by
196
+ surgical replace on the 2 stale strings; no dependency tree
197
+ regeneration (keeps dep versions identical to v1.21.0).
198
+
199
+ ### P2 — Analyzer tool count: 32/33 → 38 (actual)
200
+
201
+ README.md previously said "32 spectral/analyzer tools" in one place
202
+ and "38 analyzer tools" in another — inconsistent within the same file.
203
+ `docs/M4L_BRIDGE.md` said "33 MCP tools in the analyzer domain" in two
204
+ places. Actual `@mcp.tool` count in `mcp_server/tools/analyzer.py` is
205
+ **38** (grep-verified). All stale 32/33 refs corrected to 38.
206
+
207
+ ### P2 — Reversibility language hedge
208
+
209
+ README's header NOTE block said "Everything is reversible with undo,"
210
+ which is too strong. Live-session mutations (clips, devices, mixer,
211
+ arrangement) do route through Ableton's undo stack and are reversible
212
+ — but Splice downloads, memory/ledger writes, installer actions, atlas
213
+ scans, and filesystem writes persist beyond undo. Hedged the language
214
+ to reflect this.
215
+
216
+ ### Deferred to v1.22
217
+
218
+ Audit-surfaced items that aren't patch-release material:
219
+
220
+ - **Atlas statistics reconciliation.** Docs claim "1305 devices / 120
221
+ enriched / 641 pack-indexed" across 9 description fields, but the
222
+ shipped `device_atlas.json` has 5264 devices and 135 entries with
223
+ `.enriched` truthy. The "1305" number appears to be a long-stale
224
+ cargo-culted count. Requires deciding whether `.devices` contains
225
+ duplicates, what the canonical "enriched" definition is, and
226
+ whether to restructure atlas JSON or fix the readers that look for
227
+ `.meta.version`. v1.22 scope.
228
+ - **`sync_metadata` expansion** to check package-lock.json project
229
+ version, semantic-move count via `registry.count()`, and
230
+ analyzer-tool count via grep on `analyzer.py`. Would convert this
231
+ entire class of drift into CI failures.
232
+ - **Dev-install path** for local contributors hitting missing
233
+ `soundfile` / `scipy` / `pretty_midi` / `pytest_asyncio` deps during
234
+ bare-python local runs. CI has these installed via requirements.txt.
235
+
236
+ ### Credits
237
+
238
+ External audit performed same day v1.21.0 shipped. Findings
239
+ file-linked and reproducible. Response time: ~2 hours from audit
240
+ receipt to v1.21.1 patch shipping.
241
+
242
+ ### Scope stats
243
+
244
+ - 1 code fix (`mcp_server/experiment/tools.py` — commit_experiment status check)
245
+ - 4 new regression tests (all initially FAILING pre-fix to reproduce
246
+ the audit, all PASSING post-fix)
247
+ - 3 doc corrections (README.md × 2, docs/M4L_BRIDGE.md × 2, plus the
248
+ reversibility hedge)
249
+ - 15 version-string sites + `.amxd` binary patch (2 bytes) +
250
+ package-lock.json (2 version fields)
251
+ - Test suite: 3120 → 3124 pass (+4). Zero regressions.
252
+
253
+ ---
254
+
3
255
  ## 1.21.0 — Consolidation: experiment ledger + preset library + record-readiness + reader audit (April 24 2026)
4
256
 
5
257
  Consolidation release closing five items from the v1.20 plan §12 non-goals
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 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 |
43
+ | **Device Atlas** | Knowledge of every device in Ableton's library — 5264 devices indexed 7 ways (by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack). 120 enriched with YAML 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
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 |
43
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 |
44
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** | 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 |
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
 
@@ -58,7 +60,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
58
60
  │ ────────────── ────────────── ────────────── │
59
61
  │ │
60
62
  │ Device Atlas 9-band FFT recall by mood, │
61
- 1305 devices RMS / peak genre, texture │
63
+ 5264 devices RMS / peak genre, texture │
62
64
  │ 120 enriched pitch tracking 29 techniques │
63
65
  │ 683 drum kits key detection replay into session │
64
66
  │ │
@@ -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). 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.
108
+ **Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 5264 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. 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
- **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.
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
 
@@ -233,7 +235,7 @@ WARP ─────────── get / add / move / remove markers
233
235
  The atlas is an in-memory indexed database of Ableton's entire device library.
234
236
 
235
237
  ```
236
- 1305 devices total
238
+ 5264 devices total
237
239
  120 enriched with sonic intelligence (mood, genre, texture, chains)
238
240
  47 with aesthetic-tagged signature_techniques
239
241
  683 drum kits mapped with note assignments
@@ -618,7 +620,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for architecture details, code guidelines
618
620
  |----------|---------------|
619
621
  | [Manual](docs/manual/index.md) | Complete reference: architecture, all 430 tools, workflows |
620
622
  | [Intelligence Layer](docs/manual/intelligence.md) | How the 12 engines connect — conductor, moves, preview, evaluation |
621
- | [Device Atlas](docs/manual/device-atlas.md) | 1305 devices indexed — search, suggest, chain building |
623
+ | [Device Atlas](docs/manual/device-atlas.md) | 5264 devices indexed — search, suggest, chain building |
622
624
  | [Samples & Slicing](docs/manual/samples.md) | 3-source search, fitness critics, slice workflows |
623
625
  | [Automation](docs/manual/automation.md) | 16 curve types, 15 recipes, spectral suggestions |
624
626
  | [Composition](docs/manual/composition.md) | Composer, section analysis, arrangement planning |
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.21.0";
37
+ var VERSION = "1.21.2";
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.21.0"
2
+ __version__ = "1.21.2"
@@ -18,7 +18,17 @@ class AtlasManager:
18
18
  with open(atlas_path, "r") as f:
19
19
  data = json.load(f)
20
20
 
21
- self._meta = data.get("meta", {})
21
+ # v1.21.2 audit-response #2: atlas JSONs ship with `.version` at
22
+ # top level (e.g. "2.0.0"), not under `.meta.version`. Pre-fix,
23
+ # `self._meta = data.get("meta", {})` always evaluated to {} on
24
+ # the shipped atlas because there's no `meta` key, which made
25
+ # `self.version` return "unknown". Read both locations and let
26
+ # the top-level win — falling back to `.meta.version` preserves
27
+ # backward-compat with any internal/dev atlas using the older
28
+ # schema.
29
+ self._meta = dict(data.get("meta", {}))
30
+ if "version" in data and "version" not in self._meta:
31
+ self._meta["version"] = data["version"]
22
32
  self._devices: List[Dict[str, Any]] = data.get("devices", [])
23
33
 
24
34
  # ── Build indexes ───────────────────────────────────────────
@@ -695,23 +695,47 @@ async def commit_experiment(
695
695
  if not experiment:
696
696
  return {"error": f"Experiment {experiment_id} not found"}
697
697
 
698
- # Refuse to commit branches the classifier rejected or that were
699
- # analytical-only. Those statuses exist specifically so callers
700
- # can't route them into re-application, and ranked_branches()
701
- # already excludes them so reaching commit with such a branch
702
- # means the caller is bypassing the ranking layer.
698
+ # v1.21.1 fix (external audit 2026-04-24): accept ONLY status='evaluated'.
699
+ # Pre-fix, the check was an exclusion list —
700
+ # `if target.status in ("rejected", "analytical", "failed"):` — which
701
+ # implicitly allowed 'pending', 'running', 'discarded', and
702
+ # 'interesting_but_failed' to commit even though
703
+ # compare_experiments() never ranks them. The code's inline comment
704
+ # below ("only status='evaluated' branches are ranking candidates")
705
+ # already described the correct contract; this fix flips the
706
+ # polarity so the implementation matches. See
707
+ # docs/plans/v1.21-impl-status.md Appendix C for the audit-response log.
708
+ #
709
+ # Status semantics (from ExperimentBranch lifecycle):
710
+ # pending — create_experiment landed; run_experiment hasn't touched it
711
+ # running — run_experiment is mid-flight on this branch
712
+ # evaluated — run_experiment finished; ranking candidate ✓
713
+ # rejected — hard-rule classifier rolled back (protect violation, etc.)
714
+ # analytical — no executable plan (seed was analytical_only)
715
+ # failed — zero steps applied successfully
716
+ # committed — already committed (re-commit is wrong)
717
+ # discarded — caller explicitly threw it out
718
+ # interesting_but_failed — exploration-mode audit trail; not ranked
703
719
  target = experiment.get_branch(branch_id)
704
720
  if target is None:
705
721
  return {"error": f"Branch {branch_id} not found"}
706
- if target.status in ("rejected", "analytical", "failed"):
722
+ if target.status != "evaluated":
707
723
  return {
708
724
  "error": (
709
- f"Cannot commit branch with status '{target.status}'. "
710
- f"'rejected' = hard-rule classifier rolled back; "
711
- f"'analytical' = no executable plan; "
712
- f"'failed' = zero steps applied successfully. "
713
- f"Use compare_experiments to see eligible winners "
714
- f"(only status='evaluated' branches are ranking candidates)."
725
+ f"Cannot commit branch with status '{target.status}' "
726
+ f"only status='evaluated' branches are commit candidates. "
727
+ f"Reason depends on current status: "
728
+ f"'pending' / 'running' = run_experiment hasn't evaluated "
729
+ f"this branch yet (run it first); "
730
+ f"'rejected' = hard-rule classifier rolled it back; "
731
+ f"'analytical' = no executable plan (analytical_only seed); "
732
+ f"'failed' = zero steps applied successfully during run; "
733
+ f"'committed' = already committed (don't re-run); "
734
+ f"'discarded' = caller explicitly threw this branch out; "
735
+ f"'interesting_but_failed' = kept for audit in "
736
+ f"exploration mode, but classifier excluded from ranking. "
737
+ f"Use compare_experiments to see eligible (ranked) "
738
+ f"winners — they are always status='evaluated'."
715
739
  ),
716
740
  "branch_id": branch_id,
717
741
  "branch_status": target.status,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "livepilot",
3
- "version": "1.21.0",
3
+ "version": "1.21.2",
4
4
  "mcpName": "io.github.dreamrec/livepilot",
5
- "description": "Agentic production system for Ableton Live 12 — 430 tools, 53 domains, 43 semantic moves. Device atlas (1305 devices, 120 enriched, 7 indexes), Splice intelligence (gRPC + GraphQL describe-a-sound + preview + collections + presets), 9-band spectral perception auto-loaded via ensure_analyzer_on_master, Creative Director skill, technique memory, 12 creative intelligence engines",
5
+ "description": "Agentic production system for Ableton Live 12 — 430 tools, 53 domains, 44 semantic moves. Device atlas (5264 devices, 120 enriched, 7 indexes), Splice intelligence (gRPC + GraphQL describe-a-sound + preview + collections + presets), 9-band spectral perception auto-loaded via ensure_analyzer_on_master, Creative Director skill, technique memory, 12 creative intelligence engines",
6
6
  "author": "Pilot Studio",
7
7
  "license": "BSL-1.1",
8
8
  "type": "commonjs",
@@ -5,7 +5,7 @@ Entry point for the ControlSurface. Ableton calls create_instance(c_instance)
5
5
  when this script is selected in Preferences > Link, Tempo & MIDI.
6
6
  """
7
7
 
8
- __version__ = "1.21.0"
8
+ __version__ = "1.21.2"
9
9
 
10
10
  from _Framework.ControlSurface import ControlSurface
11
11
  from . import router
package/server.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.dreamrec/livepilot",
4
- "description": "430-tool agentic MCP production system for Ableton Live 12 — 53 domains, 43 semantic moves, device atlas (1305 devices), Splice intelligence (gRPC + GraphQL), 9-band spectral perception auto-loaded, Creative Director skill, technique memory, 12 creative engines",
4
+ "description": "430-tool agentic MCP production system for Ableton Live 12 — 53 domains, 44 semantic moves, device atlas (5264 devices), Splice intelligence (gRPC + GraphQL), 9-band spectral perception auto-loaded, Creative Director skill, technique memory, 12 creative engines",
5
5
  "repository": {
6
6
  "url": "https://github.com/dreamrec/LivePilot",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.21.0",
9
+ "version": "1.21.2",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "livepilot",
14
- "version": "1.21.0",
14
+ "version": "1.21.2",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }