Handel Dixit Dominus HWV 232 — Stage-1 direct-PDF parser
Canonical parser v4
Status: canonical parser infrastructure from Movement 9 onward
Adopted: 8 Oct 2026
Executable: dixit_stage1_parser_v4_CANONICAL.py
Regression suite: test_dixit_stage1_parser_v4_regression.py
This document is authoritative for the literal score-extraction / Stage-1 parser layer. It does not define vocal renderer phonemes, performing-edition overrides, piano reduction, or player balance.
The parser's job is to establish what the selected score source actually prints before any downstream interpretation is added.
1. Canonical ordering
The fundamental v4 rule is that movement/system ownership is resolved before clef, key, accidental, lyric or note state is interpreted.
PDF page
-> identify score systems
-> retain only systems belonging to selected movement
-> bind named vocal staves within retained system
-> bind clef/key glyphs to staff + retained-system geometry
-> decode printed opening key signature
-> require exact vocal-staff consensus
-> parse pitches/rhythm/ties/lyrics
A page is not a musical-state boundary. Two movements can occupy different systems on the same PDF page.
2. Printed key signature: never inherit, never fall back
The movement profile deliberately contains no key signature.
For every named vocal staff independently:
- locate that staff's printed clef;
- identify the key-signature corridor between clef and time signature / first musical event;
- read accidental glyphs in that corridor;
- convert vertical staff position to a note letter using the active clef;
- compare the resulting semantic letter→accidental map across all named vocal staves.
The parse continues only if all named vocal staves agree exactly.
If the signature is absent, ambiguous, conflicting, or contaminated by another system, abort. There is no configured or inherited fallback.
3. Clef/key glyph ownership is two-dimensional
Nearest-staff assignment alone is insufficient on a page containing multiple score systems.
A candidate clef/key glyph belongs to a vocal staff only when:
nearest staff == candidate staff
AND
vertical distance from candidate staff <= canonical system-proximity limit
This rule must also be used conceptually by instrumental extraction: a clef on another score system cannot own notes merely because it is the closest clef on the page in one dimension.
4. Shared-page regression: PDF page 51
Philip Legge's score gives the project a particularly strong regression case: Movement 8 ends in the upper system of PDF page 51 and Movement 9 begins in the lower system of the same page.
Movement 8 — Conquassabit
profile pages: 48-51
p51 selection: TOP system only
vocal topology: Soprano I / Soprano II / Alto / Tenor / Bass
metre: 3/4
last bar: 50
printed key: B-flat only
named-staff consensus: 5/5
systems: 7
vocal events: 821
lyric fragments: 362 / 362 assigned
continuation notes: 310
classified ties: 0
core issues: 0
unmapped glyphs: 0
Movement 9 — De torrente in via
profile pages: 51-54
p51 selection: LOWER system only
vocal topology: Soprano I / Soprano II / Tenor / Bass
metre: 4/4
last bar: 36
printed key: B-flat + E-flat
named-staff consensus: 4/4
systems: 7
vocal events: 414
lyric fragments: 233 / 233 assigned
continuation notes: 66 (16 tied, 50 melismatic)
classified ties: 17
cross-system ties: 2
core issues: 0
unmapped glyphs: 0
The two cross-system Movement-9 ties are Soprano II bar 5→6 G4 and bar 26→27 C5.
5. Frozen byte-level regression hashes
The v4 regression suite requires exact SHA-256 identity for the principal extraction CSVs. These hashes establish that the infrastructure promotion does not alter already-validated musical extraction.
Movement 8
2c9674dae77674d0509c8da2ee0d2c584e199dca963b27081f8a7286b3f50b42 dixit-mvt8-vocals-v4.csv
f8d915b0266984d98e2716979115a669ae6ce509f924d73d2c09ee1ac4663504 dixit-mvt8-lyrics-v4.csv
092ffe3ade647cb685be7279771d235840a12e751aac55303b147c9de28a4e1e dixit-mvt8-syllables-v4.csv
c5ba9a4c52cfe96fe601257064fd10a00584aa9567f38e0ddc62257afacc17cc mvt8-key-signature-audit.csv
The first three files are byte-identical to the validated M8-v3 extraction counterparts after filename/schema promotion.
Movement 9
dd404fc078a6145288c91fcfd9a91e1c5caf4085010c47c03632956abeb2ad88 dixit-mvt9-vocals-v4.csv
a5cac5f09d700af9a8daf2e771ece56e92a2193dca251cd0dd7d5f03a4cacec7 dixit-mvt9-lyrics-v4.csv
7dda1cab37dec2d00253eb3c457cf959b55720777bec5c9cd01184c155a5b27c dixit-mvt9-syllables-v4.csv
cb080446a54644f27838ea4c55104bdf1b7c3243acda96396bba76ceac6c2035 mvt9-key-signature-audit.csv
The first three files are byte-identical to the validated M9-v1 Stage-1 extraction counterparts.
6. Canonical profiles are explicit, but musical state is still read from score
A profile may contain structural facts required to delimit the parse:
movement number/title
PDF page span
shared-page system slice
named vocal topology
metre / expected final bar
A profile must not contain a guessed key signature used to rescue parsing. The key is score-derived state.
The current executable freezes M8 and M9 as profiles because they form the shared-page regression pair. Future movement profiles should be added only after movement boundaries/topology are established and then frozen by regression.
7. Rhythm closure is necessary, not sufficient
Zero bar-closure errors validate durations/topology only. They do not validate:
- key signature;
- clef ownership;
- movement/system ownership;
- lyric assignment;
- edition correctness.
The canonical QC layers remain separate.
8. Running the parser
python3 dixit_stage1_parser_v4_CANONICAL.py \
IMSLP13037-232dixit-philip-legge.pdf \
--movement 9 \
--no-render
For the frozen M8 regression:
python3 dixit_stage1_parser_v4_CANONICAL.py \
IMSLP13037-232dixit-philip-legge.pdf \
--movement 8 \
--no-render
Preview tempo defaults to 100 BPM and can be overridden with --tempo. The
literal score extraction is not changed by preview tempo.
9. Running the regression suite
python3 test_dixit_stage1_parser_v4_regression.py \
dixit_stage1_parser_v4_CANONICAL.py \
IMSLP13037-232dixit-philip-legge.pdf
A successful run must report both movement passes and the explicit shared-page boundary pass.
10. Promotion rule
dixit_stage1_parser_v4_CANONICAL.py supersedes the M8-specific v3 parser as
the infrastructure starting point for subsequent movements.
The older movement-specific parsers remain historical/frozen reconstruction artifacts. Do not rewrite locked Movement-8 production data merely because the shared parser implementation has been promoted.