Skip to content

Migration Guide

This page is for scripts: anything calling Producer Pal through MCP, the REST API, or an agent skill. If you only chat with Producer Pal, there is nothing here for you: the assistant reads the current tool descriptions on every conversation and writes calls in the current spelling.

There are two migrations here and they are not equally urgent:

WhatWhenUrgency
Response fields movedalready shipped, in 2.3Do this now. No field kept a back-compat key.
Input params removed2.4Forward notice. Everything still works, and warns, until then.

Most upgrade guides lead with the deprecations. This one leads with the responses, because that is the half that breaks the moment you install 2.3.

Responses changed in 2.3

Fields were renamed, removed, and reshaped across most tools, and not one kept a back-compat key. A script reading a removed field sees undefined rather than an error, so these fail quietly.

The theme is that a result now says where its object is, once, in a path you can pass straight back into the next call, instead of scattering trackIndex, sceneIndex, deviceIndex and arrangementStart across the response.

ToolChangeWhat to do
ppal-read-live-setmasterTrackmainTrackrename the key you read
read-live-set, read-track, selecttype is gone from return tracks and the main trackread path (rt0, mt); type is now only midi/audio, on regular tracks
create-track, read-track, read-live-set, selecttrackIndex, returnTrackIndex removedparse path (t3, rt0)
create-scene, read-scene, read-live-setsceneIndex removedparse path (s2)
create-devicedeviceIndex removedparse path (t1/d2)
every clip resultarrangementStart removedthe clip's path carries it: t0[5|1]
ppal-deletea successful delete reports deletedPath, not pathbranch on deleted: read deletedPath when true, path when false
ppal-playbackcurrentTime removedit was never the playhead; read startTime for where the next play begins
ppal-playbackarrangementLoop: {start, end}loop/loopStart/loopEndread the three flat fields
ppal-playbacksceneIndex, sceneNamescene: {id, path, name}read scene.name; scene.path is an address you can spend
ppal-duplicatea buried copy has no id{path, overwritten: true} marks a copy this call destroyed
ppal-duplicatename removed from scene→arrangement clip entriesit only echoed your own argument; entries are {id, path}
update-device, create-devicea device inside a drum pad reports p<pitch>/c<n>, not c<n>don't rebuild rack-relative chain paths from a write result

type is the subtle one. It used to answer two questions (which signal a track carries, and what role it plays) and now answers only the first. A script branching on type === "return" or type === "master" gets undefined.

Fields that became conditional

ppal-playback's loop, loopStart and loopEnd come back only when your call didn't name them. Don't read them unconditionally.

The device tools' params can be longer than the number of params you wrote: a name that reached nothing comes back as {name, reason} with no value. Key off value, not the entry's presence.

ppal-delete can return an array where it used to return a single object, since failures are now included instead of dropped. Its results are also in request order now, not internal deletion order, so you can pair results to targets by index.

Three values read differently without the field changing

  • An all-digit name is a string. A track named 5678 used to serialize as the JSON number 5678. Strict type checks will notice.
  • Gain is rounded to 0.01 dB. A gain of -6.333333 used to read back as -6.333000183105469. Exact comparisons need the same rounding.
  • update-live-set's scale is the spelling Live stores, not yours: "F# Dorian" in, "Gb Dorian" out. Every read already said this; the write result was the one that disagreed.

Error and warning text

Error executing tool 'ppal-update-clip': <reason> is now Error: <reason>, and no warning carries a tool-name prefix any more. Anything matching on that text needs updating.

Params being removed in 2.4

Every param below still works today and emits a deprecation warning saying what to use instead. They are removed in 2.4.

A param that warns is not always one of these. ppal-read-clip takes trackIndex and sceneIndex as aliases: names a model reaches for on its own, folded onto path. They warn too, and they are staying.

They all say the same thing: an object is named by one path, counting from 0, instead of by a scattering of index params. t2 is the third track, rt0 the first return, mt the main track, s1 the second scene, t2/s1 a clip slot, t2[5|1] a spot on an arrangement, t2/l0 a take lane.

OldNew
trackIndex + trackTypepath: t2, rt0, mt
trackIndex: -1 on create-trackpath: "t+" (append)
sceneIndexpath: "s2"
slot: "1/0", slots, toSlotpath / toPath: t1/s0
arrangementStart: "5|1"fused onto the path: t1[5|1]
takeLane: "1"/l0 on the path
locator: "Chorus" on duplicatetoPath: "[loc:Chorus]"
startLocator, loopStartLocator, loopEndLocatorstartTime / loopStart / loopEnd: loc:Chorus
devicePath on selectpath
inputRoutingTypeId and the other three *Iddrop the Id suffix

The last row is a plain rename: the surviving param already accepts a name or an id. Most of the rest are mechanical. Two are not, and a find-and-replace on them writes a call that quietly does the wrong thing.

takeLane counts from 1; l<n> counts from 0

OldNew
takeLane: 1t0/l0, the first take lane
takeLane: 2t0/l1
takeLane: 0t0, the main lane (no take lane)

