Skip to content

feat(editor): author teaching marks fg/ch/sd in the note inspector (§6.2.2) - #16

Merged
byrongamatos merged 1 commit into
mainfrom
feat/teaching-marks-authoring
Jun 21, 2026
Merged

feat(editor): author teaching marks fg/ch/sd in the note inspector (§6.2.2)#16
byrongamatos merged 1 commit into
mainfrom
feat/teaching-marks-authoring

Conversation

@byrongamatos

Copy link
Copy Markdown
Collaborator

Part of got-feedBack/feedBack#334

PR 3 of 3 for the teaching marks epic (feedpak 1.5.0 §6.2.2). Adds editor authoring for the three optional per-note marks, mirroring the bend-shape authoring (#13). Display only — these never affect grading.

Pairs with core PR got-feedBack/feedBack#536 (wire) + #538 (render). The getattr load/signature sites are hardened with a -1 default so this loads cleanly even against a core build that predates the marks.

Authoring (screen.js)

The note inspector gains a Teaching marks section:

  • Finger (fg) — a fret-hand-finger picker (Unset / Thumb / Index / Middle / Ring / Pinky).
  • Scale deg. (sd) — a numeric override field (-1..11); blank / -1 = auto-derive (the highway derives it from the active key when no override is set).
  • Strum group (ch)Group assigns every selected note a shared, unused ch key (one strum/rake gesture; pkd gives direction) and Ungroup clears it. The header shows the selection's shared group (#N / / mixed).

Each edit is one undoable batch (SetTeachingMarkCmd, snapshot/rollback per note) routed through S.history.exec. New pure helpers live in a @pure:teaching-marks block: FRET_FINGER_OPTIONS and nextUnusedStrumGroup.

Save round-trip (routes.py)

  • fret_finger / strum_group / scale_degree join _NOTE_TECH_FIELDS, so they load (_tech_dict) and participate in the content signature used for arrangement alignment.
  • The sloppak wire serializer emits fg/ch/sd default-omitted (matching core's note_to_wire), for both standalone and chord notes.
  • The chart-XML export writes fretFinger (core's _parse_note reads it back); ch/sd are wire-only (no core chart-XML attribute), as intended.
  • getattr-over-_NOTE_TECH_FIELDS sites default to -1 so a core build predating the marks can't break load/alignment.

Tested locally

  • python -m pytest tests/65 passed, 2 skipped — includes new test_xml_export.py cases: fg/ch/sd round-trip through the wire, default-omit when unset, and fretFinger through chart XML.
  • node tests/teaching_marks.test.js5 passed (FRET_FINGER_OPTIONS order + nextUnusedStrumGroup edge cases via the @pure extract).
  • node tests/bend_shape.test.js still passes; node --check screen.js clean.
  • Independent Codex review (read-only): no P1/P2/P3 findings (undo snapshot/rollback, signature consistency, wire/XML round-trip, strum-key logic all verified).

Scope notes (per the epic)

  • sd is override + display only — full "auto from an active-key lane" UI is out of scope (there is no key-editing lane yet).

🤖 Generated with Claude Code

…6.2.2)

Author the three optional per-note teaching marks, mirroring the bend-shape
authoring (#13). Display only — these never affect grading.

- screen.js: a @pure:teaching-marks block (FRET_FINGER_OPTIONS +
  nextUnusedStrumGroup) and SetTeachingMarkCmd (one undoable batch edit per
  field, snapshot/rollback per note). Inspector gains a fret-hand-finger picker
  (fg, -1..4), a scale-degree override field (sd, -1..11; blank/-1 = auto), and
  Group/Ungroup-as-strum buttons (ch — Group assigns the next unused key across
  the selection, Ungroup clears it). All routed through S.history.exec.
- routes.py: fret_finger/strum_group/scale_degree join _NOTE_TECH_FIELDS so they
  load (_tech_dict) and feed the content signature; the wire serializer emits
  fg/ch/sd default-omitted (matching core), and the chart-XML export writes
  fretFinger (core's _parse_note reads it). getattr-over-fields sites default to
  -1 so a core build predating the marks doesn't break load/alignment.

Tests: tests/teaching_marks.test.js (@pure extract — FRET_FINGER_OPTIONS +
nextUnusedStrumGroup edge cases); test_xml_export.py round-trip for fg/ch/sd
through the wire + fretFinger through chart XML, plus default-omit assertions.

Part of got-feedBack/feedBack#334

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos
byrongamatos merged commit 4abd064 into main Jun 21, 2026
@byrongamatos
byrongamatos deleted the feat/teaching-marks-authoring branch June 21, 2026 05:58
byrongamatos added a commit that referenced this pull request Jun 21, 2026
…itted (#17)

Post-merge Codex review of #16 noted the backend serializer emitted fg/ch/sd
whenever != -1, so a malformed or hand-edited out-of-range value (the inspector
clamps, but loaded data may not) could be saved as a schema-invalid teaching mark
(spec §6.2.2: fg 0–4, sd 0–11, ch ≥ 0).

- _arr_dict_to_wire: emit fg only when 0≤fg≤4, ch only when ≥0, sd only when
  0≤sd≤11; out-of-range is treated as unset (omitted) rather than emitted or
  fabricated into a nearest finger.
- chart-XML fretFinger: new _fret_finger_attr collapses out-of-range to -1 so it
  can't round-trip an invalid finger into core's _parse_note.

+2 tests (out-of-range omitted / collapsed; in-range boundaries still ride).
60 xml-export tests pass. Codex-reviewed: clean.

Part of got-feedBack/feedBack#334.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
byrongamatos added a commit that referenced this pull request Jun 21, 2026
…6) (#21)

Add E1 chord-inspector authoring for the two feedpak 1.7.0 harmony annotations,
mirroring the teaching-marks editor work (#16/#17):

- voicing (TEMPLATE): a select in the chord inspector, undoable via the existing
  EditChordTemplateCmd. Added to relinkChordTemplate's carry-forward set so the
  save rebuild doesn't BLANK it (same E0 gotcha as name/displayName/fingers/arp).
- fn (INSTANCE): rn/q/deg inputs, one undo unit (EditChordFnCmd). fn rides the
  chord instance, so it's keyed by chord time in a transient arr._chordFn map
  (one chord per time) rather than stamped on movable notes — a note dragged out
  of a chord can't carry a stale fn into another group. reconstructChords reads
  the map back and prunes orphans for times that no longer hold a chord.

routes.py round-trips both: _song_to_dict hydrates chord fn + template voicing on
load; _arr_dict_to_wire emits them default-omitted and range-guarded (new
_chord_fn_wire mirrors core's _validate_fn — never a partial/out-of-range fn;
voicing only when a non-empty string). Display only — no scoring path touched.

Client-side deg auto-fill is deferred: the editor has no active-key/tonic
accessor (per-note sd auto-derivation is server-side), so deg is manual 0..11.

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