Skip to content

feat: round-trip direction voice and pin the synthetic segno/coda fixtures#238

Merged
webern merged 1 commit into
mainfrom
claude/issue-237-roundtrip-vsvcas
Jun 23, 2026
Merged

feat: round-trip direction voice and pin the synthetic segno/coda fixtures#238
webern merged 1 commit into
mainfrom
claude/issue-237-roundtrip-vsvcas

Conversation

@webern

@webern webern commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Brings the audit's synthetic segno/coda fixtures onto the strict api read -> write -> read gate.

What

  1. Round-trip the editorial-voice <voice> on a <direction> (read from editorialVoiceDirection().voice(), written back). Today a direction's own <voice> is dropped. The write is emitted only when the direction has direction-type content, so a voice carried in from a figured-bass/harmony element never produces an empty <direction>. <voice> is common in the wild corpus, so this is real fidelity, not synthetic-only.

  2. Pin synthetic/{segno,coda}.{3.0,3.1}.xml in roundtrip-baseline.txt. To make them round-trip losslessly the fixtures are tidied:

    • stripped the <footnote>/<level> editorial boilerplate the audit generator stamps onto every probe (editorial appears in zero real-world files, so the api does not model it — see feat: model editorial (footnote/level) in mx::api #236, closed)
    • added a <divisions> + a <note> so the writer's first-measure <divisions> is legitimate instead of a spurious injection into a note-less measure

    They still isolate the segno/coda surface (3.0 base attributes; 3.1 adds id + smufl) and now also exercise a part-group and direction <voice>/<staff> through the public DocumentManager path.

Why a real note instead of an impl fix for <divisions>

A note-less measure that the writer fills with a synthesized <divisions> is the #228 writer-policy issue. A naive "suppress divisions when the part has no notes" guard regresses ksuite/k016a_Miscellaneous_Fields.xml, which legitimately carries <divisions> in a note-less measure — distinguishing "source had it" from "api invented it" needs divisions-presence tracking in the public api, out of scope here. So the fixtures carry a real note; the writer-policy gap stays open under #228.

Follow-up

A separate PR on top of this one strips the editorial boilerplate from every other synthetic file that isn't the dedicated footnote/level probe, so the synthetic corpus stops implying api features the wild corpus never uses.

Testing

  • api round-trip regression gate: 38 passed, 0 failed (was 34; +4 synthetic)
  • discovery: no regressions
  • Full mxtest: 4268 assertions in 307 test cases, all pass
  • corert: 832 test cases pass, pinned corpus count unchanged
  • clang-format clean; audit *.features.xml sidecars + corpus.xml regenerated

References

@webern webern added feature new feature request testing 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 22, 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 ce847bd484327bd77cb46a9e98e9e0d34fd800ab.

@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 78.3% 5955 / 7604
Functions 62.4% 1979 / 3173
Branches 47.7% 5032 / 10549

Core HTML report | API HTML report

Commit ce847bd484327bd77cb46a9e98e9e0d34fd800ab.

Comment thread src/private/mx/impl/DirectionWriter.cpp Outdated
Comment thread src/private/mxtest/api/roundtrip-baseline.txt Outdated
@webern webern force-pushed the claude/issue-204-editorial-vsvcas branch from 384f715 to c14698f Compare June 22, 2026 18:43
@webern webern force-pushed the claude/issue-237-roundtrip-vsvcas branch from 10dcbbb to 180ee48 Compare June 22, 2026 18:43
@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 1bab5daee1ceb62a9b4dd881af5d32f39262460b.

@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 78.3% 5955 / 7604
Functions 62.4% 1979 / 3173
Branches 47.7% 5032 / 10549

Core HTML report | API HTML report

Commit 1bab5daee1ceb62a9b4dd881af5d32f39262460b.

@webern webern force-pushed the claude/issue-237-roundtrip-vsvcas branch from 180ee48 to 86e5978 Compare June 22, 2026 19:38
@webern webern changed the base branch from claude/issue-204-editorial-vsvcas to claude/issue-204-plan-vsvcas June 22, 2026 19:38
Base automatically changed from claude/issue-204-plan-vsvcas to main June 23, 2026 05:19
Read and write the editorial-voice <voice> on a <direction> so a direction's
voice assignment survives the api round-trip. It is emitted only when the
direction also writes direction-type content, so a voice carried in from a
figured-bass or harmony element never produces an empty <direction>.

Strip the footnote/level editorial boilerplate from the synthetic segno/coda
fixtures -- no file in the wild corpus uses editorial, so the api does not model
it -- and give each measure a divisions plus a note so it round-trips. Pin the
four fixtures in the api read->write->read gate.
@webern webern force-pushed the claude/issue-237-roundtrip-vsvcas branch from 86e5978 to 1366899 Compare June 23, 2026 05:28
@webern webern merged commit 5ea5ee4 into main Jun 23, 2026
7 checks passed
@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 d98cb724abe447eb943d9d6405e40d09c84cda2c.

@webern webern deleted the claude/issue-237-roundtrip-vsvcas branch June 23, 2026 05:29
@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 78.1% 5874 / 7518
Functions 62.1% 1962 / 3159
Branches 47.5% 4956 / 10431

Core HTML report | API HTML report

Commit d98cb724abe447eb943d9d6405e40d09c84cda2c.

webern added a commit that referenced this pull request Jun 23, 2026
)

## Summary

The audit generator stamps `<footnote>`/`<level>` editorial onto nearly
every synthetic single-feature fixture, but editorial appears in zero
real-world corpus files — all 382 editorial-bearing files were under
`data/synthetic/`. That boilerplate implied mx::api needed editorial
support it does not actually need for the wild corpus, which is why #236
was closed without merging.

This removes the `<footnote>x</footnote>`/`<level>x</level>` boilerplate
from every synthetic fixture except the dedicated footnote/level probes,
which keep only their own target element. One uniform rule does this
safely: the boilerplate is always the plain `>x<` form, while each
probe's target is always attributed (`<footnote default-x=...>`, `<level
reference=...>`), so deleting the plain form strips the noise everywhere
and preserves every probe's intent.

- 382 fixtures changed (379 stripped fully; the footnote/level probes
keep their attributed target)
- `*.features.xml` sidecars and `corpus.xml` regenerated

## Testing

- [x] corert: 832 test cases pass, pinned corpus count unchanged (the
now-empty part-groups round-trip)
- [x] api round-trip regression gate: 38/38
- [x] Full mxtest: 4268 assertions in 307 test cases (ApiLoadSmokeTest
walks every stripped file)
- [x] No code changes; `clang-format` not applicable

## Notes

While regenerating sidecars, `audit files --force` also corrected a
stale `<audited-version>` (3.0 → 3.1) in
`data/custom/segno_coda_roundtrip.3.1.features.xml`. That file belongs
to #235, so the correction was reverted out of this PR to keep it
focused — worth fixing on the #235 branch.

## References

- Stacked on #238 (base branch `claude/issue-237-roundtrip-vsvcas`)
- Follows from #236 (editorial dropped from mx::api; closed)
- Progresses #221 — footnote/level are no longer dropped across ~373
classifier files now that the synthetic boilerplate is gone
- Part of #208 (api round-trip corpus coverage)
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 feature new feature request non-breaking fixes or implementation that do not require breaking changes testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant