Skip to content

Browser: drag tracks onto a favourite; record why ANLZ writes stay unbuilt - #43

Merged
cole-hackman merged 1 commit into
mainfrom
claude/lexicon-anlz-write
Aug 8, 2026
Merged

cole-hackman merged 1 commit into
mainfrom
claude/lexicon-anlz-write

Conversation

@cole-hackman

Copy link
Copy Markdown
Owner

Two things: the last Library & browser gap, and an investigation that closes off four others by saying no.

Track rows are a drag source

The Favorite Playlists row had been sitting on "drag-and-drop target not done — no drag source in the table yet". One change, two rows. Library & browser is now 17 done / 0 partial.

Three rules, all in lib/track-drag.ts rather than inside the handlers — jsdom does not run drag events, so a rule that lives in a handler is a rule nothing tests:

  • Dragging a row inside the selection carries the whole selection; dragging one outside it carries only that row. Always-just-the-row makes a multi-select highlight a lie; extending the selection to include the dragged row changes state the user did not ask to change.
  • The drop reads its payload, not the live selection. The selection can change between the drag starting and the drop landing, and the payload is the record of what was actually picked up.
  • The favourite only accepts a drag carrying our own MIME type. Without the check the chip lights up for a dragged file and then does nothing — worse than never lighting up.

ANLZ writing: investigated, deliberately unbuilt

Four rows depend on it — Beatgrid editing, the last two cue-point recipes, Don't Touch My Grids, Beatshift correction — so it was worth answering once rather than discovering it four times.

Producing the bytes is the easy half and is not the blocker. The format is self-describing (PMAI magic, big-endian header length, a chain of sections each carrying tag / header length / total length), for_each_section already walks it correctly, and rewriting PQTZ in place is mechanical with an obvious round-trip test.

What cannot be answered here is whether Rekordbox accepts a file we wrote:

  1. Whether anything beyond the length fields is validated.
  2. Whether the .DAT and its .EXT companion must stay mutually consistent — we only read one.
  3. Whether master.db carries state that must change alongside it (AnalysisUpdated, AnalysisDataPath), and what Rekordbox does on next launch if it does not.

The failure mode is not data loss, but it is not nothing: a rejected or misparsed ANLZ leaves a track with no waveform and no grid in Rekordbox until it is re-analysed there.

I could have written the writer anyway, round-trip-tested it against our own parser, and called the rows closed. That would be wrong twice over — it is untestable production code by exactly the argument that keeps crates/enrichment unwritten, and it would sit unwired, which this project's definition of done forbids ("reachable from the UI, never tests-only").

So the deliverable is the finding. The four rows now share one recorded reason instead of four vague ones, and GAPS.md names the specific check that resolves all four at once: extend the disposable-DB smoke harness to write one PQTZ section into a copy of a real ANLZ, then open that copy in Rekordbox. Fifteen minutes on a machine with Rekordbox installed.

Verification

cargo fmt --all -- --check          clean
cargo test --workspace              54 test binaries, 0 failures
cargo clippy --all-targets -D warn  clean
pnpm test                           816 tests, 71 files
pnpm typecheck / pnpm lint          clean
pnpm e2e                            59 passed

Matrix moves to 60 done / 20 partial / 14 missing / 2 blocked / 16 deferred — and the domain rows now sum to the total, which the two concurrent branches had briefly desynced.


Generated by Claude Code

The last Library & browser gap, and the one the Favorite Playlists row had been
waiting on — its Notes read "drag-and-drop target not done — no drag source in
the table yet". One change, two rows.

Three rules, all in `lib/track-drag.ts` rather than inside the handlers,
because jsdom does not run drag events and a rule that lives in a handler is a
rule nothing tests:

- **Dragging a row inside the selection carries the whole selection**;
  dragging one outside it carries only that row. Always-just-the-row would make
  a multi-select highlight a lie, and extending the selection to include the
  dragged row would change state the user did not ask to change.
- **The drop reads its payload, not the live selection.** The selection can
  change between the drag starting and the drop landing, and the payload is the
  record of what was actually picked up.
- **The favourite only accepts a drag carrying our own MIME type.** Without the
  check the chip lights up for a dragged file and then does nothing, which is
  worse than never lighting up.

Also records a finding rather than code: **ANLZ writing is investigated and
deliberately unbuilt**, in `GAPS.md` §Environment blockers. Producing the bytes
is mechanical — the format is self-describing and `for_each_section` already
walks it. What cannot be answered without a real Rekordbox install is whether
Rekordbox accepts a file we wrote: whether anything beyond the length fields is
validated, whether `.DAT` and `.EXT` must stay consistent, and whether
`master.db` carries state that must change alongside it.

Writing it anyway would be untestable production code by the same argument that
keeps `crates/enrichment` unwritten, and it would sit unwired, which the
definition of done forbids. The four rows that depend on it — Beatgrid editing,
the last two cue-point recipes, Don't Touch My Grids, Beatshift correction —
now share one recorded reason, and GAPS names the fifteen-minute check on a
machine with Rekordbox that resolves all four together.

Per docs/lexicon/02-library.md §Browser.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 08b65010-69d0-4ceb-8e67-f1c51f9c4534

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cole-hackman
cole-hackman marked this pull request as ready for review August 8, 2026 07:33
@cole-hackman
cole-hackman merged commit e774f26 into main Aug 8, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants