Skip to content

fix: round-trip the caesura value#342

Merged
webern merged 4 commits into
mainfrom
claude/caesura-value
Jul 12, 2026
Merged

fix: round-trip the caesura value#342
webern merged 4 commits into
mainfrom
claude/caesura-value

Conversation

@webern

@webern webern commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Human Summary

A quick glance and it looks like this is correctly following the pattern for adding MarkData features.

Summary

The <caesura> articulation's optional text value (MusicXML 3.1+: normal, thick, short,
curved, single) was dropped on read, and the writer always emitted
<caesura>normal</caesura> — so the common empty form <caesura/> gained a spurious value
and the other variants were flattened to normal. mx::core already models the empty form
(core::CaesuraValue::empty()); only the api impl dropped it.

Following the tremoloSingle/fermata-variant precedent, MarkType::caesura now means the
common empty form and new caesuraNormal / caesuraThick / caesuraShort / caesuraCurved
/ caesuraSingle enumerators carry the explicit values, wired through
ArticulationsFunctions (read) and NotationsWriter (write). isMarkArticulation and the
articulations converter map cover the new enumerators.

Stacked on #340 to keep the roundtrip-baseline.txt additions conflict-free; the 3 files this
flips are pinned (217 -> 220).

Testing

  • New CaesuraEmpty / CaesuraVariants round-trip tests (CaesuraEmpty fails before, passes after)
  • Full mxtest suite passes (4950 assertions in 420 test cases)
  • Discovery: 220 PASS, zero regressions; regression mode: 220/220

References

webern added 3 commits July 12, 2026 15:29
A source that declares no <divisions> anywhere reads as ticksPerQuarter = 0
(LCM of an empty set). The writer then emitted divisions for the first
measure of every part, and core::PositiveDivisions clamped the 0 to a bogus
1e-06. The writer now omits <divisions> when the score has no time
resolution; an otherwise-empty <attributes> is no longer flushed.

Pins the 41 corpus files this flips to PASS (212 total).
Adds ArpeggiateMarkData (number, unbroken, id) and NonArpeggiateMarkData
(top/bottom placement, number, id) as MarkDataChoice alternatives and
wires them through the reader and writer. The non-arpeggiate bracket end
previously always wrote as type="top".

Pins the 5 corpus files this flips to PASS (217 total).
MarkType::caesura now round-trips as the common empty <caesura/> form
instead of acquiring a spurious 'normal' text value, and new
caesuraNormal/Thick/Short/Curved/Single mark types carry the explicit
MusicXML 3.1 values that were previously flattened to 'normal'.

Pins the 3 corpus files this flips to PASS (220 total).
@github-actions

Copy link
Copy Markdown

Coverage report

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

Metric Coverage Covered / Total
Lines 77.8% 28514 / 36648
Functions 74.2% 6352 / 8556
Branches 50.7% 22680 / 44751

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

Metric Coverage Covered / Total
Lines 84.4% 7225 / 8560
Functions 74.9% 2512 / 3353
Branches 52.3% 6293 / 12036

Core HTML report | API HTML report

Commit 6b443d240c86c99a6cae76b976f807cc71ba680c.

@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 6b443d240c86c99a6cae76b976f807cc71ba680c.

Base automatically changed from claude/arpeggiate-attributes to main July 12, 2026 19:42
@webern webern merged commit 988beeb into main Jul 12, 2026
3 of 9 checks passed
@webern webern deleted the claude/caesura-value branch July 12, 2026 19:45
webern added a commit that referenced this pull request Jul 12, 2026
## Human Summary

Looks like a bug fix where we purported to support the metronome
parenthesis attribute, but dropped it. Simple fix.

## Summary

`<metronome parentheses="yes">` lost its parentheses on an api
round-trip. The api model
already had the field — `TempoData::isParenthetical` — but it dangled:
`MetronomeReader`
never set it and `DirectionWriter` never emitted it. This wires both
sides: the reader maps
`core::Metronome::parentheses()` into the `Bool`, and the writer emits
the attribute when the
field is not `unspecified`. No api surface change.

Stacked on #342 to keep the `roundtrip-baseline.txt` additions
conflict-free; the 2 files
this flips are pinned (220 -> 222).

## Testing

- [x] New `roundTripParentheses` MetronomeApi test (fails before, passes
after)
- [x] Full mxtest suite passes (4951 assertions in 421 test cases)
- [x] Discovery: 222 PASS, zero regressions; regression mode: 222/222

## References

- Closes #343
- Stacked on #342; part of #208

---
_Generated by [Claude
Code](https://claude.ai/code/session_01TuPExUzFXLRG9ns3X199ej)_
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.

api: caesura loses its value; the empty form gains a spurious "normal" text

1 participant