Commit 1ad990b
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
0 commit comments