Promote stock-rnbo-runner to main (Phase 3) #12

Merged
peterswimm merged 32 commits from chore/promote-stock-runner into main 2026-10-04 22:32:01 +00:00
Owner

Scope

Phase 3 of the OPSL alignment: make the stock-runner line the canonical Oplsdt main. This branch is peterswimm/oplsdt-stock-runner stock-rnbo-runner @ cf5ebab (31 commits, including the OPSL 0.3 vendoring from that repo's PR #1) plus a merge of this repo's main (the org contributor-review policy, #11).

The movemap-public checkout (branch chore/ignore-spel, 1c3b83f) holds nothing unique: its only commit adds .spel/ to .gitignore, which stock-rnbo-runner already has. It will be archived after this merges.

Merge conflict resolved: CONTRIBUTING.md existed on both sides. Kept Oplsdt's own conventions and appended the managed ORG-POLICY block after them (the block says it supplements the repo's conventions). The PR template and pr-policy-check.yml came in unchanged.

Testing

  • Merge is clean apart from CONTRIBUTING.md; the ORG-POLICY block appears exactly once.
  • The stock-rnbo-runner content was tested in peterswimm/oplsdt-stock-runner PR #1: cargo test 35/35, npm test 52/52, oplsdt-compile validate, build --check, lint rnbo. CI state of those lanes is being investigated separately (codebox has a long-standing failure).

Impact Statement

  • Existing features: main gains the full stock-runner pipeline (compiler, RNBO patchers, Launchpad/M8 support, codebox harness).
  • Public APIs / contracts: movemap.* schemas now come from vendored OPSL 0.3 (vendor/opsl); Oplsdt-only schemas use toilville.dev/schemas/oplsdt/ ids.
  • Schemas: as above; schema_version 0.3 with 0.2 accepted.
  • Data / artifacts: generated rnbo/patchers/ committed per the build-drift rule.
  • Integrations: CI workflows from stock-rnbo-runner plus the org PR policy check.
  • Mappings: Move/M8/Launchpad maps as on stock-rnbo-runner.
  • Backward compatibility: the old movemap-public layout on main is superseded; nothing depended on main's 4-commit state beyond the docs.

Docs

ARCHITECTURE.md, CONTRIBUTING.md (merged), README from stock-rnbo-runner.

## Scope Phase 3 of the OPSL alignment: make the stock-runner line the canonical Oplsdt `main`. This branch is `peterswimm/oplsdt-stock-runner` `stock-rnbo-runner` @ cf5ebab (31 commits, including the OPSL 0.3 vendoring from that repo's PR #1) plus a merge of this repo's `main` (the org contributor-review policy, #11). The movemap-public checkout (branch `chore/ignore-spel`, 1c3b83f) holds nothing unique: its only commit adds `.spel/` to `.gitignore`, which stock-rnbo-runner already has. It will be archived after this merges. Merge conflict resolved: `CONTRIBUTING.md` existed on both sides. Kept Oplsdt's own conventions and appended the managed ORG-POLICY block after them (the block says it supplements the repo's conventions). The PR template and `pr-policy-check.yml` came in unchanged. ## Testing - Merge is clean apart from `CONTRIBUTING.md`; the ORG-POLICY block appears exactly once. - The stock-rnbo-runner content was tested in peterswimm/oplsdt-stock-runner PR #1: `cargo test` 35/35, `npm test` 52/52, `oplsdt-compile validate`, `build --check`, `lint rnbo`. CI state of those lanes is being investigated separately (`codebox` has a long-standing failure). ## Impact Statement - Existing features: main gains the full stock-runner pipeline (compiler, RNBO patchers, Launchpad/M8 support, codebox harness). - Public APIs / contracts: `movemap.*` schemas now come from vendored OPSL 0.3 (`vendor/opsl`); Oplsdt-only schemas use `toilville.dev/schemas/oplsdt/` ids. - Schemas: as above; schema_version 0.3 with 0.2 accepted. - Data / artifacts: generated `rnbo/patchers/` committed per the build-drift rule. - Integrations: CI workflows from stock-rnbo-runner plus the org PR policy check. - Mappings: Move/M8/Launchpad maps as on stock-rnbo-runner. - Backward compatibility: the old movemap-public layout on main is superseded; nothing depended on main's 4-commit state beyond the docs. ## Docs ARCHITECTURE.md, CONTRIBUTING.md (merged), README from stock-rnbo-runner.
Surface profiles bind layout slots to physical (or emulated) controls. Adds the
Ableton Move profile (addresses from Cycling '74's input/led drivers), the
Launchpad Pro MK3 programmer-mode profile presented to a Dirtywave M8, the
Move<->LPP control map, an approximate LPP palette (unverified entries flagged),
a synthetic protocol fixture, and pinned OPSL device copies for hermetic builds.
Resolves a bundle + layouts + OPSL devices through a surface profile into flat
tables (sections, pages, encoder/step slots, CC/NRPN bindings, 128-step
transform LUTs including compile-time formula evaluation), assembles codebox
sources, and writes .rnbopat cores, rnbo~ wrapper .maxpats and the .maxproj.
validate checks every example against its JSON Schema; build --check fails on
generated-file drift.
Four patchers for an RNBO Move Takeover graph: oplsdt-surface (edit view),
oplsdt-target (CC/NRPN out with throttling and layout-hash muting),
oplsdt-lpp-move and oplsdt-lpp-m8 (Launchpad Pro MK3 programmer mode for a
Dirtywave M8 over USB-A or USB-C). Codebox logic lives in rnbo/code; generated
patchers are committed. A node harness runs the generated codebox code with 30
behaviour tests. Vendors Cycling '74's MIT input/led drivers unmodified.
README leads with the stock-Move path; adds docs/stock-runner.md,
docs/m8-launchpad.md, docs/verification.md (gates G0-G5), RELEASING.md,
CONTRIBUTING.md and Forgejo CI. The rooted QuickJS runner is marked experimental.
rnbo.move.control filters encoder MIDI (CC 71-78, touch notes 0-7) away from the
graph while a user view shows a param overlay, and a display buffer with
paramview meta gets one by default. Drop the paramview meta, send
/rnboctl/show/userview/display [view, -2] on activation, and give the surface
and Launchpad patchers distinct user views (0 and 1) so they don't layer.
- led-driver holds its own midiout/sysexout and has no outlets; stop drawing
  connections from outlets it doesn't have (Max reports them on load)
- tag every RNBO box with rnbo_classname/rnbo_serial/rnbo_uniqueid like Max does
- appversion 9.2.0; move_view_index gets @order first (upstream convention)
- test: every patchline is in range, and every `p @file X` box declares the
  inlets/outlets X.rnbopat really has
RNBO 1.4.5 rejects `return` in inlet functions and in the top-level body
("You cannot return from an inlet function"), which failed every core
codebox and then dropped all of its patchcords. Move each inlet handler and
the main body into an on_* helper that the inlet calls. Test the rule on
the assembled code.
oplsdt-compile lint checks .maxpat/.rnbopat/.maxproj/.codebox without Max:
patchcord ranges, p @file abstraction inlet/outlet counts, codebox io counts,
return inside inlet functions, brace balance, and the .maxproj file list.
It flags both problems Max reported on 8b6a757. The top-level codebox body may
return (Cycling '74's drivers do); only inlet functions may not.

New max-lint CI job; .githooks/pre-commit runs lint, build --check and the
codebox tests.
`let ev = v;` (copying a list parameter into a local) passes RNBO's
checks but generates C++ that doesn't compile. Bisected in Max 9.2.0 /
RNBO 1.4.5: the target core compiles once on_listin1 reads its
parameter directly. All four cores now compile in Max.

- Read the list parameter directly in each on_listin1.
- Bit maths uses arithmetic instead of Int locals, which gave
  "Type and Value mismatch".
- Lint: flag `let x = l;` where `l` is a list parameter.
- Harness: add pow.
- Ignore rnbo/logs/.
Read from the Move's OSCQuery server (runner 1.4.5-9):
- Move In MIDI is move-control:midi_out, not rnbo-control:midi_out.
- External In/Out MIDI confirmed as the USB-A ports.
- No USB-C MIDI port in the graph, so Path B is blocked (gate G1).
- lpp-m8: announce the Launchpad identity reply once a second until the M8
  sends Novation SysEx (as vlpp_move does); SysEx now arrives on sysexin and
  leaves on sysexout, since midiin/midiout don't carry it. Identity family
  bytes are zero, matching vlpp_move.
- lpp-m8 and target: RNBO's midiout reads a list as [byte, port], so
  listout1 = [status, d1, d2] sent only the status byte, to port d1. Both
  cores now send voice messages byte by byte. Confirmed on the Move: notes
  from lpp-m8 reach the M8 over USB-A.
- Harness: takeMidi() groups byte output into messages; tests updated.
- CONTRIBUTING: the midiout rule.
The Move's JACK driver drops outgoing USB-MIDI packets whose three bytes are
all zero, and incoming closing packets that carry three bytes (CIN 7). The
identity reply had eight zero bytes, so the M8 never saw it, and sysexin
never saw the M8's inquiry or programmer-mode message.

- Identity reply family/version are non-zero; the compiler rejects an
  identity reply containing an all-zero packet.
- lpp-m8 recognises the inquiry (F0 7E 7F) and Novation SysEx (F0 00 20 29)
  from their first bytes on midiin, and assumes programmer mode on first link.
- raw_sysex inport for probing.
- tools/move-probe: JACK probe for the Move, CoreMIDI M8 probe for a Mac.
- docs: user guide, driver bug write-up for Cycling '74, G0/G2/G5 results.

Verified 2026-10-03 with an M8 (6.6.2) on USB-A: one inquiry, linked in
17 ms, LEDs on the pads, presses reach the M8.
Edit view (oplsdt-surface), following the Move manual's paradigms:
- Jog wheel browses instruments (switches while turning, like Autoload);
  click shows the current one. The user view takes the jog wheel
  ([0, -2, 0, 1, 0]); Back stays with the Takeover. ctlin 14/3 feed it,
  since the input driver doesn't decode the jog wheel.
- Plus/Minus shift the pad octave, clamped to 0..127; held pads keep their note.
- Undo reverts the last param edit (again = redo); Delete + encoder resets
  the param to its default.
- Section step LEDs: white = selected, entry colour = available.

Both views show a boxed action message for ~1.5 s (lib-display toast):
PAGE n/m SECTION, the instrument, OCTAVE +1, UNDO/RESET <param>, and in the
Launchpad view the Launchpad control each press sent (PAD r,c, TOP n,
RIGHT n, LEFT n, BOTTOM n, LOWEST n, LAUNCHPAD SHIFT), ROWS 1-4/5-8 and
M8 LINKED. The M8 side and the bytes it receives are unchanged.

Move profile: CC 58 is Loop (was "repeat"), jog wheel and click added,
track buttons noted as reversed.
drawtoast called drawstr(4, 54, S_PAGE, 4). drawstr takes a string-table
index, and S_PAGE is a list, so RNBO's generated C++ failed to compile
(the node harness tolerated it). Use drawtext.

lint_code now flags any call passing a list @state where the callee
declares a number parameter.
The four track buttons (left of the pads) are page buttons in both views:
- Edit view: page 1-4 of the current section, with the PAGE n/m message.
- Launchpad view: knob page 1-4 from lpp map knob_pages (FADERS CC 21-28,
  DAW CC 102-109, pages 3-4 empty).

In the Launchpad view the encoders send their page's CCs on channel
knob_channel_base + track (channels 2-9). Steps 1-8 still send TOP 1-8 and
also pick the knob track. Each track keeps its own values; Shift is fine.
oplsdt-lpp-m8 sends them as press kind 3 [cc, value, 3, channel], so no
new port. Steps 9-16 no longer send the Launchpad right column; they're
reserved for user knob banks.

Don't load move-view-controller alongside: it uses the same buttons.

The harness now ignores a trailing comment on a declaration line.
Steps 1-8 select M8 tracks (Launchpad track select), steps 9-16 select the
project's MIDI instrument slots, which the pads play on the slot's channel.
Session/Note flips NOTE/SEQUENCER, the buttons left of the pads launch scenes
(Shift: knob pages), Shift + Plus/Minus switch grid halves. Jog, jog click,
Play, Shift and Mute+steps press M8 remote keys on the control map channel.
Knob CCs move to the single CONTROL MAP CHANNEL with per-target CC blocks;
projects/*.json configure slots and knobs per M8 song (param project).

lpp-m8 gains press kinds 4-6 (note, poly aftertouch, program change) and a
button_message switch (CC or note).
Pages NAV (track select, modes), PERFORM (scene launch 1-8, ARM..STOP CLIP),
SLOTS (MIDI instrument slots the pads play) and KNOBS (knob targets only);
Shift + left buttons pick the knob page. Move Shift is now the Launchpad's
SHIFT so the M8's Shift combos (instrument scroll, snapshot, beat repeat,
song/live) work as written. Function buttons follow the Launchpad sticker
map (Capture SEQUENCER, Loop SOLO, Mute MUTE, Undo ARM, Play PLAY). The jog
wheel switches grid halves (Shift: M8 cursor); Session/Note alternates
SESSION and NOTE. QUANTISE and FIXED LENGTH move to NAV steps 9-10.
- schemas/oplsdt.lpp-map.schema.json and oplsdt.lpp-project.schema.json;
  oplsdt-compile validate now also checks data/ and projects/, with tests
  that bad actions, a missing page and a slot on channel 1 are rejected
- docs/move-control-map.html: interactive map of every Move control per
  page and Shift layer, with the Move's MIDI numbers
- m8-launchpad.md: comparison with vlpp_move and schwung-m8
- user guide and RELEASING: blank display when /active_view is set before
  the Takeover registers the view
- CONTRIBUTING: what to update when the map or project format changes;
  drawtext vs drawstr codebox rule
The Takeover reads a show request's first number as the view's position
among loaded views, not its view meta, so lpp-move's fixed 1 only worked
with oplsdt-surface loaded; otherwise the display froze on its first frame.
param takeover_position sets it. Nothing sends /active_view without
move-view-controller, so param start_active (default on) shows the view
~3 s after loading and resends until the Takeover reads the display.

RELEASING: build the graph as a saved set, unload before reloading a slot,
check the set's wiring after a fresh load; drop move-view-controller.
Pull device.schema.json and the newly-published movemap.{bundle,layout,
session}.schema.json from Toilville/opsl tag v0.3.0 (commit 730919c), plus
the device/layout/session/bundle examples for Access Virus, Ensoniq SQ-80
and the o1nk devices. schema_version accepts 0.2 and 0.3 and top-level `x-`
fields are allowed, so nothing downstream needs to change yet.
Point oplsdt-compile schema_for() and the every_example_validates test list
at vendor/opsl/schemas/movemap.{layout,bundle,session}.schema.json now that
OPSL publishes them, and delete the local copies in schemas/ they replace.
The three schemas that stay local (movemap.surface, oplsdt.lpp-map,
oplsdt.lpp-project) move their $id under
https://toilville.dev/schemas/oplsdt/ so they no longer share a namespace
with the OPSL-owned ones.
vendored_schemas_match_recorded_hashes hashes every file under
vendor/opsl/schemas with a plain FNV-1a and compares it against hashes
recorded in the test, so hand-editing a vendored schema fails `cargo test`
instead of silently drifting from the pinned OPSL release. Verified the
guard actually fails: appended a byte to vendor/opsl/schemas/device.schema.json,
reran the test (failed with a hash mismatch naming the file), then reverted.
README.md repository-layout table and the device/movemap paragraph now
describe schemas/ as Oplsdt-only and vendor/opsl/ as owning the OPSL
device and movemap.* schemas, matching the previous two commits.
Reviewed-on: peterswimm/oplsdt-stock-runner#1
Merge main (org contributor-review policy) into stock-rnbo-runner
Some checks failed
PR policy check / policy-gate (pull_request) Successful in 0s
ci / codebox (pull_request) Failing after 10s
ci / max-lint (pull_request) Successful in 22s
ci / compiler (pull_request) Successful in 29s
a1df6ed24d
CONTRIBUTING.md: keep Oplsdt's own conventions and append the managed
ORG-POLICY block after them, as the block's own text says it supplements
the repo's conventions.
peterswimm deleted branch chore/promote-stock-runner 2026-10-04 22:32:01 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Toilville/oplsdt!12
No description provided.