feat(editor): keys hand authoring — set, stamp, shade; hand edits refresh the notation - #302
Closed
ChrisBeWithYou wants to merge 2 commits into
Closed
feat(editor): keys hand authoring — set, stamp, shade; hand edits refresh the notation#302ChrisBeWithYou wants to merge 2 commits into
ChrisBeWithYou wants to merge 2 commits into
Conversation
… the per-note hand field arrives The keys LH/RH hand-assignment arc, slice A. MusicXML grand-staff scores carry authored per-stave hand assignments; until now the import deleted them (`delete arr.notation`) and every save re-derived hand splits with core's mean-pitch heuristic — the documented "produces wrong hand splits" path. Authored notation now rides the existing GP rail end to end: - src/import.js keeps the payload, shifts its measure/beat times with the notes (new @pure `_shiftNotationTimes`, the JS mirror of `_warp_notation_sidecar`'s walk — an unshifted payload would be born stale against the fingerprint), and lets add-arrangement stamp it. - add-arrangement stamps `source:"musicxml"` + `source_notes_fp` at the one moment payload and notes are known in sync (new `_stamp_musicxml_notation`, the MusicXML counterpart of `_attach_gp_notation`) and returns it; the client carries it as `_gp_notation`, which the save body already ships. - The authored-over-lift rail generalizes its source check to `_AUTHORED_NOTATION_SOURCES` ({"gp","musicxml"}) and preserves the ORIGINAL provenance on every re-stamp (kept-verbatim, measure-merge) instead of rewriting it to "gp". Fingerprint invalidation and the measure-granular merge behave identically for both sources. And the per-note `hand` field ('lh'/'rh', absent = unassigned) becomes real editor data: registered in `_NOTE_TECH_FIELDS` (string-valued, default None — the signature builders now share `_note_tech_default` so object- and dict-shaped notes can't drift), emitted on the save wire as the spelled-out `hand` key (`rh` is taken by right_hand), strictly validated to the enum, and it survives `reconstructChords` because it rides `techniques`. MusicXML imports arrive with it pre-filled from staff provenance (staff 1→rh, 2→lh; organ-pedal staves stay unassigned rather than guessed). Companion PR: feedBack-plugin-musicxml-import #8 (the parse-arrangement endpoint that produces the arrangement + payload; without it the MusicXML path stays "plugin not installed" exactly as before). Tracked follow-ups (in CHANGELOG): core sloppak loader learns `hand` for reload round-trips; hand EDITS don't yet invalidate a preserved authored sidecar (the fingerprint deliberately ignores techniques); `split_hands` respecting per-note overrides. Tests: tests/test_musicxml_notation.py (23 — registry invariants, wire validation incl. junk rejection, chord-member ride-along, stamping, and the rail honoring/invalidating source:"musicxml" against the real core lift) + tests/musicxml_hand_import.test.mjs (7 — notation shift semantics, hand through reconstructChords/flatten round-trips). Full gates: 325 pytest green, JS suite green (song_fit's failure reproduces on pristine main — environmental), lint 0 errors / 3 baseline warnings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
…efresh the notation The hand arc, Step B (stacked on feat/keys-hand-import / #299). The per-note `hand` field becomes authorable and visible: - Note ▸ Hand (keys): Left / Right / Clear on the selection — one undo step via SetTechScalarCmd. Runtime-guarded to keys arrangements. - Track ▸ Assign hands by split…: the split point is a STAMPING generator, never a live layer (the piano-pedagogy call): pick a split note (default C4, note names or MIDI accepted — new @pure _parseSplitPitchPure), and every targeted note takes lh below / rh at-or-above in ONE undoable command. New SetTechScalarPerNoteCmd — SetTechScalarCmd's per-note-values sibling (no bend special-casing; plain scalar marks only). Selection scopes the stamp; per-note edits win from then on. - Hand shading on the piano roll (View ▸, default ON): LH warm (#e8965a) / RH cool (#5a9de8) body color; unassigned keeps the octave palette so "no call made" never reads as an assignment. Same color-job argument as fretted-in-roll: Y already says the pitch, so color says WHO PLAYS IT. - `hand` joins `_notes_fingerprint`'s identity tuple — the ONE technique that does — closing the tracked trap where a hand EDIT couldn't invalidate a preserved authored sidecar (it would freeze the old hands forever). Editor/wire shapes stay fingerprint-identical; junk enums read unassigned on both. One-time effect: previously stamped payloads re-fingerprint on next save and take the measure-granular merge (unedited bars keep authored hands). The relift honors per-note hands via core split_hands (core PR #992). New src/hand.js module; menus in menu-bar.js; USER-GUIDE "Hands on keys tracks" section. Tests: tests/hand_authoring.test.mjs (4 — parser incl. accidentals and junk, stamp orientation matching core, per-note command exec→undo→redo round-trip incl. a bare-techniques note) + a fingerprint-invalidation pytest (hand flips it; other techniques and junk don't; shapes agree). Gates: 326 pytest, JS suite green (song_fit env failure = pristine main), lint 0 errors / 3 baseline warnings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
byrongamatos
force-pushed
the
feat/keys-hand-import
branch
from
July 16, 2026 18:44
e92816d to
52ca211
Compare
This was referenced Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The hand arc, Step B — stacked on #299 (base =
feat/keys-hand-import; merge that first, then this). The per-notehandfield #299 introduced becomes authorable and visible:C4; note names with accidentals or raw MIDI — new@pure _parseSplitPitchPure), and every targeted note takeslhbelow /rhat-or-above in one undoable command (newSetTechScalarPerNoteCmd, SetTechScalarCmd's per-note-values sibling). Selection scopes the stamp; per-note edits win from then on.handjoins_notes_fingerprint's identity tuple — the one technique that does — closing the trap flagged in feat(editor): MusicXML keys imports keep their authored hand splits — the per-note hand field arrives #299's changelog where a hand edit couldn't invalidate a preserved authored sidecar (it would freeze the old hands forever). Editor/wire shapes stay fingerprint-identical; junk enums read unassigned on both. One-time effect: previously stamped payloads re-fingerprint on their next save and take the measure-granular merge (unedited bars keep their authored hands). The relift then honors per-note hands via core #992.The pipeline this completes
MusicXML authored hands (musicxml-import #8 → #299) · reload round-trip (core #990) · hand-aware relift (core #992) · authoring + visibility + honest invalidation (this PR). Next slice: hands-separate practice (mute one hand, with grading masked).
Tests
tests/hand_authoring.test.mjs(parser incl. accidentals + junk; stamp orientation pinned to coresplit_hands's>= middle C → rh; per-note command exec→undo→redo round-trip incl. a bare-techniques note) + a fingerprint pytest (hand flips it, other techniques and junk don't, editor/wire shapes agree). Gates: 326 pytest, JS suite green (song_fitenv failure reproduces on pristine main), lint 0 errors / 3 baseline warnings. USER-GUIDE §5 gains "Hands on keys tracks".🤖 Generated with Claude Code
https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q