Skip to content

fix: part-group round-trip fidelity, and a classifier category for supported-element drops#224

Merged
webern merged 3 commits into
mainfrom
claude/keen-faraday-jzoms9
Jun 20, 2026
Merged

fix: part-group round-trip fidelity, and a classifier category for supported-element drops#224
webern merged 3 commits into
mainfrom
claude/keen-faraday-jzoms9

Conversation

@webern

@webern webern commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

Progresses #219 in two parts: fix part-group round-trip, and make the round-trip classifier surface the dropped-supported-element signal #219 is about (instead of burying it in unknown).

part-group round-trip (mx::api / mx::impl)

#219 flagged part-group as the most-dropped support=full element on api round-trip (373 files). It was two problems:

  1. Misleading corpus signal. All 373 drops were synthetic files with an unmatched <part-group type="start"> (no stop) -- schema-valid but semantically invalid, a start/stop pairing constraint XSD cannot express. mx::api correctly drops an unmatched start (it models a complete start..stop span), and zero real-world files drop part-group. Fixed by making the synthetic part-groups well-formed (trailing start -> stop, 386 files; schema-valid per version, audit surface unchanged).

  2. Overstated support. Even well-formed groups lost data on write: group-abbreviation read but never written, group-barline fabricated as a constant yes, displayName/displayAbbreviation dead. Now: model group-barline (api::GroupBarline), write group-abbreviation, wire the display names to group-name-display/group-abbreviation-display via a shared NameDisplayFunctions helper. api.features.xml corrected full -> partial; group-time/editorial stay unmodeled by design.

classifier (audit/classify.py)

The classifier put every file dropping a support=full/partial element into unknown -- category B only fires when every dropped class is support=none, so any file mixing a supported drop with unsupported ones (nearly all) fell through. That buried #219's premise: 759 of 828 files were unknown. Added category G (supported-element drop): an actionable impl-bug-or-audit-overstatement signal, evaluated after B/C/D/E (a precise enum/attribute finding still wins) and listing the dropped supported classes as blocking_features. On the corpus this moves 575 files out of unknown (-> 183) and ranks the real offenders: staff (280), lyric (117), text (115), voice (96), measure-numbering (88).

Testing

  • New partGroupRoundTrip test: red before the part-group fix, green after
  • Full api/impl suite: 4113 assertions / 271 cases
  • corert over the corpus: 830 files (the 386 synthetic edits round-trip in mx::core)
  • make test-audit: 13 cases incl. 2 new category-G tests
  • api round-trip: files dropping part-group 373 -> 0; classifier unknown 759 -> 183
  • Changed synthetic files schema-valid (3.0/3.1/4.0); make check and the api-roundtrip regression gate pass

No corpus files become green (each still drops footnote/level/staff), so the pinned baseline is unchanged; the targeted test is the demonstration.

References

webern added 2 commits June 20, 2026 16:28
mx::api modeled part-group but mx::impl lost data on round-trip: the writer
never emitted group-abbreviation (it was read into PartGroupData and then
dropped), fabricated a constant <group-barline>yes</group-barline> on every
group, and left displayName/displayAbbreviation dead. The round-trip
classifier contradicted api.features.xml, which called this support=full.

- Model group-barline as api::GroupBarline {unspecified,yes,no,mensurstrich};
  read and write it, and stop fabricating a constant yes.
- Write group-abbreviation from PartGroupData.abbreviation.
- Wire displayName/displayAbbreviation to group-name-display /
  group-abbreviation-display as best-effort text, via a shared
  NameDisplayFunctions helper extracted from PartReader/PartWriter.
- Correct api.features.xml: part-group full -> partial, documenting the real
  gaps (group-time, editorial, dropped formatting).

group-time and editorial (footnote/level) stay unmodeled by design.

Progresses #219
Each synthetic file wrapped its single score-part in two
<part-group type="start"> with no matching stop -- schema-valid but
semantically invalid, since the start/stop pairing constraint is beyond what
XSD can express. mx::api correctly drops an unmatched start (it models a
complete start..stop span), so part-group was the single most-dropped element
on api round-trip (373 files) even though every real-world file round-trips it.

Change the trailing start to a stop so each group is well-formed. Files stay
schema-valid (validated per version) and exercise the same element/attribute
surface (audit sidecars and corpus.xml unchanged). The api round-trip now
reports part-group dropped in zero files (was 373).

Progresses #219
@webern webern added bug software defect area/mx::api area/mx::impl ai Issues opened by, or through, a coding agent. labels Jun 20, 2026 — with Claude
@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 c7953269b36ced3c7d0bb8aabb8d9a7e80f56295.

The round-trip classifier put every file that dropped a support=full/partial
element into "unknown": category B only fires when every dropped class is
support=none, so any file mixing a supported drop with unsupported ones (nearly
all of them) fell through. On the corpus that buried the #219 signal -- 759 of
828 files were "unknown".

Add category G: a dropped class the audit marks full/partial is either an impl
round-trip bug or an api.features.xml overstatement, both actionable. G is
evaluated after B/C/D/E (a precise enum/attribute finding still wins) and lists
the dropped supported classes as blocking_features. On the corpus this moves
575 files out of "unknown" (now 183) and ranks the real offenders: staff (280),
lyric (117), text (115), voice (96), measure-numbering (88).

Progresses #219
@webern webern changed the title fix: part-group round-trip fidelity in mx::api fix: part-group round-trip fidelity, and a classifier category for supported-element drops Jun 20, 2026
@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 8fb0dca255d0b86191c869cd8364ca6a50a2b971.

@github-actions

Copy link
Copy Markdown

Coverage report

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

Metric Coverage Covered / Total
Lines 77.9% 28539 / 36624
Functions 74.4% 6360 / 8550
Branches 50.7% 22672 / 44725

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

Metric Coverage Covered / Total
Lines 72.5% 5397 / 7449
Functions 60.3% 1828 / 3033
Branches 43.3% 4511 / 10415

Core HTML report | API HTML report

Commit 8fb0dca255d0b86191c869cd8364ca6a50a2b971.

@webern webern merged commit 779eb2c into main Jun 20, 2026
7 checks passed
@webern webern deleted the claude/keen-faraday-jzoms9 branch June 20, 2026 17:44
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. area/mx::api area/mx::impl bug software defect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant