Skip to content

Add a native-script Wikidata corpus: real names in their own scripts - #693

Merged
derek73 merged 6 commits into
masterfrom
feat/native-script-corpus
Oct 17, 2026
Merged

derek73 merged 6 commits into
masterfrom
feat/native-script-corpus

Conversation

@derek73

@derek73 derek73 commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Every real-name corpus in tools/differential/ was Latin script, apart from Wikidata's Russian patronymics. CLDR covers native scripts, but its names are invented. So the parser's script-conditional readings had never been checked against a real name: family-first order for Han, Hangul and kana, and Korean surname segmentation. No text holds an honorific, so the corpus does not exercise the CJK honorific peel; it only shows the peel leaves plain names alone. This PR adds 910 real names written in 16 scripts and languages, sampled in 20 strata by country and language.

What it adds

  • build_wikidata_native_corpus.py reuses build_wikidata_corpus.py's fetches and token alignment through corpus_build.py, and adds:
    • A script filter. Every letter and combining mark of a text must be in the stratum's script. Wikidata's mul labels are Latin, so without this Anushka Sharma passed as a Hindi name.
    • Character alignment for unspaced Han, kana and Hangul, with the same exactly-one-part rule. Nothing is inferred: a Chinese item that states only P734 is left out, rather than reading the rest of the text as the given name.
    • Feminine surname forms per language (Greek, Bulgarian, Ukrainian, Kazakh), so the filter does not drop women.
    • No shape where the script decides the order, because that order is what the corpus tests. Pure katakana keeps the shape of the order it is written in, matching the parser.
    • A floor per stratum. Below its floor the build stops before writing. A pool that comes back short also stops it, unless the stratum is marked as its whole population (Thailand, Kazakhstan, Bangladesh), and a stratum so marked whose pool fills stops it too.
    • --lc-check, which compares each split with the native-script variants of the person's Library of Congress record. It is a report only: it runs after the build, and adds nothing to the output files and filters nothing.
  • cldr_agreement.py scores the source into wikidata_native_agreement.json. Its locale view now maps each source's rendering key to a pack (NATIVE_LABELS for the native strata), so real Han and kana names are scored under ZH and JA too, into wikidata_native_agreement_locales.json.

What it found (2026-10-16)

  • Spaced scripts: every one agrees in every role except:
    • seven Arabic two-word surnames (عبد العزيز, عز الدين, شكر الله, ذو الغنى), where the parser reads the first word as a middle name;
    • one Persian double surname;
    • one Bulgarian name whose two stated family names include what is probably the father's name.
  • Hangul: 120/120.
  • Han and kana, default parser: every unspaced Chinese name disagrees, and 49 of 60 Japanese names.
  • Han and kana, locale view: 74/80 under ZH, which misses the surnames 詹, 卫, 柯, 茅, 华 and 文. 59/60 under JA.
  • LC cross-check: 99 names have an LC variant that splits them, and 96 of those agree. The other three are Arabic names whose LC record names a person written in the opposite order, so the P244 link is suspect rather than the split. LC writes most Han variants unspaced, so it confirms almost no Chinese or Japanese split.

Wikidata's labels encode a convention in places, and the README records where: an Arabic nisba is entered as P734.

Left out

  • Urdu and Marathi: 8 and 5 of 600 aligned.
  • The thin strata: Devanagari, Bengali and Thai items are mostly labelled in Latin only, so those strata hold only 10 to 25 names.

Cost

Measured locally without coverage, 2026-10-16:

  • the 907 corpus lines (904 names new to the snapshot) add about 2.9 s to the corpus-reading test modules;
  • the agreement tests add about 0.6 s.

Records

The snapshot gains 904 names and moves none. The CLDR, GND, Wikidata, conventions and CLDR locale records do not move. The JA rows were recorded with the ja extra installed. The suite passes both with and without namedivider.

Reviews

  • First round (correctness, silent failures with 27 mutations): two low-severity bugs, two unguarded paths, and eight untested guards, all fixed in 72a845c.
  • Second round, on the fix: one vacuous test row and a stale comment, fixed in 3509acf.
  • CI fix: on Python 3.14+, an unclosed HTTPError fails the run; the 404 path now closes it (2bfd5cd).
  • Full review (code, comments, tests, types): no correctness bug; the honorific-peel claim corrected, prose made accurate, a stale-whole guard added, and tests for every mutation a second 35-mutation pass left alive (b54bb32).
  • /simplify (reuse, simplification, efficiency, altitude): the locale view reads each source's pack from its own rendering key (NATIVE_LABELS beside LOCALE_FILES), so the fields rows no longer carry pack; one shared alignment rule (claim) and one order rule (script_orders, pinned against Policy().script_orders); the agreement tests share one summary run (3.6 s, from 4.6 s). Its second review found a new zh/ja stratum could go unscored; a test now ties the two tables (428148f).
  • Negative controls: each new guard was mutated in turn, and each mutation fails a test. The offline rebuild from the cache is byte-identical to the committed files.

Follow-ups (not filed; drafts on request)

  • Arabic two-word surnames: should عبد and similar first words bind to the word after them in native script?
  • ZH surname coverage: six surnames missed on real names.

🤖 Generated with Claude Code

