Repository navigation
Add a CLDR person-name corpus and an agreement ratchet against its stated fields - #654
Merged
Merged
Conversation
build_cldr_corpus.py fetches CLDR release-48's personNameTest files and writes corpus_cldr.jsonl (the harness format, radar tier; 1,870 lines from 2,640 renderings in 110 locale files), cldr_fields.jsonl (each rendering's CLDR fields, for the agreement ratchet that follows) and cldr_LICENSE (the Unicode-3.0 notice, outside the JSONL because the corpus loader refuses non-JSON lines). Enrolled in _CORPUS_TIERS and _CORPUS_FLOORS in the same commit because both rosters are fail-closed. Adding it surfaced three things, each decided rather than re-recorded: - Fourteen 1.4.0-ledger rules' recorded corpus reach grew (the comma rules 461 -> 771 names, feat(#269) 6 -> 196). The CLDR names stay out of _CORPUS_NAMES, the population the ledger guards measure; the harness still classifies their diffs on the radar tier. - The guard also caught the fix(#379) member 'di' case-folding onto the Turkish dotless i in Yildirim. Its three ledger copies now use (?ai), which folds ASCII letters only. - Four Slavic renderings fail the NFC/NFD property test: decomposed 'inz.' with a combining caron is not a period abbreviation (#652). differential_corpus() leaves the file out until that is fixed; it would cost test_properties.py 34.5 s -> 39.6 s (py3.11) to include. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cldr_agreement.py decides whether nameparser and CLDR agree on one role of one rendering: a CLDR field counts only where its value occurs in the rendering (word by word where it has whitespace, by substring where it has none), and two values agree when they hold the same multiset of words (or the same unspaced text). Roles: title, given names (given + given2 against given + middle), family, suffix, and the particle/base split where CLDR states it. Each rule's tests carry the wrong answer its plausible alternative gives; four mutations each fail a test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cldr_agreement.py parses every CLDR rendering with the working tree and records, role by role, where it disagrees with CLDR's stated fields, in cldr_agreement.json (996 of 2,640 renderings disagree somewhere; 8,982 of 11,880 role verdicts agree, 2026-10-12). tests/v2/test_cldr_agreement.py fails on a regression, on progress not yet recorded, on a changed wrong answer, and on a recorded rendering that no longer exists. The record is also a measured worklist of where the parser and CLDR read a name differently. Re-record: uv run python tools/differential/cldr_agreement.py --record Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README's corpus table gains corpus_cldr.jsonl, with its source, tier and blind spot, and a section on the agreement ratchet, its re-record command, and the two populations that leave the file out (the ledger guards' _CORPUS_NAMES, and differential_corpus() until #652). AGENTS.md points rule changes at the agreement report. The gate at 1.4.0 refused 17 CLDR names ending 'M.D. Ph.D.': the trailing-'Ph. D.' [[never]] entry's last component, ph\.\s*d\., also matched the unsplit 'Ph.D.', and their only diff is the suffix separator ('M.D., Ph.D.' -> 'M.D. Ph.D.'). Narrowed to ph\.\s+d\., the split pair its why is about, as the entry's own comment asks; its recorded effect moves 7 -> 3 captures, the four unsplit names leaving its reach. One CLDR name still fails at 1.4.0 -- a delimited nickname loses its comma -- to be fixed in its own PR before this one merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The comparison stripped only the ASCII comma, so a word glued to an Arabic, fullwidth, Myanmar or Ethiopic comma did not occur and CLDR's field was expected empty. It now strips nameparser's COMMA_CHARS plus those marks (and, defensively, the ideographic comma, which no CLDR 48 rendering holds). - An unspaced rendering compares in order, and fields were joined in CLDR's field order; they are now joined in the order they occur in the text, so a rendering that writes given2 first can agree. Re-recorded: 57 recorded disagreements were the comparison's, not the parser's, and now agree. One former agreement was false -- the glued Arabic comma had hidden CLDR's generation field in a Persian rendering -- and is now recorded as the disagreement it is. The record holds 956 renderings, from 996. - A ledger-guard comment called _CORPUS_NAMES the gate's population, which stopped being true when corpus_cldr.jsonl was left out of it; it now says what follows (a recorded or watched row on a CLDR-only name reads as departed), and the recipe beside _carries filters the same file, so it prints its prose's '2 8' again. - The narrowed Ph. D. entry's comment said 17 names ending 'M.D. Ph.D.' differ from 1.4. 27 end so in either case: 11 carry a FAMILY_FIRST shape and are not compared at 1.4.0, the 16 compared differ only in the separator, and the entry's 17th refusal was an unsplit 'Ph.D.' name whose diff is a title move. - The module docstring no longer claims the record never goes stale: the ratchet keeps recorded answers current, and text/expected are refreshed only by a re-record. - Release-log counts drawn from the classified summary now include CLDR's fictional renderings; the README's CLDR section and AGENTS.md's release-log paragraph say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #654 +/- ##
=======================================
Coverage 99.02% 99.02%
=======================================
Files 46 46
Lines 4520 4520
=======================================
Hits 4476 4476
Misses 44 44 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The 1.4.0 gate raised three UNPINNED CONTESTs on CLDR renderings -- two pairs of initials-per-word rules with equal fields, and the CJK order rule against the delimited-nickname rule -- and a pin would have had to name, for two of them, a rule that does not describe what moved. The decision (Derek, 2026-10-12) extends the ledger guards' ruling to the run: compare._UNMEASURED_CORPORA names the files whose names are classified but not adjudicated, a name held by any other corpus still is, and a test holds the set equal to the guards' copy in _differential_fixtures. The README's CLDR section says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gives nameparser its first check against ground truth from outside the project: Unicode CLDR's person-name test data, which states every name's fields separately (title, given, given2, surname or prefix + core, surname2, generation, credentials), in native scripts.
What's added
A radar corpus.
tools/differential/build_cldr_corpus.pyfetches CLDRrelease-48's personNameTest files and writes:corpus_cldr.jsonl: 1,870 lines from 2,640 renderings in 110 locale files; each name's long, formal rendering in givenFirst, surnameFirst and sorting order, with surname-first forms taggedshape: 4;cldr_fields.jsonl: each rendering's CLDR fields;cldr_LICENSE: the Unicode-3.0 notice.An agreement ratchet.
tools/differential/cldr_agreement.pyparses every rendering and compares, role by role, with CLDR. The roles are title, given names, family and suffix, plus the particle/base split where CLDR states it. Disagreements are recorded incldr_agreement.json.tests/v2/test_cldr_agreement.pyfails on:Re-record with
uv run python tools/differential/cldr_agreement.py --record.Agreement today (2026-10-12): 9,025 of 11,880 role verdicts. The record holds 956 disagreeing renderings, which doubles as a measured worklist of names the parser and CLDR read differently.
Decisions along the way
_UNMEASURED_CORPORAkeeps them out of_CORPUS_NAMES; the harness still classifies their diffs on the radar tier.Dr. inž. Janez Novakreadsinž.as the given name #652 (decomposedinž.isn't a period abbreviation, so the comma reverses the name).differential_corpus()leaves the file out until that's fixed. Including it coststest_properties.py34.5 s → 39.6 s (py3.11).fix(#379)ledger regex: its memberdicase-folded onto the Turkish dotlessı(Yıldırım). Its three ledger copies now use(?ai); on every corpus name, nothing else changes whether it matches.Ph. D.[[never]]entry was narrowed to the split pair itswhyis about, as its own comment asked. It had refused 17 CLDR names: 16 whose only 1.4 diff is the suffix separator (M.D., Ph.D.→M.D. Ph.D.), plus an unsplitPh.D.title move. Its recorded effect moves from 7 to 3 captures.fields, and the CJK order rule against the delimited-nickname rule). For two of them, an honest pin would have had to name a rule that doesn't describe what moved.compare._UNMEASURED_CORPORAnow names the files whose names are classified (by file order) but not adjudicated; a name any other corpus holds still is. A test holds it equal to the guards' copy.Jane (Janey, Jo) Smithgives nicknameJaney Jo(1.4.0:Janey, Jo) #653's fix). The README and AGENTS.md say to count them out of release figures.Review
Executed from a written spec and plan; every deviation is recorded in the commit messages.
A whole-branch review found false disagreements: only the ASCII comma was stripped, and unspaced fields were joined in field order. Fixing them moved 57 entries to agreeing and unmasked one false agreement. A review of that fix commit then corrected one explanation (the 17).
Negative controls:
Each fails at least one test.
Full suite on the merged branch: 15,108 passed. Ruff and mypy are clean.
Deferred (minor):
changes()doesn't report a CLDR bump that changes onlytext/expected; the docstring says so.conftest.py, until A decomposed (NFD) name loses an abbreviated title:Dr. inž. Janez Novakreadsinž.as the given name #652) names the file directly rather than through_UNMEASURED_CORPORA; it's a different reason with a different lifetime.shape_forchecks only the ASCII comma (harmless).Related: #652; #653 (fixed by #655).
🤖 Generated with Claude Code