Repository navigation
Add a Wikidata corpus: real second surnames and patronymics, token-aligned - #689
Merged
Merged
Conversation
Wikidata's P1950 (second family name in a Spanish name) and P5056 (patronym or matronym) are the only open labels for the two fields 3.0 is weighing. build_wikidata_corpus.py samples public figures (deceased, or 10+ sitelinks) one country and one property per WDQS query, in MD5 order of the QID, and keeps a written name (P1559 or the own-language label) only where every word aligns with exactly one stated part. cldr_agreement.py gains the source, and PENDING_ROLES: surname2 is scored inside family and patronymic inside given_names until ParsedName has those attributes, then on its own. Derek chose that, the given_names placement (a patronymic is not a family name) and connectives joining the part after them. The CLDR, GND and locale records do not move; the snapshot gains 668 names, none moved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, not six Review: the merge in expected_roles never ends, and the comments said it would. It is kept on purpose (whether family drops surname2 is 3.0's design), so the comments now 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 #689 +/- ##
=======================================
Coverage 99.03% 99.03%
=======================================
Files 46 46
Lines 4541 4541
=======================================
Hits 4497 4497
Misses 44 44 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Review of #689: - strata get floors, and build() raises before writing when one falls short (Malaysia's floor is 8: its pool held 17 on 2026-10-16); - responses are shape-checked before caching (an API error arrives as HTTP 200), cached atomically, and a damaged cache file is named; - a query matching nothing and an item the API leaves out or reports missing both raise; - the pending-role gate skips only PENDING_ROLES, now a map to the role each is scored inside, which the summary prints; - Malay Bin/Binti capitalized are connectives; _REJECT, which pinned nothing, is gone; - tests for every decision a mutation showed unpinned, the build, the cache, and the committed strata; doc wording fixed, and the README no longer stretches the GND release-figure attribution to Wikidata. The rebuilt corpus is byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the build Second review of #689: the missing-item raise covered part items too, so one deleted item in any pool would block every rebuild with advice (--refresh) that cannot fix it. A missing pooled person still raises. Also: the summary asks got_roles itself whether a pending field is answered, Malaysia's floor leaves 3 names of slack, the README's Malaysian count says 'matched the query', and the lesson is recorded in the README's Adding a source paragraph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed stub /simplify over #689: - expected_roles derives the pending merge from PENDING_ROLES instead of spelling surname2 and patronymic three times; - align() splits span search and the widest-span reduction into helpers, and says why the connective pass runs right to left; - the missing-person check moves to build(), so entities() has one contract and no flag; - the urlopen stub both build tests copied is shared in _differential_fixtures; the field-occurs check is one test over every stated-field source; the coverage check uses the public same(). Skipped: reverting the pending-role gate to 'role in got' (the last review asked for the loud failure it gives), case-folding the Malay connectives (would admit B. and Bt.), a per-language feminizer hook and a shared shape constant across tools (one language; the build-helpers follow-up). Corpus and every record byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 17, 2026
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.
Summary
CLDR (#654) and GND (#688) gave the harness ground truth, but neither can test the two output fields 3.0 is weighing. GND holds
García Márquezas one surname and puts a patronymic inside the forename, and CLDR's names are fictional. Wikidata's P1950 (second family name in a Spanish name) and P5056 (patronym or matronym) are the only open labels for those fields that the 2026-10-16 corpus survey found. Wikidata's structured data "is released into the public domain" (CC0).What's in it
build_wikidata_corpus.pyruns one WDQS query per stratum (one country of citizenship, one property), sequentially, with a descriptive bot User-Agent. It keeps public figures only (deceased, or at least 10 sitelinks), orders them by an MD5 of the item URI so the sample is deterministic, and fetches the items withwbgetentitiesin batches of 50. Raw responses are cached outside the tree ($NAMEPARSER_TOOLS_CACHEor~/.cache/nameparser-tools/wikidata). Only name strings and QIDs are committed, withwikidata_LICENSEas the credit.Token alignment, not concatenation. Each written name (P1559, and the own-language label) is matched word by word against the stated parts' spellings: labels in the text's language, the stratum's language and
mul, plus P1705. A text is kept only if every word is claimed by exactly one part. Other rules:bin,binti,i,y) joins the part after it:bin Hamzah,i Coll.ЛядоваforЛядов).Pedro Martínez(display form, no second surname) is out.Totals: 668 renderings of 649 names. Malaysia is small: only 17 Malaysian public figures with a P5056 statement matched the query, and 9 of them aligned. Its floor is set to 6. Iceland's pool (270 against 600 asked) also limits it, though it still fills its quota.
The build fails rather than shrinks. It follows the GND review's pattern:
build()raises before writing if one falls short;A deleted name-part item only refuses the person stating it, since
--refreshcould not fix it.cldr_agreement.pygains the source (wikidata_agreement.json) andPENDING_ROLES. These are the design choices Derek made in session:surname2is scored insidefamily, as CLDR's own merge does.patronymic(a field CLDR lacks) is scored insidegiven_names. A patronymic is not a family name (the GND/LC convention), soVigdís Finnbogadóttirexpects family''. CLDR's ownisdata statesAdólfsdóttiras a surname instead; that conflict between the sources is known and accepted.ParsedNamehas an attribute of that name, with no tool change. Whether the merged role then drops it is left to the 3.0 design.PENDING_ROLESmaps each field to the role it is scored inside, and a test holdsexpected_rolesto that map. Until then the summary prints how many renderings state each field and where it is scored: CLDR surname2 369, Wikidata surname2 330, patronymic 338.The CLDR, GND and locale records do not move. The snapshot gains 668 names, and no existing reading moves.
What the record says (the worklist)
Rafael Viciana Aráezgives familyAráez. This is thesurname2question.Soffía Karlsdóttirgives familyKarlsdóttir, andMahathir bin Mohamadgives familybin Mohamad. This is the "family may be empty" question.Francisco Hernández PlareadsPlaas a suffix.Blind to (also in the docstring and the README row)
Everything the strict filter drops. The survivors are tidy names whose every word has an item. Short display forms, nicknames and names with an unrecorded part are missing, only eight citizenships are sampled, and no text carries a title, credential or suffix.
Test plan
uv run pytest: 19,242 passeduv run mypy,uv run ruff checktests/v2/test_wikidata_corpus.pypins the builder offline on hand-written items. A mutation pass showed 12 decisions unpinned, and each now has a test:Iis not one; MalayBinis);It also stubs the build (quota cut, one stratum per person, the short-stratum refusal) and the network (cache reuse, an error not cached, a damaged cache named, an empty query, missing items).
Committed-file checks: every field occurs in its text, the fields cover every word of it, and each stratum holds floor ≤ names ≤ quota with its own field written and one stratum per person.
tests/v2/test_cldr_agreement.pyadds Wikidata to the per-source ratchet. It pins the pending-field rules: merged now, and scored on their own once a parsed name answers them.Three review rounds, the last over the fix commits. They found:
All are fixed. Each new guard was mutated and fails, while a harmless control edit passes.
A
/simplifypass:PENDING_ROLES;align()is split into span helpers;The corpus and every record are byte-identical, and the guards were re-mutated and still fail.
🤖 Generated with Claude Code