Skip to content

Commit 1ad990b

Browse files
bwhitmanclaude
andauthored
AMYboard Web: SYNC 2 — sketch.py is the single source of truth (#1041)
* AMYboard Web: SYNC 2 — sketch.py is the single source of truth Replace the bidirectional "Write sketches & knobs" flow (which silently pulled + merged live AMY state) with two unidirectional verbs over one ground-truth sketch: - Knob log: _auto_generated_knobs is now a log of UI-originated actions only (knob moves, CC, presets, channel activate/deactivate) — never a dump of live AMY state. New: send_amy_message_in_sketch / set_knobs_from_sketch / reset_synth_in_sketch / remove_synth_from_sketch. - Write to your AMYboard: zT transfer + zP restart_sketch (no bootloader reboot). Read from your AMYboard: zD pull only (no zA). Removed the implicit pull/merge. - Firmware (amyboard.py): _apply_knobs_text always resets AMY + installs the default synth on channel 1, then applies the block on top, so restart_sketch() is a clean "sketch = ground truth" reload. - Default-channel knobs are positioned from the K257 baseline (patch_code_for_patch_number[257]) since the block is empty for a default synth. - Editor draft persists in localStorage and is restored on page load (no Pull dialog); default sketch shown when there is no draft. - Pull box gained a "replace your sketch" warning + a dismiss X. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * web: bridge get_synth_commands() to JS callers in both web apps On web, AMY runs in a separate WASM worklet (AMY_IS_EXTERNAL) and the micropython module does not link it, so tulip.amy_get_synth_commands (modtulip.c) is compiled out via #ifndef __EMSCRIPTEN__. Bridge the same capability from JS instead, driving AMY's already-exported low-level generator yield_synth_commands. Adds a canonical get_synth_commands(synth, include_fx=true) global to tulip/amyboardweb/static/spss.js and tulip/web/static/spss.js that returns an array of wirecode strings (the JS analogue of amy/src/pyamy.c's wrapper). The amyboardweb editor's get_wire_commands_for_channel now delegates to it; its heap buffer free moves into a try/finally. Pure JS in static/, no AMY submodule change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * AMYboard Web: SYNC 2 follow-ups — sketch-error reporting, load-only downloads, centered confirm - Report sketch load errors from hardware: run_sketch() now pushes a sysex 'X' frame (amyboard.py _report_sketch_error -> tulip.midi_out) when a sketch fails to load. The web surfaces it as the error modal + a persistent red (!) on the Code tab (visible from any tab). Simulate already caught it off the JS console; both now also flag the Code tab. Cleared at the start of each Write. - AMYboard World downloads (and ?env= share links) are now load-only: confirm, load into the editor, position the knobs, switch to the Code tab. No auto-write to the board — the user clicks Write. So the error modal only appears after an explicit Write. Also drops the old reboot-based upload from the share-link path. - Center the "replace your sketch" confirm modal (modal-dialog-centered). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * amyboardweb: add #generated AMYboard World filter (generator user's sketches) Adds a third tag pill alongside #featured and #official. #generated isn't a real tag — it's a shortcut for every sketch by the `generator` user, so the world-files query filters by username=generator instead of tag. Toggles like the others and composes with the search box. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * amyboardweb: show up to 100 AMYboard World sketches (was 30) Raise the world-files display cap from 30 to 100 and fetch with limit=200 for headroom so filtering doesn't leave it short. The list container already scrolls (max-height 360px), so the longer list scrolls in place. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * amyboardweb: SYNC 2 fixes from testing — channel activate, dedup, level, CC, cleanup - Activating a channel (or per-synth Clear) now just loads the K257 default patch (i<ch>ic255 + i<ch>K257iv6) and positions the UI knobs to its defaults, instead of replaying ~50 generic knob/CC commands that overrode the Juno and silenced it. - Fix ReferenceError on channel select: reapply_last_zd_dump referenced the removed _last_zd_dump_text. Replaced with position_current_channel_from_log() (positions the current channel from the knob log + K257 baseline, no AMY send). - Dedup by a STRUCTURAL key derived from the wire message (i<synth>/v<osc> kept literal, other values normalized), used by both live moves and rebuild — so a line loaded from a sketch collapses with a later move instead of duplicating (the i1v0F-written-twice bug); also self-heals an already-duplicated block. - Record the dedicated level slider into the knob log so it persists in the sketch and round-trips on Read (it bypassed the grid's send_change_code path). - Fix CC mapping wire command: emit i<ch>ic<...> (the midi_cc wire op) not a bare i<ch>c<...> — `c` is chained_osc and doesn't consume the change_code, so the sketch failed to boot ("Unrecognized synth-level command"). - Remove the obsolete "Sync to show your AMYboard's current sketch" button. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * AMYboard Web: firmware-compatibility gate for the SYNC 2 protocol The editor deploys to amyboard.com the moment it merges, but a board's firmware only changes on reflash, so a board on pre-SYNC-2 firmware would silently misbehave against the new web<->firmware contract. - amyboard.py: report_version() replies to the web's probe with the build date over an 'V' sysex frame (F0 00 03 45 'V' <tulip.version()> F7). - spss.js: check_amyboard_firmware() probes on the first Write/Read; boards older than MIN_COMPATIBLE_FW_DATE (or too old to reply) get a blocking 'update firmware' modal. Compatible-but-behind boards get a soft '!' badge on the Upgrade Firmware tab, keyed on the latest 'amyboard' release date. - index.html: oldFirmwareModal + the upgrade-tab badge/note. - CI: amyboard-breaking-fw-check.yml enforces that a 'breaking'-labelled PR changes MIN_COMPATIBLE_FW_DATE and vice versa. MIN_COMPATIBLE_FW_DATE is a pre-release placeholder (2026-06-01); bump it to the firmware release date that ships this PR when #1041 is released. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * AMYboard Web: remove the firmware-compatibility gate (lock-out risk) The gate hard-blocked Read/Write whenever the version probe got no reply within the timeout. But 'no reply' is ambiguous: it happens for transient reasons (USB re-enumeration right after a flash, probe delayed behind the sysex ack path, a dropped packet), not just for genuinely old firmware. The result was real users walled off with no escape, including immediately after upgrading. Fail-closed on an ambiguous signal was the wrong design. Removes the web-side gate entirely: the version probe, the blocking modal, the soft out-of-date badge, the Write/Read checks, MIN_COMPATIBLE_FW_DATE, the 'V' sysex handler, and the breaking-label CI guard. Keeps amyboard.report_version() in the firmware (inert — nothing probes it now) so the probe round-trip can be verified on real hardware before any gate is reintroduced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * amyboardweb: Write must not modify the sketch — fix knob block wiped on Write Write rebuilt the sketch's knob block from the in-memory knob_log (splice_knobs_into_sketch + editor.setValue), but pasting/typing a sketch never rebuilt that log — only Read/download/pageload did. So in a fresh tab (e.g. incognito) the log is empty; pasting a sketch then hitting Write spliced the empty log over the pasted _auto_generated_knobs block and wiped it to "". (The same desync wiped the block on the first knob move after a paste, too.) Fix — keep the editor and knob_log in sync BOTH ways, and stop Write from touching the sketch: - Write now sends editor.getValue() VERBATIM. It first flushes any pending knob-block reflect (log -> editor) and any pending user-edit rebuild (editor -> log), so the editor is current; no splice/setValue. - User edits/pastes rebuild the knob_log from the editor's block (the reverse of reflect_knob_log_to_editor), gated on CodeMirror change.origin !== "setValue" (so our own programmatic updates don't loop) and a block-changed check (so typing code doesn't churn the knob grid). Pasting a sketch now also repositions the knobs to match it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * amyboardweb: relabel sketch-generator button + add overwrite warning Rename "Create the sketch" to "Create or Modify your Sketch" and add small red text next to it: "Could change or remove your sketch. Back up your old sketch if you want to keep it." — the generator can replace the current sketch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * amyboardweb: fix channel active-detection fooled by iv<N> inside CC change_codes _compute_active_from_block scanned every line of a channel for the iv<N> num_voices token, but CC-mapping lines carry iv<N> *inside* their change_code templates (e.g. i10ic80,...,i%iv0F..., i%iv2f...). A channel with CC maps thus read its last "iv" as iv0/iv2 from a change_code and looked deactivated — e.g. channel 10 in a pasted sketch showed inactive while channels 5/6 (no CC lines) were fine. Skip CC lines (i<ch>ic...) when scanning for num_voices. This also restores channel 10's per-knob CC titles: an inactive channel early-returns before its knob grid renders, so the (correctly parsed) CC maps never showed; with the channel active again, switching to it renders them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * AMYboard Web: fail-safe firmware detector (#1056) Fail-safe version probe + breaking-build hard block. Stacked onto #1041. * AMYboard Web: dismissable firmware-update warning (#1057) Replaces the breaking/MIN_COMPATIBLE gate with a simple dismissable warning. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 27a8a15 commit 1ad990b

5 files changed

Lines changed: 1178 additions & 394 deletions

File tree

0 commit comments

Comments
 (0)