So it is off by one everywhere, and at zero it isn't a take lane at all. A take lane in a path also always needs its track: t1/l0[5|1] works, l0[5|1] is refused.

takeLane: "new" is gone: name the lane by index instead, and lanes up to it are created as needed. Read a track's takeLanes first and use the next free index, since a track holds 8 lanes and none can be deleted. Appending a lane without knowing how many a track has isn't possible for now; it will come back as a track-tool feature.

js
// before: two clips, one new lane
{ trackIndex: 1, arrangementStart: "21|1,25|1", takeLane: "new" }
// after
{ path: "t1/l0[21|1],t1/l0[25|1]" }

takeLaneName is deprecated

It still works on ppal-create-clip and ppal-duplicate, with a warning, and will be removed. Name take lanes in Live.

arrangementStart becomes a coordinate, not a param

It stops being its own param and becomes a […] coordinate on the destination path, so it has to be paired with a track rather than renamed in place. The bar|beat value itself doesn't change.

js
// before
{ trackIndex: 1, arrangementStart: "33|1,37|1" }
// after: one destination track broadcasts across every position
{ path: "t1[33|1],t1[37|1]" }

On ppal-update-clip and ppal-duplicate there was never a destination-track param: the clip stayed on its own track. A path spells that as a bare coordinate.

js
// before
{ path: "t1[41|1],t1[45|1]", arrangementStart: "49|1,53|1" }
// after
{ path: "t1[41|1],t1[45|1]", toPath: "[49|1],[53|1]" }

split is not a rename either

ppal-update-clip has both split (deprecated) and arrangementSplit (the survivor), and they read positions in different coordinate systems:

  • split positions are offsets from each clip's own start.
  • arrangementSplit positions are on the song timeline.

The same value cuts somewhere else. On a clip starting at bar 13, split: "2|1" cuts at song bar 14; arrangementSplit: "2|1" on that clip matches nothing at all and warns that it cut nothing.

Converting means adding each clip's arrangement start to each offset, which means reading the clips first. There is no offline rewrite, which is why the adapter script below leaves split alone and tells you so.

Three values, not params, are also retiring

These don't show up as a param rename because it's the value that retires:

OldNewDeprecated since
ppal-create-track type: "return"path: "rt+"2.2
ppal-library action: "searchBatch"action: "search" with a searches list2.3
a device path in front of params[].nameaddress the device by path, send the bare name2.3

The params[].name prefix

On ppal-create-device and ppal-update-device, a param name could carry a device path in front of it. The device gets addressed by path instead:

js
// before
{ path: "t5/d0", params: [{ name: "pC1/c0/d0/Volume", value: "-6" }] }
// after
{ path: "t5/d0/pC1/c0/d0", params: [{ name: "Volume", value: "-6" }] }

Two things to know before you rewrite these:

  • A real param name containing a slash needs no change. Dry/Wet on a Reverb is matched as a name first, and only falls back to path-routing when the device has no such param.
  • params applies to every path in a call. So where the prefixed form let one call set a different value on each pad, the replacement needs one call per distinct value. Setting the same value across pads still works in one call: path: "t5/d0/pD1/d0,t5/d0/pE1/d0".

The drum-pad sample shortcut ({name: "pC1/sample"}) is not deprecated: it creates the pad's Simpler as well as addressing it, so it isn't just a spelling for a path.

The adapter script

examples/migration/ has a zero-dependency adapter in both Node and Python that rewrites the mechanical cases for you:

bash
node ppal-migrate.mjs ppal-read-track '{"trackIndex": 2}'
# {"path": "t2"}

python ppal_migrate.py ppal-create-clip '{"trackIndex":1,"arrangementStart":"33|1,37|1"}'
# {"path": "t1[33|1],t1[37|1]"}

As a library it returns the rewritten args plus notes, anything it could not translate on its own:

js
import { migrateArgs } from "./ppal-migrate.mjs";

const { args, notes } = migrateArgs("ppal-update-clip", {
  path: "t1[9|1]",
  split: "2|1",
});
// args  -> unchanged
// notes -> ['split "2|1" left as-is: its positions are offsets from each
//           clip's start, and arrangementSplit reads the song timeline…']

Always check notes. An empty list means the call migrated cleanly. Anything in it names a param the adapter left exactly as it was, because answering needed a Live read or a judgement call: split, params[].name prefixes, searchBatch, takeLane on a duplicate whose source is addressed by id, a track and a scene selected on a return track, and any value the tool itself refuses. A half-migrated call is worse than an untouched one.

It also exports buildPath and parsePath if you'd rather assemble paths from parts than build strings by hand.

Finding what you're still sending

Every deprecated param warns when you send it, and the warning says what to use instead:

WARNING: param "takeLane" is deprecated and will be removed; use "path" instead.
Lanes count from 0 in a path: takeLane 1 is "l0", and takeLane 0 is the main
lane.

Over the REST API these arrive in a warnings array beside the result; over MCP they're appended to the tool result. Run your existing scripts against 2.3, log every warning, and you have the list of calls to fix, with no auditing by hand.

Released under the GPL-3.0 License.