Skip to content

feat(core): carry chord harmony fn + template voicing on the wire (§6.3.1, §6.6) - #540

Merged
byrongamatos merged 1 commit into
mainfrom
feat/chord-harmony-wire
Jun 21, 2026
Merged

feat(core): carry chord harmony fn + template voicing on the wire (§6.3.1, §6.6)#540
byrongamatos merged 1 commit into
mainfrom
feat/chord-harmony-wire

Conversation

@byrongamatos

Copy link
Copy Markdown
Contributor

Part of #334

PR 1 of 3 (stacked) implementing the per-chord harmony annotations from feedpak 1.7.0 (FEP #24). This PR is the core wire carry; renderer (PR2) and editor authoring (PR3) follow.

What

Two OPTIONAL per-chord harmony annotations, mirroring the teaching-marks (fg/ch/sd) wire work (#536):

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

Honesty rule

Display/teaching only. Nothing here is wired into scoring / NoteVerifier.

Deferral: fn auto-derivation = carry-only

The spec requires all three keys when fn is present. deg could be derived from the chord root, but a complete rn/q needs real chord-quality (interval) analysis — out of scope — and a deg-only fn would be schema-invalid. So server.py is unchanged: it carries author-provided fn and derives nothing. Author-provided fn always wins by construction. caged/guideTones from the FEP are also deferred per the spec.

GP import is unchanged — GP files have no reliable per-chord function/voicing.

Tested locally

  • pytest tests/test_song.py134 passed (round-trip, omit-when-default, malformed/partial/out-of-range → None, emit-side validation of directly-constructed chords, voicing fallback).
  • Independent Codex review (read-only, diff-only): caught one P3 (emit path didn't re-validate fn) — fixed by validating on emit too; re-review clean.
  • The pre-existing unrelated tests/test_diagnostics_redact.py::test_song_filename_redacted_consistently failure on clean main is not touched here.

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

🤖 Generated with Claude Code

….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>
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