feat(notation_lift): authored per-note hands steer the split — heuristic only guesses the rest#992
feat(notation_lift): authored per-note hands steer the split — heuristic only guesses the rest#992ChrisBeWithYou wants to merge 1 commit into
Conversation
…tic only guesses the rest The keys LH/RH hand arc, the lift slice. split_hands was purely heuristic (mean pitch vs middle C / largest-gap), so ANY edit to a keys arrangement re-derived hand splits that could contradict the score's authored grand-staff assignment — the documented "produces wrong hand splits" failure, now that authored hands actually reach the wire (editor #299 emits per-note `hand`; core #990 round-trips it). - decode_wire_notes carries `hand` through ('lh'/'rh' strict enum; junk → None so a hand-edited pack can't steer the split). - split_hands: an authored hand always wins, and explicit notes are REMOVED from their simultaneous group BEFORE the heuristic math runs — one authored assignment must never skew its chordmates' guesses (e.g. an authored LH melody note above middle C dragging the group mean down and flipping the rest). All-explicit groups skip the heuristic entirely; unassigned notes behave exactly as before. Design per the piano-pedagogy review of the arc: binary lh/rh + absent = unassigned; per-note explicit > heuristic precedence; crossing-hands textures are exactly why the override is load-bearing. Tests: five new in test_notation_lift.py (authored wins incl. a crossing-hands case, group-removal-before-math with exact mean arithmetic, all-explicit group, junk enum, decode carry-through); the decode shape pin updated for the new key. Suite: 1723 passed (+5 vs main; the pre-existing env failures reproduce identically on pristine main). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q Signed-off-by: ChrisBeWithYou <chris@rifflarr.local>
📝 WalkthroughWalkthroughWire note decoding now preserves valid authored hand assignments for notes and chord notes. ChangesAuthored hand flow
Estimated code review effort: 2 (Simple) | ~15 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
lib/notation_lift.py (1)
78-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract shared
VALID_HANDSconstant. The("lh", "rh")enum literal is duplicated betweendecode_wire_notesandsplit_hands; both gatekeep the same authored-hand contract independently.
lib/notation_lift.py#L78-L81: use a sharedVALID_HANDS = ("lh", "rh")module constant instead of the inline tuple.lib/notation_lift.py#L134-L134: reference the sameVALID_HANDSconstant here to keep the two enum checks from drifting apart.♻️ Proposed fix
+VALID_HANDS = ("lh", "rh") + def decode_wire_notes(arr_data: dict) -> list[dict]: ... - "hand": hand if hand in ("lh", "rh") else None, + "hand": hand if hand in VALID_HANDS else None,for n in full_group: - if n.get("hand") in ("lh", "rh"): + if n.get("hand") in VALID_HANDS: hands[n["hand"]].append(n)🤖 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/notation_lift.py` around lines 78 - 81, Define a module-level VALID_HANDS = ("lh", "rh") constant in lib/notation_lift.py, then update decode_wire_notes at lines 78-81 and split_hands at line 134 to reference it instead of inline tuples; both sites require direct changes and behavior must remain unchanged.
🤖 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 `@lib/notation_lift.py`:
- Around line 78-81: Define a module-level VALID_HANDS = ("lh", "rh") constant
in lib/notation_lift.py, then update decode_wire_notes at lines 78-81 and
split_hands at line 134 to reference it instead of inline tuples; both sites
require direct changes and behavior must remain unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 13bc7e6d-4062-4207-8330-4b40be8f1572
📒 Files selected for processing (2)
lib/notation_lift.pytests/test_notation_lift.py
What
The keys LH/RH hand arc, lift slice.
split_handswas purely heuristic (mean-pitch vs middle C / largest-gap), so any edit to a keys arrangement re-derived hand splits that could contradict the score's authored grand-staff assignment — the documented "produces wrong hand splits" path. Now that authored hands reach the wire (editor #299 emits per-notehand, core #990 round-trips it), the lift honors them:decode_wire_notescarrieshandthrough ('lh'/'rh'strict enum; junk →None— a hand-edited pack can never steer the split).split_hands: an authored hand always wins, and explicit notes are removed from their simultaneous group before the heuristic math runs — one authored assignment must never skew its chordmates' guesses (e.g. an authored LH melody note above middle C dragging the group mean down and flipping the rest — the crossing-hands texture that makes the override load-bearing). All-explicit groups skip the heuristic entirely; charts with no hand data behave exactly as before (existing heuristic tests untouched and green).Relationship to the other arc PRs
Independent and mergeable in any order with #990 / editor #299 / musicxml-import #8 — it reads the raw wire dicts, not the
Notemodel. Together they complete: authored hands survive import → edit → save → relift.Tests
Five new in
tests/test_notation_lift.py: authored-wins (incl. crossing hands), group-removal-before-math (exact mean arithmetic that would flip without removal), all-explicit group, junk-enum fallback, decode carry-through. The decode shape pin updated for the new key. Suite: 1723 passed (+5 vs main; pre-existing env failures identical on pristine main).🤖 Generated with Claude Code
https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
Summary by CodeRabbit
New Features
Bug Fixes
Tests