From ecddabe5bf9c0ebea79caed72aac571bf6de78d7 Mon Sep 17 00:00:00 2001 From: Derek Gulbranson Date: Tue, 27 Oct 2026 23:41:34 -0700 Subject: [PATCH] Record the orcid-researchers evidence in NAME-DATA-JOBS and O6 mechanisms.md#NAME-DATA-JOBS gains the corpus #927 added: names split by their bearers rather than a filer, capped at 20,000 records a country so it shows each country's convention and never a frequency, and grouped by where a holder works or lives. decisions/O6 measures two_family_names on it by country: agreement rises to 85.6-93.7% in the Spanish-speaking countries other than Argentina, and the switch breaks names in Argentina and the Philippines, whose middle name the holders file with their given names. customize.rst's caution gains the Philippines and the Portuguese contrast. Co-Authored-By: Claude Opus 5.5 --- docs/customize.rst | 14 ++++++++++++-- docs/design/decisions/O6-two-family-names.md | 19 +++++++++++++++++++ docs/design/mechanisms.md | 4 ++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/customize.rst b/docs/customize.rst index e9deb7139..9fa02c61a 100644 --- a/docs/customize.rst +++ b/docs/customize.rst @@ -996,8 +996,18 @@ family name, are mostly that shape. Portuguese and Brazilian names carry two family names too, the mother's first, but the people who write them often give the last one alone as their family name: among the UK company register's 5,333 Portuguese filers the switch breaks -1,481 names and mends 1,221 (measured 2026-10-24). The parser reports nothing for the names the switch -moves: the shape it reads is the same either way. +1,481 names and mends 1,221 (measured 2026-10-24), while among ORCID's +researchers working or living in Portugal, who split their own names +into its boxes, it mends 4,295 and breaks 2,034 of 20,000 (measured +2026-10-27): the two populations disagree, so measure the data you have +before switching it on. Filipino names often carry a middle name +between the given name and the family name, traditionally the mother's +maiden surname, and their bearers count it with their given names: +``"Maria Santos Reyes"`` has family ``Reyes``, which the switch reads +as ``Santos Reyes``. Among ORCID's researchers in the Philippines it +breaks 8,234 names of 20,000 and mends 265. The parser +reports nothing for the names the switch moves: the shape it reads is +the same either way. .. _east-asian-defaults: diff --git a/docs/design/decisions/O6-two-family-names.md b/docs/design/decisions/O6-two-family-names.md index d4f145cb9..26e2fbd39 100644 --- a/docs/design/decisions/O6-two-family-names.md +++ b/docs/design/decisions/O6-two-family-names.md @@ -38,3 +38,22 @@ Among Spanish filers it fixes names of four name words 1,480 to 162 and of three 2,325 to 732, counted by the default reading's given, middle and family words. Of the records it breaks, 889 of 902 Spanish and 1,331 of 1,481 Portuguese filed a one-word surname. In the GND and Wikidata fielded corpora, the given-first renderings of the Spanish and Latin American double-surname strata (GND `compound-iberia` and `compound-latam`, Wikidata `surname2-es`, `-mx` and `-co`) disagree with the source on 315 of 390 by default, the RFC's count, and on 17 with the switch: 298 fixed, none broken; their index-form renderings disagree on none either way. The same switch over every other given-first rendering breaks 43 of GND's 408 and 141 of Wikidata's 250, and in the native-script Wikidata corpus moves 31 three- and four-word Arabic, Cyrillic, Bengali and Devanagari names, 11 to agreeing and 20 away. Recompute: for PSC, `local_agreement.read` per record under `Parser()` and under `Parser(policy=Policy(two_family_names=True))`, grouped by `group_of`, skipping records set aside on either side; for the fielded corpora, `cldr_agreement.disagreements` over the renderings with `shape` None, once with `_PARSERS[None]` as shipped and once set to the switched parser (clearing `_parser`'s cache), split by `label` and `order`. The comparator is the default parser on the same tree. - 2026-10-27 #752 (Derek) — THE SWITCH SURFACES NO ALTERNATIVE: the reading it did not take is not reported, as the 2026-10-24 #754 entry's No report bullet left for #752 to answer. Phase 2m surfaces the reading not taken as `Ambiguity.alternative`, which rides on a report, so a switch that reports nothing carries none. The caller turned the convention on, which is the declaration that settles the fork, as a declared order settles field order without a report; a report on most names of three words or more would still teach the caller to ignore the field. +- 2026-10-27 #754 — MEASURED ON NAMES THE BEARERS SPLIT: ORCID's researchers (`local_agreement.py orcid-researchers`, the local corpus #927 added) typed their own given-names and family-name boxes, so a reading of the switch there is scored against the person, at a scale PSC holds only for Spain (20,000 records a country where PSC held 116 Peruvian filers). The switch is the convention most Spanish-speaking countries write: in the six below other than Argentina, agreement rises under it to between 85.6% and 93.7%, each fixing at least eight records for every one it breaks (a ratio of these six, not of every Spanish-speaking country: Uruguay's is 7 to 1). Where the convention is not written it breaks names, most of them filed with a one-word family box, and nearly all outside Brazil (1,922 of Brazil's 2,267). Argentina repeats PSC's finding. The Philippines is a population PSC barely held: Filipino names often carry a middle name between the given name and the family name, traditionally the mother's maiden surname, and the holders file it with their given names (8,024 of the 8,234 records the switch breaks hold a one-word family box; `Maria Santos Reyes` reads family `Reyes` by default, `Santos Reyes` with the switch). Portugal reads the other way from PSC's Portuguese filers, the switch mending more than it breaks among researchers working or living there; the populations differ in more than who wrote the names (nationality against country of work, company owners against researchers), so this is consistent with the docs' point that the result depends on who wrote the name down rather than a measure of it. All boxes agreeing before → after, records fixed and broken, and the broken records whose family box holds one word; a country is where the holder works or lives: + + | Country | Records | Before → after | Fixed | Broken | Broken, one-word family | + |---|---:|---|---:|---:|---:| + | PE | 20,000 | 13.4% → 93.7% | 16,385 | 324 | 321 | + | CO | 20,000 | 23.6% → 91.7% | 14,413 | 789 | 780 | + | MX | 20,000 | 31.3% → 91.9% | 12,655 | 539 | 528 | + | VE | 20,000 | 37.4% → 85.6% | 10,871 | 1,230 | 1,215 | + | CL | 20,000 | 45.8% → 92.2% | 9,976 | 700 | 685 | + | ES | 20,000 | 48.0% → 91.5% | 9,353 | 649 | 626 | + | BR | 20,000 | 47.3% → 71.0% | 6,999 | 2,267 | 1,922 | + | PT | 20,000 | 70.0% → 81.3% | 4,295 | 2,034 | 1,975 | + | AR | 20,000 | 80.3% → 59.7% | 3,069 | 7,204 | 7,056 | + | IN | 20,000 | 73.0% → 64.5% | 582 | 2,268 | 2,265 | + | US | 20,000 | 90.8% → 88.3% | 620 | 1,109 | 1,102 | + | GB | 20,000 | 95.2% → 90.7% | 592 | 1,494 | 1,477 | + | PH | 20,000 | 94.7% → 54.9% | 265 | 8,234 | 8,024 | + + Measured on master at 598167f4 over the 2025 Public Data File's extract fetched 2026-10-27. Recompute: `local_agreement.read` per record of `local_corpora.load("orcid-researchers")` with `FIELDED["orcid-researchers"]`, under `Parser()` and under `Parser(policy=Policy(two_family_names=True))` with `_title_words` of the default lexicon, grouped by the record's `country`; a record is fixed where it agrees only with the switch, broken where it agrees only without it, and the last column counts the broken records whose `family` box holds one word. A corpus capped at 20,000 records a country shows each country's convention, not how many people write it. diff --git a/docs/design/mechanisms.md b/docs/design/mechanisms.md index e17306f20..ed9e7ecd1 100644 --- a/docs/design/mechanisms.md +++ b/docs/design/mechanisms.md @@ -424,9 +424,9 @@ print(len(MAIDEN_MARKERS), 'members ->', len(cls), 'structural classes')" **Contract statement.** A name source does one or more of five jobs — WATCH, SCORE, COUNT, DERIVE and SURVEY — and the job decides where its data lives. Only WATCH and SCORE in CI need the names in the repository, so a source whose names cannot be committed still does COUNT and SURVEY, WATCH and SCORE outside CI, and DERIVE where its terms let a derived list ship. -**How it works.** WATCH asks whether a change moved a name: a committed corpus, `snapshot.jsonl` and a test that fails, or a local corpus read with `snapshot.py --local`, which no CI run sees. SCORE asks whether the parser agrees with someone who knows the fields: a committed fields file and agreement record, read by `cldr_agreement.py` from the repository alone, or a local corpus whose records carry the fields their filers typed, with its record kept in the cache beside it (`local_agreement.py`). The local score tells a change's author what moved among real filed names, by field, and gives agreement by group and how often each disagreement shape occurs. It cannot guard anything: no CI run and no reviewer sees it, and no test fails when it moves (the tool itself exits 1). Its truth is the filer's, so a box filled wrongly (a job typed as a title) reads as a miss unless the scorer sets it aside, and the scorer has to say what it sets aside. COUNT asks how one word is used: a cached source behind a row of `tools/name_usage.py`, with only the counts cited. DERIVE asks what a shipped list should hold: a cached source and a script writing the vocabulary (`tools/zh_surnames.py`). SURVEY asks how people write whole names, and how OFTEN they write each shape: a cached local corpus (`tools/differential/local_corpora.py`). +**How it works.** WATCH asks whether a change moved a name: a committed corpus, `snapshot.jsonl` and a test that fails, or a local corpus read with `snapshot.py --local`, which no CI run sees. SCORE asks whether the parser agrees with someone who knows the fields: a committed fields file and agreement record, read by `cldr_agreement.py` from the repository alone, or a local corpus whose records carry the fields their filers, or the bearers themselves, typed, with its record kept in the cache beside it (`local_agreement.py`). The local score tells a change's author what moved among real filed names, by field, and gives agreement by group and how often each disagreement shape occurs. It cannot guard anything: no CI run and no reviewer sees it, and no test fails when it moves (the tool itself exits 1). Its truth is the filer's, so a box filled wrongly (a job typed as a title) reads as a miss unless the scorer sets it aside, and the scorer has to say what it sets aside; where the bearer typed the boxes, a miss is a disagreement with the person, though one who typed a family-first name into the given box reads as a miss too. COUNT asks how one word is used: a cached source behind a row of `tools/name_usage.py`, with only the counts cited. DERIVE asks what a shipped list should hold: a cached source and a script writing the vocabulary (`tools/zh_surnames.py`). SURVEY asks how people write whole names, and how OFTEN they write each shape: a cached local corpus (`tools/differential/local_corpora.py`). -A second axis cuts across the jobs: whether the names are real or invented, labelled with fields or bare text, chosen or unselected. Every committed corpus is CHOSEN: invented to fill fields (CLDR, the conventions table, the rules examples), cataloged (GND), filtered to names whose every word aligns with a stated part (both Wikidata corpora, a filter that drops credentials and typos), or picked by someone testing or reporting a case (v1's test names in `corpus.jsonl`, the case-table rows in `corpus_cjk.jsonl` and `corpus_shapes.jsonl`, and the strings reporters typed into `corpus_issues.jsonl`). The last group does hold hand-typed shapes — a job title in the name (`Bob Jones, author`), a credential before the comma (`Washington Jr. MD, Franklin`) — but one instance of each, as chosen. What SURVEY adds is the unselected population and its frequencies: ClinicalTrials.gov's study officials (local corpus `ctgov-officials`, measured 2026-10-21) are typed into a free-text field by sponsors' staff, so they show how often each shape occurs, and hold some no committed corpus does, such as `MBBS,FCPS` with no space. The FEC's individual contributors (local corpus `fec-individuals`, measured 2026-10-22) are unselected too, but mostly a filer's software writing a form's fields in a fixed order, so nearly all of its 1.89 million names share one shape (capitals, family, comma, given) and the hand-typed part is what goes inside the fields (a credential run or a suffix after a second comma): the population for that shape's variants, and blind to case. The UK Electoral Commission's individual donors (local corpus `ukec-individuals`, measured 2026-10-22) are the opposite shape: names as a donee's return gave them, mixed case and in spoken order, `parse()` reading a title in 8,219 of 15,214 (`Mr`, `Lord`, `Sir`) and only 2 holding a comma. Brazil's individual donors to candidates (local corpus `tse-donors`, measured 2026-10-22) are the FEC's comma-less counterpart: 848,351 filed names, nearly all in capitals and in spoken order, full of particles and agnomens (`DE`, `DA`, `DOS`; `FILHO`, `NETO`, `JUNIOR`), each beside the tax registry's name for the same donor, so a typed form can be read against its reference form (205,925 of 854,531 pairs differ). A source that pairs two spellings of one person scores without fields: the parse of each spelling should find the same surname, so where the spellings differ only in what cannot move a surname (accents, case), a disagreement is the parser reading one name two ways, found with no one labelling either (`local_corpora.py pairs --agreement`; on `tse-donors` on 2026-10-22, 1,304 of 43,552 accents-only pairs, 1,121 of them from `JÚNIOR`/`JUNIOR` and `SÁ`/`SA`). Read it per shape: a pair that leaves a word out can change the surname for real. JRC-Names (local corpus `jrc-names`, measured 2026-10-24) widens the pair to a group: one person's spellings harvested from news text across scripts, 218,822 persons with two or more. Words in two scripts cannot be compared, so `local_corpora.py groups` compares the readings' structure instead (title, suffix, given, family, the family's length and where it stands), counting persons. Read it per pair of scripts, since a structure can differ by convention: a script read family first stands its family elsewhere, and a writing variant moves the reading for real (the Arabic comma, katakana with and without `・`). Companies House's people with significant control (local corpus `psc-individuals`, measured 2026-10-22) are unselected and labelled at once: each filer types the name into four boxes (title, forename, middle name, surname) and states a nationality, so the corpus is the one real population a local SCORE reads by nationality. It is filed through a form's boxes, so it holds almost no comma (838 of 1,040,708 sampled records) or credential. One source may do several jobs, each through its own code: Wikidata does COUNT live and WATCH and SCORE through its builders. A live source has no fixed file to pin, so its cached copy is the pin, carrying the source's own data date and a sha256 checked on every load (a truncated file otherwise reads as a run of false zeros, #775). +A second axis cuts across the jobs: whether the names are real or invented, labelled with fields or bare text, chosen or unselected. Every committed corpus is CHOSEN: invented to fill fields (CLDR, the conventions table, the rules examples), cataloged (GND), filtered to names whose every word aligns with a stated part (both Wikidata corpora, a filter that drops credentials and typos), or picked by someone testing or reporting a case (v1's test names in `corpus.jsonl`, the case-table rows in `corpus_cjk.jsonl` and `corpus_shapes.jsonl`, and the strings reporters typed into `corpus_issues.jsonl`). The last group does hold hand-typed shapes — a job title in the name (`Bob Jones, author`), a credential before the comma (`Washington Jr. MD, Franklin`) — but one instance of each, as chosen. What SURVEY adds is the unselected population and its frequencies: ClinicalTrials.gov's study officials (local corpus `ctgov-officials`, measured 2026-10-21) are typed into a free-text field by sponsors' staff, so they show how often each shape occurs, and hold some no committed corpus does, such as `MBBS,FCPS` with no space. The FEC's individual contributors (local corpus `fec-individuals`, measured 2026-10-22) are unselected too, but mostly a filer's software writing a form's fields in a fixed order, so nearly all of its 1.89 million names share one shape (capitals, family, comma, given) and the hand-typed part is what goes inside the fields (a credential run or a suffix after a second comma): the population for that shape's variants, and blind to case. The UK Electoral Commission's individual donors (local corpus `ukec-individuals`, measured 2026-10-22) are the opposite shape: names as a donee's return gave them, mixed case and in spoken order, `parse()` reading a title in 8,219 of 15,214 (`Mr`, `Lord`, `Sir`) and only 2 holding a comma. Brazil's individual donors to candidates (local corpus `tse-donors`, measured 2026-10-22) are the FEC's comma-less counterpart: 848,351 filed names, nearly all in capitals and in spoken order, full of particles and agnomens (`DE`, `DA`, `DOS`; `FILHO`, `NETO`, `JUNIOR`), each beside the tax registry's name for the same donor, so a typed form can be read against its reference form (205,925 of 854,531 pairs differ). A source that pairs two spellings of one person scores without fields: the parse of each spelling should find the same surname, so where the spellings differ only in what cannot move a surname (accents, case), a disagreement is the parser reading one name two ways, found with no one labelling either (`local_corpora.py pairs --agreement`; on `tse-donors` on 2026-10-22, 1,304 of 43,552 accents-only pairs, 1,121 of them from `JÚNIOR`/`JUNIOR` and `SÁ`/`SA`). Read it per shape: a pair that leaves a word out can change the surname for real. JRC-Names (local corpus `jrc-names`, measured 2026-10-24) widens the pair to a group: one person's spellings harvested from news text across scripts, 218,822 persons with two or more. Words in two scripts cannot be compared, so `local_corpora.py groups` compares the readings' structure instead (title, suffix, given, family, the family's length and where it stands), counting persons. Read it per pair of scripts, since a structure can differ by convention: a script read family first stands its family elsewhere, and a writing variant moves the reading for real (the Arabic comma, katakana with and without `・`). Companies House's people with significant control (local corpus `psc-individuals`, measured 2026-10-22) are unselected and labelled at once: each filer types the name into four boxes (title, forename, middle name, surname) and states a nationality, so the corpus is the one real population a local SCORE reads by nationality. It is filed through a form's boxes, so it holds almost no comma (838 of 1,040,708 sampled records) or credential. ORCID's researchers (local corpus `orcid-researchers`, measured 2026-10-27) are labelled by the bearer rather than a filer: each holder typed their own name into two boxes (given names, family name), from 250 countries. Two things keep it from being a SURVEY. It keeps at most 20,000 records a country, so it shows which shapes each country holds and how the parser reads them, never how often a shape occurs across countries. And its country is where a holder works or lives, most often taken from an employment, so a country's row is its institutions' researchers, not its nationals. It is the SCORE to read for a language pack: 80.5% of its 1,831,533 records agreed on every field, as few as 13.4% in Peru, and the other 17 countries of at least 1,000 records under half were Brazil and 16 Spanish-speaking ones (Argentina read 80.3%); in the two commonest disagreement shapes, 249,526 of the 357,517 records that disagreed, a two-word family box was read as one word (measured on master at 598167f4). One source may do several jobs, each through its own code: Wikidata does COUNT live and WATCH and SCORE through its builders. A live source has no fixed file to pin, so its cached copy is the pin, carrying the source's own data date and a sha256 checked on every load (a truncated file otherwise reads as a run of false zeros, #775). **Reach for it when.** A source is proposed, before choosing between a builder and a tool column; and when a corpus would hold real people's names nobody vetted for publication, which is the case for a local corpus rather than a committed one.