Skip to content

feat(sloppak): load multiple drum parts (feedpak 1.17.0 drums-as-arrangements)#1020

Merged
byrongamatos merged 4 commits into
mainfrom
feat/drum-parts-loader
Jul 21, 2026
Merged

feat(sloppak): load multiple drum parts (feedpak 1.17.0 drums-as-arrangements)#1020
byrongamatos merged 4 commits into
mainfrom
feat/drum-parts-loader

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

The core half of the multiple-drum-parts program. The Arrangement Editor now writes packs with several drum charts per the feedpak 1.17.0 FEP (got-feedBack/feedpak-spec#63, editor side: got-feedBack/feedBack-plugin-editor#339): the primary stays the song-level drum_tab: key, and each part rides the manifest as a type: drums arrangement entry with a per-arrangement drum_tab pointer and no note file — an entry this loader's file/notation gate already skips, which is exactly why old builds are unaffected. This PR teaches core to read those parts.

What it does

  • lib/sloppak.py — collects the pointer entries (still never loading one as a fretted Arrangement: that skip is the grading invariant — an empty drum chart must not reach the fretted pipeline / note-detection grading — now pinned by test) and resolves them into LoadedSloppak.drum_parts: [{id, name, drum_tab}], primary first. The entry aliasing the song-level file contributes its id/name but is never loaded twice (the primary's payload is loaded.drum_tab). Legacy single-drum packs read as a one-part list; a pointer-only pack promotes its first part so has_drum_tab / the default stream / the drum-only placeholder keep working. The song-level loading block is extracted verbatim into _load_drum_tab_file() and shared, so every part gets the same permissive posture (the 9 pinned drumtab-load tests pass unchanged).
  • lib/routers/ws_highway.pysong_info gains drum_parts (names only; always a list) so a part picker can bind unconditionally; ?drum_part=<id> on the WS URL selects which part's tab streams (drum_tab messages carry part_id only when a parts list exists — the legacy frame stays byte-identical; unknown ids fall back to the primary).

What it deliberately does NOT do

No picker UI (that's the drum-highway / visualization plugins' side, using song_info.drum_parts + the drum_part param), no library-index changes, no scoring changes.

Verification

  • New tests/test_sloppak_drum_parts.py (9): the grading invariant + parallel-ids pin, primary-first resolution with alias identity, legacy one-part list, pointer-only promotion, per-part failure isolation (bad JSON, path traversal, duplicate rels), drum-only placeholder with pointer entries.
  • Existing test_sloppak_drumtab_load.py 9/9 unchanged (the helper extraction is behavior-identical).
  • Full suite: the only failures on my box are 9 machine-environmental tests (installed desktop plugins under %LOCALAPPDATA%, CRLF/path-shape assertions) that fail identically on an untouched origin/main checkout.
  • tools/check_spec_conformance.py passes against the spec's current HEAD (drum_tab and type are already declared key names).

⚠️ Merge order: the per-arrangement placement semantics land in got-feedBack/feedpak-spec#63 — please merge that first (this PR's gate is green either way, since the key names are already declared).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added feedpak 1.17.0 support for multiple drum parts, including per-part drum_tab loading and exposure with IDs and names.
    • WebSocket streaming can now select which drum part’s drum_tab is sent (including part_id when applicable).
    • Drum-only packs are supported, including correct song-length derivation.
  • Bug Fixes

    • Drum parts no longer enter the fretted arrangement/notation pipeline.
    • Malformed or unsafe drum-part references are handled safely, disabling only the affected part.
  • Tests

    • Added coverage for drum-part resolution and wire-compat selection behavior.
  • Documentation

    • Updated unreleased changelog entries for drum-part and chart-transform coordinator behavior.

…ngements)

A song can now ship SEVERAL drum charts (a second drummer, an aux-percussion
layer). The Arrangement Editor already writes them per the feedpak 1.17.0 FEP
(feedpak-spec#63): the primary stays the song-level `drum_tab:` key (what this
app has always played), and each part rides the manifest as a `type: drums`
arrangement entry carrying a per-arrangement `drum_tab` file pointer and NO
note `file` — an entry this loader's file/notation gate already skips, which
is exactly why old builds are unaffected by such packs.

lib/sloppak.py:
- The arrangements loop collects drum-part pointer entries instead of merely
  skipping them — but still NEVER turns one into a fretted Arrangement. That
  skip is the grading invariant (an empty drum chart must not reach the
  fretted pipeline / note-detection grading) and is now pinned by test.
- New `LoadedSloppak.drum_parts`: [{id, name, drum_tab}], primary FIRST. The
  entry aliasing the song-level file contributes its id/name but is never
  loaded twice (the primary's payload IS `loaded.drum_tab`, same object).
  Legacy single-drum packs read as a one-part list; a pointer-only pack (a
  writer omitted the alias) promotes its first part so has_drum_tab, the
  default stream, and the drum-only placeholder keep working.
- The song-level drum_tab loading block is extracted verbatim into
  `_load_drum_tab_file()` and shared by both paths, so every part gets the
  same permissive posture: missing file → that part silently absent;
  traversal / parse / validation failure → that part skipped with a warning,
  never an aborted load. (The 9 pinned drumtab-load tests pass unchanged.)

lib/routers/ws_highway.py:
- `song_info` gains `drum_parts` (names only; always a list, empty without
  drums) so a part picker can bind unconditionally.
- `?drum_part=<id>` on the WS URL selects which part's tab streams as the
  `drum_tab`/`drum_hits` messages; the default and any unknown id fall back
  to the primary — byte-identical legacy behavior. The `drum_tab` message
  carries `part_id` only when a parts list exists, keeping the legacy frame
  unchanged.

Tests: tests/test_sloppak_drum_parts.py (9) — the grading invariant +
parallel-ids pin, primary-first resolution with alias identity, legacy
one-part list, pointer-only promotion, per-part failure isolation (bad JSON,
path traversal, duplicate rels), and the drum-only placeholder with pointer
entries. Full suite: the only failures are 9 machine-environmental tests
(installed desktop plugins under LOCALAPPDATA, CRLF/path-shape assertions)
that fail identically on an untouched origin/main checkout on this box.
tools/check_spec_conformance.py passes against the spec's current HEAD
(`drum_tab` and `type` are declared keys); the semantics of the
per-arrangement placement land in feedpak-spec#63 — this PR should merge
after it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xGPjDBF8NTwTK7VQvizix
Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@byrongamatos, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 58 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1132d57f-fb52-407a-8001-c5dfd3c20cdc

📥 Commits

Reviewing files that changed from the base of the PR and between c7fa0ca and 981284b.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • lib/sloppak.py
  • tests/test_sloppak_drum_parts.py
  • tests/test_tuning_provider_isolation.py
📝 Walkthrough

Walkthrough

Sloppak loading now represents multiple drum parts separately from fretted arrangements. The highway WebSocket advertises available parts, accepts a selected part id, and streams the corresponding drum tab. Tests cover resolution, fallback, validation, deduplication, and drum-only packs.

Changes

Multiple drum parts

Layer / File(s) Summary
Drum-part loading and resolution
lib/sloppak.py
Drum arrangement pointers are excluded from fretted arrangements, referenced tabs are safely validated, and primary/extra parts are resolved into LoadedSloppak.drum_parts.
Drum-part WebSocket selection
lib/routers/ws_highway.py
The WebSocket advertises drum parts, selects tabs using drum_part, preserves fallback behavior, and emits the selected part_id when applicable.
Loader and wire compatibility coverage
tests/test_sloppak_drum_parts.py, tests/test_highway_ws_drum_parts.py
Tests cover multiple and legacy parts, invalid paths, duplicates, unique ids, drum-only packs, and wire-format behavior.
Release documentation
CHANGELOG.md
The unreleased changelog documents multiple drum parts and the chart-transform coordinator.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant highway_ws
  participant load_song
  participant DrumTabFiles
  Client->>highway_ws: Connect with drum_part
  highway_ws->>load_song: Load sloppak
  load_song->>DrumTabFiles: Resolve drum-tab references
  DrumTabFiles-->>load_song: Validated drum parts
  load_song-->>highway_ws: LoadedSloppak with drum_parts
  highway_ws-->>Client: song_info and selected drum_tab
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: loading multiple drum parts for feedpak 1.17.0.
Description check ✅ Passed The description covers the change, feedpak impact, testing, and scope exclusions, so it is mostly complete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/drum-parts-loader

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CHANGELOG.md (1)

22-28: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Orphaned bullet continuation — Line 23 dangles without its lead-in.

The new drum-parts bullet (11-22) ends with a period, then Line 23 abruptly continues with "coordinator. Synchronous transforms run after difficulty filtering..." — this is leftover tail text from the removed "chart-transform" bullet whose opening sentence was deleted. As written, this renders as an incoherent fragment attached to the wrong bullet.

📝 Proposed fix
-  by test. Legacy single-drum packs read exactly as before, as a one-part list.
-  coordinator. Synchronous transforms run after difficulty filtering; host
+  by test. Legacy single-drum packs read exactly as before, as a one-part list.
+- **Chart-transform coordinator.** Synchronous transforms run after difficulty filtering; host

(Restore the deleted lead-in for the chart-transform bullet — exact wording TBD — rather than leaving lines 23-28 attached to the drum-parts bullet.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` around lines 22 - 28, Restore a complete lead-in and separate
changelog bullet for the chart-transform behavior before the continuation
beginning “coordinator.” Ensure the drum-parts bullet ends cleanly, and preserve
the existing chart-transform details about synchronous transforms, provider
isolation, sorting, fallback behavior, renderer metadata, song info, and
provider selection.
🧹 Nitpick comments (2)
lib/sloppak.py (2)

927-979: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider extracting this block into a dedicated helper.

The primary/alias/dedup/promotion resolution is dense (flagged by the line-range complexity hint) and buried inside an already-long load_song(). Pulling it into a _resolve_drum_parts(source_dir, drum_tab_rel, drum_tab_data, drum_pointer_entries) helper alongside _load_drum_tab_file() would isolate it for independent unit testing and shrink load_song().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/sloppak.py` around lines 927 - 979, Extract the drum-part resolution
block from load_song() into a dedicated _resolve_drum_parts(source_dir,
drum_tab_rel, drum_tab_data, drum_pointer_entries) helper near
_load_drum_tab_file(). Preserve the existing primary alias matching,
deduplication, extra-part loading, pointer-only promotion, and
drum_parts/drum_tab results, then replace the inline logic in load_song() with a
call to the helper.

811-819: 📐 Maintainability & Code Quality | 🔵 Trivial

Silent drop for drum_tab-bearing entries with the wrong/missing type.

An entry with no file/notation and a string drum_tab but a type outside {"drums","drum"} (or missing) is dropped with no log — unlike every other malformed-entry path in this loop (e.g. Line 804), which warns. A pack author mistyping type would get silently-missing drums with no diagnostic.

🔎 Proposed diagnostic
             _etype = str(entry.get("type") or "").strip().lower()
             if _etype in ("drums", "drum") and isinstance(entry.get("drum_tab"), str):
                 drum_pointer_entries.append(entry)
+            elif isinstance(entry.get("drum_tab"), str):
+                log.warning(
+                    "sloppak: arrangement entry has drum_tab %r but type=%r — ignored",
+                    entry.get("drum_tab"), entry.get("type"),
+                )
             continue
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/sloppak.py` around lines 811 - 819, Update the entry-filtering logic
around the _etype and drum_pointer_entries handling to detect entries with a
string drum_tab but no file/notation when type is missing or outside
“drums”/“drum”. Emit the same warning used by the malformed-entry path near line
804, then prevent the invalid entry from being silently dropped while preserving
valid drum-pointer collection and the fretted-pipeline skip.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/routers/ws_highway.py`:
- Around line 606-634: Update the drum-tab message construction around
_dt_part_id and the loaded_slop.drum_parts check so part_id is added to _dt_msg
only when the pack contains multiple drum parts. Preserve the existing
primary-part selection and ensure legacy packs with a synthesized single-element
drum_parts list omit part_id entirely.

In `@lib/sloppak.py`:
- Around line 925-979: Ensure resolved drum part IDs are unique before appending
entries to _extra_parts or _parts, covering both explicit IDs and generated
drums-N fallbacks. Update the ID generation in the drum_parts resolution block
to detect collisions and assign the next unused identifier, so drum_part
selection remains unambiguous.

---

Outside diff comments:
In `@CHANGELOG.md`:
- Around line 22-28: Restore a complete lead-in and separate changelog bullet
for the chart-transform behavior before the continuation beginning
“coordinator.” Ensure the drum-parts bullet ends cleanly, and preserve the
existing chart-transform details about synchronous transforms, provider
isolation, sorting, fallback behavior, renderer metadata, song info, and
provider selection.

---

Nitpick comments:
In `@lib/sloppak.py`:
- Around line 927-979: Extract the drum-part resolution block from load_song()
into a dedicated _resolve_drum_parts(source_dir, drum_tab_rel, drum_tab_data,
drum_pointer_entries) helper near _load_drum_tab_file(). Preserve the existing
primary alias matching, deduplication, extra-part loading, pointer-only
promotion, and drum_parts/drum_tab results, then replace the inline logic in
load_song() with a call to the helper.
- Around line 811-819: Update the entry-filtering logic around the _etype and
drum_pointer_entries handling to detect entries with a string drum_tab but no
file/notation when type is missing or outside “drums”/“drum”. Emit the same
warning used by the malformed-entry path near line 804, then prevent the invalid
entry from being silently dropped while preserving valid drum-pointer collection
and the fretted-pipeline skip.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 32042956-5b4b-4531-923b-514debd1a307

📥 Commits

Reviewing files that changed from the base of the PR and between a9be210 and e0f1e2b.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • lib/routers/ws_highway.py
  • lib/sloppak.py
  • tests/test_sloppak_drum_parts.py

Comment thread lib/routers/ws_highway.py
Comment thread lib/sloppak.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/sloppak.py (1)

876-891: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Drum-pointer entries should be gated by type first

if not rel and not has_notation_key lets a type: drums entry with a stray notation string bypass the drum-pointer path and fall through to arrangement_from_wire(...), appending an empty fretted arrangement instead of keeping the part out of the fretted pipeline. That breaks the stated invariant.

  • type: drums with a missing/invalid drum_tab is still dropped silently; add a warning there too.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/sloppak.py` around lines 876 - 891, Update the arrangement-entry handling
before the rel/has_notation_key branching so type values drums or drum always
use the drum-pointer path and never reach arrangement_from_wire. Append only
valid string drum_tab entries to drum_pointer_entries; warn and drop drum
entries with missing or invalid drum_tab. Preserve existing handling for
non-drum entries and stray drum_tab values.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/sloppak.py`:
- Around line 793-816: Normalize both the pointer entry path and the top-level
drum_tab path before comparing them in the primary-match logic around
_load_drum_tab_file. Use the repository’s existing path-normalization convention
if available, so equivalent spellings such as "drum_tab.json" and
"./drum_tab.json" match; retain the existing primary id/name overrides and
prevent the matched entry from being appended to extra_parts.

---

Outside diff comments:
In `@lib/sloppak.py`:
- Around line 876-891: Update the arrangement-entry handling before the
rel/has_notation_key branching so type values drums or drum always use the
drum-pointer path and never reach arrangement_from_wire. Append only valid
string drum_tab entries to drum_pointer_entries; warn and drop drum entries with
missing or invalid drum_tab. Preserve existing handling for non-drum entries and
stray drum_tab values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 17d4de7b-6c00-4e45-aedc-8e18402c3505

📥 Commits

Reviewing files that changed from the base of the PR and between e0f1e2b and 1b8186b.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • lib/routers/ws_highway.py
  • lib/sloppak.py
  • tests/test_highway_ws_drum_parts.py
  • tests/test_sloppak_drum_parts.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • CHANGELOG.md
  • lib/routers/ws_highway.py
  • tests/test_sloppak_drum_parts.py

Comment thread lib/sloppak.py
ChrisBeWithYou and others added 2 commits July 20, 2026 23:01
- Gate the drum-pointer skip on type FIRST: a type:drums/drum entry never becomes
  a fretted Arrangement even if it carries a note file/notation (with drum_tab it
  is collected as a drum part, without it dropped+warned). Closes the spec
  §5.2/§7.5 MUST-NOT hole (a malformed drums+file entry was being fretted-graded).
- Make test_drum_pointer_with_wrong_type_logs_warning robust (attach handler to the
  feedBack logger + set WARNING, restore in finally) and fix the root-cause level
  leak in test_tuning_provider_isolation.py (finally restored the handler but not
  the level, leaking ERROR onto the feedBack tree and turning the suite red under
  full ordering).
- Restore the chart-transform CHANGELOG bullet (#952) the drum entry had truncated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos
byrongamatos merged commit 605dbdf into main Jul 21, 2026
5 of 6 checks passed
@byrongamatos
byrongamatos deleted the feat/drum-parts-loader branch July 21, 2026 11:27
byrongamatos pushed a commit that referenced this pull request Jul 21, 2026
…1021) (#1028)

* feat(player): drum-part picker for multiple drum charts (feedpak 1.17.0)

The last mile of the multiple-drum-parts feature: let a player CHOOSE which
drum chart plays. #1020 taught the loader + highway WS to carry several drum
parts (song_info.drum_parts + ?drum_part=<id> + a part_id echo on drum_tab);
this adds the host-chrome selector that drives it.

A "Drum part" <select> sits beside the arrangement switcher in the advanced
settings popover, shown only when a song has 2+ drum charts (drum_parts is
always present — empty for non-drum songs — so single-drum / no-drum songs
hide the row and nothing changes for them). Selecting a part re-streams that
part's tab over the highway WS, exactly like an arrangement switch.

- static/highway.js:
  - reconnect() gains a third `drumPart` arg → sets `?drum_part=<id>` on the WS
    URL (mirrors the existing `arrangement` param one line up). Empty/undefined
    → the primary part, i.e. byte-identical to today for any pack untouched.
  - song_info handler populates #drum-part-select from msg.drum_parts and
    shows/hides #v3-drum-part-row on `length > 1` (parallel to the #arr-select
    block right above it).
  - drum_tab handler carries msg.part_id onto hwState.drumTab (plugins can read
    bundle.drumTab.part_id) and reflects it as the picker's selected value, so
    the dropdown stays honest even when the server resolves an unknown/absent
    selection to the primary.
- static/app.js:
  - changeArrangement() gains an optional `drumPart`; at reconnect it forwards
    the explicit part, else preserves the current picker selection — so an
    ARRANGEMENT switch keeps the chosen drum part (parts are song-level).
  - new changeDrumPart(id) delegates to changeArrangement with the current
    arrangement held + the new part applied (a part switch is the same
    re-stream, so it reuses all the transition ceremony). Exported on window.
- static/v3/index.html: the #drum-part-select row (hidden by default).

No plugin change: the drum renderers just draw whatever drum_tab streams.

RUNTIME-VERIFIED (Playwright, the core player, a 2-drum pack + a no-drum pack):
10/10 — the picker populates with both parts and shows for the multi-drum song;
song_info.drum_parts reaches getSongInfo(); the primary is pre-selected;
selecting the 2nd part drives highway.reconnect with the id and the WS URL
carries `?drum_part=drums-2`; the picker then reflects the server's part_id
echo; a no-drum song hides the row; no page errors. ESLint 0 errors (the two
max-lines warnings are pre-existing on these files). No pytest touched (JS-only).
Stacked on #1020.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xGPjDBF8NTwTK7VQvizix
Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>

* Update reconnect source contract test

---------

Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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