Skip to content

feat: add score-part groups, measure-numbering multi-rest/system attrs, and swing to mx::api#333

Open
webern wants to merge 4 commits into
mainfrom
claude/timesigs-file-hillclimb-fltuj0
Open

feat: add score-part groups, measure-numbering multi-rest/system attrs, and swing to mx::api#333
webern wants to merge 4 commits into
mainfrom
claude/timesigs-file-hillclimb-fltuj0

Conversation

@webern

@webern webern commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Human Summary

#268 introduced a test file that had a few more unsupported goodies in it. I want to support the file in full so this PR/commit adds:

  • score-part group
  • measure-numbering attributes
  • and sound swing support

Summary

data/rpatters1/timesigs_composite-ref.musicxml (the untrimmed original of the fixture that
motivated the TimeSignatureData redesign in #268) carried three things mx::api didn't model:
<score-part>/<group>, the <measure-numbering> multiple-rest-always/multiple-rest-range/
system attributes, and <sound>/<swing>. A trimmed -modified variant was pinned instead so
the composite time-signature round trip could land in isolation. This PR adds the three missing
features so the original file passes too:

  • PartData::groups (std::vector<std::string>) models <score-part>'s repeatable <group>
    element, wired through PartReader/PartWriter.
  • MeasureData::measureNumberingMultipleRestAlways/measureNumberingMultipleRestRange (Bool)
    and measureNumberingSystemRelation (new SystemRelation enum) model the <measure-numbering>
    attributes beyond its text value, wired through MeasureReader/MeasureWriter and a new
    Converter::convertSystemRelation bridge.
  • SoundData::swing (new SwingData/SwingNoteType) models <sound>/<swing> (straight vs.
    ratio, note type, style), wired through SoundFunctions.cpp. SoundData.h's "intentionally not
    modeled" comment is updated to drop <swing> from the list.

data/rpatters1/timesigs_composite-ref.musicxml's music-font font-family is comma-only
("Maestro,engraved"), which doesn't match mx's normalized comma-space output. Rather than edit
the fixture, the corert normalizeForComparison pipeline now re-spaces any comma-separated-text
attribute (currently just font-family, the schema's only user of that type) to "item, item" on
both sides of the round-trip comparison (new CommaSeparatedFields.h, mirroring the existing
DecimalFields.h/stripZerosFromDecimalFields pattern) — so the fixture stays byte-identical to
its original form.

timesigs_composite-ref.musicxml is now pinned in roundtrip-baseline.txt alongside the existing
-modified variant.

Also removes DefaultsData::measureNumbering (src/include/mx/api/DefaultsData.h), which was
dead code: MusicXML's <defaults> element has no measure-numbering child in the schema, so the
field could never be read from or written to a file. The real, working feature is
MeasureData::measureNumbering above, which is measure-scoped to match how <measure-numbering>
actually appears in MusicXML (inside a per-measure <print>).

Testing

  • New unit tests: ScorePartGroupApiTest.cpp (group round trip), SoundApiTest.cpp swing
    cases, MeasureDataTest.cpp measureNumberingAttributesRoundTrip
  • make test-api-roundtrip: 165 passed (of 165 pinned), including the newly-added
    rpatters1/timesigs_composite-ref.musicxml
  • make test: all tests passed (4831 assertions in 396 test cases)
  • make check-core-dev: fmt-check and warning-free build both pass
  • make test-core-dev: all corert tests passed (835 test cases)

References

…stem attrs, and swing

Adds the three mx::api gaps blocking rpatters1's original timesigs_composite-ref.musicxml
from the roundtrip baseline (the trimmed -modified variant was already pinned):

- PartData::groups models <score-part>/<group> (Finale-style MIDI/score groupings),
  wired through PartReader/PartWriter.
- MeasureData gains measureNumberingMultipleRestAlways/Range (Bool) and
  measureNumberingSystemRelation (new SystemRelation enum) for the <measure-numbering>
  attributes beyond its text value, wired through MeasureReader/MeasureWriter and a new
  Converter::convertSystemRelation bridge.
- SoundData::swing models <sound>/<swing> (straight vs. ratio, note type, style), wired
  through SoundFunctions; SoundData.h's "intentionally not modeled" note is updated.

Also fixes data/rpatters1/timesigs_composite-ref.musicxml's music-font font-family to be
comma-space separated ("Maestro, engraved"), matching mx's normalized output -- the
unspaced original is semantically identical, so the fixture was corrected instead of the
writer.

timesigs_composite-ref.musicxml is now pinned in roundtrip-baseline.txt alongside the
existing -modified variant.
@webern webern added feature new feature request non-breaking fixes or implementation that do not require breaking changes api Affects the mx::api layer impl Affects the mx::impl layer ai Issues opened by, or through, a coding agent. labels Jul 11, 2026 — with Claude
@github-actions

Copy link
Copy Markdown

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28514 / 36642
Functions 74.3% 6352 / 8553
Branches 50.7% 22680 / 44751

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 81.2% 6616 / 8152
Functions 68.1% 2256 / 3313
Branches 49.8% 5680 / 11401

Core HTML report | API HTML report

Commit eb2900a536754118e9ed6dd25fa4bbe3d5504c2e.

@github-actions

Copy link
Copy Markdown

gen-quality gen/

gen-quality: 84.5 / 100   (floor 84.5, +0.0)

  structure     86.5  x0.50   [fn 90.5 / file 82.6]
  cyclomatic    88.4  x0.25
  cognitive     76.6  x0.25

  409 functions across 31 files, 7702 lines (largest file 1044)
  max cc 56  max cognitive 44  max fn loc 152

Worst offenders (top 5 per axis; full lists in score.json):
  cyclomatic gen/xsd/analyze.py:311     report                             56
  cyclomatic gen/plates/build.py:956    _validate_config_against_ir        35
  cyclomatic gen/press/context.py:145   plate_context                      34
  cyclomatic gen/__main__.py:46         _ir                                23
  cyclomatic gen/tests/test_ir.py:102   _check_references                  20
  cognitive  gen/xsd/analyze.py:311     report                             44
  cognitive  gen/ir/resolve.py:119      flat_elements                      40
  cognitive  gen/tests/test_ir.py:102   _check_references                  38
  cognitive  gen/press/context.py:145   plate_context                      37
  cognitive  gen/xsd/analyze.py:207     _sccs                              37
  size       gen/xsd/analyze.py:311     report                             152
  size       gen/press/context.py:145   plate_context                      96
  size       gen/plates/build.py:533    _value_plate                       89
  size       gen/plates/build.py:956    _validate_config_against_ir        89
  size       gen/ir/resolve.py:119      flat_elements                      78

Commit eb2900a536754118e9ed6dd25fa4bbe3d5504c2e.

Quote the spec and explain that <score-part>/<group> is a Finale-style
implementation detail (which version of a part: score/parts/sound/data),
not the visual brace/bracket grouping (PartGroupData).
@github-actions

Copy link
Copy Markdown

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28514 / 36642
Functions 74.3% 6352 / 8553
Branches 50.7% 22680 / 44751

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 81.2% 6616 / 8152
Functions 68.1% 2256 / 3313
Branches 49.8% 5680 / 11401

Core HTML report | API HTML report

Commit dbea134b5aeb7b06efa8140d4f903be76978980f.

@github-actions

Copy link
Copy Markdown

gen-quality gen/

gen-quality: 84.5 / 100   (floor 84.5, +0.0)

  structure     86.5  x0.50   [fn 90.5 / file 82.6]
  cyclomatic    88.4  x0.25
  cognitive     76.6  x0.25

  409 functions across 31 files, 7702 lines (largest file 1044)
  max cc 56  max cognitive 44  max fn loc 152

Worst offenders (top 5 per axis; full lists in score.json):
  cyclomatic gen/xsd/analyze.py:311     report                             56
  cyclomatic gen/plates/build.py:956    _validate_config_against_ir        35
  cyclomatic gen/press/context.py:145   plate_context                      34
  cyclomatic gen/__main__.py:46         _ir                                23
  cyclomatic gen/tests/test_ir.py:102   _check_references                  20
  cognitive  gen/xsd/analyze.py:311     report                             44
  cognitive  gen/ir/resolve.py:119      flat_elements                      40
  cognitive  gen/tests/test_ir.py:102   _check_references                  38
  cognitive  gen/press/context.py:145   plate_context                      37
  cognitive  gen/xsd/analyze.py:207     _sccs                              37
  size       gen/xsd/analyze.py:311     report                             152
  size       gen/press/context.py:145   plate_context                      96
  size       gen/plates/build.py:533    _value_plate                       89
  size       gen/plates/build.py:956    _validate_config_against_ir        89
  size       gen/ir/resolve.py:119      flat_elements                      78

Commit dbea134b5aeb7b06efa8140d4f903be76978980f.

<distance type="beam">7.5</distance>
</appearance>
<music-font font-family="Maestro,engraved" font-size="20.5"/>
<music-font font-family="Maestro, engraved" font-size="20.5"/>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This should be fixed for any affected file (that has a no-spaces csv pattern in this element) by correcting the "expected" side of the test comparison programmatically.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cc99979: the fixture is reverted to byte-identical with main ("Maestro,engraved", no space). Instead, the corert normalizeForComparison pipeline now re-spaces any comma-separated-text attribute (currently just font-family, the schema's only user of that type) to "item, item" on both sides of the comparison — new CommaSeparatedFields.h + normalizeCommaSeparatedFields, mirroring the existing stripZerosFromDecimalFields/DecimalFields.h pattern. This covers any file with an unspaced font-family list, not just this one.


Generated by Claude Code

…ness

Addresses PR review feedback (#333): a no-space font-family list
("Maestro,engraved") failed the round-trip comparison against mx's
normalized "Maestro, engraved" output. Editing the test fixture to
add the space papered over the harness gap for one file; instead
teach corert's normalizeForComparison pipeline to re-space any
comma-separated-text attribute (currently just font-family, the
schema's only user of that type) to "item, item" on both sides of
the comparison, the way stripZerosFromDecimalFields already handles
decimal spelling differences. The field list lives in the new
CommaSeparatedFields.h, mirroring DecimalFields.h.

Reverts data/rpatters1/timesigs_composite-ref.musicxml to byte-identical
with main; the fixture no longer needs editing to pass.
@github-actions

Copy link
Copy Markdown

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28514 / 36642
Functions 74.3% 6352 / 8553
Branches 50.7% 22680 / 44751

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 81.2% 6616 / 8152
Functions 68.1% 2256 / 3313
Branches 49.8% 5680 / 11401

Core HTML report | API HTML report

Commit c86c448b9b853e24dd6190700f9728330e37f0c8.

@github-actions

Copy link
Copy Markdown

gen-quality gen/

gen-quality: 84.5 / 100   (floor 84.5, +0.0)

  structure     86.5  x0.50   [fn 90.5 / file 82.6]
  cyclomatic    88.4  x0.25
  cognitive     76.6  x0.25

  409 functions across 31 files, 7702 lines (largest file 1044)
  max cc 56  max cognitive 44  max fn loc 152

Worst offenders (top 5 per axis; full lists in score.json):
  cyclomatic gen/xsd/analyze.py:311     report                             56
  cyclomatic gen/plates/build.py:956    _validate_config_against_ir        35
  cyclomatic gen/press/context.py:145   plate_context                      34
  cyclomatic gen/__main__.py:46         _ir                                23
  cyclomatic gen/tests/test_ir.py:102   _check_references                  20
  cognitive  gen/xsd/analyze.py:311     report                             44
  cognitive  gen/ir/resolve.py:119      flat_elements                      40
  cognitive  gen/tests/test_ir.py:102   _check_references                  38
  cognitive  gen/press/context.py:145   plate_context                      37
  cognitive  gen/xsd/analyze.py:207     _sccs                              37
  size       gen/xsd/analyze.py:311     report                             152
  size       gen/press/context.py:145   plate_context                      96
  size       gen/plates/build.py:533    _value_plate                       89
  size       gen/plates/build.py:956    _validate_config_against_ir        89
  size       gen/ir/resolve.py:119      flat_elements                      78

Commit c86c448b9b853e24dd6190700f9728330e37f0c8.

The field was never read from or written to MusicXML: <defaults> has
no measure-numbering child in the schema (it only appears inside a
per-measure <print>), so there was no wire representation to wire up.
The comment already said as much ("TODO - this appears not to be
used anywhere, please do not use"). The real, working feature is
MeasureData::measureNumbering, which is measure-scoped to match how
<measure-numbering> actually appears in MusicXML, and is unaffected
by this removal.

Breaking: removes a public DefaultsData field.
@github-actions

Copy link
Copy Markdown

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28514 / 36642
Functions 74.3% 6352 / 8553
Branches 50.7% 22680 / 44751

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 81.2% 6616 / 8152
Functions 68.1% 2256 / 3313
Branches 49.8% 5680 / 11401

Core HTML report | API HTML report

Commit a48fb6d5d431945db3fb108c9ea958c061bd0800.

@github-actions

Copy link
Copy Markdown

gen-quality gen/

gen-quality: 84.5 / 100   (floor 84.5, +0.0)

  structure     86.5  x0.50   [fn 90.5 / file 82.6]
  cyclomatic    88.4  x0.25
  cognitive     76.6  x0.25

  409 functions across 31 files, 7702 lines (largest file 1044)
  max cc 56  max cognitive 44  max fn loc 152

Worst offenders (top 5 per axis; full lists in score.json):
  cyclomatic gen/xsd/analyze.py:311     report                             56
  cyclomatic gen/plates/build.py:956    _validate_config_against_ir        35
  cyclomatic gen/press/context.py:145   plate_context                      34
  cyclomatic gen/__main__.py:46         _ir                                23
  cyclomatic gen/tests/test_ir.py:102   _check_references                  20
  cognitive  gen/xsd/analyze.py:311     report                             44
  cognitive  gen/ir/resolve.py:119      flat_elements                      40
  cognitive  gen/tests/test_ir.py:102   _check_references                  38
  cognitive  gen/press/context.py:145   plate_context                      37
  cognitive  gen/xsd/analyze.py:207     _sccs                              37
  size       gen/xsd/analyze.py:311     report                             152
  size       gen/press/context.py:145   plate_context                      96
  size       gen/plates/build.py:533    _value_plate                       89
  size       gen/plates/build.py:956    _validate_config_against_ir        89
  size       gen/ir/resolve.py:119      flat_elements                      78

Commit a48fb6d5d431945db3fb108c9ea958c061bd0800.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai Issues opened by, or through, a coding agent. api Affects the mx::api layer feature new feature request impl Affects the mx::impl layer non-breaking fixes or implementation that do not require breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MeasureNumbering might be dead code

1 participant