feat(opsl): accept OPSL 0.3 artifacts alongside 0.2 #3

Merged
peterswimm merged 2 commits from feat/opsl-0.3 into main 2026-10-04 21:41:10 +00:00
Owner

Scope

Phase 4a of OPSL alignment: bring Pedalboard3's OPSL reader/exporter up to
spec with OPSL 0.3.0 (vendored from opsl-public@730919c, tag v0.3.0),
while continuing to accept existing 0.2 artifacts.

  • Re-vendored all four schemas under docs/opsl/schemas/ (device,
    movemap.layout, movemap.bundle, and the new movemap.session) byte-exact
    from v0.3.0, with a VERSION marker file (v0.3.0 730919c).
  • OpslReader now accepts schema_version "0.2"/"0.2.x" and
    "0.3"/"0.3.x" (previously "0.2" exact-match only); anything else
    still hits the existing hard-error path.
  • OpslDeviceExporter now stamps exported documents with
    schema_version: "0.3".
  • No reader code changes were needed to tolerate the 0.3 relaxations
    (optional canonical_id/transforms/param tags, device roles
    capture/derived, top-level x- extension fields) beyond the version
    gate itself — the existing non-throwing, forward-compatible parse policy
    already handles them.
  • Deliberately did not widen the OpslAdapter::Protocol,
    OpslBinding::AddressType, OpslAdapter::Direction,
    OpslSlotBinding::Role/Action, or OpslTransformSpec::Type enums.
    Nothing in the codebase branches on these beyond "is it unknown", so
    there's no functional benefit, and a near-miss while investigating
    OpslTransformSpec::Type showed widening it would make behavior
    worse (an unimplemented type would silently fall back to a linear
    transform instead of explicitly warning). New 0.3 vocabulary (e.g. the
    sysex_bulk adapter protocol used by real hardware devices) still
    decodes to unknown with an explicit warning, by design.

Testing

Built and ran via the documented CLAUDE.md workflow:

cmake --preset release
cmake --build --preset release --target Pedalboard3_Tests
./build/release/tests/Pedalboard3_Tests "[opsl]" --order rand

Result: 72 OPSL test cases, 1775 assertions, all passing.

Also ran the full suite to check for collateral damage:

./build/release/tests/Pedalboard3_Tests --order rand

