Skip to content

feat(highway): render chord harmony fn.rn + voicing on 2D + 3D (§6.3.1, §6.6)#541

Merged
byrongamatos merged 2 commits into
mainfrom
feat/chord-harmony-render
Jun 21, 2026
Merged

feat(highway): render chord harmony fn.rn + voicing on 2D + 3D (§6.3.1, §6.6)#541
byrongamatos merged 2 commits into
mainfrom
feat/chord-harmony-render

Conversation

@byrongamatos

Copy link
Copy Markdown
Contributor

Part of #334

PR 2 of 3 (stacked on #540). Renders the per-chord harmony annotations PR1 put on the wire. Editor authoring (PR3) follows.

Stacked: based on feat/chord-harmony-wire (#540), not main. Review/merge #540 first; this diff shows only the render changes.

What

Draws two chord-level harmony labels, mirroring the teaching-marks render (#538):

  • fn.rn — the chord instance's harmonic-function Roman numeral (e.g. ii7, V7).
  • voicing — the template's key-independent voicing string (e.g. open, drop2).

Both are stacked above the chord name on the 2D (static/highway.js) and 3D (plugins/highway_3d/screen.js) highways. A shared pure helper chordHarmonyLabels(fn, voicing) formats them ('' when absent/malformed) and is node-tested against both files via the extract-and-eval pattern.

Gating

Both labels are gated behind the existing teaching-marks opt-in (_showTeachingMarks / teachingMarksVisible bundle flag) — they're chord-level teaching overlays, same class as sd/ch, so they don't clutter the default highway.

Honesty rule

Render only. No scoring / NoteVerifier path is touched.

Tested locally

  • node tests/js/highway_chord_harmony.test.js6 passed (rn+voicing surfaced, whitespace trimmed, absent/non-string → '', both 2D and 3D helpers agree).
  • node tests/js/highway_teaching_marks.test.js still passes.
  • node --check static/highway.js and node --check plugins/highway_3d/screen.js clean.
  • Independent Codex review (read-only, diff-only): caught one P2 (empty-note chord could deref sorted[0].f in the 2D fallback) — fixed by gating on sorted.length > 0; re-review clean.

CI may be red on infra; the above was verified locally.

🤖 Generated with Claude Code

byrongamatos and others added 2 commits June 21, 2026 10:26
….3.1, §6.6)

Add two OPTIONAL per-chord harmony annotations (feedpak 1.7.0), mirroring the
teaching-marks (fg/ch/sd) wire work:

- Chord.fn (instance): {rn, q, deg} harmonic-function object, key-dependent.
  Validated by _validate_fn on BOTH decode and emit so a partial / out-of-range
  fn (which would fail the schema's required-keys rule) never rides the wire.
  Default-omitted, mirroring bend bnv.
- ChordTemplate.voicing (template): key-independent voicing-type string
  ("open", "triad", "shell", "drop2", "barre", ...). Emitted only when
  non-empty; non-string wire values fall back to "".

Display/teaching only — never fed to a grader (honesty rule). fn auto-derivation
is DEFERRED (carry-only): a complete rn/q needs chord-quality analysis, and a
deg-only fn would be schema-invalid, so server.py carries author-provided fn
unchanged. GP import unchanged (no reliable per-chord function/voicing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…1, §6.6)

Draw the chord's harmonic-function Roman numeral (instance fn.rn) and its
template voicing string, stacked above the chord name on both highways. A shared
pure helper chordHarmonyLabels(fn, voicing) formats the two labels (empty when
absent/malformed) and is node-tested against both files.

Both labels are gated behind the EXISTING teaching-marks opt-in
(_showTeachingMarks / teachingMarksVisible bundle flag) — they're chord-level
teaching overlays, same class as sd/ch, so they stay off the default highway.
2D guards the empty-note-chord case; 3D reuses the gold chord-label sprite style.

Render only — no scoring / NoteVerifier path is touched (honesty rule).

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.

1 participant