Skip to content

fix(editor): drum-transcription delete is undoable; deleted drums stay deleted after Save - #306

Merged
byrongamatos merged 3 commits into
mainfrom
feat/editor-drum-delete-undo
Jul 17, 2026
Merged

fix(editor): drum-transcription delete is undoable; deleted drums stay deleted after Save#306
byrongamatos merged 3 commits into
mainfrom
feat/editor-drum-delete-undo

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

The bug (tester report: "I deleted my drum MIDI and can''t undo — that work is lost")

Two halves, both in the Tracks column''s drum-transcription delete:

  1. It reset the entire undo stack. The delete blanked S.drumTab in place and called S.history.reset() — so one confirm click lost not just the delete but the undo for every prior edit in the session. The reset was the shortcut for "commands in the stack hold references into the tab".
  2. The delete never persisted anyway. _buildSaveBody only shipped drum_tab when non-null, so a delete never reached the backend''s explicit-removal wire (a literal null unlinks drum_tab.json). Save after deleting → the pack keeps its drum tab → the "deleted" drums resurrect on the next load.

The fix

  • DeleteDrumTabCmd (songScope): the delete is one EditHistory command. Stack order now provides the guarantee the reset was buying — no older drum edit can be undone until the rollback has put the very same tab object back (identity restore, so older commands'' references into its hits stay valid). Captures + restores: the tab, the drumTabDirty flag (disk-clean returns clean), the drums mixer strip, the pairing map, and the tree (folder placement + display rename survive undo).
  • A dirty null ships on the save wire as the explicit removal; a clean untouched tab still ships nothing (preserve path unchanged). Also unstales the routes.py comment claiming the UI has no remove-drums control.

Tests

tests/drum_delete_undo.test.mjs — exec/rollback/redo round-trip through the real EditHistory with identity-restore pinned; prior commands stay undoable (fails on main — the reset emptied the stack); the dirty-null wire (fails on main — field was omitted) vs the clean preserve path. Full suite: 205 JS + 315 pytest green, lint 0 errors.

Note for review: arrangement-track deletion (the editorRemoveArrangement path) still resets history — that one splices backend session state and renumbers arrangement indices under every stacked command, so it needs its own design pass rather than a rider here.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Drum transcription deletion from the Tracks column is now undoable/redone without wiping existing undo history.
    • Deletions now persist after Save and reload, using explicit removal so old transcription data won’t reappear.
    • Undo/redo correctly restores drum settings, related track connections, and cleans up selection when needed.
  • Tests
    • Added automated coverage for delete/undo/redo, save/reload persistence, and cases where the drum tab was already marked dirty.
  • Documentation
    • Updated the changelog entry describing the fix.

…eted tab persists as an explicit removal

Two halves of the same tester report ("I deleted my drum MIDI and
cannot undo — that work is lost"):

1. The Tracks column''s drum delete blanked S.drumTab in place and
   RESET the entire undo stack — losing not just the delete but every
   prior edit''s undo. The reset was the shortcut for "commands in the
   stack hold references into the tab"; stack ORDER gives the same
   guarantee for free once the delete is itself a command: no older
   drum edit can be undone until the rollback has put the very same
   tab object back. New DeleteDrumTabCmd (songScope) captures the tab
   reference, the drumTabDirty flag, the drums mixer strip, the
   pairing map, and the tree — undo restores folder placement and
   display rename along with the chart.

2. _buildSaveBody only shipped drum_tab when it was non-null, so a
   delete never reached the backend''s explicit-removal wire (a literal
   null unlinks drum_tab.json): the pack kept its drum tab on disk and
   the "deleted" drums resurrected on the next load. A dirty null now
   ships; a clean untouched tab still ships nothing (preserve path).
   Also unstales the routes.py comment claiming the UI has no
   remove-drums control.

Tests: tests/drum_delete_undo.test.mjs — exec/rollback/redo round-trip
through the real EditHistory (identity restore pinned), prior commands
stay undoable (fails on main: the reset emptied the stack), and the
dirty-null wire vs the clean preserve path (the null leg fails on
main).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: ChrisBeWithYou <chris@rifflarr.local>
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0ae74a38-5f78-42a7-a956-1f94f4831857

📥 Commits

Reviewing files that changed from the base of the PR and between f4657e2 and 807a48d.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • routes.py
  • src/file-ops.js
  • src/track-session.js

📝 Walkthrough

Walkthrough

Drum transcription deletion now runs through an undoable command, preserving prior history and restoring related state on undo. Save payload generation includes dirty null drum-tab values as explicit removals, with tests covering undo/redo and persistence behavior.

Changes

Drum transcription deletion

Layer / File(s) Summary
Undoable deletion command
src/track-session.js, tests/drum_delete_undo.test.mjs
Adds DeleteDrumTabCmd to capture and restore drum-tab, mixer, pairing, dirty-state, selection, and session-tree data; deleteTrack() executes it through history when available.
Explicit save removal
src/file-ops.js, routes.py, CHANGELOG.md
Exports the save-body builder, serializes dirty null drum-tab state, documents the unlink behavior, and records both fixes in the changelog.
Deletion and persistence validation
tests/drum_delete_undo.test.mjs
Validates undo/redo restoration, history preservation, dirty-state handling, and drum_tab: null versus omitted save payloads.

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

Sequence Diagram(s)

sequenceDiagram
  participant TracksColumn
  participant EditHistory
  participant DeleteDrumTabCmd
  participant SharedState
  TracksColumn->>EditHistory: Execute DeleteDrumTabCmd
  EditHistory->>DeleteDrumTabCmd: exec()
  DeleteDrumTabCmd->>SharedState: Clear drum tab and related state
  EditHistory->>DeleteDrumTabCmd: rollback()
  DeleteDrumTabCmd->>SharedState: Restore captured state
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main user-facing fix: undoable drum deletion and persistence after Save.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/editor-drum-delete-undo

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

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/drum_delete_undo.test.mjs (1)

39-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise track-tree restoration with a non-default fixture.

trackSession: null cannot verify the command’s folder-placement/rename guarantee. Seed a customized drum row and assert the same tree is restored after both initial undo and redo→undo.

Also applies to: 71-96

🤖 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 `@tests/drum_delete_undo.test.mjs` around lines 39 - 52, Update the seed
fixture in seed() to initialize a non-default customized drum track row instead
of trackSession: null, including the folder placement and renamed-track
properties relevant to restoration. Extend both the initial undo and redo→undo
assertions to verify that this customized track tree is restored exactly,
including its folder placement and name.
🤖 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.

Nitpick comments:
In `@tests/drum_delete_undo.test.mjs`:
- Around line 39-52: Update the seed fixture in seed() to initialize a
non-default customized drum track row instead of trackSession: null, including
the folder placement and renamed-track properties relevant to restoration.
Extend both the initial undo and redo→undo assertions to verify that this
customized track tree is restored exactly, including its folder placement and
name.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 79059086-62b9-40d1-9781-4451525a5aa5

📥 Commits

Reviewing files that changed from the base of the PR and between a5bdae7 and 35cbdef.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • routes.py
  • src/file-ops.js
  • src/track-session.js
  • tests/drum_delete_undo.test.mjs

@byrongamatos
byrongamatos merged commit d23d994 into main Jul 17, 2026
@byrongamatos
byrongamatos deleted the feat/editor-drum-delete-undo branch July 17, 2026 21:50
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