Result: 390/391 test cases pass. The one failure
("Push 2 display transport emits framed RGB565 payloads with row padding
and xor") is pre-existing and unrelated — confirmed via
git log/git diff that this branch touches none of
tests/push2_surface_test.cpp, src/Push2DisplayTransport.{h,cpp}, and
that file was last changed by an unrelated commit on main.

CTest is also wired up (catch_discover_tests) and confirms the same
OPSL coverage independently:

ctest --preset release --output-on-failure -R OPSL

(note: the filter is case-sensitive and test names are OPSL ...,
uppercase — -R opsl matches nothing). Result: 62/62 CTest-registered
OPSL tests pass.

New fixtures added under tests/fixtures/opsl/:

  • legacy-0.2-o1nk-device.json — 0.2 back-compat (byte-exact from
    opsl-public).
  • access-virus.pad.device.json — a real 0.3 hardware device (role: "patch", kind: "hardware") exercising manufacturer_id,
    bulk_state, lineage, and the as-yet-unmodeled sysex_bulk protocol
    (byte-exact from opsl-public).
  • device-with-extension.json — same device plus a top-level x-tool
    extension field, to assert extensions are inert (byte-exact from
    opsl-public).

The four existing shared o1nk/toilville fixtures had only their
schema_version bumped in place to "0.3", rather than being replaced
wholesale with upstream's versions: upstream's v0.3.0 examples fix two
corpus quirks (a dangling select_page target and a fader role) that
existing OpslReaderTest/OpslControlSurfaceMapTest cases specifically
assert as regression coverage for malformed-input handling. JSON Schema
doesn't enforce those cross-artifact invariants, so the fixtures remain
fully schema-conformant under 0.3 with just the version bump.

New OpslReaderTest cases cover: the version gate's accept/reject
boundaries (0.2, 0.2.x, 0.3, 0.3.x accepted; 0.1/0.4/garbage rejected), a
0.2 document still loading, the hardware device loading with exactly the
one expected sysex_bulk warning, the x- field being ignored (no
additional warnings/param changes vs. the same fixture without it), a
document omitting canonical_id/transforms/tags loading cleanly, and
devices with capture/derived roles loading.

This branch also carries a one-commit prerequisite fix
(cmake/CPM.cmake being force-included despite the repo's *.cmake
.gitignore glob) that three sibling branches — including the in-flight
codex/integrate-pending-20260920 — had each independently rediscovered
and fixed identically; without it, cmake --preset release cannot
configure from a fresh clone/worktree at all.

Impact Statement

  • Existing features: no behavior change for 0.2 artifacts; all
    existing OPSL reader/exporter/catalog/control-surface tests continue to
    pass unmodified except for 3 assertions that intentionally now expect
    "0.3" instead of "0.2" as the exporter's emitted schema version.
  • Public APIs / contracts: OpslReader's public surface is
    unchanged; kSupportedSchemaVersion is replaced by
    kCurrentSchemaVersion ("0.3") and kMinimumSupportedSchemaVersion
    ("0.2"). Any external caller referencing the old constant name will
    need to update.
  • Schemas: docs/opsl/schemas/*.schema.json updated to v0.3.0
    verbatim; movemap.session.schema.json added (not yet consumed by any
    C++ reader — parsing that artifact kind is out of scope for this PR).
  • Data / artifacts: exported device artifacts now declare
    schema_version: "0.3". This is self-consistent (the exporter's actual
    output already satisfied the stricter parts of the 0.2 schema and
    continues to satisfy 0.3), but any downstream consumer that
    exact-matches on "0.2" should be checked.
  • Integrations: none changed; the exporter's OSC-protocol
    non-conformance relative to the formal schema is pre-existing and
    unchanged by this PR (documented in docs/opsl/README.md).
  • Mappings: no control-surface mapping behavior changed; the two
    corpus-quirk fixtures used in mapping regression tests were
    deliberately kept as-is (only re-versioned), not replaced.
  • Backward compatibility: preserved. 0.2 and 0.2.x documents continue
    to load exactly as before; only documents declaring an unsupported
    version (0.1, 0.4, non-numeric, etc.) are rejected, same as before.

Docs

docs/opsl/README.md rewritten to describe: v0.3.0 provenance
(opsl-public@730919c), the 0.2/0.3 version gate, the reader's
hard-error/warning/silence policy, which 0.3-only fields remain
unmodeled at the C++ layer (lineage, shape_hash, state_hash,
manufacturer_id, revision, plugin_format, program_location,
bulk_state, opaque_state, and the movemap.session artifact kind
entirely), and the pre-existing OSC-protocol/exporter schema caveat.

## Scope Phase 4a of OPSL alignment: bring Pedalboard3's OPSL reader/exporter up to spec with **OPSL 0.3.0** (vendored from `opsl-public@730919c`, tag `v0.3.0`), while continuing to accept existing **0.2** artifacts. - Re-vendored all four schemas under `docs/opsl/schemas/` (device, movemap.layout, movemap.bundle, and the new movemap.session) byte-exact from `v0.3.0`, with a `VERSION` marker file (`v0.3.0 730919c`). - `OpslReader` now accepts `schema_version` `"0.2"`/`"0.2.x"` and `"0.3"`/`"0.3.x"` (previously `"0.2"` exact-match only); anything else still hits the existing hard-error path. - `OpslDeviceExporter` now stamps exported documents with `schema_version: "0.3"`. - No reader code changes were needed to tolerate the 0.3 relaxations (optional `canonical_id`/`transforms`/param tags, device roles `capture`/`derived`, top-level `x-` extension fields) beyond the version gate itself — the existing non-throwing, forward-compatible parse policy already handles them. - Deliberately did **not** widen the `OpslAdapter::Protocol`, `OpslBinding::AddressType`, `OpslAdapter::Direction`, `OpslSlotBinding::Role`/`Action`, or `OpslTransformSpec::Type` enums. Nothing in the codebase branches on these beyond "is it `unknown`", so there's no functional benefit, and a near-miss while investigating `OpslTransformSpec::Type` showed widening it would make behavior *worse* (an unimplemented type would silently fall back to a linear transform instead of explicitly warning). New 0.3 vocabulary (e.g. the `sysex_bulk` adapter protocol used by real hardware devices) still decodes to `unknown` with an explicit warning, by design. ## Testing Built and ran via the documented CLAUDE.md workflow: ``` cmake --preset release cmake --build --preset release --target Pedalboard3_Tests ./build/release/tests/Pedalboard3_Tests "[opsl]" --order rand ``` Result: **72 OPSL test cases, 1775 assertions, all passing.** Also ran the full suite to check for collateral damage: ``` ./build/release/tests/Pedalboard3_Tests --order rand ``` Result: 390/391 test cases pass. The one failure ("Push 2 display transport emits framed RGB565 payloads with row padding and xor") is **pre-existing and unrelated** — confirmed via `git log`/`git diff` that this branch touches none of `tests/push2_surface_test.cpp`, `src/Push2DisplayTransport.{h,cpp}`, and that file was last changed by an unrelated commit on `main`. CTest is also wired up (`catch_discover_tests`) and confirms the same OPSL coverage independently: ``` ctest --preset release --output-on-failure -R OPSL ``` (note: the filter is case-sensitive and test names are `OPSL ...`, uppercase — `-R opsl` matches nothing). Result: **62/62 CTest-registered OPSL tests pass.** New fixtures added under `tests/fixtures/opsl/`: - `legacy-0.2-o1nk-device.json` — 0.2 back-compat (byte-exact from opsl-public). - `access-virus.pad.device.json` — a real 0.3 hardware device (`role: "patch"`, `kind: "hardware"`) exercising `manufacturer_id`, `bulk_state`, lineage, and the as-yet-unmodeled `sysex_bulk` protocol (byte-exact from opsl-public). - `device-with-extension.json` — same device plus a top-level `x-tool` extension field, to assert extensions are inert (byte-exact from opsl-public). The four existing shared o1nk/toilville fixtures had only their `schema_version` bumped in place to `"0.3"`, rather than being replaced wholesale with upstream's versions: upstream's `v0.3.0` examples fix two corpus quirks (a dangling `select_page` target and a fader role) that existing `OpslReaderTest`/`OpslControlSurfaceMapTest` cases specifically assert as regression coverage for malformed-input handling. JSON Schema doesn't enforce those cross-artifact invariants, so the fixtures remain fully schema-conformant under 0.3 with just the version bump. New `OpslReaderTest` cases cover: the version gate's accept/reject boundaries (0.2, 0.2.x, 0.3, 0.3.x accepted; 0.1/0.4/garbage rejected), a 0.2 document still loading, the hardware device loading with exactly the one expected `sysex_bulk` warning, the `x-` field being ignored (no additional warnings/param changes vs. the same fixture without it), a document omitting `canonical_id`/`transforms`/tags loading cleanly, and devices with `capture`/`derived` roles loading. This branch also carries a one-commit prerequisite fix (`cmake/CPM.cmake` being force-included despite the repo's `*.cmake` .gitignore glob) that three sibling branches — including the in-flight `codex/integrate-pending-20260920` — had each independently rediscovered and fixed identically; without it, `cmake --preset release` cannot configure from a fresh clone/worktree at all. ## Impact Statement - **Existing features:** no behavior change for 0.2 artifacts; all existing OPSL reader/exporter/catalog/control-surface tests continue to pass unmodified except for 3 assertions that intentionally now expect `"0.3"` instead of `"0.2"` as the exporter's emitted schema version. - **Public APIs / contracts:** `OpslReader`'s public surface is unchanged; `kSupportedSchemaVersion` is replaced by `kCurrentSchemaVersion` ("0.3") and `kMinimumSupportedSchemaVersion` ("0.2"). Any external caller referencing the old constant name will need to update. - **Schemas:** `docs/opsl/schemas/*.schema.json` updated to v0.3.0 verbatim; `movemap.session.schema.json` added (not yet consumed by any C++ reader — parsing that artifact kind is out of scope for this PR). - **Data / artifacts:** exported device artifacts now declare `schema_version: "0.3"`. This is self-consistent (the exporter's actual output already satisfied the stricter parts of the 0.2 schema and continues to satisfy 0.3), but any downstream consumer that exact-matches on `"0.2"` should be checked. - **Integrations:** none changed; the exporter's OSC-protocol non-conformance relative to the formal schema is pre-existing and unchanged by this PR (documented in `docs/opsl/README.md`). - **Mappings:** no control-surface mapping behavior changed; the two corpus-quirk fixtures used in mapping regression tests were deliberately kept as-is (only re-versioned), not replaced. - **Backward compatibility:** preserved. 0.2 and 0.2.x documents continue to load exactly as before; only documents declaring an unsupported version (0.1, 0.4, non-numeric, etc.) are rejected, same as before. ## Docs `docs/opsl/README.md` rewritten to describe: v0.3.0 provenance (`opsl-public@730919c`), the 0.2/0.3 version gate, the reader's hard-error/warning/silence policy, which 0.3-only fields remain unmodeled at the C++ layer (`lineage`, `shape_hash`, `state_hash`, `manufacturer_id`, `revision`, `plugin_format`, `program_location`, `bulk_state`, `opaque_state`, and the `movemap.session` artifact kind entirely), and the pre-existing OSC-protocol/exporter schema caveat.
The *.cmake glob in .gitignore matched cmake/CPM.cmake, the bootstrap
CMakeLists.txt includes before its first CPMAddPackage call. The adjacent
!cmake/ negation un-ignores the directory but not a *.cmake file inside it,
so the bootstrap stayed untracked on origin/main.

A fresh clone/worktree of main fails to configure with "include could not
find requested file: cmake/CPM.cmake" and then "Unknown CMake command
CPMAddPackage" until this file exists locally. Hit setting up this worktree
for the OPSL 0.3 work in the next commit. Three in-flight branches
(codex/integrate-pending-20260920 among them) independently carry the exact
same fix already; this carries it onto feat/opsl-0.3 so the branch builds
from origin/main without relying on one of those merging first.

The file is a 13-line downloader for CPM v0.40.2, not generated output, so
tracking it is correct.
Vendor the OPSL 0.3.0 schemas (device, movemap.layout, movemap.bundle,
movemap.session) from opsl-public@730919c with a VERSION marker, and
widen OpslReader's schema_version gate to accept both 0.2.x and 0.3.x
(previously 0.2 exact-match only). OpslDeviceExporter now stamps
exported documents with schema_version "0.3".

Readers already tolerate the 0.3 relaxations (optional canonical_id,
transforms, and param tags; device roles capture/derived; top-level
x- extension fields) because of the existing non-throwing/forward-
compatible parse policy, so no reader code changes were needed for
those beyond the version gate itself. Vocabulary this reader does not
yet model (e.g. the "sysex_bulk" adapter protocol used by hardware
devices) still decodes to unknown with a warning, by design; no enums
were widened since nothing branches on them functionally and one
near-miss (OpslTransformSpec::Type) would have made behavior worse (a
silent bad fallback instead of an explicit warning).

Test fixtures: bumped schema_version in place on the four existing
shared o1nk/toilville fixtures (kept their known corpus quirks intact
since several reader/control-surface tests assert against them) and
added three new fixtures copied byte-for-byte from opsl-public:
legacy-0.2-o1nk-device.json (0.2 back-compat), access-virus.pad.
device.json (a real 0.3 hardware device using capture role and
unmodeled vocabulary), and device-with-extension.json (x- field
handling). New OpslReaderTest cases cover the version gate's
boundaries, 0.2 still loading, hardware/capture-role devices loading
with the expected single warning, x- fields being ignored, and
optional-field omission. OpslExporterTest and OpslReaderTest's
existing schema_version assertions were updated from "0.2" to "0.3".

docs/opsl/README.md rewritten to describe the 0.3.0 provenance, the
version gate, the reader's hard-error/warning/silence policy, which
0.3-only fields remain unmodeled in the C++ layer (lineage,
shape_hash, state_hash, manufacturer_id, revision, plugin_format,
program_location, bulk_state, opaque_state, and the movemap.session
artifact kind), and the pre-existing OSC-protocol exporter caveat.
peterswimm deleted branch feat/opsl-0.3 2026-10-04 21:41:10 +00:00
Sign in to join this conversation.
No reviewers
No labels
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
fux-capacity/Pedalboardx8!3
No description provided.