derek73 and others added 3 commits October 16, 2026 22:14
Every real-name corpus was Latin script apart from Wikidata's Russian
patronymics, and CLDR's native-script names are invented, so the
script-conditional rules (family-first Han, Hangul and kana, Korean
segmentation, the honorific peel) had met no real name.
build_wikidata_native_corpus.py samples public figures by country and
language (20 strata, 910 names: Hangul, Han, kana, Arabic, Persian,
Hebrew, Georgian, Armenian, Greek, Serbian, Bulgarian, Ukrainian,
Kazakh, Bengali, Devanagari, Thai), reusing build_wikidata_corpus.py's
alignment. It adds a script filter, character alignment for unspaced
text with nothing inferred, per-language feminine surnames, and no
shape where the script decides the order. --lc-check compares the
splits with the Library of Congress's native-script variants: 96 of
the 99 it can split agree, and the other three are suspect P244 links.

cldr_agreement.py scores the source (wikidata_native_agreement.json)
and its locale view now reads a row's own `pack`, so the Han and kana
names are scored under ZH and JA too (wikidata_native_agreement_
locales.json). The other records do not move; the snapshot gains 904
names, none moved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ths mutations missed

A katakana-only name written unspaced keeps its family-first shape, and
only Han, kana and Hangul are aligned by character. A pool shorter than
its size stops the build unless the stratum is marked as its whole
population. The LC check counts an LCCN LC no longer has. The locale
summary still checks a pack with no renderings left, and reports a row
naming a pack no view measures. New tests cover each of these, the
query matching nothing, a missing pooled person, the languages filter,
the LC cache, every locale record being read, and the carry-over of an
unmeasured pack's entries. The rebuilt corpus is byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Second review of the fixes: the Hebrew-point row was all Hebrew, so it
passed with the mark check deleted; the pool comment still called a
short pool the whole population; the whole-strata test only restated
the table. The README's Adding-a-source paragraph records both lessons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73 derek73 added this to the 2.5 milestone Oct 17, 2026
@derek73 derek73 self-assigned this Oct 17, 2026
CI's 3.14 and 3.15 jobs failed the two LC error tests on an unraisable
ResourceWarning: an HTTPError holds its response until closed. The 404
path in lc_variants closes it, which a real run needed too, and the 503
test closes the one it catches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.03%. Comparing base (8d7789e) to head (428148f).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #693   +/-   ##
=======================================
  Coverage   99.03%   99.03%           
=======================================
  Files          46       46           
  Lines        4541     4541           
=======================================
  Hits         4497     4497           
  Misses         44       44           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

derek73 and others added 2 commits October 16, 2026 22:42
…t on

The full PR review (code, comments, tests, types) found no correctness
bug. It did find that the corpus was said to test the CJK honorific
peel, which it cannot: no text holds an honorific, so AGENTS.md would
have sent a peel change to a record blind to it. The builder docstring,
README and AGENTS.md now say so. Also corrected: Arabic nasab chains
fail because `بن` is no connective in Arabic script, not for want of a
family name; which strata have lowered floors and why; the Japanese
blind spot; `--lc-check` runs after the build; 907 corpus lines, 904
new to the snapshot.

Code: a stratum marked whole=True whose pool fills stops the build,
since the mark is then stale; entities() reads `languages` once;
unmapped() takes the rows it needs; --record reuses the loaded rows.

Tests for each mutation that survived the second pass: feminine forms
added, not substituted; a given name required; the LC tallies; the
Arabic comma, same-order and spaced-text branches of lc_split; texts in
a variant language, and not in another; given-first unspaced order;
each locale view's own default count. Every one fails its mutation.
The offline rebuild is byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/simplify (reuse, simplification, efficiency, altitude):

- The locale view reads each source's pack from its own rendering key
  (LocaleSource: CLDR's file through LOCALE_FILES, the native source's
  stratum through the new NATIVE_LABELS), so the builder no longer
  writes a `pack` field. That removes row_pack, the fallback for a
  rendering that is gone, the rows argument to unmapped, and the stray-
  pack check. NATIVE_LABELS sits beside LOCALE_FILES; the strata it must
  cover live in the builder, so a test ties the two both ways (the
  second review found a new zh or ja stratum would otherwise go unscored
  under its pack).
  The fields file loses `pack` on its 140 Han and kana rows and nothing
  else; no record moves.
- build_wikidata_corpus.claim() and by_field() hold the alignment rule
  once; align() and align_chars() find spans and call them.
- renderings() orders both branches with _order(), and script_orders()
  names the script-decides-the-order rule its test used to restate. A
  test now pins the scripts it names against Policy().script_orders.
- lc_split reads both commas in one partition.
- The two summary tests share one main([]) run, and the summary no
  longer scores a pack twice: the four test modules run in 3.6 s, from
  4.6 s.

Skipped: a shared build/pool driver for both Wikidata builders and a
shared fetch helper (they reach into the parent builder and GND/CLDR;
a follow-up if the builders grow), lazy pool fetching (changes every
cache key; only --refresh pays), moving the test helpers to the shared
fixtures, and corpus_lines's re-export, which test_corpus_build reads.

Each of 13 mutations of the refactored code fails a test; the offline
rebuild differs from the committed file only by the dropped `pack`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant