Skip to content

feat: round-trip <encoding> faithfully and add a writeMxVersion toggle#243

Merged
webern merged 4 commits into
mainfrom
claude/untitled-session-knafxz
Jun 26, 2026
Merged

feat: round-trip <encoding> faithfully and add a writeMxVersion toggle#243
webern merged 4 commits into
mainfrom
claude/untitled-session-knafxz

Conversation

@webern

@webern webern commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

Resolves the <encoding> child-order round-trip divergence (#220) and adds a way to suppress mx's own provenance stamp. Three parts:

  1. Encoding child order is treated as insignificant in the api round-trip. mx::api flattens <encoding> into typed buckets (EncodingData) and re-emits its children in a fixed canonical order, so files that list those children in any other order round-tripped with an identical multiset but a different order — the sole blocker for a batch of files. The api round-trip harness now stable-sorts <encoding> children into the api's canonical order on both sides before comparing; corert stays order-faithful and is untouched. This also realigns the user <software> against mx's trailing stamp, clearing a secondary spurious value mismatch. Pins the 48 files this unblocks (discovery PASS 46 to 94).

  2. The writer now emits <software> before <encoding-date>, matching what MuseScore, Finale, and Sibelius write, so fewer real-world files need any reordering. MusicXML's encoding choice imposes no order, so this is schema-valid.

  3. New api::EncodingData::writeMxVersion (defaults true, including for files parsed without the stamp). When set false, writeToFile/writeToStream serialize without mx's provenance <software> while still writing the user's own software entries. The directive cannot ride on the core model, so DocumentManager carries it per document (StoredDocument) and branches at write time.

Testing

  • make test-api-roundtrip — 94 / 94 pinned files pass (was 46)
  • make test — full unit suite (4275 assertions in 309 cases), including 2 new writeMxVersion round-trip tests (default-on stamps; off suppresses)
  • make discover-api-roundtrip — 94 PASS / 737 FAIL, no regressions from the writer reorder
  • make check — clang-format clean

References

webern added 3 commits June 26, 2026 05:57
…#220)

mx::api flattens <encoding> into typed buckets (api::EncodingData) and
re-emits its children in a fixed canonical order (encoder, encoding-date,
encoding-description, software, supports). Real-world files list those
children in other orders, so the round-trip diverged only by <encoding>
child order -- a deliberate property of the simplified api, not a fidelity
loss. The reorder also misaligned the user <software> against mx's trailing
provenance stamp, surfacing a spurious <software> value mismatch.

Fix in the api test harness: stable-sort each document's <encoding> children
into the api's canonical order before comparing (both expected and actual).
Genuine drops/adds/value changes still surface; only pure reordering is
normalized away. corert stays order-faithful (untouched).

Pins the 48 files this unblocks. api round-trip PASS: 46 -> 94.
…tools

mx::api re-emits <encoding> children in a fixed canonical order. Move
<software> ahead of <encoding-date> (the order MuseScore/Finale/Sibelius
write), which minimizes the number of corpus files whose <encoding> needs
reordering on round-trip. MusicXML's encoding choice imposes no order, so
any order is schema-valid.

The api round-trip harness canonicalizes <encoding> child order on both
sides (#220), so this is order-agnostic for the gate: discovery holds at
94 PASS / 737 FAIL, full unit suite green. The harness rank is kept in sync
with the writer order for clarity.
mx stamps its own provenance <software> into <encoding> on every api write.
Add api::EncodingData::writeMxVersion (defaults true, including for files
parsed without the stamp) so callers can turn it off. When false, writeToFile/
writeToStream serialize without the stamp via core::serialize() instead of
core::serializeWithAttribution(); the user's own <software> entries are still
written.

The directive cannot ride on the core model, so DocumentManager stores it per
document (StoredDocument) alongside the core::Document and branches at write
time. createFrom{File,Stream} default it true; createFromScore takes it from
score.encoding.writeMxVersion.

Adds a standalone api round-trip test: parse a file (whose source lacked the
stamp), confirm the default write stamps it, then turn writeMxVersion off and
assert the written output no longer contains the mx <software> marker.
@webern webern added bug software defect feature new feature request non-breaking fixes or implementation that do not require breaking changes area/mx::api area/mx::impl ai Issues opened by, or through, a coding agent. labels Jun 26, 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 4b8a34a41daf8a4c20fba9bf8f2f0012398d41bb.

Comment thread src/include/mx/api/EncodingData.h Outdated
std::vector<SupportedItem> supportedItems;
std::vector<MiscellaneousField> miscelaneousFields;

// When true (the default, including for files parsed without it), mx writes its

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.

This should explain that the intent is to aid in fixing mx defects. In other words, since this is a bit of an intrusive defect we should include our reason for doing so.

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.

Expanded the comment in 97fb7a2 to give the rationale: the stamp is deliberately intrusive (it injects a <software> node the source never had into every written file), it exists to tie bad output back to the exact mx build that produced it, and this flag is the escape hatch for byte-faithful output when diagnosing mx defects.


Generated by Claude Code

Expand the EncodingData::writeMxVersion comment to state the rationale: mx's
provenance stamp is deliberately intrusive (it injects a node the source never
had into every written file) and exists to tie bad output back to the mx build
that produced it; the flag is the escape hatch for byte-faithful output when
diagnosing mx defects.
@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 707dae8bd505fdd482c9607fb23977498b944fd9.

@github-actions

Copy link
Copy Markdown

Coverage report

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

Metric Coverage Covered / Total
Lines 77.8% 28487 / 36624
Functions 74.3% 6349 / 8550
Branches 50.6% 22632 / 44725

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

Metric Coverage Covered / Total
Lines 78.5% 5913 / 7535
Functions 63.5% 2006 / 3159
Branches 47.8% 4998 / 10455

Core HTML report | API HTML report

Commit 707dae8bd505fdd482c9607fb23977498b944fd9.

@webern webern merged commit 6cd860f into main Jun 26, 2026
7 checks passed
@webern webern deleted the claude/untitled-session-knafxz branch June 26, 2026 11:52
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 feature new feature request non-breaking fixes or implementation that do not require breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

api reorders identification/encoding children on round-trip

1 participant