metronome/tests
Me Here da7c94e67f Implement per-track playback flow (rep / end / relative goto)
Adds the per-track end-action model designed in docs/track-format.md §3, end to
end across both engines, both firmwares, and the editors.

Grammar (parsed + serialized by engine.js and both app.py):
  rep=<n>     cycles before the end-action fires (default 1)
  end=stop    stop after rep cycles
  end=next    advance one track (sugar for end=+1)
  end=<±N>    relative goto after rep cycles (e.g. end=-2 = D.S.)
  (absent)    loop forever — the metronome default

Firmware runtime (pico-cp + pico-explorer): _on_new_bar now consults a per-track
_end_plan() and fires stop / gapless-advance / relative-goto at the right bar.
A cycle = b<bars>, else one master bar; fire bar = rep * cycle. Explicit end=
governs; with no end, the global Continue toggle stays a default (=end=next, still
needs b<bars>) so existing set-lists and the CONT UI are unchanged. _prepare_next
takes a target index; the seam machinery, _do_advance and live-sync all carry rep/end.

Editors (editor.html + editor-beta.html): state.rep/state.end thread through
applySetup / currentSetup / currentPatch so load -> edit -> save preserves the
flow; authoring is via the program-string field (no graphical control yet).

Tests: the 3 playback-flow vectors now pass on both engines (39 pass / 3 known).
Runtime decision logic (_end_plan / _goto_target) unit-tested for stop, rep,
relative goto clamp/wrap, and legacy-Continue precedence. Codec round-trip
verified idempotent. Both firmwares compile + mpy-cross clean.

Also: untrack stale __pycache__/*.pyc build artifacts and gitignore them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:37:06 -05:00
..
adapters Implement per-track playback flow (rep / end / relative goto) 2026-05-31 00:37:06 -05:00
fixtures Implement per-track playback flow (rep / end / relative goto) 2026-05-31 00:37:06 -05:00
.gitignore Formalize track format: spec + golden-vector conformance suite 2026-05-30 23:54:20 -05:00
README.md Formalize track format: spec + golden-vector conformance suite 2026-05-30 23:54:20 -05:00
run.mjs Formalize track format: spec + golden-vector conformance suite 2026-05-30 23:54:20 -05:00

Track-format conformance tests

Golden-vector suite that pins the track ("program"/"patch") format to a single meaning and checks that both implementations agree:

  • websrc/engine.js
  • firmwarepico-cp/app.py

The spec is docs/track-format.md. Any new implementation (e.g. a Rust engine) must pass the same vectors — that is what keeps "the same groove on the device and in the browser" true.

Run

node tests/run.mjs        # table of pass / known-divergence / FAIL per case
node tests/run.mjs -v     # also print expected-vs-actual diffs for unexpected failures

Exit code is non-zero on any unexpected failure or round-trip (idempotency) break, so it works as a CI gate.

Layout

  • fixtures/track-format.json — the vectors. Each has in (a patch), norm (expected normalized meaning, see spec §5), a status, and optional expectFail listing impls known to differ today.
  • adapters/js_adapter.mjs — loads the real src/engine.js grammar (no copy) and normalizes.
  • adapters/py_adapter.py — extracts the real pico-cp/app.py grammar functions via ast (no copy) and normalizes.
  • run.mjs — runs every vector through both adapters and reports.

Reading the result

  • ✓ pass — implementation matches the spec for that vector.
  • · known — a divergence/feature listed in expectFail; expected, not a failure.
  • ✗ FAIL — an unexpected mismatch (a regression). Investigate.
  • ★ fixed — an impl listed in expectFail now passes; remove it from expectFail.

When you fix a divergence in code, delete that impl from the case's expectFail. When you implement the new playback-flow tokens (rep / end), those cases flip to pass.