Skip to content

feat(filters): scope filters read/set, project-layer listing (#6) - #38

Merged
steveclarke merged 2 commits into
masterfrom
6-project-layer
Jul 27, 2026
Merged

steveclarke merged 2 commits into
masterfrom
6-project-layer

Conversation

@steveclarke

@steveclarke steveclarke commented Jul 26, 2026 •

Copy link
Copy Markdown
Owner

Refs #6. Discovery first, as the brief asked — and it inverted the expected outcome. The brief expected Part 1 (project layer) to ship and Part 2 (scope filters) to be the risky half. It went the other way: the filters needed no new wire format at all, and it is the project store/recall that has no capture behind it.

Part 2 — the scope filters are ordinary parameters

The blue Project-Filter / Scene-Filter tiles are not a preset-layer format. Every tile is a plain on/off key in the ZB snapshot: 1 = the setting travels with a store/recall/reset, 0 = it is left alone. Reading them is a snapshot read; setting one is a single PV write.

Read off the 32R (firmware 3.4.0), all three groups:

Group Prefix Tiles
Scene Filter global/fltr name, mute, fx, eqdynins, eqdynouts, aux, assign, preamps, fader, geq, dcagrp, 48v, mutegroups, user, patch
Advanced Scene Filter advancedscenefilters/fltr_ channel_info, preamp, channelstrip, input_fatch, output_fatch, channel_delay, mutes, main_mix_level, main_mix_assigns, subgroup_assigns, aux_matrix_mixes, fx_mixes, fx_type, dca_groups, mute_groups
Project Filter projectfilters/fltr_ input_source, flexmixmode, flexmixprepostmode, fxmixpreposmode, talkbackassigns, solosettings, generalsettings, avbstreamrouting, inputpatching, outputpatching, avbpatching, sdpatching, usbpatching, geq, user_functions

The datum that identifies the set: every tile on the board read 1 except global/fltr48v, which read 0. That is the board's documented default of excluding phantom power from a scene recall — it is what pins these keys to the Store/Recall/Reset filters rather than to something else named fltr, without asserting a UI-label mapping.

A fourth, adjacent set governs channel presets and copy/paste, not store/recall/reset: channelfilters/preset_* / paste_* (plus do_load / dont_load). Documented in PROTOCOL.md, deliberately not wired to a command — different feature, and unlike the three above these are not uniformly on (preset_48v reads 0, preset_eq reads 1).

ucmix filters ls [scene|advanced|project]
ucmix filters set <group> <tile> <on|off>

Tile names are the board's own key names with the prefix stripped, so a tile always names the parameter it writes; - and _ are interchangeable. A tile the board never reported prints "not reported by the mixer" rather than "excluded" — absent and switched-off are different facts.

Part 1 — the project layer, listing only

ucmix project ls        # projects with titles, marking the loaded one

The loaded marker comes from presets/loaded_project_name, which the board states as proj/03.135 Main Live.proj.

project store / project recall are not implemented, on purpose. No capture of UC Surface storing or recalling a project exists — I checked all three pcaps in internal/proto/testdata/, none contains a project-level operation. So which token the request carries is unestablished:

  • The .proj directory (presets/proj/03.135 Main Live.proj), or
  • the .cnfg file inside it (presets/proj/03.135 Main Live.proj/135 Main Live.cnfg).

The read-only evidence leans toward the directory — presets/loaded_project_name names the .proj dir, and a store ack's presetFile is exactly loaded_*_name with presets/ prepended. That is an inference from a read, not an observed request, and this issue has already cost several rounds of encoded guesses. project store and project recall fail with that explanation and a pointer to project ls / filters ls project.

Steve declined the capture for now, so this ships as listing only.

Other discovery, written into PROTOCOL.md

  • The project-layer file in a scene listing is 135 Main Live.cnfg — no NN. slot prefix, one per project, named after the project title. Suffix, not slot position, distinguishes it from a scene.
  • The board reports what it has loaded in presets/loaded_project_name / _title, loaded_scene_name / _title, isProjectLoaded, isSceneLoaded — as paths relative to the preset root, i.e. a JM presetFile minus the leading presets/.
  • Two resources the board silently drops (no FD, no error): Listpresets (the preset root is not listable) and Listpresets/…/<file>.cnfg (List applies to folders, not files).

Hardware verification

  • Read paths (project ls, filters ls for all three groups) confirmed live against the 32R.
  • Write path confirmed with Steve's explicit go-ahead for one tile: global/fltr48v false → filters set scene 48v on → reads true → filters set scene 48v off → reads false. The board is back on its original value.
  • No store, recall, apply or reset was sent to the board.

Guardrails

  • 513 unit tests (was 490), 104 e2e (master counts 88 @test blocks, not the 90 in the brief), just lint 0 issues, just vet clean, just test-race clean.
  • filters set goes through the same write-and-read-back path as set and the noun verbs (second commit): it re-reads the tile on a fresh connection and reports what the board holds, with --no-verify to opt out. It first reported success on send — the exact report-on-send shape this repo has spent several issues removing from the preset commands. That also covers what a static tile table cannot: on a firmware missing a tile, the read-back comes up absent instead of confirming a write nobody saw.
  • Adding schema rows does not leak the filters into board-as-code: boardconfig.ToConfig only accumulates line/, aux/, fxreturn/ and fx/ prefixes, so dump --as-config is unchanged (checked against the live board — no filter keys in the output). Whether the filters belong in board.yml is a separate decision, deliberately not taken here.
  • internal/fakeboard clamps the new keys through the existing clampControl, since they are schema KindBool. It does not snap a mid-range PV (e.g. 0.5) to 0/1 — what a real board does with that is unobserved, and inventing the behaviour would be the same class of fiction the clamp exists to prevent. The client cannot send it: a KindBool write encodes 1/0.
  • ucmix verify against the show config is NOT clean, and the timeline rules this branch out. verify was clean at 322 settings at the start of the session, after the read-only listing probe. Between that clean run and the drift, the only frames this branch sent to the board were two PV writes to global/fltr48v (and reads). The drift is 12 of 322: every aux master at -10 dB, all on the identical wire value 0.7077 — one gesture on one bus family, not a scene recall and not a fader-by-fader move. presets/diskusage also moved (0.17012705 → 0.17198637), i.e. something was written to the board's disk during the session; no store was sent from here. Another client has been on the desk. The faders were left alone.

Follow-up found on the way

internal/proto/testdata/uc-surface-store-rename.pcap contains an inbound "id": "RenamedPreset". PROTOCOL.md line 262 states a rename is unacknowledged, and RenameScene re-lists the project to confirm it. If that ack is real, the doc is wrong and RenameScene can wait on it like store/recall/delete do. Not touched here — outside #6.

https://claude.ai/code/session_016eTEKycNo9xMKWaPRPbYEQ

…ayer (#6)

The blue Project-Filter / Scene-Filter tiles are not a preset-layer format.
Read off a 32R (firmware 3.4.0), each tile is an ordinary parameter in the ZB
snapshot: 1 = the setting travels with a store/recall/reset, 0 = it is left
alone. So reading them is a snapshot read and setting one is a single PV write —
no new wire format, no guessing.

The identifying datum: every tile on the board read 1 except global/fltr48v,
which read 0. That is the documented default of excluding phantom power from a
scene recall, and it is what pins this key set to the Store/Recall/Reset filters
rather than to something else named fltr.

  ucmix filters ls [scene|advanced|project]
  ucmix filters set <group> <tile> <on|off>

Tile names are the board's own key names with the prefix stripped, so a tile
always names the parameter it writes; - and _ are interchangeable. A tile the
board never reported reads as "not reported by the mixer", not as excluded.

Project layer: ucmix project ls lists the projects and marks the loaded one, from
presets/loaded_project_name. Storing and recalling a project as a unit is NOT
implemented — no capture of UC Surface doing it exists, so which token the
request carries (the .proj directory or the .cnfg file inside it) is
unestablished. `project store` and `project recall` fail with that explanation.

Discovery is written up in PROTOCOL.md: the .cnfg naming, the loaded_* keys, the
two resources the board does not answer, and the full tile roster per group.

Read paths confirmed live against the board; the write path confirmed by
flipping global/fltr48v and reading it back on a fresh connection.
Claude-Session: https://claude.ai/code/session_016eTEKycNo9xMKWaPRPbYEQ
`filters set` printed success as soon as SetFilter returned — the same
report-on-send shape this repo has spent several issues removing from the preset
commands. It now goes through applyNoun, so the write is re-read on a fresh
connection and the reported value is the one the board holds, with --no-verify
available like every other write command.

This also covers the case the static tile table cannot: a firmware that does not
carry a tile reads back absent rather than confirming a write nobody saw.

Confirmed live on the 32R with global/fltr48v, restored to its original value.
Claude-Session: https://claude.ai/code/session_016eTEKycNo9xMKWaPRPbYEQ
@steveclarke
steveclarke merged commit 3d39810 into master Jul 27, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant