Skip to content

feat(song): wire caged + guideTones chord-template fields (§6.6) - #544

Merged
byrongamatos merged 1 commit into
mainfrom
feat/template-caged-guidetones-wire
Jun 21, 2026
Merged

byrongamatos merged 1 commit into
mainfrom
feat/template-caged-guidetones-wire

Conversation

@byrongamatos

Copy link
Copy Markdown
Contributor

Part of #334

First of three stacked PRs completing the deferred FEP #24 pair (caged + guideTones), mirroring the already-shipped voicing field end-to-end.

What this PR does (core wire)

lib/song.py:

  • ChordTemplate dataclass: add caged: str = "" and guideTones: list = field(default_factory=list) next to voicing.
  • chord_template_to_wire: emit caged/guideTones only when non-empty (default-omitted, like voicing).
  • arrangement_from_wire decoder: sanitize on the way in —
    • caged accepted only when it's one of C/A/G/E/D, else "" (_sanitize_caged).
    • guideTones kept only as int entries in 0..11, dropping non-ints (bool rejected — it's an int subclass) and out-of-range values (_sanitize_guide_tones). A malformed value can't round-trip.
  • GP import untouched — GP carries no CAGED / guide-tone data (mirrors voicing).

Teaching annotations only — not wired into any scoring / NoteVerifier path.

Tests

tests/test_song.py — for each field, mirrors the three voicing tests: round-trip, omit-when-default, and malformed-tolerance (parametrized).

Verified locally

  • pytest tests/test_song.py153 passed (incl. 9 new).
  • Independent Codex read-only diff review → no P1/P2/P3 regressions.

(CI may be red on infra; the above is what I verified locally.)

🤖 Generated with Claude Code

Mirror the voicing field for the two deferred FEP #24 harmony annotations on
ChordTemplate:

- caged: str ("C"/"A"/"G"/"E"/"D", "" = unset)
- guideTones: list[int] (semitone offsets 0..11 above the root, [] = unset)

Both are default-omitted on the wire and sanitized on decode (caged enum-guarded,
guideTones filtered to in-range ints, rejecting bool) so a malformed value can't
round-trip. GP import is untouched — GP carries no CAGED / guide-tone data.
Teaching annotations only; never fed to a grader.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos
byrongamatos force-pushed the feat/template-caged-guidetones-wire branch from 914bee7 to cbb2e6d Compare June 21, 2026 09:51
@byrongamatos
byrongamatos merged commit 4195b73 into main Jun 21, 2026
0 of 3 checks passed
@byrongamatos
byrongamatos deleted the feat/template-caged-guidetones-wire branch June 21, 2026 09:58
byrongamatos added a commit that referenced this pull request Jun 21, 2026
…follow-up) (#547)

Post-merge Codex review of #544 found chord_template_to_wire emitted ct.caged and
ct.guide_tones raw — so a directly-constructed ChordTemplate(caged="X") or
guide_tones=[99] would write a schema-invalid value to the feedpak wire, even
though the decoder guards on input. The spec constrains caged to C/A/G/E/D and
guideTones to 0..11.

Run the same _sanitize_caged / _sanitize_guide_tones guards on emit: caged is
written only when a valid enum value, guideTones only as the in-range ints (empty
result -> key omitted). +1 test (invalid caged dropped, mixed guideTones filtered to
the valid in-range subset, wholly-invalid list omitted).

Codex-reviewed: clean. 154 song tests pass.

Part of #334.

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