Skip to content

fix: preserve staff-scoped staff-layout number="N"#326

Open
webern wants to merge 1 commit into
mainfrom
fix-staff-layout-number
Open

fix: preserve staff-scoped staff-layout number="N"#326
webern wants to merge 1 commit into
mainfrom
fix-staff-layout-number

Conversation

@webern

@webern webern commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Human Summary

TODO: human writes here

Summary

Both the per-measure <print> and score-level <defaults> readers took only the first
<staff-layout>'s distance and dropped its number attribute entirely, folding it into the single
unscoped SystemLayoutData::staffDistance. For a multi-staff part (e.g. a 2-staff piano/organ)
whose only staff-layout is scoped to staff 2, this silently misattributed the distance to staff 1
(the schema default for a missing number) instead of just dropping the attribute -- a real,
if quiet, correctness bug, not only a fidelity loss.

Added SystemLayoutData::staffDistances (a vector of a new StaffDistanceData: staffIndex +
staffDistance), populated only when the source's <staff-layout> had an explicit number
attribute; the existing singular staffDistance keeps its meaning for the common unscoped case.
Both readers (ScoreReader::scanForSystemInfo, LayoutFunctions::addStaffLayout) and both writers
(MeasureWriter::writeSystemInfo, LayoutFunctions::addSystemMargins) handle it symmetrically,
since <defaults> and per-measure <print> already share SystemLayoutData.

Testing

  • New staffScopedStaffDistance test (PrintLayoutRoundTripTest.cpp)
  • make test: all pass (4729 assertions in 379 test cases, plus the three examples)
  • make test-api-roundtrip: 158 passed, 0 failed (of 158 pinned; no regressions -- this fix
    corrects a wrong-staff misattribution rather than unlocking a new fully-passing file, since none
    of the affected corpus files had this as their sole blocker)
  • make fmt / make check: clean

References

@webern webern added bug software defect 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 7, 2026 — with Claude
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Coverage report

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

Metric Coverage Covered / Total
Lines 77.8% 28513 / 36640
Functions 74.3% 6352 / 8551
Branches 50.7% 22678 / 44751

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

Metric Coverage Covered / Total
Lines 80.7% 6401 / 7933
Functions 67.7% 2217 / 3275
Branches 49.5% 5496 / 11100

Core HTML report | API HTML report

Commit 798455f0292544538e266fed9bfd514f07e9cb02.

@github-actions

github-actions Bot commented Jul 7, 2026

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 798455f0292544538e266fed9bfd514f07e9cb02.

@webern webern force-pushed the fix-staff-layout-number branch from c48404d to beefd31 Compare July 7, 2026 09:23
@webern webern changed the title fix: preserve staff-scoped &lt;staff-layout number="N"&gt; fix: preserve staff-scoped staff-layout number="N" Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Coverage report

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

Metric Coverage Covered / Total
Lines 77.8% 28513 / 36640
Functions 74.3% 6352 / 8551
Branches 50.7% 22678 / 44751

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

Metric Coverage Covered / Total
Lines 80.8% 6434 / 7963
Functions 67.8% 2221 / 3276
Branches 49.6% 5524 / 11148

Core HTML report | API HTML report

Commit 1b8a0ab9ac2c26582882e07d4be01984f5f5b484.

@github-actions

github-actions Bot commented Jul 7, 2026

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 1b8a0ab9ac2c26582882e07d4be01984f5f5b484.

{
/// A <staff-layout> scoped to one staff of a multi-staff part (MusicXML's number attribute):
/// the space between the bottom line of the previous staff and the top line of this one.
/// staffIndex is zero-based; a source with an explicit number attribute always populates

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.

A source? Meaning a file?

Since the coding agent has been tasked with round trip success, it is writing API documentation from that vantage-point which would be confusing to a user. Instead this documentation should explain how someone using mx::api to build up a score should use the interface. And the interface should avoid adding confusing things that exist solely to represent what is not music-notation-semantics-meaningful in a round-trip corpus test file.

/// staffIndex is zero-based; a source with an explicit number attribute always populates

Both the per-measure <print> and score-level <defaults> readers took only
the first <staff-layout>'s distance and dropped its number attribute
entirely, folding it into the single unscoped SystemLayoutData::staffDistance.
For a multi-staff part (e.g. a 2-staff piano/organ) whose only staff-layout
is scoped to staff 2, this silently misattributed the distance to staff 1
(the schema default for a missing number) instead of dropping the number.

Added SystemLayoutData::staffDistances (vector of new StaffDistanceData:
staffIndex + distance), populated only when the source's <staff-layout> had
an explicit number attribute; the existing singular staffDistance keeps its
meaning for the common unscoped case. Both readers (ScoreReader::
scanForSystemInfo, LayoutFunctions::addStaffLayout) and both writers
(MeasureWriter::writeSystemInfo, LayoutFunctions::addSystemMargins) handle
it symmetrically since defaults and per-measure print share SystemLayoutData.

## Testing

- [x] New `staffScopedStaffDistance` test (PrintLayoutRoundTripTest.cpp)
- [x] make test: all pass (4729 assertions in 379 test cases, plus examples)
- [x] make test-api-roundtrip: 158 passed, 0 failed (of 158 pinned; no
  regressions -- this fix corrects a wrong-staff misattribution rather than
  unlocking a new fully-passing file)
- [x] make fmt / make check: clean

Closes #281
@webern webern force-pushed the fix-staff-layout-number branch from beefd31 to 2e7307a Compare July 11, 2026 14:07
@github-actions

Copy link
Copy Markdown

Coverage report

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

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

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

Metric Coverage Covered / Total
Lines 81.0% 6567 / 8111
Functions 68.0% 2249 / 3306
Branches 49.7% 5646 / 11369

Core HTML report | API HTML report

Commit 4d5d770eb8f65c58d321191a2c1d52c5ccc557ea.

@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 4d5d770eb8f65c58d321191a2c1d52c5ccc557ea.

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 bug software defect 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.

api: round-trip drops staff-layout

1 participant