metronome/tests
Me Here bf74c860e5 Track format: unify default (no-pattern) groove across web + firmware
A lane with no =pattern produced different defaults on web vs device — a real,
shipped divergence the new conformance suite caught (e.g. hatClosed:4/2 in
"Four-on-the-floor" played steady 8ths in the browser but quarter-notes on the
device). Adopt one rule everywhere: every subdivision sounds at normal level,
accents fall ONLY on group starts (the grouping is the accent map).

- pico-cp/app.py, pico-explorer/app.py: off-beat subdivisions sound at normal (1)
  instead of resting (0); group-start accenting was already correct.
- src/engine.js: default beatsOn accents group starts only (was: every beat);
  laneCfgToStr isDefault check updated to match so round-trips stay idempotent.
- docs + fixtures: document the rule; default-pattern vectors now pass on both.

Audible effect (intended): device subdivided hat/ride lanes gain their off-beat
strokes (now match the web); web stops over-accenting every beat. Lanes with an
explicit =pattern are unchanged. Verified green via node tests/run.mjs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:03:34 -05:00
..
adapters Formalize track format: spec + golden-vector conformance suite 2026-05-30 23:54:20 -05:00
fixtures Track format: unify default (no-pattern) groove across web + firmware 2026-05-31 00:03:34 -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.