Diagnosed from the user's console output - 25 chunks pushed cleanly at
~124ms each, then stalled. Two coupled causes:
1) Bus contention. tick() and Live sync share self.midi with the chunk
ACKs. While the device was processing a chunk, a Note On / Clock Out /
Live-sync FULL heartbeat could land on the same MIDI OUT stream and
the host's parser dropped the interleaved ACK SysEx.
Fix: self._fw_pushing flag set on 0x21 BEGIN, cleared on 0x23 COMMIT
or any error. midi_send / Clock Out / _sync_broadcast / _sync_broadcast_full
all early-out when _fw_pushing is True. Only ACKs go out during a push.
2) SysEx assembler garbage. self._sx = bytearray() per chunk leaks ~70
bytes / chunk that only GC'd every 50 chunks. 25 chunks of trash plus a
slow heap walked the wrong way explains the ramp-up to 174 -> 119 -> 124
ms ACK times. GC every chunk now (~30ms cost on RP2040/RP2350 with
small heap) so the assembler buffer is always fresh.
Same patch on both pico-cp/ and pico-explorer/ since the bug is identical.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>