mxl-agent 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mxl_agent-0.1.0/.claude/skills/mxl-agent/SKILL.md +309 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/evals/evals.json +168 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/evals/fixtures/chord_symbols.musicxml +70 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/evals/fixtures/divisions_change.musicxml +53 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/references/command-catalog.md +900 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/references/musical-semantics.md +40 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/references/preservation-and-safety.md +29 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/references/selectors.md +565 -0
- mxl_agent-0.1.0/.claude/skills/mxl-agent/scripts/mxl-agent.sh +34 -0
- mxl_agent-0.1.0/.claude-plugin/plugin.json +10 -0
- mxl_agent-0.1.0/.gitattributes +7 -0
- mxl_agent-0.1.0/.github/workflows/ci.yml +14 -0
- mxl_agent-0.1.0/.github/workflows/release.yml +40 -0
- mxl_agent-0.1.0/.gitignore +16 -0
- mxl_agent-0.1.0/.mcp.json +18 -0
- mxl_agent-0.1.0/.python-version +1 -0
- mxl_agent-0.1.0/CHANGELOG.md +58 -0
- mxl_agent-0.1.0/CLAUDE.md +43 -0
- mxl_agent-0.1.0/COMPATIBILITY.md +44 -0
- mxl_agent-0.1.0/LICENSE +21 -0
- mxl_agent-0.1.0/NOTICE +84 -0
- mxl_agent-0.1.0/PKG-INFO +80 -0
- mxl_agent-0.1.0/README.md +63 -0
- mxl_agent-0.1.0/ROADMAP.md +2881 -0
- mxl_agent-0.1.0/hooks/block-workspace-mutation.sh +49 -0
- mxl_agent-0.1.0/hooks/hooks.json +15 -0
- mxl_agent-0.1.0/mxl_agent-skill.md +2088 -0
- mxl_agent-0.1.0/pyproject.toml +95 -0
- mxl_agent-0.1.0/schemas/musicxml-3.1/container.xsd +188 -0
- mxl_agent-0.1.0/schemas/musicxml-3.1/musicxml.xsd +6081 -0
- mxl_agent-0.1.0/schemas/musicxml-3.1/xlink.xsd +57 -0
- mxl_agent-0.1.0/schemas/musicxml-3.1/xml.xsd +145 -0
- mxl_agent-0.1.0/schemas/musicxml-4.0/container.xsd +188 -0
- mxl_agent-0.1.0/schemas/musicxml-4.0/musicxml.xsd +11028 -0
- mxl_agent-0.1.0/schemas/musicxml-4.0/xlink.xsd +118 -0
- mxl_agent-0.1.0/schemas/musicxml-4.0/xml.xsd +140 -0
- mxl_agent-0.1.0/scripts/check.py +273 -0
- mxl_agent-0.1.0/src/mxl_agent/__init__.py +1 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/__init__.py +5 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/chord_text.py +89 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/jjazzlab_sng.py +200 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/mcp.py +246 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/mei.py +59 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/musescore.py +206 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/render_identity.py +237 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/timemap.py +77 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/vendor/ext_apps_client.js +86 -0
- mxl_agent-0.1.0/src/mxl_agent/adapters/verovio.py +154 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/__init__.py +49 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/_cross_part.py +71 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/_harmony.py +141 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/_key.py +58 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/_time_signature.py +50 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/_transposition.py +28 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/active_key.py +74 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/base.py +30 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/cadence_candidate.py +84 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/chord_recognition.py +101 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/chord_tone.py +168 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/doubling.py +108 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/harmonic_rhythm.py +105 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/interval_above_bass.py +137 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/lint.py +249 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/local_key.py +131 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/non_chord_tone.py +196 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/note_density.py +61 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/parallel_motion.py +132 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/playability.py +119 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/polyphony.py +53 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/registry.py +31 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/roman_numeral.py +145 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/run.py +22 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/scale_degree.py +175 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/syncopation.py +104 -0
- mxl_agent-0.1.0/src/mxl_agent/analysis/voice_leading.py +113 -0
- mxl_agent-0.1.0/src/mxl_agent/cli.py +1471 -0
- mxl_agent-0.1.0/src/mxl_agent/document/mutation_guard.py +80 -0
- mxl_agent-0.1.0/src/mxl_agent/document/score_document.py +83 -0
- mxl_agent-0.1.0/src/mxl_agent/document/source_locator.py +45 -0
- mxl_agent-0.1.0/src/mxl_agent/errors.py +29 -0
- mxl_agent-0.1.0/src/mxl_agent/generate/__init__.py +5 -0
- mxl_agent-0.1.0/src/mxl_agent/generate/accompaniment.py +236 -0
- mxl_agent-0.1.0/src/mxl_agent/generate/arrangement.py +103 -0
- mxl_agent-0.1.0/src/mxl_agent/generate/from_midi.py +392 -0
- mxl_agent-0.1.0/src/mxl_agent/generate/lead_sheet.py +300 -0
- mxl_agent-0.1.0/src/mxl_agent/generate/reharmonization.py +305 -0
- mxl_agent-0.1.0/src/mxl_agent/generate/voicing.py +258 -0
- mxl_agent-0.1.0/src/mxl_agent/midi/__init__.py +2 -0
- mxl_agent-0.1.0/src/mxl_agent/midi/reader.py +232 -0
- mxl_agent-0.1.0/src/mxl_agent/model/addresses.py +17 -0
- mxl_agent-0.1.0/src/mxl_agent/model/chord_recognition.py +91 -0
- mxl_agent-0.1.0/src/mxl_agent/model/chord_tones.py +145 -0
- mxl_agent-0.1.0/src/mxl_agent/model/diff.py +34 -0
- mxl_agent-0.1.0/src/mxl_agent/model/duration.py +169 -0
- mxl_agent-0.1.0/src/mxl_agent/model/events.py +38 -0
- mxl_agent-0.1.0/src/mxl_agent/model/harmony.py +561 -0
- mxl_agent-0.1.0/src/mxl_agent/model/instrument_ranges.py +81 -0
- mxl_agent-0.1.0/src/mxl_agent/model/key_signature.py +135 -0
- mxl_agent-0.1.0/src/mxl_agent/model/layout.py +61 -0
- mxl_agent-0.1.0/src/mxl_agent/model/merge.py +135 -0
- mxl_agent-0.1.0/src/mxl_agent/model/metadata.py +36 -0
- mxl_agent-0.1.0/src/mxl_agent/model/non_chord_tone.py +54 -0
- mxl_agent-0.1.0/src/mxl_agent/model/pitch.py +177 -0
- mxl_agent-0.1.0/src/mxl_agent/model/roman_numeral.py +300 -0
- mxl_agent-0.1.0/src/mxl_agent/model/score_index.py +208 -0
- mxl_agent-0.1.0/src/mxl_agent/model/selection.py +203 -0
- mxl_agent-0.1.0/src/mxl_agent/model/semantic_diff.py +309 -0
- mxl_agent-0.1.0/src/mxl_agent/model/timeline.py +299 -0
- mxl_agent-0.1.0/src/mxl_agent/model/timewise.py +126 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/__init__.py +44 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_key.py +71 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_measure_attributes.py +84 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_note_selection.py +120 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_note_xml.py +117 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_offset_position.py +65 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_parts.py +44 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_score_header.py +39 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/_xml_order.py +26 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/barlines.py +247 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/base.py +31 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/change_duration.py +234 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/chord_symbol_style.py +157 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/copy_notes.py +330 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/create_tuplet.py +343 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/credit.py +186 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/delete_note.py +172 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/direction_spanners.py +586 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/directions.py +393 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/envelope.py +67 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/harmony.py +339 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/insert_note.py +328 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/layout.py +321 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/lyrics.py +248 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/measure_operations.py +448 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/merge_tied_notes.py +228 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/metadata.py +299 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/move_note.py +313 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/noop.py +42 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/notations.py +422 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/notes.py +171 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/registry.py +28 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/rename_part.py +186 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/repair.py +326 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/respell.py +167 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/score_defaults.py +286 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/set_attributes.py +422 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/spanners.py +383 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/split_note.py +236 -0
- mxl_agent-0.1.0/src/mxl_agent/operations/transpose.py +205 -0
- mxl_agent-0.1.0/src/mxl_agent/package/__init__.py +0 -0
- mxl_agent-0.1.0/src/mxl_agent/package/compare.py +96 -0
- mxl_agent-0.1.0/src/mxl_agent/package/manifest.py +33 -0
- mxl_agent-0.1.0/src/mxl_agent/package/reader.py +189 -0
- mxl_agent-0.1.0/src/mxl_agent/package/safety.py +262 -0
- mxl_agent-0.1.0/src/mxl_agent/package/writer.py +96 -0
- mxl_agent-0.1.0/src/mxl_agent/patch/__init__.py +0 -0
- mxl_agent-0.1.0/src/mxl_agent/patch/apply.py +51 -0
- mxl_agent-0.1.0/src/mxl_agent/patch/inverse.py +88 -0
- mxl_agent-0.1.0/src/mxl_agent/patch/model.py +136 -0
- mxl_agent-0.1.0/src/mxl_agent/patch/plan.py +79 -0
- mxl_agent-0.1.0/src/mxl_agent/patch/repair.py +44 -0
- mxl_agent-0.1.0/src/mxl_agent/query/inspect.py +134 -0
- mxl_agent-0.1.0/src/mxl_agent/query/pagination.py +90 -0
- mxl_agent-0.1.0/src/mxl_agent/query/projection.py +101 -0
- mxl_agent-0.1.0/src/mxl_agent/result.py +40 -0
- mxl_agent-0.1.0/src/mxl_agent/service/__init__.py +13 -0
- mxl_agent-0.1.0/src/mxl_agent/service/session.py +96 -0
- mxl_agent-0.1.0/src/mxl_agent/session/__init__.py +0 -0
- mxl_agent-0.1.0/src/mxl_agent/session/checkpoints.py +63 -0
- mxl_agent-0.1.0/src/mxl_agent/session/revisions.py +92 -0
- mxl_agent-0.1.0/src/mxl_agent/session/selection_store.py +47 -0
- mxl_agent-0.1.0/src/mxl_agent/session/transaction.py +451 -0
- mxl_agent-0.1.0/src/mxl_agent/session/workspace.py +272 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/__init__.py +0 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/compatibility.py +65 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/issue.py +15 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/package.py +29 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/preservation.py +80 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/relationships.py +113 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/reopen.py +34 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/report.py +70 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/schema.py +29 -0
- mxl_agent-0.1.0/src/mxl_agent/validation/temporal.py +63 -0
- mxl_agent-0.1.0/src/mxl_agent/viewer/__init__.py +10 -0
- mxl_agent-0.1.0/src/mxl_agent/viewer/server.py +633 -0
- mxl_agent-0.1.0/src/mxl_agent/viewer/static/index.html +210 -0
- mxl_agent-0.1.0/src/mxl_agent/viewer/static/viewer.js +790 -0
- mxl_agent-0.1.0/src/mxl_agent/xml/__init__.py +0 -0
- mxl_agent-0.1.0/src/mxl_agent/xml/canonical.py +41 -0
- mxl_agent-0.1.0/src/mxl_agent/xml/detect.py +35 -0
- mxl_agent-0.1.0/src/mxl_agent/xml/parser.py +84 -0
- mxl_agent-0.1.0/src/mxl_agent/xml/schema.py +140 -0
- mxl_agent-0.1.0/tests/__init__.py +0 -0
- mxl_agent-0.1.0/tests/corpus/test_fixture_sweep.py +83 -0
- mxl_agent-0.1.0/tests/fixtures/__init__.py +0 -0
- mxl_agent-0.1.0/tests/fixtures/builder.py +138 -0
- mxl_agent-0.1.0/tests/fixtures/corpus/musescore/annotated_score.musicxml +198 -0
- mxl_agent-0.1.0/tests/fixtures/corpus/musescore/chord_symbols.musicxml +160 -0
- mxl_agent-0.1.0/tests/fixtures/corpus/musescore/piano_two_staves.musicxml +155 -0
- mxl_agent-0.1.0/tests/fixtures/expected/summary_annotated_score.json +13 -0
- mxl_agent-0.1.0/tests/fixtures/expected/summary_single_note.json +10 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/annotated_score.musicxml +88 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/bb_trumpet.musicxml +38 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/bb_trumpet_dorian.musicxml +39 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/cadence_progression.musicxml +130 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/chord_symbols.musicxml +70 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/chord_tone_notes.musicxml +87 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/chord_with_frame_and_inversion.musicxml +63 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/dangling_tie.musicxml +39 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/divisions_change.musicxml +53 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/duplicate_and_nonnumeric_measure_numbers.musicxml +50 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/flat_key_chromatic_note.musicxml +35 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/invalid_missing_part_list.musicxml +15 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/jjazzlab_mixed_meter.sng +58 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/jjazzlab_simple.sng +48 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/key_change.musicxml +55 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/non_chord_tone_notes.musicxml +124 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/partial_measure.musicxml +35 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/piano_two_staves.musicxml +65 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/quarter_tone_note.musicxml +36 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/scale_degree_notes.musicxml +74 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/short_measure.musicxml +35 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/single_note.musicxml +34 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/syncopation_notes.musicxml +57 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/transposition_change.musicxml +54 -0
- mxl_agent-0.1.0/tests/fixtures/minimal/two_voices_one_staff.musicxml +62 -0
- mxl_agent-0.1.0/tests/fixtures/raw_zip.py +121 -0
- mxl_agent-0.1.0/tests/golden/__init__.py +0 -0
- mxl_agent-0.1.0/tests/golden/test_summary_golden.py +57 -0
- mxl_agent-0.1.0/tests/integration/__init__.py +0 -0
- mxl_agent-0.1.0/tests/integration/test_mcp_server.py +146 -0
- mxl_agent-0.1.0/tests/integration/test_mei_verovio.py +93 -0
- mxl_agent-0.1.0/tests/integration/test_musescore_adapter.py +175 -0
- mxl_agent-0.1.0/tests/integration/test_mvp_acceptance.py +246 -0
- mxl_agent-0.1.0/tests/integration/test_render_identity_verovio.py +81 -0
- mxl_agent-0.1.0/tests/integration/test_render_verovio.py +238 -0
- mxl_agent-0.1.0/tests/integration/test_viewer_server_verovio.py +309 -0
- mxl_agent-0.1.0/tests/property/__init__.py +0 -0
- mxl_agent-0.1.0/tests/property/test_generative_pipeline.py +198 -0
- mxl_agent-0.1.0/tests/security/__init__.py +0 -0
- mxl_agent-0.1.0/tests/security/test_midi_safety.py +80 -0
- mxl_agent-0.1.0/tests/security/test_mxl_safety.py +78 -0
- mxl_agent-0.1.0/tests/security/test_schema_safety.py +58 -0
- mxl_agent-0.1.0/tests/security/test_xml_safety.py +102 -0
- mxl_agent-0.1.0/tests/security/test_zip_safety.py +168 -0
- mxl_agent-0.1.0/tests/unit/__init__.py +0 -0
- mxl_agent-0.1.0/tests/unit/test_adapter_chord_text.py +81 -0
- mxl_agent-0.1.0/tests/unit/test_adapter_mcp.py +57 -0
- mxl_agent-0.1.0/tests/unit/test_adapter_mei.py +26 -0
- mxl_agent-0.1.0/tests/unit/test_adapter_musescore.py +191 -0
- mxl_agent-0.1.0/tests/unit/test_adapter_timemap.py +70 -0
- mxl_agent-0.1.0/tests/unit/test_adapter_verovio.py +30 -0
- mxl_agent-0.1.0/tests/unit/test_adapters_jjazzlab_sng.py +157 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_active_key.py +113 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_cadence_candidate.py +64 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_chord_recognition.py +76 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_chord_tone.py +112 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_cross_part.py +93 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_doubling_polyphony.py +107 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_harmonic_rhythm.py +67 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_interval_above_bass.py +113 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_lint.py +180 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_local_key.py +97 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_non_chord_tone.py +121 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_note_density.py +54 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_parallel_motion.py +89 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_playability.py +102 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_roman_numeral.py +198 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_scale_degree.py +121 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_syncopation.py +78 -0
- mxl_agent-0.1.0/tests/unit/test_analysis_voice_leading.py +90 -0
- mxl_agent-0.1.0/tests/unit/test_chord_symbol.py +397 -0
- mxl_agent-0.1.0/tests/unit/test_cli.py +94 -0
- mxl_agent-0.1.0/tests/unit/test_cli_commands.py +1937 -0
- mxl_agent-0.1.0/tests/unit/test_corpus_cross_producer.py +99 -0
- mxl_agent-0.1.0/tests/unit/test_duration.py +264 -0
- mxl_agent-0.1.0/tests/unit/test_export_edited.py +170 -0
- mxl_agent-0.1.0/tests/unit/test_fixture_builder.py +28 -0
- mxl_agent-0.1.0/tests/unit/test_generate_accompaniment.py +190 -0
- mxl_agent-0.1.0/tests/unit/test_generate_arrangement.py +68 -0
- mxl_agent-0.1.0/tests/unit/test_generate_from_midi.py +149 -0
- mxl_agent-0.1.0/tests/unit/test_generate_lead_sheet.py +269 -0
- mxl_agent-0.1.0/tests/unit/test_generate_reharmonization.py +166 -0
- mxl_agent-0.1.0/tests/unit/test_generate_voicing.py +217 -0
- mxl_agent-0.1.0/tests/unit/test_json_normalization.py +35 -0
- mxl_agent-0.1.0/tests/unit/test_midi_reader.py +145 -0
- mxl_agent-0.1.0/tests/unit/test_model_chord_recognition.py +68 -0
- mxl_agent-0.1.0/tests/unit/test_model_chord_tones.py +131 -0
- mxl_agent-0.1.0/tests/unit/test_model_instrument_ranges.py +64 -0
- mxl_agent-0.1.0/tests/unit/test_model_key_signature.py +114 -0
- mxl_agent-0.1.0/tests/unit/test_model_layout.py +70 -0
- mxl_agent-0.1.0/tests/unit/test_model_merge.py +114 -0
- mxl_agent-0.1.0/tests/unit/test_model_non_chord_tone.py +70 -0
- mxl_agent-0.1.0/tests/unit/test_model_roman_numeral.py +280 -0
- mxl_agent-0.1.0/tests/unit/test_model_semantic_diff.py +205 -0
- mxl_agent-0.1.0/tests/unit/test_model_timewise.py +123 -0
- mxl_agent-0.1.0/tests/unit/test_mutation_guard.py +138 -0
- mxl_agent-0.1.0/tests/unit/test_mxl_multi_score.py +131 -0
- mxl_agent-0.1.0/tests/unit/test_mxl_reader.py +75 -0
- mxl_agent-0.1.0/tests/unit/test_operation_barlines.py +284 -0
- mxl_agent-0.1.0/tests/unit/test_operation_change_duration.py +285 -0
- mxl_agent-0.1.0/tests/unit/test_operation_chord_symbol_style.py +136 -0
- mxl_agent-0.1.0/tests/unit/test_operation_copy_notes.py +319 -0
- mxl_agent-0.1.0/tests/unit/test_operation_create_tuplet.py +340 -0
- mxl_agent-0.1.0/tests/unit/test_operation_credit.py +186 -0
- mxl_agent-0.1.0/tests/unit/test_operation_delete_note.py +296 -0
- mxl_agent-0.1.0/tests/unit/test_operation_direction_spanners.py +346 -0
- mxl_agent-0.1.0/tests/unit/test_operation_directions.py +384 -0
- mxl_agent-0.1.0/tests/unit/test_operation_envelope.py +76 -0
- mxl_agent-0.1.0/tests/unit/test_operation_insert_delete_harmony.py +250 -0
- mxl_agent-0.1.0/tests/unit/test_operation_insert_note.py +302 -0
- mxl_agent-0.1.0/tests/unit/test_operation_layout.py +313 -0
- mxl_agent-0.1.0/tests/unit/test_operation_lyrics.py +294 -0
- mxl_agent-0.1.0/tests/unit/test_operation_measure_operations.py +292 -0
- mxl_agent-0.1.0/tests/unit/test_operation_merge_tied_notes.py +255 -0
- mxl_agent-0.1.0/tests/unit/test_operation_metadata.py +217 -0
- mxl_agent-0.1.0/tests/unit/test_operation_move_note.py +270 -0
- mxl_agent-0.1.0/tests/unit/test_operation_notations.py +379 -0
- mxl_agent-0.1.0/tests/unit/test_operation_rename_part.py +213 -0
- mxl_agent-0.1.0/tests/unit/test_operation_repair.py +214 -0
- mxl_agent-0.1.0/tests/unit/test_operation_replace_harmony.py +226 -0
- mxl_agent-0.1.0/tests/unit/test_operation_replace_note_pitch.py +306 -0
- mxl_agent-0.1.0/tests/unit/test_operation_respell_pitch.py +212 -0
- mxl_agent-0.1.0/tests/unit/test_operation_score_defaults.py +180 -0
- mxl_agent-0.1.0/tests/unit/test_operation_set_attributes.py +304 -0
- mxl_agent-0.1.0/tests/unit/test_operation_spanners.py +417 -0
- mxl_agent-0.1.0/tests/unit/test_operation_split_note.py +289 -0
- mxl_agent-0.1.0/tests/unit/test_operation_transpose_notes.py +269 -0
- mxl_agent-0.1.0/tests/unit/test_pagination.py +104 -0
- mxl_agent-0.1.0/tests/unit/test_patch.py +251 -0
- mxl_agent-0.1.0/tests/unit/test_patch_conflicts.py +207 -0
- mxl_agent-0.1.0/tests/unit/test_patch_repair.py +110 -0
- mxl_agent-0.1.0/tests/unit/test_pitch.py +138 -0
- mxl_agent-0.1.0/tests/unit/test_pitch_microtonal.py +153 -0
- mxl_agent-0.1.0/tests/unit/test_plugin_manifest.py +152 -0
- mxl_agent-0.1.0/tests/unit/test_query_inspect.py +77 -0
- mxl_agent-0.1.0/tests/unit/test_release_artifacts.py +41 -0
- mxl_agent-0.1.0/tests/unit/test_render_identity.py +297 -0
- mxl_agent-0.1.0/tests/unit/test_revision_history.py +210 -0
- mxl_agent-0.1.0/tests/unit/test_score_document.py +30 -0
- mxl_agent-0.1.0/tests/unit/test_score_index.py +155 -0
- mxl_agent-0.1.0/tests/unit/test_selection.py +165 -0
- mxl_agent-0.1.0/tests/unit/test_service_session.py +102 -0
- mxl_agent-0.1.0/tests/unit/test_session_merge.py +173 -0
- mxl_agent-0.1.0/tests/unit/test_session_selection_store.py +55 -0
- mxl_agent-0.1.0/tests/unit/test_skill_wrapper.py +259 -0
- mxl_agent-0.1.0/tests/unit/test_timeline.py +406 -0
- mxl_agent-0.1.0/tests/unit/test_timeline_transposition.py +58 -0
- mxl_agent-0.1.0/tests/unit/test_transaction.py +127 -0
- mxl_agent-0.1.0/tests/unit/test_transaction_apply.py +262 -0
- mxl_agent-0.1.0/tests/unit/test_transaction_timewise.py +58 -0
- mxl_agent-0.1.0/tests/unit/test_validation_compatibility.py +59 -0
- mxl_agent-0.1.0/tests/unit/test_validation_fast.py +273 -0
- mxl_agent-0.1.0/tests/unit/test_viewer_server.py +550 -0
- mxl_agent-0.1.0/tests/unit/test_workspace.py +134 -0
- mxl_agent-0.1.0/tests/unit/test_writer.py +66 -0
- mxl_agent-0.1.0/tests/unit/test_xml_canonical.py +56 -0
- mxl_agent-0.1.0/tests/unit/test_xml_detect.py +38 -0
- mxl_agent-0.1.0/tests/unit/test_xml_parser.py +66 -0
- mxl_agent-0.1.0/tests/unit/test_xml_schema.py +105 -0
- mxl_agent-0.1.0/tests/unit/test_zip_compare.py +60 -0
- mxl_agent-0.1.0/tests/unit/test_zip_safety_helpers.py +31 -0
- mxl_agent-0.1.0/uv.lock +1359 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mxl-agent
|
|
3
|
+
description: Inspect, edit, and validate MusicXML and compressed MXL scores using the local mxl-agent engine. Use for .mxl, .musicxml, or .xml score files, chord-symbol edits, note-pitch edits, transposition, score summaries/measure inspection, MusicXML schema/temporal validation, or exporting an edited score to a new file. Not for general MIDI, audio, DAW, or PDF-only tasks that never touch a MusicXML file.
|
|
4
|
+
compatibility: Requires the mxl-agent CLI in this project (invoked via `uv run mxl-agent`, or on PATH). No network access. MuseScore (`musescore` command) and Verovio (`render`/`playback`) are optional, each unavailable if `doctor` reports it missing.
|
|
5
|
+
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/mxl-agent.sh *)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# mxl-agent
|
|
9
|
+
|
|
10
|
+
A deterministic local engine for MusicXML/MXL scores. It owns all XML structure, arithmetic, and
|
|
11
|
+
file I/O. You interpret the user's musical intent and choose typed operations; you never write or
|
|
12
|
+
edit MusicXML by hand.
|
|
13
|
+
|
|
14
|
+
## Hard rules
|
|
15
|
+
|
|
16
|
+
- **Never** read a whole score file into your context (no `cat`, no opening it as a text file).
|
|
17
|
+
Every command below returns a small, bounded JSON summary instead.
|
|
18
|
+
- **Never** author or emit a MusicXML document, or an XML fragment reconstructing one, as your own
|
|
19
|
+
text output.
|
|
20
|
+
- **Never** overwrite the user's original file. `export` always writes a new path.
|
|
21
|
+
- Only call `${CLAUDE_SKILL_DIR}/scripts/mxl-agent.sh <command> ...`. Do not invoke `mxl-agent`,
|
|
22
|
+
`uv`, or `python` directly, and do not construct shell strings around the wrapper's arguments.
|
|
23
|
+
- If a selection could match more than one thing (see "Ambiguity" below), stop and ask, or narrow
|
|
24
|
+
the selection. Do not guess and apply anyway.
|
|
25
|
+
|
|
26
|
+
## Workflow for an editing request
|
|
27
|
+
|
|
28
|
+
1. **Open** the score into a session (`session open`) if you don't already have a `session_id`
|
|
29
|
+
for it in this conversation. Reuse the same session for follow-up edits instead of reopening.
|
|
30
|
+
2. **Inspect** only the region you need (`inspect summary`, then `inspect measures` or
|
|
31
|
+
`inspect events` narrowed to the relevant part/measures). Use the returned `event_id` values
|
|
32
|
+
in later steps rather than re-deriving positions yourself.
|
|
33
|
+
3. **Plan** (`plan`) the operation first. Read `changed[].before`/`after` and `warnings`. If
|
|
34
|
+
`changed` is empty, has more than one entry when the user asked for one thing, or the command
|
|
35
|
+
fails with an ambiguity error, stop and clarify with the user rather than applying.
|
|
36
|
+
4. **Apply** (`apply`) only once the plan matches what the user asked for. Use the exact same
|
|
37
|
+
operation JSON you planned with.
|
|
38
|
+
5. **Validate** (`validate`) after applying. If `status` is `"failed"`, report the issues; do not
|
|
39
|
+
silently continue.
|
|
40
|
+
6. **Export** (`export --output <new-path>`) to a new file when the user wants a saved result.
|
|
41
|
+
Never pass the original input path as `--output`.
|
|
42
|
+
7. Summarize what changed in plain language (part, measure, before -> after) using the `plan`/
|
|
43
|
+
`apply` result. Do not paste raw XML.
|
|
44
|
+
|
|
45
|
+
For a read-only question ("what's the form of this tune", "what chords are in bars 9-16"), stop
|
|
46
|
+
after inspecting -- do not plan or apply anything.
|
|
47
|
+
|
|
48
|
+
`undo` reverts the session's current revision to its parent, one step. Offer it if the user asks
|
|
49
|
+
to revert an edit.
|
|
50
|
+
|
|
51
|
+
## Commands
|
|
52
|
+
|
|
53
|
+
All commands take `--json` (always pass it) and `--workspace-root <path>` (omit it to use the
|
|
54
|
+
default `.mxl-agent/workspaces` under the current project). Full parameter/error reference:
|
|
55
|
+
`references/command-catalog.md`.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
mxl-agent.sh session open <path-to-score> [--score SELECTOR] --json
|
|
59
|
+
mxl-agent.sh session status <session_id> --json
|
|
60
|
+
mxl-agent.sh session checkout <session_id> (--revision N | --checkpoint NAME) --json
|
|
61
|
+
mxl-agent.sh session revisions <session_id> --json
|
|
62
|
+
mxl-agent.sh create lead-sheet --spec '<lead sheet spec JSON>' --json
|
|
63
|
+
mxl-agent.sh create from-midi <midi-path> [--quantization NAME] [--beats N] [--fifths N] --json
|
|
64
|
+
mxl-agent.sh chord-chart parse --text '<chord chart text>' [--beats N] [--beat-type N] --json
|
|
65
|
+
mxl-agent.sh voice chord --symbol '<chord symbol>' --ranges '<voice ranges JSON>' [--profile classical|jazz] --json
|
|
66
|
+
mxl-agent.sh voice arrange --symbol '<chord symbol>' --instruments '<names JSON array>' [--profile classical|jazz] --json
|
|
67
|
+
mxl-agent.sh reharmonize check <session_id> --plan '<reharmonization plan JSON>' [--profile classical|jazz] --json
|
|
68
|
+
mxl-agent.sh accompany bass --spans '<chord spans JSON>' --range '<voice range JSON>' --json
|
|
69
|
+
mxl-agent.sh accompany guide-tones --spans '<chord spans JSON>' --range '<voice range JSON>' [--role ROLE ...] --json
|
|
70
|
+
mxl-agent.sh accompany comping --spans '<chord spans JSON>' --ranges '<voice ranges JSON>' --pulse '<n/d>' --json
|
|
71
|
+
|
|
72
|
+
mxl-agent.sh inspect summary <session_id> --json
|
|
73
|
+
mxl-agent.sh inspect parts <session_id> --json
|
|
74
|
+
mxl-agent.sh inspect measures <session_id> [--part-id P1] [--limit N] [--cursor C] --json
|
|
75
|
+
mxl-agent.sh inspect events <session_id> --part-id P1 [--limit N] [--cursor C] --json
|
|
76
|
+
|
|
77
|
+
mxl-agent.sh plan <session_id> --operation '<operation JSON>' --json
|
|
78
|
+
mxl-agent.sh apply <session_id> --operation '<operation JSON>' --json
|
|
79
|
+
mxl-agent.sh inverse <session_id> --operation '<operation JSON>' --json
|
|
80
|
+
mxl-agent.sh plan-patch <session_id> --patch '<patch JSON>' --json
|
|
81
|
+
mxl-agent.sh apply-patch <session_id> --patch '<patch JSON>' --json
|
|
82
|
+
mxl-agent.sh merge <session_id> --revision-a N --revision-b N --json
|
|
83
|
+
mxl-agent.sh analyze <session_id> --analysis NAME [--selection '<JSON>'] --json
|
|
84
|
+
mxl-agent.sh lint <session_id> [--selection '<JSON>'] --json
|
|
85
|
+
mxl-agent.sh repair <session_id> --fix <fix_id> --json
|
|
86
|
+
mxl-agent.sh repair-plan <session_id> --fix <fix_id> [--fix <fix_id> ...] --json
|
|
87
|
+
mxl-agent.sh repair-apply <session_id> --fix <fix_id> [--fix <fix_id> ...] --json
|
|
88
|
+
mxl-agent.sh undo <session_id> --json
|
|
89
|
+
mxl-agent.sh redo <session_id> --json
|
|
90
|
+
mxl-agent.sh checkpoint create <session_id> --name NAME [--revision N] --json
|
|
91
|
+
mxl-agent.sh checkpoint list <session_id> --json
|
|
92
|
+
mxl-agent.sh validate <session_id> [--revision N] --json
|
|
93
|
+
mxl-agent.sh compat-check <session_id> --target-version 3.1|4.0 [--revision N] --json
|
|
94
|
+
mxl-agent.sh diff <session_id> --json
|
|
95
|
+
mxl-agent.sh semantic-diff <session_id> [--from-revision N] [--to-revision N] --json
|
|
96
|
+
mxl-agent.sh log <session_id> --json
|
|
97
|
+
mxl-agent.sh export <session_id> --output <new-path> --json
|
|
98
|
+
mxl-agent.sh render <session_id> --output <new-svg-path> [--page N] [--revision N] --json
|
|
99
|
+
mxl-agent.sh playback <session_id> --output <new-midi-path> [--revision N] --json
|
|
100
|
+
mxl-agent.sh musescore check <session_id> [--revision N] --json
|
|
101
|
+
mxl-agent.sh musescore export <session_id> --output <path> --format pdf|png|mid|wav|musicxml|mxl [--revision N] --json
|
|
102
|
+
mxl-agent.sh mei export <session_id> --output <path> [--revision N] --json
|
|
103
|
+
mxl-agent.sh mei render <mei-path> --output <svg-path> [--page N] --json
|
|
104
|
+
mxl-agent.sh selection current <session_id> --json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Every result is one JSON object on stdout: `{"ok": true, ...}` or, on an expected failure,
|
|
108
|
+
`{"ok": false, "error": {"code": ..., "message": ...}}` with a non-zero exit code -- never a raw
|
|
109
|
+
traceback. Treat any non-zero exit as "the operation did not happen."
|
|
110
|
+
|
|
111
|
+
## Generating a new score
|
|
112
|
+
|
|
113
|
+
Two-stage rule: propose a compact typed plan, never author or paste MusicXML. Full shapes:
|
|
114
|
+
`references/command-catalog.md`. `create from-midi` is the exception (MIDI in, not JSON) and is
|
|
115
|
+
explicitly lossy -- read its `report` before trusting the result.
|
|
116
|
+
|
|
117
|
+
- `create lead-sheet --spec '<JSON>'` builds a brand-new single-part score (metadata, meter, key,
|
|
118
|
+
measures of melody/rests and harmony) and opens it as a new session, exactly like
|
|
119
|
+
`session open`. Doesn't pad short measures itself -- run `lint`/`repair` on the result.
|
|
120
|
+
- `chord-chart parse --text '<chart>'` turns a small text dialect (bars on `|`, whitespace-
|
|
121
|
+
separated chords, `%` repeats the previous bar) into the `{"offset_q", "symbol"}` list `create
|
|
122
|
+
lead-sheet`'s `measures[].harmony` expects. Session-independent.
|
|
123
|
+
- `voice chord --symbol '<chord>' --ranges '<JSON>'` voices a chord across ordered voice ranges
|
|
124
|
+
(pitch pairs or instrument names) -- real chord tones only (tensions under `--profile jazz`), no
|
|
125
|
+
crossing, doubling avoided until forced; raises `VOICING_UNSATISFIABLE` naming the first
|
|
126
|
+
unplaceable voice. `voice arrange --symbol '<chord>' --instruments '<names JSON>'` is the same
|
|
127
|
+
engine for a named ensemble, also reporting each instrument's transposed written pitch.
|
|
128
|
+
Session-independent.
|
|
129
|
+
- `reharmonize check <session_id> --plan '<JSON>'` validates a reharmonization plan you propose
|
|
130
|
+
(frozen measures, target positions, candidate chords, optional rationale) -- never invents a
|
|
131
|
+
chord itself. Reports structural `issues` plus an informational `melody_compatibility` per
|
|
132
|
+
candidate (not pass/fail). Read-only.
|
|
133
|
+
- `accompany bass`/`guide-tones` materialize one note per `--spans` entry (ordered
|
|
134
|
+
`{measure_ordinal, offset_q, duration_q, symbol}`), the nearest chord-tone role to the previous
|
|
135
|
+
note, within one `--range`. `accompany comping` reuses `voice chord`'s engine to stab a full
|
|
136
|
+
voicing every `--pulse`. All raise `ACCOMPANIMENT_UNSATISFIABLE` naming the failing span.
|
|
137
|
+
Session-independent.
|
|
138
|
+
|
|
139
|
+
## Candidate branches
|
|
140
|
+
|
|
141
|
+
`undo`/`checkout` then a *different* `apply` creates a real branch -- revision numbers are never
|
|
142
|
+
reused (Slice 7.4). To generate/compare alternatives without disturbing the current session:
|
|
143
|
+
`checkout` to the shared starting revision, `apply` each candidate as its own branch, `session
|
|
144
|
+
revisions` to list them, `semantic-diff --from-revision --to-revision` to compare, and
|
|
145
|
+
`validate`/`render`/`playback --revision N` to audition one without moving `current_revision`.
|
|
146
|
+
Accept = `checkout` to that revision; discard = do nothing.
|
|
147
|
+
|
|
148
|
+
## Operations (for `plan`/`apply --operation`)
|
|
149
|
+
|
|
150
|
+
The operation envelope's shape and every operation's `selection`/`parameters` fields are in
|
|
151
|
+
`references/selectors.md`. Currently implemented operations:
|
|
152
|
+
|
|
153
|
+
- `replace_harmony` -- change one existing chord symbol.
|
|
154
|
+
- `insert_harmony` -- add a chord symbol where none exists yet at that offset.
|
|
155
|
+
- `delete_harmony` -- remove one existing chord symbol.
|
|
156
|
+
- `replace_note_pitch` -- change one note's pitch (written, or sounding pitch for transposing
|
|
157
|
+
instruments).
|
|
158
|
+
- `respell_pitch` -- enharmonic respelling of one note (same sound, different spelling), explicit
|
|
159
|
+
or key-aware.
|
|
160
|
+
- `transpose_notes` -- transpose every note a selection resolves to by a fixed number of
|
|
161
|
+
semitones.
|
|
162
|
+
- `insert_note` -- insert a note or rest at an exact onset in an existing voice (append after its
|
|
163
|
+
current content, or fill a matching rest).
|
|
164
|
+
- `delete_note` -- delete one note, rest, or grace note, either outright or replaced with an
|
|
165
|
+
equal-duration rest to conserve the voice's total duration.
|
|
166
|
+
- `change_duration` -- change one note or rest's duration, rejecting a change that would desync
|
|
167
|
+
another voice unless explicitly told to compensate for it.
|
|
168
|
+
- `split_note` -- split one note or rest into two, tied together with correct start/stop pairing
|
|
169
|
+
if pitched.
|
|
170
|
+
- `merge_tied_notes` -- merge a whole tie chain into one note, when it's entirely within one
|
|
171
|
+
measure.
|
|
172
|
+
- `create_tuplet` -- convert a consecutive run of notes/rests into a tuplet (time-modification +
|
|
173
|
+
bracket notation).
|
|
174
|
+
- `move_note` -- move one note/rest to a different staff and/or voice, conserving durations on
|
|
175
|
+
both ends.
|
|
176
|
+
- `copy_notes` -- copy a consecutive note/rest range to a destination voice, literally or
|
|
177
|
+
transposed (a "sequence").
|
|
178
|
+
- `set_attributes` -- change key/time/clef/transpose starting at one measure; reports sounding
|
|
179
|
+
consequences of a transposition change.
|
|
180
|
+
- `set_lyric` / `delete_lyric` -- set or remove a lyric verse (text, syllabic, elision, extend)
|
|
181
|
+
on one note.
|
|
182
|
+
- `insert_direction` / `delete_direction` -- add or remove a words/dynamics/metronome/segno/coda
|
|
183
|
+
marking at an exact offset; the `sound` playback attributes stay separate from what's printed.
|
|
184
|
+
- `add_notation` / `remove_notation` -- add or remove an articulation, ornament, or technical
|
|
185
|
+
mark on one note (idempotent).
|
|
186
|
+
- `set_notehead` -- change one note's notehead shape.
|
|
187
|
+
- `start_slur` / `stop_slur` -- open/close a slur, auto-allocating/pairing its number and
|
|
188
|
+
validating the stop follows the start.
|
|
189
|
+
- `insert_measure` / `delete_measure` / `duplicate_measure` / `renumber_measures` -- whole-score
|
|
190
|
+
structural edits, always applied to every part at once to keep them in sync.
|
|
191
|
+
- `set_barline` -- set or clear one measure's left/right barline: style, a repeat sign
|
|
192
|
+
(forward/backward, optional play count), and/or a first/second-ending bracket. No
|
|
193
|
+
`bar_style`/`repeat`/`ending` given clears it back to default.
|
|
194
|
+
- `rename_part` -- change one part's printed name and/or abbreviation in the part-list.
|
|
195
|
+
- `set_metadata` -- change score-wide title/movement/creator/rights/source metadata.
|
|
196
|
+
- `set_measure_layout` -- force/forbid a system or page break at one measure, and/or set its
|
|
197
|
+
printed width.
|
|
198
|
+
- `set_measures_per_system` -- deterministic helper: system breaks exactly periodic (every N
|
|
199
|
+
measures) over a range, all parts at once.
|
|
200
|
+
- `remove_dangling_tie` / `pad_short_measure` -- safe automatic repairs for a `lint` finding (see
|
|
201
|
+
below); always take a `fix_id` from `lint`'s output rather than hand-built parameters.
|
|
202
|
+
|
|
203
|
+
Chord-symbol vocabulary, spelling policies, and written-vs-sounding pitch are covered in
|
|
204
|
+
`references/musical-semantics.md`.
|
|
205
|
+
|
|
206
|
+
## Analysis (for `analyze`)
|
|
207
|
+
|
|
208
|
+
Read-only, never mutates -- no `plan`/`apply` two-step needed. Full parameter/result reference:
|
|
209
|
+
`references/command-catalog.md`.
|
|
210
|
+
|
|
211
|
+
- `active_key` -- the explicit key signature (fifths, mode) in effect at each address the
|
|
212
|
+
selection resolves to. Locally inferred keys (for passages with no explicit signature) aren't
|
|
213
|
+
implemented yet.
|
|
214
|
+
- `scale_degree` -- key-relative scale degree (1-7, mode-aware) and chromatic alteration for each
|
|
215
|
+
selected note. Needs an event-granular selection (one note, or a note range) -- narrow with
|
|
216
|
+
staff/voice/offset or `event_ids` rather than a bare measure selection; same requirement below
|
|
217
|
+
wherever "event-granular" is mentioned.
|
|
218
|
+
- `chord_tone` -- classifies each selected note against the chord symbol active at its onset:
|
|
219
|
+
chord tone (with a role), available tension (jazz profile only), non-chord-tone, or unresolved
|
|
220
|
+
(no harmony/ambiguous polychord/Roman-numeral harmony). Event-granular. Never guesses an
|
|
221
|
+
unrelated chord -- only classifies against a chord symbol the score already has.
|
|
222
|
+
- `roman_numeral` -- Roman-numeral (`"V7"`, `"bVI"`) or Nashville-number (`"5m7"`, `"b6"`) label
|
|
223
|
+
for the chord symbol active at each address, relative to the active key. Flags (but doesn't
|
|
224
|
+
further classify) chromatic/borrowed chords via `is_diatonic`. Flexible measure-or-event
|
|
225
|
+
granularity, like `active_key`.
|
|
226
|
+
- `harmonic_rhythm` -- the ordered sequence of chord-symbol changes in a part, each with its
|
|
227
|
+
exact onset and (same-measure only) duration.
|
|
228
|
+
- `cadence_candidate` -- bare root-motion cadence pattern matches (authentic/plagal/half/
|
|
229
|
+
deceptive) between consecutive chord pairs -- candidates, not judgments; more matches than real
|
|
230
|
+
cadences is normal since it can't see phrase position or meter.
|
|
231
|
+
- `non_chord_tone` -- for an already-classified non-chord tone (see `chord_tone`), which figure it
|
|
232
|
+
resembles (passing, neighbor, suspension, anticipation, appoggiatura, pedal, unknown) from its
|
|
233
|
+
melodic approach/departure alone, no metric reasoning. Event-granular.
|
|
234
|
+
- `voice_leading` -- common tones and guide-tone (3rd/7th) motion between consecutive chord pairs.
|
|
235
|
+
Tendency-tone resolution, parallel 5ths/8ves, spacing, and voice crossing aren't implemented yet.
|
|
236
|
+
- `playability` -- checks each note's written/sounding pitch against a named instrument's typical
|
|
237
|
+
range (`parameters.instrument`, e.g. `"violin"`, `"clarinet_bb"` -- a small curated set, not a
|
|
238
|
+
full orchestration reference). Event-granular.
|
|
239
|
+
- `note_density` -- notes per quarter note in each resolved measure (same flexible granularity
|
|
240
|
+
as `active_key`).
|
|
241
|
+
- `syncopation` -- whether each selected note starts off the beat and sustains past the next
|
|
242
|
+
beat boundary. Motives, polyphony, and doubling aren't implemented yet.
|
|
243
|
+
|
|
244
|
+
## Lint and repair
|
|
245
|
+
|
|
246
|
+
`lint` scans the score (or the selection you narrow it to) for a small, stable catalog of
|
|
247
|
+
findings, each with a `finding_id`, `severity`, location, human-readable `message`, and
|
|
248
|
+
`fixable`/`fix_id`:
|
|
249
|
+
|
|
250
|
+
- `dangling-tie` (error, always fixable) -- a `<tie type="start">` never closed, or a
|
|
251
|
+
`<tie type="stop">` with no matching prior start.
|
|
252
|
+
- `measure-too-short` (warning, always fixable) -- a single-voice measure short of its active
|
|
253
|
+
time signature's expected length. A pickup measure or a measure with more than one voice is
|
|
254
|
+
never flagged.
|
|
255
|
+
- `voice-duration-mismatch` (warning, **not** fixable) -- two voices in the same measure disagree
|
|
256
|
+
on total duration; report it, don't repair it, since which voice is wrong isn't decidable from
|
|
257
|
+
the mismatch alone.
|
|
258
|
+
|
|
259
|
+
For a fixable finding, pass its exact `fix_id` to `repair --fix` -- never hand-construct it.
|
|
260
|
+
`repair` applies through the same typed, footprint-guarded machinery as `plan`/`apply`, producing
|
|
261
|
+
a normal revision `undo` can revert.
|
|
262
|
+
|
|
263
|
+
To fix several findings together, repeat `--fix` on `repair-plan`/`repair-apply` instead of
|
|
264
|
+
calling `repair` per finding. `repair-plan` dry-runs the group (writes nothing), reporting
|
|
265
|
+
`PATCH_CONFLICT` if two fixes are incompatible. `repair-apply` commits the group atomically, but
|
|
266
|
+
each fix still gets its own revision/`log` entry, so `undo` reverts one at a time.
|
|
267
|
+
|
|
268
|
+
## Rendering
|
|
269
|
+
|
|
270
|
+
`render` produces a static SVG page, `playback` a MIDI file -- neither changes the score. Both
|
|
271
|
+
need Verovio (`VEROVIO_UNAVAILABLE` if `doctor` reports it missing). Only whole-score output
|
|
272
|
+
(`render --page N`, default `1`) -- no selection-level rendering yet.
|
|
273
|
+
|
|
274
|
+
`musescore check`/`export` (a second, independent optional integration) run an open/import
|
|
275
|
+
diagnostic, or export to PDF/PNG/MIDI/WAV/MusicXML, through a real MuseScore CLI --
|
|
276
|
+
`MUSESCORE_UNAVAILABLE` if `doctor` finds none. Its output is diagnostic/export-only, never
|
|
277
|
+
written back into a session.
|
|
278
|
+
|
|
279
|
+
`mei export`/`render` (through Verovio) convert to MEI or render a standalone MEI file to SVG --
|
|
280
|
+
interoperability only, never an editable input.
|
|
281
|
+
|
|
282
|
+
If the user has the local viewer (`preview`) open and clicks a note there, `selection current`
|
|
283
|
+
returns `{"addresses": [...]}` -- the fully resolved locator(s) they last selected (`part_id`,
|
|
284
|
+
`measure_ordinal`, `event_id`, etc.), or `{"addresses": []}` if nothing's been selected yet.
|
|
285
|
+
Build a `--selection` from it as `{"event_ids": [<address.event_id>, ...]}`. Only note-level
|
|
286
|
+
clicks are supported (a chord or measure click resolves to whichever note element was actually
|
|
287
|
+
clicked).
|
|
288
|
+
|
|
289
|
+
## Ambiguity
|
|
290
|
+
|
|
291
|
+
If a selection resolves to zero or more than one target when the user meant exactly one, the
|
|
292
|
+
operation raises a structured `*_AMBIGUOUS` or `*_NOT_FOUND` error (or, for note-range
|
|
293
|
+
operations, `plan` legitimately returns multiple `changed` entries -- that's normal for a
|
|
294
|
+
*range*, not an error). When it's an error: narrow the selection (add a measure/voice/staff/
|
|
295
|
+
offset, or use an `event_id` from a prior `inspect` call) or ask the user which one they meant.
|
|
296
|
+
Never retry with a broader selection hoping it "just picks one" -- it won't; ambiguity blocks
|
|
297
|
+
mutation by design.
|
|
298
|
+
|
|
299
|
+
## Safety and preservation
|
|
300
|
+
|
|
301
|
+
- The original file is never touched. `session open` copies it into an immutable workspace.
|
|
302
|
+
- Every `apply` is mutation-guarded: an operation that changes anything outside its declared
|
|
303
|
+
footprint is rejected before it's ever written.
|
|
304
|
+
- `apply` always creates a new revision; `undo` is always available for the most recent one.
|
|
305
|
+
- MusicXML 3.1 and 4.0 both supported: `validate` auto-selects the matching schema; `compat-check
|
|
306
|
+
--target-version` cross-validates against the other version.
|
|
307
|
+
- An `.mxl` with more than one score ("opus") needs `--score` on `session open`;
|
|
308
|
+
`MULTIPLE_SCORE_ROOTS_AMBIGUOUS` lists the available paths/indices.
|
|
309
|
+
- Full invariants: `references/preservation-and-safety.md`.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.0",
|
|
3
|
+
"skill": "mxl-agent",
|
|
4
|
+
"note": "Structured eval cases per Section 18.1/14.6. Trigger and workflow cases are evaluated by a clean-context Claude Code eval run (not by this repo's deterministic test suite). The end-to-end command sequences below are also covered directly, deterministically, by tests/unit/test_cli_commands.py and tests/unit/test_skill_wrapper.py, which check the tooling those transcripts depend on actually works.",
|
|
5
|
+
|
|
6
|
+
"should_trigger": [
|
|
7
|
+
{
|
|
8
|
+
"id": "trigger-open-and-transpose",
|
|
9
|
+
"prompt": "Open chart.mxl and transpose the melody up a whole step.",
|
|
10
|
+
"expect_skill_used": true
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "trigger-chord-edit",
|
|
14
|
+
"prompt": "In bars.musicxml, change the chord at measure 3 to Dm7.",
|
|
15
|
+
"expect_skill_used": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "trigger-inspect-only",
|
|
19
|
+
"prompt": "What key is this piece in? It's in song.musicxml.",
|
|
20
|
+
"expect_skill_used": true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "trigger-mxl-mention",
|
|
24
|
+
"prompt": "Can you validate this .mxl file for me?",
|
|
25
|
+
"expect_skill_used": true
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "trigger-note-pitch",
|
|
29
|
+
"prompt": "The second note in measure 5 of lead.musicxml should be an F sharp, not F natural.",
|
|
30
|
+
"expect_skill_used": true
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
|
|
34
|
+
"should_not_trigger": [
|
|
35
|
+
{
|
|
36
|
+
"id": "no-trigger-daw-mixing",
|
|
37
|
+
"prompt": "How do I set up a sidechain compressor in my DAW?",
|
|
38
|
+
"expect_skill_used": false
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "no-trigger-pdf-only",
|
|
42
|
+
"prompt": "Extract the text from this PDF scan of sheet music.",
|
|
43
|
+
"expect_skill_used": false
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "no-trigger-plain-midi",
|
|
47
|
+
"prompt": "Play this MIDI file and tell me what instrument sound 12 is in General MIDI.",
|
|
48
|
+
"expect_skill_used": false
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "no-trigger-generic-xml",
|
|
52
|
+
"prompt": "Validate this config.xml against our internal schema.",
|
|
53
|
+
"expect_skill_used": false
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "no-trigger-theory-only",
|
|
57
|
+
"prompt": "What's the difference between a Lydian and Ionian mode?",
|
|
58
|
+
"expect_skill_used": false
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
|
|
62
|
+
"workflow_adherence": [
|
|
63
|
+
{
|
|
64
|
+
"id": "workflow-dry-run-before-apply",
|
|
65
|
+
"description": "For any edit request, the transcript must show a `plan` call before the matching `apply` call with the same operation JSON.",
|
|
66
|
+
"assertion": "sequence contains plan(op) immediately or shortly before apply(op) with equal operation/selection/parameters"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "workflow-inspect-before-edit",
|
|
70
|
+
"description": "Before constructing a selection by measure/voice/offset, the transcript shows an `inspect summary` or `inspect measures`/`inspect events` call establishing that address -- not a guessed measure_ordinal.",
|
|
71
|
+
"assertion": "an inspect_* call for the relevant part/measure precedes the first plan/apply call"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "workflow-validate-after-apply",
|
|
75
|
+
"description": "After the final `apply` of a session, a `validate` call appears before reporting success to the user.",
|
|
76
|
+
"assertion": "validate(session_id) appears after the last apply(session_id) and before the final assistant summary"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "workflow-no-raw-xml",
|
|
80
|
+
"description": "No tool call or assistant message contains a `<score-partwise` or `<score-timewise` opening tag, or any multi-element MusicXML fragment reconstructed by the model.",
|
|
81
|
+
"assertion": "absence check across the full transcript"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "workflow-ambiguity-stops",
|
|
85
|
+
"description": "When `plan` returns a `*_AMBIGUOUS` error, the assistant does not retry with a broader/different selection hoping for a single match -- it asks the user or narrows using data already inspected.",
|
|
86
|
+
"assertion": "no plan/apply retry with a materially different selection immediately following an *_AMBIGUOUS error, without an intervening clarification"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
|
|
90
|
+
"output_correctness": [
|
|
91
|
+
{
|
|
92
|
+
"id": "correctness-chord-edit-e2e",
|
|
93
|
+
"description": "End-to-end: one chord-symbol edit.",
|
|
94
|
+
"fixture": "fixtures/chord_symbols.musicxml",
|
|
95
|
+
"steps": [
|
|
96
|
+
"session open fixtures/chord_symbols.musicxml",
|
|
97
|
+
"inspect summary <session_id>",
|
|
98
|
+
"inspect measures <session_id> --part-id P1",
|
|
99
|
+
"plan <session_id> --operation '{\"operation\":\"replace_harmony\",\"selection\":{\"part_ids\":[\"P1\"],\"measure_ordinals\":[0]},\"parameters\":{\"symbol\":\"Cm7\"},...}'",
|
|
100
|
+
"apply <session_id> --operation '<same operation>'",
|
|
101
|
+
"validate <session_id>",
|
|
102
|
+
"export <session_id> --output <new path>"
|
|
103
|
+
],
|
|
104
|
+
"assert": [
|
|
105
|
+
"plan.changed[0].before == 'Cmaj7'",
|
|
106
|
+
"plan.changed[0].after == 'Cm7'",
|
|
107
|
+
"apply.revision_after == apply.revision_before + 1",
|
|
108
|
+
"validate.status == 'passed'",
|
|
109
|
+
"exported file reopens and contains a Cm7 harmony at part P1 measure 0"
|
|
110
|
+
],
|
|
111
|
+
"deterministic_test": "tests/unit/test_operation_replace_harmony.py, tests/unit/test_cli_commands.py::test_plan_then_apply_then_validate_then_export_then_undo"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"id": "correctness-transpose-e2e",
|
|
115
|
+
"description": "End-to-end: transpose a note range up a whole step and verify both written and sounding pitch.",
|
|
116
|
+
"fixture": "fixtures/divisions_change.musicxml",
|
|
117
|
+
"steps": [
|
|
118
|
+
"session open fixtures/divisions_change.musicxml",
|
|
119
|
+
"inspect events <session_id> --part-id P1",
|
|
120
|
+
"plan <session_id> --operation '{\"operation\":\"transpose_notes\",\"selection\":{\"part_ids\":[\"P1\"],\"measure_ordinals\":[0]},\"parameters\":{\"semitones\":2},...}'",
|
|
121
|
+
"apply <session_id> --operation '<same operation>'",
|
|
122
|
+
"validate <session_id>"
|
|
123
|
+
],
|
|
124
|
+
"assert": [
|
|
125
|
+
"every changed note's written step/octave shifted up a whole step",
|
|
126
|
+
"unselected measure(s) are byte-for-byte unchanged (diff.changed_measures excludes them)",
|
|
127
|
+
"validate.status == 'passed'"
|
|
128
|
+
],
|
|
129
|
+
"deterministic_test": "tests/unit/test_operation_transpose_notes.py"
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
|
|
133
|
+
"efficiency": [
|
|
134
|
+
{
|
|
135
|
+
"id": "efficiency-token-bound",
|
|
136
|
+
"description": "For the chord-edit and transpose scenarios above, total tool-result bytes returned to the model context should stay under a few KB per call (Section 19.1's 32 KiB/200-item caps), and no call should return a full document.",
|
|
137
|
+
"measurement": "sum of stdout bytes across all mxl-agent.sh invocations in the transcript"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "efficiency-no-redundant-open",
|
|
141
|
+
"description": "The same score is not reopened into a new session mid-conversation when an open session_id for it is already known.",
|
|
142
|
+
"measurement": "count of session_open calls per distinct input path within one conversation"
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
|
|
146
|
+
"safety": [
|
|
147
|
+
{
|
|
148
|
+
"id": "safety-malformed-archive-fixture",
|
|
149
|
+
"description": "session open on a hostile/malformed .mxl (zip-slip, encrypted entry, compression bomb) must fail with a structured ZIP_* error and must not mutate anything or crash the wrapper.",
|
|
150
|
+
"deterministic_test": "tests/security/test_mxl_safety.py, tests/security/test_zip_safety.py"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": "safety-ambiguous-selector-no-mutation",
|
|
154
|
+
"description": "plan/apply on an ambiguous selection must not write any revision.",
|
|
155
|
+
"deterministic_test": "tests/unit/test_operation_replace_harmony.py::test_ambiguous_target_without_offset_raises, tests/unit/test_transaction.py"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"id": "safety-stale-revision-rejected",
|
|
159
|
+
"description": "apply with a stale base_revision must fail with REVISION_CONFLICT and must not overwrite the newer revision.",
|
|
160
|
+
"deterministic_test": "tests/unit/test_transaction.py::test_stale_base_revision_raises_revision_conflict"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "safety-wrapper-rejects-unknown-command",
|
|
164
|
+
"description": "The wrapper script refuses any first argument outside its allowlist, without forwarding it to a shell.",
|
|
165
|
+
"deterministic_test": "tests/unit/test_skill_wrapper.py"
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<score-partwise version="4.0">
|
|
3
|
+
<part-list>
|
|
4
|
+
<score-part id="P1">
|
|
5
|
+
<part-name>Lead Sheet</part-name>
|
|
6
|
+
</score-part>
|
|
7
|
+
</part-list>
|
|
8
|
+
<part id="P1">
|
|
9
|
+
<measure number="1">
|
|
10
|
+
<attributes>
|
|
11
|
+
<divisions>1</divisions>
|
|
12
|
+
<key>
|
|
13
|
+
<fifths>0</fifths>
|
|
14
|
+
</key>
|
|
15
|
+
<time>
|
|
16
|
+
<beats>4</beats>
|
|
17
|
+
<beat-type>4</beat-type>
|
|
18
|
+
</time>
|
|
19
|
+
<clef>
|
|
20
|
+
<sign>G</sign>
|
|
21
|
+
<line>2</line>
|
|
22
|
+
</clef>
|
|
23
|
+
</attributes>
|
|
24
|
+
<harmony>
|
|
25
|
+
<root>
|
|
26
|
+
<root-step>C</root-step>
|
|
27
|
+
</root>
|
|
28
|
+
<kind text="maj7">major-seventh</kind>
|
|
29
|
+
</harmony>
|
|
30
|
+
<note>
|
|
31
|
+
<pitch>
|
|
32
|
+
<step>C</step>
|
|
33
|
+
<octave>4</octave>
|
|
34
|
+
</pitch>
|
|
35
|
+
<duration>4</duration>
|
|
36
|
+
<type>whole</type>
|
|
37
|
+
</note>
|
|
38
|
+
</measure>
|
|
39
|
+
<measure number="2">
|
|
40
|
+
<harmony>
|
|
41
|
+
<root>
|
|
42
|
+
<root-step>D</root-step>
|
|
43
|
+
</root>
|
|
44
|
+
<kind text="m7">minor-seventh</kind>
|
|
45
|
+
</harmony>
|
|
46
|
+
<note>
|
|
47
|
+
<pitch>
|
|
48
|
+
<step>D</step>
|
|
49
|
+
<octave>4</octave>
|
|
50
|
+
</pitch>
|
|
51
|
+
<duration>2</duration>
|
|
52
|
+
<type>half</type>
|
|
53
|
+
</note>
|
|
54
|
+
<harmony>
|
|
55
|
+
<root>
|
|
56
|
+
<root-step>G</root-step>
|
|
57
|
+
</root>
|
|
58
|
+
<kind text="7">dominant</kind>
|
|
59
|
+
</harmony>
|
|
60
|
+
<note>
|
|
61
|
+
<pitch>
|
|
62
|
+
<step>G</step>
|
|
63
|
+
<octave>4</octave>
|
|
64
|
+
</pitch>
|
|
65
|
+
<duration>2</duration>
|
|
66
|
+
<type>half</type>
|
|
67
|
+
</note>
|
|
68
|
+
</measure>
|
|
69
|
+
</part>
|
|
70
|
+
</score-partwise>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<score-partwise version="4.0">
|
|
3
|
+
<part-list>
|
|
4
|
+
<score-part id="P1">
|
|
5
|
+
<part-name>Music</part-name>
|
|
6
|
+
</score-part>
|
|
7
|
+
</part-list>
|
|
8
|
+
<part id="P1">
|
|
9
|
+
<measure number="1">
|
|
10
|
+
<attributes>
|
|
11
|
+
<divisions>1</divisions>
|
|
12
|
+
<key>
|
|
13
|
+
<fifths>0</fifths>
|
|
14
|
+
</key>
|
|
15
|
+
<time>
|
|
16
|
+
<beats>4</beats>
|
|
17
|
+
<beat-type>4</beat-type>
|
|
18
|
+
</time>
|
|
19
|
+
<clef>
|
|
20
|
+
<sign>G</sign>
|
|
21
|
+
<line>2</line>
|
|
22
|
+
</clef>
|
|
23
|
+
</attributes>
|
|
24
|
+
<note>
|
|
25
|
+
<pitch>
|
|
26
|
+
<step>C</step>
|
|
27
|
+
<octave>4</octave>
|
|
28
|
+
</pitch>
|
|
29
|
+
<duration>1</duration>
|
|
30
|
+
<type>quarter</type>
|
|
31
|
+
</note>
|
|
32
|
+
<note>
|
|
33
|
+
<pitch>
|
|
34
|
+
<step>D</step>
|
|
35
|
+
<octave>4</octave>
|
|
36
|
+
</pitch>
|
|
37
|
+
<duration>3</duration>
|
|
38
|
+
<type>half</type>
|
|
39
|
+
<dot/>
|
|
40
|
+
</note>
|
|
41
|
+
</measure>
|
|
42
|
+
<measure number="2">
|
|
43
|
+
<attributes>
|
|
44
|
+
<divisions>2</divisions>
|
|
45
|
+
</attributes>
|
|
46
|
+
<note>
|
|
47
|
+
<rest/>
|
|
48
|
+
<duration>8</duration>
|
|
49
|
+
<type>whole</type>
|
|
50
|
+
</note>
|
|
51
|
+
</measure>
|
|
52
|
+
</part>
|
|
53
|
+
</score-partwise>
|