From d2a89dc12656c595f2e6b193052927fef9f8d69e Mon Sep 17 00:00:00 2001 From: Derek Gulbranson Date: Fri, 23 Oct 2026 21:12:41 -0700 Subject: [PATCH 1/2] Read a leading Md as the given name it abbreviates, not a title (#853) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `md` loses its title reading and keeps its credential one. A leading `Md` abbreviates Muhammad: of 1,300 UK PSC names whose forename box opens with it, 1,209 filers typed it there alone, and the title reading had put the forename box in `given` for none of them. `Mr Md Karim Uddin` now reads title 'Mr', given 'Md', middle 'Karim', as `Mohammad Karim Uddin` does; `John Smith, MD Jones` reads suffix 'MD Jones' where the title reading made 'John Smith' the family (about 380 ClinicalTrials.gov officials). Accepted costs, recorded in decisions/indic-honorifics and pinned as case rows: a particle behind Md starts the family (`Md Abu Sayed`, as `Mohammad Abu Sayed` reads), and `Uddin, Md Karim` reads the given part as credentials (9 FEC names). The Bengali মোঃ/মো entries stay titles. Rows, rule examples and comments that used MD as the title/suffix dual are respelled with RA, which reads as MD did at 1.4.0, 2.3.0 and the parent; the pair-scan benchmark's negative control was re-taken with it (120 -> 1,620 at 47fb2b21, 35 -> 131 here). Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- .../C8-listing-forms-part-after-the-comma.md | 6 +- ...aracter-suffix-word-inside-a-comma-part.md | 2 +- docs/design/decisions/H3-title-runs-floor.md | 2 +- ...t-vocabulary-still-has-to-name-somebody.md | 2 +- .../H6-title-before-a-family-comma.md | 2 +- ...S7-company-of-an-unambiguous-credential.md | 2 +- docs/design/decisions/comma-suffix-arc.md | 4 +- docs/design/decisions/indic-honorifics.md | 10 +- ...ential-opening-the-part-after-the-comma.md | 2 +- docs/design/rules/H3-title-runs-floor.md | 2 +- ...t-vocabulary-still-has-to-name-somebody.md | 4 +- ...S7-company-of-an-unambiguous-credential.md | 2 +- docs/release_log.rst | 1 + nameparser/_parser.py | 2 +- nameparser/_pipeline/_assign.py | 4 +- nameparser/_pipeline/_comma.py | 4 +- nameparser/_pipeline/_pieces.py | 18 +-- nameparser/config/_compiled.py | 1 - nameparser/vocab/en.toml | 10 +- tests/v2/cases.py | 115 ++++++++++++------ tests/v2/pipeline/test_pieces.py | 22 ++-- tests/v2/pipeline/test_post_rules.py | 2 +- tests/v2/test_benchmark.py | 10 +- tests/v2/test_cases.py | 8 +- tests/v2/test_locales.py | 4 +- tests/v2/test_name_usage_tool.py | 8 +- tools/differential/cldr_agreement.json | 44 +++---- .../differential/cldr_agreement_locales.json | 8 +- tools/differential/corpus_rules.jsonl | 6 +- tools/differential/corpus_shapes.jsonl | 8 +- tools/differential/snapshot.jsonl | 35 +++--- 32 files changed, 197 insertions(+), 155 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d07287dc..55f2cad4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -343,7 +343,7 @@ Don't use the bare `python3 -m doctest .rst` CLI (no `optionflags`) to che **Prefix-join uses value-based `list.index()`** in `join_on_conjunctions` — fragile when a token value repeats (e.g. a trailing title that's also a suffix acronym, or two `van`s); constrain such lookups to start at `i + 1`. See #100. -**Title vs suffix is positional for BARE words, and the leading period-abbreviation rule overrides even that** — a word matching `TITLES` at the front of a name becomes `title`; the same word matching `SUFFIX_ACRONYMS`/`SUFFIX_WORDS` at the end becomes `suffix` (never both, regardless of the word's real-world meaning). The `TITLES`/suffix overlap was audited in #296 (2026-08-23): the pure postnominals (`jr`, `junior`, `phd`, `do`, `se`) left `TITLES`, the v1-residue `dr`/`sra` left the suffix sets, and the twelve words still in both (`md`, `ms`, `sa`, `sr`, `lt`, `ra`, `vc`, and the ranks `cpl`, `cpo`, `cpt`, `csm`, `sgm`) are deliberate duals that position decides. External test sources (old issue gists, etc.) sometimes assert `suffix` for a leading professional abbreviation like `RA`/`PD`/`Dipl.-Ing.` — that's the source data being wrong, not a parser bug. Verify position before "fixing" it. Two qualifications the older "purely positional" wording papered over, the first measured 2026-08-01 and the second answered 2026-09-08 (#316): a PERIOD-marked leading word is claimed by the shape rule before any vocabulary is read (`"Esq. Smith"` → `title`, though `esq` is suffix-only), and a PERIOD-marked trailing word is claimed by VOCABULARY — first the suffix sets, which the peel reads before anything else (`"John Smith Esq."` → `suffix`), then the titles (`"John Smith Prof."` → `title='Prof.'`, `rules/H5`) — while an unlisted abbreviation there stays a name part (`"John Smith Xyz."` → `family='Xyz.'`) unless it is written as two or more period-separated chunks, which since 2.4 IS a trailing shape rule (`Policy.unlisted_dotted_suffixes`, default on: `"John Smith X.Y.Z."` → `suffix='X.Y.Z.'`, read by the same words-to-spare count a bare ambiguous acronym takes, `rules/S3`). The SINGLE trailing period is still not one, deliberately — it is the abbreviation shape any word can wear. The bare word is where "positional" still holds whole -- outside a part past the second comma, which holds no name word, so C2 reads a bare title word there as a title since #603 (`Eric H. Holder, Jr., Attorney General`) -- and the reason it must: `TITLES` holds words that are in no suffix set — 787 of them on 2026-10-11 (746 before #606's salutations), measured with `L = Parser().lexicon; len(L.titles - L.suffix_acronyms - L.suffix_words)`, a figure that grows with the vocabulary and never shrinks the argument — and many are ordinary surnames (`king`, `bishop`, `prince`, `pope`, `judge`, `sheriff`, `baron`, `master`, ...), so a vocabulary-first rule over BARE trailing words would read `"Mary Jane King"` as `title='King'`, `family='Jane'`. The period is what separates the safe case from that one, and it separates it by being a WRITING convention rather than by making the collision go away: `"Mary Jane King"` keeps `family='King'` while `"Mary Jane King."` reads `title='King.'`, `family='Jane'`, a cost accepted under the input-is-a-name premise rather than one the rule prevents (the trailing-position bullet in decisions/vocabulary-exclusions, measured 2026-09-09). Since #316 that sentence describes the shipped rule rather than an aspiration. +**Title vs suffix is positional for BARE words, and the leading period-abbreviation rule overrides even that** — a word matching `TITLES` at the front of a name becomes `title`; the same word matching `SUFFIX_ACRONYMS`/`SUFFIX_WORDS` at the end becomes `suffix` (never both, regardless of the word's real-world meaning). The `TITLES`/suffix overlap was audited in #296 (2026-08-23): the pure postnominals (`jr`, `junior`, `phd`, `do`, `se`) left `TITLES`, the v1-residue `dr`/`sra` left the suffix sets, and the eleven words still in both (`ms`, `sa`, `sr`, `lt`, `ra`, `vc`, and the ranks `cpl`, `cpo`, `cpt`, `csm`, `sgm`; `md` was a twelfth until #853 took its title reading out, a leading `Md` being the given name Muhammad) are deliberate duals that position decides. External test sources (old issue gists, etc.) sometimes assert `suffix` for a leading professional abbreviation like `RA`/`PD`/`Dipl.-Ing.` — that's the source data being wrong, not a parser bug. Verify position before "fixing" it. Two qualifications the older "purely positional" wording papered over, the first measured 2026-08-01 and the second answered 2026-09-08 (#316): a PERIOD-marked leading word is claimed by the shape rule before any vocabulary is read (`"Esq. Smith"` → `title`, though `esq` is suffix-only), and a PERIOD-marked trailing word is claimed by VOCABULARY — first the suffix sets, which the peel reads before anything else (`"John Smith Esq."` → `suffix`), then the titles (`"John Smith Prof."` → `title='Prof.'`, `rules/H5`) — while an unlisted abbreviation there stays a name part (`"John Smith Xyz."` → `family='Xyz.'`) unless it is written as two or more period-separated chunks, which since 2.4 IS a trailing shape rule (`Policy.unlisted_dotted_suffixes`, default on: `"John Smith X.Y.Z."` → `suffix='X.Y.Z.'`, read by the same words-to-spare count a bare ambiguous acronym takes, `rules/S3`). The SINGLE trailing period is still not one, deliberately — it is the abbreviation shape any word can wear. The bare word is where "positional" still holds whole -- outside a part past the second comma, which holds no name word, so C2 reads a bare title word there as a title since #603 (`Eric H. Holder, Jr., Attorney General`) -- and the reason it must: `TITLES` holds words that are in no suffix set — 787 of them on 2026-10-11 (746 before #606's salutations), measured with `L = Parser().lexicon; len(L.titles - L.suffix_acronyms - L.suffix_words)`, a figure that grows with the vocabulary and never shrinks the argument — and many are ordinary surnames (`king`, `bishop`, `prince`, `pope`, `judge`, `sheriff`, `baron`, `master`, ...), so a vocabulary-first rule over BARE trailing words would read `"Mary Jane King"` as `title='King'`, `family='Jane'`. The period is what separates the safe case from that one, and it separates it by being a WRITING convention rather than by making the collision go away: `"Mary Jane King"` keeps `family='King'` while `"Mary Jane King."` reads `title='King.'`, `family='Jane'`, a cost accepted under the input-is-a-name premise rather than one the rule prevents (the trailing-position bullet in decisions/vocabulary-exclusions, measured 2026-09-09). Since #316 that sentence describes the shipped rule rather than an aspiration. ### Tests (`tests/`) diff --git a/docs/design/decisions/C8-listing-forms-part-after-the-comma.md b/docs/design/decisions/C8-listing-forms-part-after-the-comma.md index 96ed0e64..4c3985df 100644 --- a/docs/design/decisions/C8-listing-forms-part-after-the-comma.md +++ b/docs/design/decisions/C8-listing-forms-part-after-the-comma.md @@ -6,17 +6,17 @@ - The condition is no name word, not all titles, counted over name pieces: `John Smith, Mr. Jr.` → given 'John', family 'Smith', suffix 'Jr.'; `Smith Jr., Mr.` → family 'Smith', suffix 'Jr.' — 2026-08-23 #428. - P1's family-first fold reaches a positionally read name before the comma as it does without the comma: `de Mesnil Jean, Dr.` → family 'de Mesnil', given 'Jean' under either family-first order — 2026-08-23 #428. - A part after the comma that is nothing but suffix pieces is the credential run, whole, read before any title reading of its words: `Smith, Ph. D. Jr.` → suffix 'Ph. D. Jr.'; `Smith, Sr.` → suffix 'Sr.' where `Sr. Garcia` reads title 'Sr.'; `Smith, Esq.` → suffix 'Esq.'; and `Smith, Dr. Jr.` → title 'Dr.', suffix 'Jr.' — 2026-08-23 #296. -- #296's audit of the title/suffix overlap stands: `do`, `jr`, `junior`, `phd` and `se` left the titles, `dr` and `sra` the suffix sets, and `md` stays a dual position decides (`Md Abdul Karim` → title 'Md'), so a leading `PhD` or `Jr` is a name word (`Jr John Smith` → given 'Jr'), `Do Quang Minh` reads given 'Do' with the particle-or-given fork, and a trailing bare `Dr` is a name word (`John Smith Dr` → family 'Dr') — 2026-08-23 #296. +- #296's audit of the title/suffix overlap stands: `do`, `jr`, `junior`, `phd` and `se` left the titles, `dr` and `sra` the suffix sets, so a leading `PhD` or `Jr` is a name word (`Jr John Smith` → given 'Jr'), `Do Quang Minh` reads given 'Do' with the particle-or-given fork, and a trailing bare `Dr` is a name word (`John Smith Dr` → family 'Dr') — 2026-08-23 #296. - `ms` and `sa` stay out of the ambiguous set, duals that position decides: `Smith, Ms.` → suffix 'Ms.'; `Smith, Ms. Jane` → title 'Ms.'; `John Smith, MS` → suffix 'MS' — 2026-08-23 #428. - A delimiter core the policy names is a word in the listing form's part after the comma, not structure, v1's limitation kept as parity (rules/C10's Accepted) — 2026-08-23 #428. -Elsewhere: a credential opening the part after the comma, which C8 held until 2026-10-19 — decisions/C13's 2026-10-04 (Derek), #603 entry, which also makes `Smith, RN - CRNA` read suffix 'RN - CRNA' with the core a word of the run; a lone name word before a part with no given name, as in `Smith, Dr.` → title 'Dr.', family 'Smith', and `Smith, Sir` → given 'Smith' — decisions/H1's 2026-10-18 #720 entry; the period gate the #428 review decided, an ambiguous acronym counting when written with a period after each letter (`Smith, Ed.` stays no credential) — decisions/S3's 2026-10-19 entry; `田中さん, Dr.` keeping its honorific glued, the #428 review's recorded limit, tolerated under rules/W3 — decisions/cjk-comma-demotion's 2026-09-01 #469 entry; the chain's report on a positionally read name before the comma (`Freiherr von Berg Ma, Mr.` reports `particle-or-given`), the #428 review's other limit — decisions/C1's 2026-10-06 (Derek), #613 entry, its decision (4). +Elsewhere: `md`, which #296's audit kept a dual, left the titles — decisions/indic-honorifics' 2026-10-23 (Derek), #853 entry. A credential opening the part after the comma, which C8 held until 2026-10-19 — decisions/C13's 2026-10-04 (Derek), #603 entry, which also makes `Smith, RN - CRNA` read suffix 'RN - CRNA' with the core a word of the run; a lone name word before a part with no given name, as in `Smith, Dr.` → title 'Dr.', family 'Smith', and `Smith, Sir` → given 'Smith' — decisions/H1's 2026-10-18 #720 entry; the period gate the #428 review decided, an ambiguous acronym counting when written with a period after each letter (`Smith, Ed.` stays no credential) — decisions/S3's 2026-10-19 entry; `田中さん, Dr.` keeping its honorific glued, the #428 review's recorded limit, tolerated under rules/W3 — decisions/cjk-comma-demotion's 2026-09-01 #469 entry; the chain's report on a positionally read name before the comma (`Freiherr von Berg Ma, Mr.` reports `particle-or-given`), the #428 review's other limit — decisions/C1's 2026-10-06 (Derek), #613 entry, its decision (4). Open: none in this file. ## Log -- 2026-08-23 #296/#325 (its `ms` and `sa` joining the ambiguous set superseded by the 2026-08-23 (review of PR #428, four reviewers) entry, which keeps both out) — the listing form's post-comma part is read for what it is. Three commits of the approved #291/#296 bundle (spec 2026-07-30, plan 2026-08-01; see decisions/comma-suffix-arc), in the plan's order. (1) A comma followed only by titles fixed no family boundary, so the pre-comma name keeps its positional read: `John Smith, Mr.` reads given 'John', family 'Smith', title 'Mr.' where 2.0 had merged the family ('Bob Jones, author' moves with it at every baseline). "Nothing but titles" is title-shaped AND not suffix vocabulary — the plan's helper said only the first, and would have read `Smith, PhD Jr.` as the title run 'PhD Jr.' and `Smith, Mr. Jr.` as 'Mr. Jr.' (both master's suffix 'Jr.'; the gate and a measurement against master caught them). (2) The TITLES/suffix overlap audit and the ordering, one commit because each alone is wrong (the plan's F1): reading the credential run first would make `Smith, Dr.` a suffix while 'dr' was SUFFIX_WORDS residue, and the audit alone leaves `Smith, Jr.` a title by the period-abbreviation inference. The audit table's dispositions ship as approved — 'do', 'jr', 'junior', 'phd', 'se' out of TITLES; 'dr' and 'sra' out of the suffix sets; 'ms' and 'sa' into the ambiguous set — with ONE deviation on evidence the table did not have: 'md' keeps dual membership, because bare 'Md' before a name is the Bengali and South Asian Muslim abbreviation of Muhammad (#343/#345's corpus rows `Md Abdul Karim`, `Md. Abdul Karim`; the drop read the first as given 'Md', middle 'Abdul') while 'MD' after the name is the degree — position decides, as for 'sr'. The ordering is wider than the plan's lone piece: a post-comma part that is nothing but suffix pieces is the credential run, whole, which is what #325 needed (`Smith, Ph. D. Jr.` had put the split credential in the given name once a second suffix made it no longer lone — a 1.4.0 regression; v1 read suffix 'Ph. D.', title 'Jr.'). Vocabulary decides which words qualify and position breaks the tie for the genuine duals: `Smith, Sr.` is Senior where `Sr. Garcia` is Señor, `Smith, Esq.` is the postnominal where H2 still reads `Esq. Smith` as a title (H2 carries that as Accepted). `Smith, Dr. Jr.` keeps the 2.0 deviation's other case — a title, then the last piece as the suffix (v1 read first 'Jr.'; 2.0 through master suffix 'Jr.') — and `Smith, John Jr.` its given name. Consequences the audit carries, all measured and pinned: a leading 'PhD' or 'Jr' is a name word; `Do Quang Minh` reads given 'Do' with the particle-or-given fork, and `Dr. Do Van Johnson, MD` given 'Do', family 'Van Johnson' (the #367 rule for that name is replaced; TITLES ∩ ambiguous is {freiherr, st} now, which keeps the chained emitter reachable); a trailing bare 'Dr' is a name word, joining every other title-only word there (#316 is the open question); `田中さん, PhD` reads suffix 'さん, PhD'; the #100 prefix-join tests use 'lt', a genuine dual, as their token. #291 (multi-word credentials) is the bundle's fourth commit and its own PR. +- 2026-08-23 #296/#325 (its `ms` and `sa` joining the ambiguous set superseded by the 2026-08-23 (review of PR #428, four reviewers) entry, which keeps both out; its `md` dual superseded by #853, decisions/indic-honorifics) — the listing form's post-comma part is read for what it is. Three commits of the approved #291/#296 bundle (spec 2026-07-30, plan 2026-08-01; see decisions/comma-suffix-arc), in the plan's order. (1) A comma followed only by titles fixed no family boundary, so the pre-comma name keeps its positional read: `John Smith, Mr.` reads given 'John', family 'Smith', title 'Mr.' where 2.0 had merged the family ('Bob Jones, author' moves with it at every baseline). "Nothing but titles" is title-shaped AND not suffix vocabulary — the plan's helper said only the first, and would have read `Smith, PhD Jr.` as the title run 'PhD Jr.' and `Smith, Mr. Jr.` as 'Mr. Jr.' (both master's suffix 'Jr.'; the gate and a measurement against master caught them). (2) The TITLES/suffix overlap audit and the ordering, one commit because each alone is wrong (the plan's F1): reading the credential run first would make `Smith, Dr.` a suffix while 'dr' was SUFFIX_WORDS residue, and the audit alone leaves `Smith, Jr.` a title by the period-abbreviation inference. The audit table's dispositions ship as approved — 'do', 'jr', 'junior', 'phd', 'se' out of TITLES; 'dr' and 'sra' out of the suffix sets; 'ms' and 'sa' into the ambiguous set — with ONE deviation on evidence the table did not have: 'md' keeps dual membership, because bare 'Md' before a name is the Bengali and South Asian Muslim abbreviation of Muhammad (#343/#345's corpus rows `Md Abdul Karim`, `Md. Abdul Karim`; the drop read the first as given 'Md', middle 'Abdul') while 'MD' after the name is the degree — position decides, as for 'sr'. The ordering is wider than the plan's lone piece: a post-comma part that is nothing but suffix pieces is the credential run, whole, which is what #325 needed (`Smith, Ph. D. Jr.` had put the split credential in the given name once a second suffix made it no longer lone — a 1.4.0 regression; v1 read suffix 'Ph. D.', title 'Jr.'). Vocabulary decides which words qualify and position breaks the tie for the genuine duals: `Smith, Sr.` is Senior where `Sr. Garcia` is Señor, `Smith, Esq.` is the postnominal where H2 still reads `Esq. Smith` as a title (H2 carries that as Accepted). `Smith, Dr. Jr.` keeps the 2.0 deviation's other case — a title, then the last piece as the suffix (v1 read first 'Jr.'; 2.0 through master suffix 'Jr.') — and `Smith, John Jr.` its given name. Consequences the audit carries, all measured and pinned: a leading 'PhD' or 'Jr' is a name word; `Do Quang Minh` reads given 'Do' with the particle-or-given fork, and `Dr. Do Van Johnson, MD` given 'Do', family 'Van Johnson' (the #367 rule for that name is replaced; TITLES ∩ ambiguous is {freiherr, st} now, which keeps the chained emitter reachable); a trailing bare 'Dr' is a name word, joining every other title-only word there (#316 is the open question); `田中さん, PhD` reads suffix 'さん, PhD'; the #100 prefix-join tests use 'lt', a genuine dual, as their token. #291 (multi-word credentials) is the bundle's fourth commit and its own PR. - 2026-08-23 (review of PR #428, four reviewers) (its `Smith, RN - CRNA` given 'RN' superseded by #603, decisions/C13; its unreported `Freiherr von Berg Ma, Mr.` superseded by #613, decisions/C1) — the repair's condition is "no name word", not "all titles": `John Smith, Mr. Jr.` (a title and a postnominal) had kept the merge the rationale argues against, and reads given 'John', family 'Smith', suffix 'Jr.' now. Its guard counts NAME pieces: the positional read peels a trailing suffix first, so `Smith Jr., Mr.` — two pieces, one name — read positionally lost its family (the code review); it keeps family 'Smith', suffix 'Jr.' at every baseline. The positional read records its ORDER, and post_rules' leading-piece scan keys on "assign records no order after a family comma" rather than on the structure, so P1's family-first fold reaches the pre-comma name as it does without the comma — `de Mesnil Jean, Dr.` under a family-first order had read family 'de' (the test review). The credential run does NOT step over a delimiter core the policy names (`Smith, RN - CRNA` reads given 'RN' under `extra_suffix_delimiters` as without it): the code review asked for it, a draft did it, and tests/test_suffixes.py's "the delimiter must not affect parsing at all" outside the suffix-comma form is v1 parity pinned on purpose — rules/C10 carries the limit as Accepted. A second deviation from the audit table: `ms` and `sa` do NOT join the ambiguous set — the gate is position-blind and the collision is not; gated, `John Smith, MS` lost its suffix-comma route and read title 'MS', and `Smith, Ms.` passed the gate on its one period and read as a credential anyway. Both are genuine duals, and position decides: `Smith, Ms.` reads suffix 'Ms.' (title at every baseline; rules/C8 carries it as Accepted, with `Smith, Ms. Jane` keeping the title) and `John Smith, MS` keeps suffix 'MS'. And the gate itself now means what S2 said (rules/S3 since 2026-10-19): an ambiguous acronym counts when written with ITS periods, one after each letter (`M.A.`, `J.D.`), not on any period — `Smith, Ed.` had become a credential on a name's trailing period, and `Jack Ma.` had read suffix 'Ma.' since 2.0 (S3 carries the boundary since 2026-10-19; 1.4.0 read family 'Ma.'). Two limits recorded rather than fixed: `田中さん, Dr.` keeps the honorific glued, joining master's `田中さん, Mr.` — the honorific peel runs in script_segment on the suffix-comma and no-comma structures, before group or assign can say the comma fixed nothing (C1 Accepted); and group's chain emitter still reports no PARTICLE_OR_GIVEN on the family-comma path, so `Freiherr von Berg Ma, Mr.` reads as its no-comma twin but without the report — group runs before assign decides which read applies, and plumbing a deferred report through the state is more than the shape is worth today. - 2026-10-04 (Derek), #603 — A credential opening the part after the comma makes it the postnominal part, whatever follows. The record moved to decisions/C13 with that clause (2026-10-19). - 2026-10-18 #728 — SPLIT FROM decisions/C1 with the rule. The entries above were written under decisions/C1 while this rule was part of rules/C1, and moved here unchanged but for the pointers the move broke, the measurement paragraphs, each cut to one line naming its population, counts, baseline and PR, and the superseded passages, each cut to its decision and what replaced it; where one says rules/C1 or C1, it means the rule as it then stood, which held this one. diff --git a/docs/design/decisions/C9-one-character-suffix-word-inside-a-comma-part.md b/docs/design/decisions/C9-one-character-suffix-word-inside-a-comma-part.md index 945b0a20..c5b4f596 100644 --- a/docs/design/decisions/C9-one-character-suffix-word-inside-a-comma-part.md +++ b/docs/design/decisions/C9-one-character-suffix-word-inside-a-comma-part.md @@ -3,7 +3,7 @@ ## Standing - Inside a comma part a one-character suffix word is read by what stands before it: behind another suffix word it continues the run, with a period or without (`Smith, PSM I` → suffix 'PSM I'; `Smith, PSM I.` → suffix 'PSM I.'), and behind a name word an ASCII period makes it a middle initial while the bare letter, with no further comma after it, is the suffix (`Smith, John V.` → middle 'V.'; `Smith, John V` → suffix 'V', v1 parity) — 2026-08-25 #430. -- The class is one-character suffix vocabulary, not a roman numeral (`Smith, John 2.` → middle '2.'), and a run's first word that is title vocabulary too stays in the run (`Smith, MD I` → suffix 'MD I'; `Smith, Jr. I` → suffix 'Jr. I') — 2026-08-25 #430. +- The class is one-character suffix vocabulary, not a roman numeral (`Smith, John 2.` → middle '2.'), and a run's first word that is title vocabulary too stays in the run (`Smith, RA I` → suffix 'RA I'; `Smith, Jr. I` → suffix 'Jr. I') — 2026-08-25 #430. - The no-comma numeral predicate is not asked in the given part, so a numeral behind an initial stays the suffix (`Chang, Andy C I` → middle 'C', suffix 'I') — 2026-08-25 #430. - The given part's walk is strict-blind and the fork reports nothing: `Smith, John V` reads suffix 'V' under `lenient_comma_suffixes=False` as under the default, with no `suffix-or-name`, where `John Smith V` reports — 2026-08-25 #430. diff --git a/docs/design/decisions/H3-title-runs-floor.md b/docs/design/decisions/H3-title-runs-floor.md index 344fd1fd..22c1ee78 100644 --- a/docs/design/decisions/H3-title-runs-floor.md +++ b/docs/design/decisions/H3-title-runs-floor.md @@ -4,7 +4,7 @@ - The leading title run gives back its last word where everything behind the run is suffix pieces and that word is not suffix vocabulary: `Dr King Jr` → title 'Dr', family 'King', suffix 'Jr' — 2026-09-08 #316. - It gives back one word only: a joined title unit stays the title (`Prince of Wales Jr` → family 'Jr'), and where the word given back is the whole name it reads as the given name (`Dr Jr` → given 'Dr', suffix 'Jr', reporting `title-or-name`) — 2026-09-08 #316. -- The floor asks about the run's last word and nothing else: a run ending in suffix vocabulary keeps it (`Dr King MD PhD` → title 'Dr King MD'), and a bare numeral behind the run is the numeral fork's, not this floor's (`Dr King V` → title 'Dr King', given 'V') — 2026-09-08 #316. +- The floor asks about the run's last word and nothing else: a run ending in suffix vocabulary keeps it (`Dr King RA PhD` → title 'Dr King RA'), and a bare numeral behind the run is the numeral fork's, not this floor's (`Dr King V` → title 'Dr King', given 'V') — 2026-09-08 #316. Open: none in this file. diff --git a/docs/design/decisions/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md b/docs/design/decisions/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md index 36581027..d5914278 100644 --- a/docs/design/decisions/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md +++ b/docs/design/decisions/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md @@ -5,7 +5,7 @@ - An input the title peel eats down to one last word that is itself title vocabulary reads that word as the name and reports `title-or-name`, the `detail` naming the word: `Lord Chancellor` → family 'Chancellor', `Dr. King` reports — 2026-09-07 #491. - `title-or-name` is emitted at assign's lone-name-word site, not at H1's retag, so it is reported under every name order, and so is the suffix half's `suffix-or-name`; the peel half's `detail` names no field — 2026-09-07 #491. - Nothing is reported where no word was chosen or where the word left standing is no title word: a title alone or a title run with nothing behind it (`Dr.`, `Prince of Wales`), and a title before an ordinary word (`Dr. Smith`) — 2026-09-07 #491. -- An input whose every word is post-nominal vocabulary reads its first word as the name and reports `suffix-or-name` (`Rinpoche`, `QC MP` → given 'QC', `PhD`), reached only where no title was peeled (`MD DDS` reports nothing) and not with a maiden name beside it (`abd née Jones`) or for a lone CJK honorific a script order placed (`さん`); `Jr.` alone is H2's title — 2026-09-07 #491. +- An input whose every word is post-nominal vocabulary reads its first word as the name and reports `suffix-or-name` (`Rinpoche`, `QC MP` → given 'QC', `PhD`), reached only where no title was peeled (`RA DDS` reports nothing) and not with a maiden name beside it (`abd née Jones`) or for a lone CJK honorific a script order placed (`さん`); `Jr.` alone is H2's title — 2026-09-07 #491. - `MA` alone reports `given-or-family` where `PhD` alone reports `suffix-or-name`, an asymmetry between this rule and O5 recorded and not fixed — 2026-09-07 #491. - A lone name unit that is a join carrying title vocabulary reports `title-or-name` too (`John of Prince`, `Smith and Prince`), stated since 2026-10-20 as rules/H7 — 2026-09-07 #491. - The join clause (rules/H7's, recorded here) stands beside the peel clause, so a peeled title, a maiden name or a claimed word does not silence it (`Dr. Smith and Prince`, `van and Prince`, `Attorney General of Minnesota` report) — 2026-09-08 #518. diff --git a/docs/design/decisions/H6-title-before-a-family-comma.md b/docs/design/decisions/H6-title-before-a-family-comma.md index 5c8c7712..4dd9298a 100644 --- a/docs/design/decisions/H6-title-before-a-family-comma.md +++ b/docs/design/decisions/H6-title-before-a-family-comma.md @@ -4,7 +4,7 @@ - A run of title words opening the part before a family comma is the title, joining the title field in input order with any title after the comma, and the comma decision is untouched, so the reading is the same under every name order: `Dr. Smith, John` → title 'Dr.', family 'Smith'; `Prof. Dr. Brühl, Ada Cornelia` → title 'Prof. Dr.' — 2026-10-17 #701. - The run admits a word by vocabulary only, never by H2's shape, since the slot is a surname's slot too and a surname can open with an abbreviated word: `Xyz. Smith, John` → family 'Xyz. Smith', `Sta. Maria, Juan` → family 'Sta. Maria' — 2026-10-17 #701; 2026-10-18 #710. -- The run leaves the family its last piece (`Dr., John` → family 'Dr.'), stops at a word that is a particle too (`St Pierre, Ed` and `Freiherr von Berg, Ed` keep their families, while `Dr. Freiherr von Berg, Ed` peels 'Dr.'), and stops where nothing but post-nominals stands behind a title (`MD PhD Jr., John` → family 'MD'), so a title/suffix dual with a name word behind it peels (`Rev. Sr. Smith, Mary` → title 'Rev. Sr.') — 2026-10-17 #701. +- The run leaves the family its last piece (`Dr., John` → family 'Dr.'), stops at a word that is a particle too (`St Pierre, Ed` and `Freiherr von Berg, Ed` keep their families, while `Dr. Freiherr von Berg, Ed` peels 'Dr.'), and stops where nothing but post-nominals stands behind a title (`RA PhD Jr., John` → family 'RA'), so a title/suffix dual with a name word behind it peels (`Rev. Sr. Smith, Mary` → title 'Rev. Sr.') — 2026-10-17 #701. - A surname-like title opening a two-word surname is the title (`King Smith, John` → title 'King'), an accepted cost — 2026-10-17 #701. - `gov` is a title (`Lt. Gov. Smith, John` → title 'Lt. Gov.'), the first piece the run leaves is the family whatever it is (`Dr. PhD Smith, John` → family 'PhD Smith'), and the particle stop is all that keeps a rank in the family, so `Graf Lambsdorff, Otto` reads title 'Graf' — 2026-10-17 #701. - The comma-less writing of an abbreviated surname is not this rule's and still splits it (`Juan Sta. Maria` → middle 'Sta.', family 'Maria'); listing `sta` and `sto` as particles is weighed and not decided — 2026-10-18 #710. diff --git a/docs/design/decisions/S7-company-of-an-unambiguous-credential.md b/docs/design/decisions/S7-company-of-an-unambiguous-credential.md index cfce1a30..20c30f6e 100644 --- a/docs/design/decisions/S7-company-of-an-unambiguous-credential.md +++ b/docs/design/decisions/S7-company-of-an-unambiguous-credential.md @@ -6,7 +6,7 @@ Examples name each field as the default order reads it; under the family-first o - A listed member of the ambiguous class standing behind an unambiguous credential in one run of suffix words, other members between passing it on, reads as the credential at every trailing slot whatever its writing or the count (`John Smith PhD Ed Ma` → suffix 'PhD Ed Ma'; `Smith, PhD MEng` and `Doe, Jane PhD MEng` → suffix 'PhD MEng'); a credential behind the member speaks for nothing (`Wang Ma PhD` keeps family 'Ma') — 2026-09-27 #544, A CREDENTIAL RUN. - Where no credential run starts (S8's, or C13's in a part a credential opens after a comma), as with one name word before the credential and no comma, what anchors nothing and ends the run is the walk's own leading piece (`PhD Ma` → given 'PhD', family 'Ma'), a connective (`Jane Doe nee Puig i Ma` keeps maiden 'Puig i Ma'), a single-letter roman numeral in any case (`Om Jr V Ma` keeps family 'Ma', where `Om Jr III Ma` reads suffix 'Jr III Ma'), and a word of both the particle and the unambiguous suffix vocabulary (`Jan vd Ma` → family 'vd Ma'; `Smith vd Ma, John` → family 'Smith vd Ma'), whose exclusion is of the word in front only; where either run starts it takes the numeral, the particle and a name word (`John Smith PhD V Ma` and `Smith, PhD V Ma` → suffix 'PhD V Ma'), while a connective right behind the credential is joined to it first, so S8's run never starts (`John Smith PhD y Ma` → family 'PhD y Ma', `Smith, John PhD and Ma` → middle 'PhD and Ma'), except in C13's part (`Smith, PhD and Ma` → suffix 'PhD and Ma'), and one further in is the run's (`John Smith PhD Jones and Ma` → suffix 'PhD Jones and Ma') — 2026-09-27 #544, A CREDENTIAL RUN. -- A title/suffix dual in the given part's leading title run after a one-word family comma is a title there, anchors nothing and silences the company for the part (`Smith, Ms Ma` → given 'Ma'; `Smith, MD PhD Ma` → title 'MD', given 'PhD', middle 'Ma'); anywhere else a dual anchors as any suffix word (`Jane Doe, MS LAc` → suffix 'MS LAc', at the accepted cost `John Smith, Ms Ma` → suffix 'Ms Ma') — 2026-09-27 #544, A CREDENTIAL RUN. +- A title/suffix dual in the given part's leading title run after a one-word family comma is a title there, anchors nothing and silences the company for the part (`Smith, Ms Ma` → given 'Ma'; `Smith, RA PhD Ma` → title 'RA', given 'PhD', middle 'Ma'); anywhere else a dual anchors as any suffix word (`Jane Doe, MS LAc` → suffix 'MS LAc', at the accepted cost `John Smith, Ms Ma` → suffix 'Ms Ma') — 2026-09-27 #544, A CREDENTIAL RUN. - A member the company decides reports `suffix-or-name` where it was decided (`Smith, PhD Ma`); a member its own capitals made the credential reports nothing new (`John Smith, PhD MA`) — 2026-09-27 #544, A CREDENTIAL RUN. - Accepted: `Om Jr Ma` reads given 'Om', suffix 'Jr Ma' with no family name, and `Smith, MA PhD Ma` reads suffix 'MA PhD Ma' with no given name; the split `Ph. D.` in a main segment (no comma, or before a suffix comma) is outside the walk, so `John Smith Ph. D. MEng` keeps family 'MEng', while the comma and given-slot spellings anchor (`Smith, Ph. D. MEng` → suffix 'Ph. D. MEng') — 2026-09-27 #544, A CREDENTIAL RUN. - An unambiguous credential in front of the member overrides P6's attachment at the given slot, as the capitals do (`Doe, John PhD Do` → suffix 'PhD Do'); the one-case record with nothing in front keeps P6's reading (`NASCIMENTO, EDSON ARANTES DO` → family 'DO NASCIMENTO') — 2026-09-27 #544, THE #531 PAIRING. diff --git a/docs/design/decisions/comma-suffix-arc.md b/docs/design/decisions/comma-suffix-arc.md index 3d8d8683..568e2582 100644 --- a/docs/design/decisions/comma-suffix-arc.md +++ b/docs/design/decisions/comma-suffix-arc.md @@ -1,7 +1,7 @@ # comma-suffix-arc — #291/#296/#316 (2026-07-26 → 2026-08-01) #291 was filed 2026-07-26 out of the 2.0 vocabulary cleanup, with -its decline-by-measurement evidence in the issue body; "z domu" folded into it 2026-07-27 (see Excluded, MAIDEN_MARKERS). Intent for #291 and #296 is settled by the approved bundle spec; #296 (with #325) SHIPPED 2026-08-23 as the bundle's first three commits, with one deviation from the audit table recorded at decisions/C8 ('md' keeps dual membership — bare 'Md' is Muhammad); #291 was UNSHIPPED then, carried as a deviates: marker on C1 (#291 closed 2026-08-24; the marker came off with #564 in 2.4). The arc's bookkeeping: #291/#296 moved milestone v2.1 → v2.2 on 2026-08-01 (with #289/#293); the 2026-07-30 design doc was amended in place 2026-08-01 (A1–A6); its durable content is HERE rather than in the gitignored doc; #316 (trailing titles) was filed the same day, carrying the esq cleanup as a Related section. +its decline-by-measurement evidence in the issue body; "z domu" folded into it 2026-07-27 (see Excluded, MAIDEN_MARKERS). Intent for #291 and #296 is settled by the approved bundle spec; #296 (with #325) SHIPPED 2026-08-23 as the bundle's first three commits, with one deviation from the audit table recorded at decisions/C8 ('md' keeps dual membership — bare 'Md' is Muhammad; reversed 2026-10-23, #853, decisions/indic-honorifics); #291 was UNSHIPPED then, carried as a deviates: marker on C1 (#291 closed 2026-08-24; the marker came off with #564 in 2.4). The arc's bookkeeping: #291/#296 moved milestone v2.1 → v2.2 on 2026-08-01 (with #289/#293); the 2026-07-30 design doc was amended in place 2026-08-01 (A1–A6); its durable content is HERE rather than in the gitignored doc; #316 (trailing titles) was filed the same day, carrying the esq cleanup as a Related section. ## Standing @@ -12,7 +12,7 @@ its decline-by-measurement evidence in the issue body; "z domu" folded into it 2 Not standing: plan amendment 1's repair, carried out by #296's first commit — 2026-08-01. -Elsewhere: the audit table's dispositions as shipped, `md` kept a dual and `ms` and `sa` kept out of the ambiguous set — decisions/C8's 2026-08-23 #296 and 2026-08-23 #428 Standing lines, the second also holding the repair's condition, no name word; the trailing slot read by vocabulary, a period-marked title word there a title (`John Smith Prof.` → title 'Prof.') — decisions/H5's 2026-09-08 #316 entry; the dotted shape class — decisions/S9's 2026-09-14 (Derek) entry headed #516's DOTTED HALF IS A SWITCH. +Elsewhere: the audit table's dispositions as shipped, `md` kept a dual (until #853) and `ms` and `sa` kept out of the ambiguous set — decisions/C8's 2026-08-23 #296 and 2026-08-23 #428 Standing lines, the second also holding the repair's condition, no name word; the trailing slot read by vocabulary, a period-marked title word there a title (`John Smith Prof.` → title 'Prof.') — decisions/H5's 2026-09-08 #316 entry; the dotted shape class — decisions/S9's 2026-09-14 (Derek) entry headed #516's DOTTED HALF IS A SWITCH. Open: none in this file. diff --git a/docs/design/decisions/indic-honorifics.md b/docs/design/decisions/indic-honorifics.md index 4aeda66c..bd094a92 100644 --- a/docs/design/decisions/indic-honorifics.md +++ b/docs/design/decisions/indic-honorifics.md @@ -10,10 +10,11 @@ Closes #346, #344 and #343 as one bundle: each CLASS is decided once, and each s - The class's accepted cost is a real given name that is also a title: `Guru Dutt` reads title 'Guru', given 'Dutt' — 2026-09-06 #346. - `rinpoche`, जी, साहब, साहिब, साहेब, महाराज and সাহেব, বাবু, মহারাজ ship as suffix words and spaced only, none a glued honorific, on the evidence recorded for जी alone (`Mohan Sharma जी` reads suffix 'जी', `Mohan Sharmaजी` keeps family 'Sharmaजी'); the Tibetan -la particle needs no entry — 2026-09-06 #346. - Bengali বাবু is trailing and Devanagari बाबू leading, different codepoints that cannot interact (`অমল বাবু` reads suffix 'বাবু'), and a sweep harmonizing the two argues with their wordlist comments — 2026-09-06 #346. -- মোঃ/মো and মোসাঃ/মোছাঃ are titles, the functional home since `given` must stay the name the person is addressed by (`মোঃ আবদুল করিম` reads given 'আবদুল'), and Latin `md` was always a title, the Muhammad reading a later reinterpretation of an entry added as the medical degree; Latin `mst` is not in the default, its refusal an `excluded` entry in bn.toml — 2026-09-06 #346. +- মোঃ/মো and মোসাঃ/মোছাঃ are titles, the functional home since `given` must stay the name the person is addressed by (`মোঃ আবদুল করিম` reads given 'আবদুল'), and Latin `mst` is not in the default, its refusal an `excluded` entry in bn.toml — 2026-09-06 #346. - An abbreviation carrying a mark is spelled with it, and the Bengali ones ship as the visarga form and the bare stem while the Devanagari ones ship as the bare stem alone, the dotted form listed in neither script — 2026-09-06 #346. - Title vocabulary beats the one-letter-plus-period initial shape, measured and pinned by rows and not a rule: `ড. মুহাম্মদ ইউনূস` reads title 'ড.' while `র. কে. নারায়ণ`, with no entry behind it, keeps its initials — 2026-09-06 #346. - An unlisted abugida honorific stays a name word, and rules/H2's executable example of it is `প্রকৌশলী. Sen`, so whoever ships প্রকৌশলী moves the example first; the Tier 4 candidates in the Indic scripts (transliterated military ranks, Advocate, Engineer, Marhum, the kinship forms, मां/माता/মা, the hafiz/syed/shahid forms) are skipped pending a corpus check and not declined — 2026-09-06 #346. +- Latin `md` is a credential and no title: a leading `Md` is the given name it abbreviates (`Md Abdul Karim` → given 'Md', middle 'Abdul'; `Mr Md Karim Uddin` → title 'Mr', given 'Md', middle 'Karim'), at two accepted costs, a particle behind it starting the family (`Md Abu Sayed` → family 'Abu Sayed') and the given part of a family comma it opens reading as credentials (`Uddin, Md Karim` → suffix 'Md Karim'); the Bengali মোঃ/মো entries stay titles — 2026-10-23 #853. Elsewhere: the bundle's class-level refusals are recorded under decisions/vocabulary-exclusions, not here: the Latin forms of the Indic titles belonging to the opt-in packs of #345 (2026-09-06, #344), the native spelling shipping alone where a transliteration collides with real names (sri/shri, sant, pandit, mst), the ban on the spaced trailing set becoming glued honorifics, and the per-word title refusals (ঠাকুর/ठाकुर, कुमारी/কুমারী, बेगम/বেগম, শেখ/शेख, आचार्य, राजा/रानी), each an `excluded` entry (`uv run python tools/vocab/query.py --excluded title`). @@ -21,7 +22,7 @@ Open: #514 (whether the Tibetan prefix titles Tulku, Khenpo, Geshe, Kyabje and J ## Log -- 2026-09-06 #346/#344/#343 — THE RENUNCIATE CLASS AND THE INDIC HONORIFIC VOCABULARY: the bullets below. +- 2026-09-06 #346/#344/#343 (its Latin `md` kept a title superseded by #853, below) — THE RENUNCIATE CLASS AND THE INDIC HONORIFIC VOCABULARY: the bullets below. - **The renunciate class (#346).** What is decided once is the CLASS — renunciate given-name titles — against rules.md's H Background, which already carried the two criteria: membership in the given-name-title list follows HOW THE TITLE ADDRESSES, and whether an empty family is correct output follows SURNAME RETENTION. Renunciation answers both at once, so "Swami Vivekananda" reads given Vivekananda, family "". Each script's MEMBERSHIP is its own list, and the three overlap rather than coincide: Latin swami/guru/baba/lama (moved out of the TITLES-only block), Devanagari स्वामी/गुरु/बाबा/संत, Bengali স্বামী/শ্রীল/গুরু/বাবা. lama is a Tibetan title whose native script is Tibetan, and the Hindi and Bengali borrowings लामा/লামা are rare enough that no entry was argued for either — "Lama Zopa" folds, "लामा जोपा" and "লামা জোপা" do not. संत (Sant) and শ্রীল (Srila) run the other way: renunciate titles native to those scripts with NO Latin entry, — Latin sant collides with a real given name (its `excluded` entry in nameparser/vocab/hi.toml) and srila has no transliteration in use — "संत कबीर" folds, "Sant Kabir" does not. A sweep "harmonizing" the three lists must therefore argue each word on the two criteria, never on symmetry. rabbi and imam address by TITLE rather than by given name, so they fail the first criterion and stay out; the second agrees, those traditions keeping surnames. "Rabbi Cohen" keeps family Cohen and is pinned as the control beside the Swami row. The four Latin entries were already TITLES vocabulary, so the promotion changes only which field the one name word lands in, and C-i's position test was not re-run for them; the known leading-position collision is Guru Dutt, whose given name is Guru — family went from Dutt to "" — accepted as the pre-existing cost of guru being a title at all, and pinned by the row renunciate_title_swallows_a_real_given_name. - **venerable is left in TITLES only, and is deliberately NOT an Excluded entry.** The traditions split on surname retention — Buddhist monastics drop the family name, Anglican archdeacons and Catholic candidates keep it — so neither answer is right for the single-name case. TITLES has no ambiguous subset and no AmbiguityKind, so, as for king in #v1-xfail-triage and roz in the maiden markers, the only two expressions available are ship and do not ship; this is the third thing, a membership that is right and an OUTPUT that is undecidable, and it stays where it is. @@ -35,4 +36,7 @@ Open: #514 (whether the Tibetan prefix titles Tulku, Khenpo, Geshe, Kyabje and J - **rules/H2's Accepted clause keeps its claim and loses its example.** The clause says an unlisted abugida honorific stays a name word, because the shape rule cannot see an unbroken run of letters through combining vowel signs; that is still true. Its example used প্রফেসর, which this bundle lists, so the example is now "প্রকৌশলী. Sen" → given="প্রকৌশলী." — Engineer, a loanword deliberately not shipped. The example is a contract-tier name in corpus_rules.jsonl, so the swap landed in the Bengali commit rather than in the records commit; leaving it would have exited the gate 1 there. - **Not recorded as excluded:** the Tier 4 candidates — transliterated military ranks, Advocate, Engineer, Marhum, and the kinship forms — are skipped pending a corpus check, not declined, and मां/माता/মা and the hafiz/syed/shahid forms are left alone the same way. An Excluded entry is a standing prohibition, and none of these has been argued to that standard. One of them carries a duty anyway: প্রকৌশলী (Engineer) is rules/H2's executable example of an unlisted abugida honorific, so whoever ships it must move the example to another unlisted word first, or the doc test and the contract-tier gate fail together. - **Measurement (2026-09-06).** Four corpus names move, all under the renunciate fold, all in one direction (the family empties and the given fills): Swami Vivekananda, Guru Nanak, Baba Ramdev and Lama Zopa, every one from the radar-tier corpus_issues.jsonl. Recompute by parsing every name in the tools/differential/corpus*.jsonl glob twice — once with the shipped lexicon, once with `Lexicon.default().remove(given_name_titles={"swami","guru","baba","lama"})` — and diffing the seven name fields. The multi-name spellings are the boundary and do not move: Swami Vivekananda Saraswati and Guru Gobind Singh keep families Saraswati and Singh, rules/H1's fold requiring exactly one name word. The Devanagari and Bengali additions move no corpus name at all, the corpora holding no name written in either script apart from rules.md's own H2 example, which this bundle rewrote. The gate's intentional counts rose by four at every baseline; read today's from the `corpus:` line of `uv run python tools/differential/compare.py --baseline X`. - +- 2026-10-23 (Derek), #853 — LATIN MD IS A CREDENTIAL AND NO TITLE: A LEADING MD IS THE GIVEN NAME IT ABBREVIATES. SUPERSEDES: the Latin md half of the 2026-09-06 #346/#344/#343 bundle (its Standing line and the "md history, corrected" bullet's conclusion that both readings agree on the field) and decisions/C8's 2026-08-23 #296/#325 entry's `md` dual. The bundle had kept `md` a title on the ground that `given` holds the name a person is addressed by, and `Md. Karim Uddin` is addressed as Karim; the filers who write these names put `Md` in the given name. The `psc-individuals` local corpus (UK people with significant control, the 1-in-8 sample of the 2026-10-22 snapshot, #849) holds 1,300 names whose forename box opens with `Md`: 1,209 typed it there alone with the rest of the given name in the middle box, 49 typed `Md Karim` together, and 38 names elsewhere typed `Md` as the whole title box. Scored against the boxes, the title reading put the forename box in `given` for none of the 1,300 and left `given` empty for 94; the credential reading puts it there for 1,207 and leaves 3 empty. A bound given name like `abdul` (given `Md Karim`) was the third reading measured and declined: it matched the forename box 152 times, and it would make `Md` bind to the next word where the spelled-out `Mohammad Karim Uddin` (given 'Mohammad', middle 'Karim') does not. With the title gone, `Md` reads as `Mohammad` reads. `md` keeps its credential reading, so `John Smith MD` and `John Smith, MD` are unchanged, and its `excluded` academic entry in en.toml carries the reason. + WHAT MOVES, measured 2026-10-23 on this tree against the same tree with `Lexicon.default().add(titles={"md"})` as the comparator (the title reading restored and nothing else), over every name holding the word `md` in the local corpora fetched that day (`tools/differential/local_corpora.py`; a fetch of another day moves the counts): psc-individuals 1,283 moved, 1,280 of them `Md` from title to given; ctgov-officials 417 moved, 379 from title to suffix, almost all the shape `John Smith, MD Jones`, whose `MD` the title reading had taken for the given part's title and whose whole name before the comma it had read as the family, plus 21 from title to given, 15 of them a Bangladeshi `Md`, two a degree written first (`MD John Smith`-shaped) and four no person's name at all; ukec-individuals 4, all to given; fec-individuals 10. The committed corpora move 19 names (`tools/differential/corpus*.jsonl`, the same comparator), 5 of them fictional CLDR renderings that write `MD` as a prefix title. + ACCEPTED COSTS (Derek). (1) A particle behind `Md` starts the family: `Md Abu Sayed` reads given 'Md', family 'Abu Sayed', where the title reading left `Abu` the leading name word and read given 'Abu', family 'Sayed'. 17 of the 1,300 take this shape, with `Abu`, `Al` or `Bin`, the filer's surname box holding the last word alone. It is the spelled-out name's reading (`Mohammad Abu Sayed` reads the same), so the gap is P2's and P5's for any given name before `abu`, not Md's. (2) The given part of a family comma that `Md` opens reads as a credential run (rules/C13): `Uddin, Md Karim` reads family 'Uddin', suffix 'Md Karim' with a `suffix-or-name` report, where the title reading gave given 'Karim'. 9 of the 10 FEC moves are this shape, FEC writing `FAMILY, GIVEN`. Marking `md` ambiguous (`may_be_name`) would fix it, measured: `Uddin, Md Karim` reads given 'Md', and every `John Smith MD` then reports `suffix-or-name`, so it was declined for the commonest credential there is. + NOT CHANGED: the period-marked `Md.` is still a title by rules/H2's leading-abbreviation shape (36 of the 1,300 PSC names), which is #853's question 3 and not this entry's; and the Bengali মোঃ/মো and মোসাঃ/মোছাঃ entries stay titles, no filed data reaching Bengali script. The case rows and rule examples that used `MD` as their title/suffix dual (rules/H3, H4, S7 and C13; decisions/H3, H4, H6, S7 and C9's Standing lines; the cases.py dual rows) are respelled with `RA`, which reads as `MD` did at 1.4.0, 2.3.0 and the parent; their dated log entries keep `MD` as written. diff --git a/docs/design/rules/C13-credential-opening-the-part-after-the-comma.md b/docs/design/rules/C13-credential-opening-the-part-after-the-comma.md index 09e06333..2bcc2884 100644 --- a/docs/design/rules/C13-credential-opening-the-part-after-the-comma.md +++ b/docs/design/rules/C13-credential-opening-the-part-after-the-comma.md @@ -19,7 +19,7 @@ - **A word of both the title and the suffix vocabulary opens nothing, and nor does a credential behind one**: in front of a given name it is the listing form's title ('Smith, Ms Jane'), and no count of the words before the comma tells a surname of two words from a given name and a family name. ``` - "John Smith, MD Jones" → given="Jones" · boundary + "John Smith, RA Jones" → given="Jones" · boundary "Smith, Ms Jane" → title="Ms" · boundary ``` diff --git a/docs/design/rules/H3-title-runs-floor.md b/docs/design/rules/H3-title-runs-floor.md index 1ab1323c..36009d59 100644 --- a/docs/design/rules/H3-title-runs-floor.md +++ b/docs/design/rules/H3-title-runs-floor.md @@ -8,7 +8,7 @@ ``` "Dr King Jr" → family="King" "Dr King Jr" → suffix="Jr" - "MD DDS" → family="DDS" + "RA DDS" → family="DDS" ``` **Because:** compound titles are written as a run of title words, connectives included; a title word standing inside the name is just a name word. And a title addresses somebody, so the run leaves somebody there to address: a post-nominal is written about a name rather than being one, so it cannot be the name the run left. diff --git a/docs/design/rules/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md b/docs/design/rules/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md index aaeaa4e5..b9d81918 100644 --- a/docs/design/rules/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md +++ b/docs/design/rules/H4-input-that-is-nothing-but-vocabulary-still-has-to-name-somebody.md @@ -28,9 +28,9 @@ **Except:** -- **A title in front takes the input out of the suffix half**, which is reached only where no title was peeled first. What it does NOT do is take the input out of the rule: the title run leaves a name word standing (H3), and where that word is title vocabulary the TITLE half claims it — `Dr. King MD` reports `title-or-name` on `King`, while `MD DDS` reports nothing, `DDS` being no title. +- **A title in front takes the input out of the suffix half**, which is reached only where no title was peeled first. What it does NOT do is take the input out of the rule: the title run leaves a name word standing (H3), and where that word is title vocabulary the TITLE half claims it — `Dr. King MD` reports `title-or-name` on `King`, while `RA DDS` reports nothing, `DDS` being no title. ``` - "MD DDS" → ambiguities=() + "RA DDS" → ambiguities=() ``` **Precedence:** diff --git a/docs/design/rules/S7-company-of-an-unambiguous-credential.md b/docs/design/rules/S7-company-of-an-unambiguous-credential.md index 7ad9e7ee..508fd628 100644 --- a/docs/design/rules/S7-company-of-an-unambiguous-credential.md +++ b/docs/design/rules/S7-company-of-an-unambiguous-credential.md @@ -31,7 +31,7 @@ - **A word of both the title and the suffix vocabulary standing in the given part's leading title run** is a title there and speaks for nothing, and no credential behind it speaks for a word of this class in that part: the title reading makes the next word the given name, and from there the part reads as any given part does, the given part's own company included. A part holding no such word, or only words whose capitals decide them, reads as it did. Anywhere else such a word speaks like any suffix word. ``` "Smith, Ms Ma" → given="Ma" · boundary - "Smith, MD PhD Ma" → given="PhD" · boundary + "Smith, RA PhD Ma" → given="PhD" · boundary ``` **Precedence:** diff --git a/docs/release_log.rst b/docs/release_log.rst index 49ed494b..1b921aba 100644 --- a/docs/release_log.rst +++ b/docs/release_log.rst @@ -17,6 +17,7 @@ Release Log - **Fix case repair lowering a capital after a full stop or an ayn.** ``j.r.r. tolkien`` repairs to ``J.R.R. Tolkien`` and ``AHMAD IBN-ʿALĪ`` to ``Ahmad ibn-ʿAlī``, where 1.4.0 through 2.5.0 gave ``J.r.r.`` and ``ʿalī``. A joined word is written the same way (``st.john`` gives ``St.John``), so an unlisted abbreviation's ending is capitalized too: ``dr.philol.`` gives ``Dr.Philol.``. :issue:`838` - **Add the Italian abbreviated salutations such as Sig.ra and Dott.ssa as titles.** ``Dott.ssa Anna Bianchi`` gives title ``Dott.ssa``, first ``Anna``, where 1.4.0 through 2.5.0 gave first ``Dott.ssa``, middle ``Anna``, and ``SIG.RA MARIA ROSSI`` repairs to ``Sig.ra Maria Rossi``. A bare ``Sig`` or ``Ing`` before a name is still read as a first name. :issue:`840` - **Add Júnior as a suffix.** ``José Silva Júnior`` gives last ``Silva``, suffix ``Júnior``, as ``José Silva Junior`` does, where 1.4.0 through 2.5.0 gave middle ``Silva``, last ``Júnior``. ``Filho``, ``Neto`` and ``Sobrinho`` still read as the last name. :issue:`843` + - **Read a leading Md as the given name it abbreviates, Muhammad, rather than a title.** ``Mr Md Karim Uddin`` gives title ``Mr``, first ``Md``, middle ``Karim``, where 1.4.0 through 2.5.0 gave title ``Mr Md``, first ``Karim``; ``John Smith, MD Jones`` gives last ``Smith``, suffix ``MD Jones`` where it gave title ``MD``, last ``John Smith``. ``MD`` after a name is still the degree, and so is ``Md`` opening the part after a family comma: ``Uddin, Md Karim`` gives suffix ``Md Karim``. :issue:`853` * 2.5.0 - October 19, 2026 diff --git a/nameparser/_parser.py b/nameparser/_parser.py index 4b46981c..55a61b17 100644 --- a/nameparser/_parser.py +++ b/nameparser/_parser.py @@ -215,7 +215,7 @@ def revise(self, name: ParsedName, **fields: str) -> ParsedName: dropped = set(state.dropped) # Force the role BEFORE the entry pass: it keys on # Role.SUFFIX, and a bare value's sub-parse reads its words - # as a name ('MD PhD' is a title and a family there), so + # as a name ('RA PhD' is a title and a family there), so # inside the sub-parse it joined nothing. The sub-parse's # tags are KEPT, "joined" included: a within-piece mark # (the Ph. D. merge) is role-blind and right for every diff --git a/nameparser/_pipeline/_assign.py b/nameparser/_pipeline/_assign.py index c6f4b98a..354a8cb5 100644 --- a/nameparser/_pipeline/_assign.py +++ b/nameparser/_pipeline/_assign.py @@ -1461,8 +1461,8 @@ def reads_as_a_suffix(m: int, titled: frozenset[int]) -> bool: # Berg'), or where everything behind it is post-nominal, which # leaves no family word to address -- H3's concern, by a # stricter test than H3's one-word give-back, so this is not - # `leading_titles` ('MD PhD Jr., John' keeps family 'MD', where - # 'MD PhD Jr. John' reads title 'MD'). A dual is a title wherever + # `leading_titles` ('RA PhD Jr., John' keeps family 'RA', where + # 'RA PhD Jr. John' reads title 'RA'). A dual is a title wherever # a name word follows it ('Rev. Sr. Smith, Mary'). # `is_title_piece` is H5's vocabulary read, so the two slots that # refuse the shape agree on what a title word is. diff --git a/nameparser/_pipeline/_comma.py b/nameparser/_pipeline/_comma.py index fad2f853..a1fd6092 100644 --- a/nameparser/_pipeline/_comma.py +++ b/nameparser/_pipeline/_comma.py @@ -213,7 +213,7 @@ def _behind_a_generation(state: ParseState, seg0: Sequence[int], # a title reads as a title, a word or a run a connective joins # ('Mr. and Mrs.', a dual inside it included); a lone dual of # the title and the suffix vocabulary is the suffix here, the - # slot being postnominal ('John Smith Jr., AD MD'): C13's two + # slot being postnominal ('John Smith Jr., AD RA'): C13's two # predicates, asked in C13's order title = (not is_suffix_piece(p, pt, tokens) and is_title_piece(p, pt, tokens)) @@ -406,7 +406,7 @@ def decide(state: ParseState) -> ParseState: # it stands behind that word, so one scan answers every # pair. Asked per pair, the scan re-walked every word in # front of each one, quadratic in the part ('John Smith, ' + - # 'MD '*k + 'G.J. '*k, the duals never ending it; #563's + # 'RA '*k + 'G.J. '*k, the duals never ending it; #563's # shape, back in #613 and caught in review) speaker = -1 for k, p in enumerate(p1): diff --git a/nameparser/_pipeline/_pieces.py b/nameparser/_pipeline/_pieces.py index 389525a0..3a9d9944 100644 --- a/nameparser/_pipeline/_pieces.py +++ b/nameparser/_pipeline/_pieces.py @@ -418,7 +418,7 @@ def leading_titles(pieces: Sequence[Sequence[int]], # with no title at all. # # Two residuals. A run whose last word IS suffix vocabulary is not - # given back, so 'Dr King MD PhD' still reads title 'Dr King MD', + # given back, so 'Dr King RA PhD' still reads title 'Dr King RA', # family 'PhD'. And the floor asks is_suffix_piece, which vetoes a # bare initial-shaped numeral, so 'Dr King V' keeps the whole run # as the title and reads the numeral as the name -- given 'V', @@ -576,7 +576,7 @@ def _anchors(piece: Sequence[int], tokens: Sequence[WorkToken]) -> bool: retired single-character vocabulary matches for the same reason. Not because a numeral is no credential -- a multi-letter suffix word ('III', 'Jr') anchors like any other suffix piece - (`is_single_letter_numeral`). A title/suffix DUAL ('ms', 'md', + (`is_single_letter_numeral`). A title/suffix DUAL ('ms', 'ra', 'sr') does anchor, except in the given part's leading title run, where it stands in title position ('Smith, Ms Ma' is Ms. Ma Smith); that exclusion is the callers', which start @@ -617,7 +617,7 @@ def credential_anchors(order: Sequence[int], The caller decides where the walk starts, and starts it past the leading title run, where a title/suffix dual reads as a title - ('Smith, MD MA Ma' keeps its middle name). Where `order` holds the + ('Smith, RA MA Ma' keeps its middle name). Where `order` holds the name part, its own leading position is the name the reserve keeps -- `rest[0]` at the no-comma peel, the given part's own first piece at the given-slot call sites -- so ITS writing never anchors @@ -654,7 +654,7 @@ def given_slot_anchors(pieces: Sequence[Sequence[int]], rather than by position: the pass over `start` to `end`, `skip` spliced out, and False before `start`, from `end` on and at every skipped piece. `start` is where the leading title run ends, so a - title/suffix dual standing in it anchors nothing ('Smith, MD MA + title/suffix dual standing in it anchors nothing ('Smith, RA MA Ma'), and the piece at `start` is the given name the reserve keeps. The one home of that query for assign's given slot.""" stop = len(pieces) if end is None else end @@ -738,9 +738,9 @@ def segment_suffix_reading(pieces: Sequence[Sequence[int]], later credential: the dual makes the next name-position word the given name, and reading the part wholly as credentials would stack the anchor's guess on the dual's ('Smith, Ms Ma', 'Smith, - Ms MD Ma' and 'Smith, MD PhD Ma' keep a given name, 'Smith, - Prof. MD Ma' too). The walk then reads the part, and its given - slot's company starts past that given name ('Smith, MD PhD Jr + Ms RA Ma' and 'Smith, RA PhD Ma' keep a given name, 'Smith, + Prof. RA Ma' too). The walk then reads the part, and its given + slot's company starts past that given name ('Smith, RA PhD Jr Ma' reads suffix 'Jr Ma'). A plain title ahead of the credential does not stop this reading ('Smith, Dr. PhD LAc' reads title 'Dr.', suffix 'PhD LAc'). @@ -805,7 +805,7 @@ def segment_suffix_reading(pieces: Sequence[Sequence[int]], -- such a word being a suffix word written as an initial. Under strict the numeral gets no suffix reading behind a suffix word, so a part no credential opened is no run and the reading is None - ('Smith, MD I'); a part a credential opened (C13) still takes the + ('Smith, RA I'); a part a credential opened (C13) still takes the word in through `opened` below and hands it to `absorbed` ('Smith, PSM I.'), which implements rules/C14's Except bullet "A credential opening the part". The walk reads the token's `initial` @@ -842,7 +842,7 @@ def segment_suffix_reading(pieces: Sequence[Sequence[int]], # stood there: once one has, nothing in the part anchors. Tracked # here rather than read off `leading_titles`, whose period-shape # inference counts a suffix like 'Esq.' into the run ('Smith, Esq. - # MD Ma' keeps its anchored 'Ma' only by this walk's reading). + # RA Ma' keeps its anchored 'Ma' only by this walk's reading). leading = True dual_led = False # #603: the first suffix word of the leading run, which may open diff --git a/nameparser/config/_compiled.py b/nameparser/config/_compiled.py index 9a7b0f9d..5b65b0ca 100644 --- a/nameparser/config/_compiled.py +++ b/nameparser/config/_compiled.py @@ -490,7 +490,6 @@ 'mcpo', 'mcpoc', 'mcpon', - 'md', 'me', 'member', 'memoirist', diff --git a/nameparser/vocab/en.toml b/nameparser/vocab/en.toml index a6973c11..02b7dd4f 100644 --- a/nameparser/vocab/en.toml +++ b/nameparser/vocab/en.toml @@ -102,11 +102,6 @@ academic = { expands = "the address of a lawyer, 'Atty. Cruz'" } text = "engineer" academic = { expands = "the address of an engineer, 'Engr. Cruz' in the Philippines and Nigeria" } -[[word]] -text = "md" -academic = { note = "the medical degree written before the name (#32); a leading Md. abbreviating Muhammad lands in the same field (decisions/indic-honorifics)" } -credential = {} - [[word]] text = "nurse" academic = { expands = "the address of a nurse, 'Nurse Smith'" } @@ -4194,6 +4189,11 @@ credential = {} text = "mct" credential = {} +[[word]] +text = "md" +credential = {} +excluded = { academic = { reason = "a leading Md abbreviates Muhammad, a given name: 1,209 of 1,300 UK PSC filers with Md first in the forename box typed it there alone (2026-10-23), and a title reading took it from them", issue = 853 } } + [[word]] text = "mdiv" credential = { cased = "MDiv" } diff --git a/tests/v2/cases.py b/tests/v2/cases.py index c4c069c9..08e90a82 100644 --- a/tests/v2/cases.py +++ b/tests/v2/cases.py @@ -1874,22 +1874,22 @@ def _check_cjk_shape_purity(self) -> None: "given name. 1.4.0 and 2.3.0 read the same", shape=2), Case("a_dual_opening_the_given_part_anchors_nothing", - "Smith, MD MEng", - {"title": "MD", "given": "MEng", "family": "Smith"}, + "Smith, RA MEng", + {"title": "RA", "given": "MEng", "family": "Smith"}, notes="the same exclusion for the other chunked member. " - "1.4.0 read the same; 2.3.0 read suffix 'MD MEng', " + "1.4.0 read the same; 2.3.0 read suffix 'RA MEng', " "the reading #540's marking moved"), Case("a_dual_opening_the_given_part_turns_the_anchor_off", - "Smith, Ms MD Ma", - {"title": "Ms MD", "given": "Ma", "family": "Smith"}, + "Smith, Ms RA Ma", + {"title": "Ms RA", "given": "Ma", "family": "Smith"}, notes="a dual opening the given part is a title there, and a " "part whose leading title run holds one is anchored by " "nothing, the second dual standing in that same run. " "1.4.0 and 2.3.0 read the same", shape=2), Case("a_dual_opening_the_given_part_silences_a_later_degree", - "Smith, MD PhD Ma", - {"title": "MD", "given": "PhD", "middle": "Ma", + "Smith, RA PhD Ma", + {"title": "RA", "given": "PhD", "middle": "Ma", "family": "Smith"}, classification="fix(#296)", ambiguities=("suffix-or-name",), @@ -1899,7 +1899,7 @@ def _check_cjk_shape_purity(self) -> None: "so 'PhD' speaks for nothing here and 'Ma' ends the " "given part as a middle name, reported by the given " "slot. 2.2.0 and 2.3.0 read the same fields; 1.4.0 " - "through 2.1.0 title 'MD PhD', first 'Ma', 'phd' being " + "through 2.1.0 title 'RA PhD', first 'Ma', 'phd' being " "title vocabulary until #296", shape=2), Case("a_numeral_in_front_anchors_nothing", @@ -7671,8 +7671,8 @@ def _check_cjk_shape_purity(self) -> None: "#601; rules/C10's maiden-marker Accepted entry). 2.0.0 " "through 2.3.0 read maiden 'Jones', stepping past the core"), Case("suffix_delimiter_core_that_survives_is_a_boundary_too", - "Smith, MD - PhD - FACS", - {"title": "MD", "given": "-", "family": "Smith", + "Smith, RA - PhD - FACS", + {"title": "RA", "given": "-", "family": "Smith", "suffix": "PhD - FACS"}, policy=_SD, classification="fix(#602)", @@ -9113,15 +9113,16 @@ def _check_cjk_shape_purity(self) -> None: "piece is ONE WORD: a joined unit led by a title is a " "title run, and handing it back would turn a title into " "a given name and leave the name with no title at all"), - Case("title_run_floor_declines_an_all_suffix_input", "MD DDS", - {"title": "MD", "family": "DDS"}, classification="parity", + Case("title_run_floor_declines_an_all_suffix_input", "RA DDS", + {"title": "RA", "family": "DDS"}, classification="parity", notes="negative control: the word the run would give back is " - "`MD`, which IS suffix vocabulary, so the floor " - "declines. `md` is title vocabulary too, so the run is " - "`MD` and H1's fold claims `DDS` -- not the bare-suffix " + "`RA`, which IS suffix vocabulary, so the floor " + "declines. `ra` is title vocabulary too, so the run is " + "`RA` and H1's fold claims `DDS` -- not the bare-suffix " "carve-out, which reads the FIRST word as the name and " - "reports `suffix-or-name` (`DDS MD`). Nothing reports " - "here"), + "reports `suffix-or-name` (`DDS RA`). Nothing reports " + "here. Spelled with `MD` until #853 took md out of the " + "titles"), Case("title_run_floor_declines_a_split_credential", "Jr. Ph. D.", {"title": "Jr.", "suffix": "Ph. D."}, classification="parity", notes="negative control: `Jr.` is suffix vocabulary wearing " @@ -9933,26 +9934,63 @@ def _check_cjk_shape_purity(self) -> None: classification="fix(#296)", notes="same as audit_phd_leading_is_a_name: 'jr' is never " "prenominal in any tradition"), - Case("audit_md_leading_stays_a_title", "Md Abdul Karim", - {"title": "Md", "given": "Abdul", "family": "Karim"}, - notes="the one word the 2026-07-30 table got wrong: 'md' KEEPS " - "dual membership. Bare 'Md' before a name is the " - "Bengali and South Asian Muslim abbreviation of " - "Muhammad (#343/#345's corpus rows), a prenominal use " - "the 'postnominal only' disposition did not consider; " - "'MD' after the name is the degree. Position decides, " - "as for 'sr'. The shape-1 tag rides on that disposition " - "-- 'md' is the ONE deviation from the approved " - "2026-07-30 audit table (decisions/comma-suffix-arc), " - "so if the deviation is " - "ever reversed this row stops being a Title Given " - "Family arrangement and the tag has to move to another " - "row rather than the expectations being edited under it", - shape=1), + Case("audit_md_leading_is_the_given_name", "Md Abdul Karim", + {"given": "Md", "middle": "Abdul", "family": "Karim"}, + classification="fix(#853)", + notes="bare 'Md' before a name abbreviates Muhammad, a given " + "name, and reads as the spelled-out 'Mohammad Abdul " + "Karim' does. 1.4.0 through 2.5.0 " + "read title 'Md', given 'Abdul': 'md' was a title from " + "#32 (the medical degree written before the name) until " + "#853, which measured the forename box UK filers type " + "it into (decisions/indic-honorifics). 'MD' after the " + "name is still the degree", + shape=1), + Case("md_after_a_title_is_the_given_name", "Mr Md Karim Uddin", + {"title": "Mr", "given": "Md", "middle": "Karim", + "family": "Uddin"}, + classification="fix(#853)", + notes="the shape #853 was filed on: 'md' in a title run had " + "joined it, title 'Mr Md', given 'Karim'", + shape=1), + Case("md_before_a_lone_family_name_is_the_given_name", "Md Rahman", + {"given": "Md", "family": "Rahman"}, + classification="fix(#853)", + notes="the title reading left the given name empty here " + "(rules/H1's fold of a title and one name word)", + shape=1), + Case("md_before_a_particle_gives_it_to_the_family", "Md Abu Sayed", + {"given": "Md", "family": "Abu Sayed"}, + classification="fix(#853)", + notes="an accepted cost of #853: 'abu' behind a given name " + "is a particle starting the family, as in the spelled-" + "out 'Mohammad Abu Sayed', where the title reading had " + "left 'abu' the leading name piece and the bound join " + "read given 'Abu' (1.4.0 read family 'Abu Sayed' " + "too, behind title 'Md'). The filer's surname box holds " + "'Sayed' (17 of #853's 1,300 PSC names take this shape, " + "with 'Abu', 'Al' or 'Bin'); the gap is the spelled-out " + "name's, not Md's (decisions/indic-honorifics)", + shape=1), + Case("md_opening_a_family_comma_given_part_is_a_credential", + "Uddin, Md Karim", + {"family": "Uddin", "suffix": "Md Karim"}, + classification="fix(#853)", + ambiguities=("suffix-or-name",), + notes="an accepted cost of #853: a credential opening the " + "part after a family comma starts a credential run to " + "its end (rules/C13), and 'md' is a credential with no " + "title reading left to stand it down, so the given name " + "is lost. 9 FEC names take this shape, against about " + "380 ClinicalTrials.gov officials ('John Smith, MD " + "Jones' shapes) the title reading had misread " + "(decisions/indic-honorifics)", + shape=2), Case("audit_md_after_comma_is_the_degree", "Smith, MD", {"family": "Smith", "suffix": "MD"}, classification="fix(#296)", - notes="the third leg of 'position decides' for 'md'"), + notes="'md' after a comma is the degree, as it is at the " + "end of a name"), # -- #346: the renunciate class. A title of religious renunciation # addresses the GIVEN name AND belongs to a tradition that # abolishes the surname, so rules/H1's given-name-title fold is @@ -10334,16 +10372,17 @@ def _check_cjk_shape_purity(self) -> None: "there is no abbreviation, so the numeral is the " "generation it looks like. This row is what makes " "#432's fix a period test rather than a numeral test"), - Case("family_comma_title_resets_the_credential_run", "Smith, MD Dr. I", - {"title": "MD Dr.", "given": "I", "family": "Smith"}, + Case("family_comma_title_resets_the_credential_run", "Smith, RA Dr. I", + {"title": "RA Dr.", "given": "I", "family": "Smith"}, notes="THE RESET. A title ends the run: what follows a bare " "title is not continuing a credential, so the numeral " "behind it does not join and the segment is no run at " "all, leaving the walk to read the dual and the title as " "the given part's titles and 'I' as its given name. " - "Without the reset the numeral continues the 'MD' in " + "Without the reset the numeral continues the 'RA' in " "front of the title and the part reads title 'Dr.', " - "suffix 'MD I'. Spelled with the dual 'MD' since #603: " + "suffix 'RA I'. Spelled with a dual since #603, 'MD' " + "until #853 took md out of the titles and 'RA' since: " "'Smith, PSM Dr. I', this row until then, is a part the " "credential 'PSM' OPENS, which reads as #602's run reads " "whatever stands behind it (rules/C13) -- title 'Dr.', " diff --git a/tests/v2/pipeline/test_pieces.py b/tests/v2/pipeline/test_pieces.py index 1ea728f7..90cd8c80 100644 --- a/tests/v2/pipeline/test_pieces.py +++ b/tests/v2/pipeline/test_pieces.py @@ -133,7 +133,7 @@ def test_strict_ends_the_run_at_the_initial_shaped_numeral() -> None: the opened part takes it in all the same, and hands it to the caller to report. """ - pieces, ptags, tokens, one_case = _comma_part("Smith, MD I") + pieces, ptags, tokens, one_case = _comma_part("Smith, RA I") args = (pieces, ptags, tokens) assert segment_suffix_reading(*args, True, one_case) == (True, True) assert segment_suffix_reading(*args, False, one_case) is None @@ -176,21 +176,21 @@ def test_a_dual_in_the_leading_title_run_turns_the_anchor_off() -> None: as the parent did, its given slot's company starting past the given name. A plain title does not do this, and a dual behind a credential anchors like any suffix word.""" - for text in ("Smith, Ms Ma", "Smith, Ms MD Ma", "Smith, MD PhD Ma", - "Smith, MD MS Ma", "SMITH, MD MS BA", - "Smith, Prof. MD Ma", "smith, prof. md ma", - "Smith, Dr. MD PhD Ma"): + for text in ("Smith, Ms Ma", "Smith, Ms RA Ma", "Smith, RA PhD Ma", + "Smith, RA MS Ma", "SMITH, RA MS BA", + "Smith, Prof. RA Ma", "smith, prof. ra ma", + "Smith, Dr. RA PhD Ma"): assert _comma_part_reading(text)[0] is None, text assert _comma_part_reading("Smith, Dr. PhD LAc") == ( (False, True, True), [2]) assert _comma_part_reading("Smith, PhD Ms Ma") == ( (True, True, True), [2]) # with no member to anchor, the dual reads as the suffix it is - assert _comma_part_reading("Smith, MD PhD") == ((True, True), []) + assert _comma_part_reading("Smith, RA PhD") == ((True, True), []) # and the walk's given slot, past the given name, reads its own # company as behind any given name - assert parse("Smith, MD PhD Jr Ma").suffix == "Jr Ma" - assert parse("Smith, MD PhD Jr Ma").given == "PhD" + assert parse("Smith, RA PhD Jr Ma").suffix == "Jr Ma" + assert parse("Smith, RA PhD Jr Ma").given == "PhD" def _leading(text: str) -> int: @@ -223,7 +223,7 @@ def test_the_leading_run_declines_to_give_back_a_suffix_word() -> None: what leaves the whole-segment carve-out and the all-title inputs exactly as they were. """ - assert _leading("MD DDS") == 1 # 'MD' is suffix vocabulary + assert _leading("RA DDS") == 1 # 'RA' is suffix vocabulary assert _leading("Jr. Ph. D.") == 1 # so is 'Jr.' assert _leading("Marquess of Bath") == 1 # nothing behind the run assert _leading("Dr.") == 1 # the whole-segment carve-out @@ -578,10 +578,10 @@ def test_a_multi_letter_numeral_anchors_like_any_suffix_word() -> None: def test_the_anchor_pass_starts_past_the_leading_title_run() -> None: # a title/suffix dual opening the given part is a title there and # anchors nothing, at the given slot and in the part read whole - assert parse("Smith, MD MA Ma").middle == "Ma" + assert parse("Smith, RA MA Ma").middle == "Ma" assert parse("Smith, Ms Ma").given == "Ma" # past that position the same dual anchors - assert parse("John Smith MD MEng").suffix == "MD MEng" + assert parse("John Smith RA MEng").suffix == "RA MEng" def test_the_given_slot_asks_the_anchor_only_when_the_writing_declines( diff --git a/tests/v2/pipeline/test_post_rules.py b/tests/v2/pipeline/test_post_rules.py index 80a6a420..c35da874 100644 --- a/tests/v2/pipeline/test_post_rules.py +++ b/tests/v2/pipeline/test_post_rules.py @@ -24,7 +24,7 @@ # absent while shipping as titles, and `la` sat in the never-given half # while shipping as ambiguous. _LEX = Lexicon( - titles=frozenset({"mr", "sir", "dr", "md"}), + titles=frozenset({"mr", "sir", "dr"}), given_name_titles=frozenset({"sir"}), particles=frozenset({"de", "der", "ibn", "la", "van", "vd"}), particles_ambiguous=frozenset({"la", "van"}), diff --git a/tests/v2/test_benchmark.py b/tests/v2/test_benchmark.py index 313f3c8c..9317fd52 100644 --- a/tests/v2/test_benchmark.py +++ b/tests/v2/test_benchmark.py @@ -800,7 +800,9 @@ def test_a_clause_link_run_does_not_cost_quadratically() -> None: # Measured 2026-10-06 on py3.11 through `_frames_for(..., # only="is_suffix_piece")`, k duals and k pairs: 44 at k=8 and 164 at # k=32 on this tree (3.7x), against 120 and 1,620 at 47fb2b21 (13.5x), -# where every pair rescanned. A change to what the scan calls must +# where every pair rescanned. The dual was 'MD' until #853 took md out +# of the titles; re-taken with 'RA' on 2026-10-23, 35 and 131 on the +# tree (3.7x) and 120 and 1,620 at 47fb2b21 again. A change to what the scan calls must # move `only=` with it, and re-take that negative control. _PAIR_SCAN_SMALL = 8 _PAIR_SCAN_LARGE = 32 @@ -810,12 +812,12 @@ def test_a_clause_link_run_does_not_cost_quadratically() -> None: def test_the_paired_initials_title_scan_does_not_cost_quadratically() -> None: if sys.getprofile() is not None: pytest.skip("a profile hook is already installed; this test owns it") - small_text = "John Smith, " + "MD " * _PAIR_SCAN_SMALL + "G.J. " * _PAIR_SCAN_SMALL - large_text = "John Smith, " + "MD " * _PAIR_SCAN_LARGE + "G.J. " * _PAIR_SCAN_LARGE + small_text = "John Smith, " + "RA " * _PAIR_SCAN_SMALL + "G.J. " * _PAIR_SCAN_SMALL + large_text = "John Smith, " + "RA " * _PAIR_SCAN_LARGE + "G.J. " * _PAIR_SCAN_LARGE # REACHABILITY: the flip REPORTS only where the scan ran and found # nothing but titles in front of the first pair, every shape word # being a pair (rules/C11's "nothing but other paired initials"). - # A change that stopped the scan being asked -- 'MD' leaving the + # A change that stopped the scan being asked -- 'RA' leaving the # titles, the pair test moving -- loses the report and fails here # rather than leaving this guard measuring nothing. for text, k in ((small_text, _PAIR_SCAN_SMALL), diff --git a/tests/v2/test_cases.py b/tests/v2/test_cases.py index cd57e53f..05c05b77 100644 --- a/tests/v2/test_cases.py +++ b/tests/v2/test_cases.py @@ -216,9 +216,11 @@ def test_the_field_sweep_sees_the_claims_it_checks() -> None: # on 2026-10-23: #848's four Ing rows, one suffix-or-name claim # each under the three orders; 1353 -> 1356 the same day: the # review's 'Novak, Ing.' row; 1356 -> 1359 the same day: the second - # review's 'Novak, ING.' row. - assert claims == 1359, ( - f"the field sweep checks {claims} claims, recorded as 1359 on " + # review's 'Novak, ING.' row; 1359 -> 1362 the same day: #853's + # 'Uddin, Md Karim' row, one suffix-or-name claim under the three + # orders. + assert claims == 1362, ( + f"the field sweep checks {claims} claims, recorded as 1362 on " f"2026-10-23") diff --git a/tests/v2/test_locales.py b/tests/v2/test_locales.py index da4a00d7..b48d9cd6 100644 --- a/tests/v2/test_locales.py +++ b/tests/v2/test_locales.py @@ -1310,7 +1310,9 @@ def test_non_interference_all_packs_combined() -> None: ("র. কে. নারায়ণ", "given", "র."), ("র. কে. নারায়ণ", "middle", "কে."), ("র. কে. নারায়ণ", "family", "নারায়ণ"), - # The mirror of cases.py's audit_md_leading_stays_a_title. Both + # These mirrored Latin md until #853 made a leading Md the given + # name (cases.py's audit_md_leading_is_the_given_name); the Bengali + # spellings stay titles, no filed data reaching them. Both # spellings of the abbreviation ship: the visarga form মোঃ (the visarga ঃ (U+0983) is a spacing # combining mark, and the lookup fold strips only edge periods and # whitespace, so it reaches the lexicon intact) and the period diff --git a/tests/v2/test_name_usage_tool.py b/tests/v2/test_name_usage_tool.py index ca38172e..4d46f936 100644 --- a/tests/v2/test_name_usage_tool.py +++ b/tests/v2/test_name_usage_tool.py @@ -1985,16 +1985,16 @@ def test_main_exits_2_on_an_unusable_tally_without_losing_the_other_sources( def test_main_rank_lists_credentials_with_vocabulary_and_readings( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: - _cache_tally(monkeypatch, tmp_path, _tally(MD=50, **{"QRS-C": 40}, ESQ=3)) + _cache_tally(monkeypatch, tmp_path, _tally(RA=50, **{"QRS-C": 40}, ESQ=3)) assert tool.main(["--no-wikidata", "--json", "--rank", "5"]) == 0 rows = {r["credential"]: r for r in json.loads(capsys.readouterr().out)["rows"]} assert [r["rank"] for r in rows.values()] == [1, 2, 3] - assert rows["MD"]["vocabulary"] == "acronym, title" - assert (rows["MD"]["after_comma"], rows["MD"]["bare"]) == ("suffix", "suffix") + assert rows["RA"]["vocabulary"] == "acronym, title" + assert (rows["RA"]["after_comma"], rows["RA"]["bare"]) == ("suffix", "suffix") assert rows["QRS-C"]["vocabulary"] == "-" assert (rows["QRS-C"]["after_comma"], rows["QRS-C"]["bare"]) == ("given", "family") assert rows["ESQ"]["vocabulary"] == "suffix word" - assert rows["MD"]["census"] == "not downloaded" + assert rows["RA"]["census"] == "not downloaded" def test_main_rank_unlisted_keeps_each_credentials_overall_rank( diff --git a/tools/differential/cldr_agreement.json b/tools/differential/cldr_agreement.json index 7e82947a..20674499 100644 --- a/tools/differential/cldr_agreement.json +++ b/tools/differential/cldr_agreement.json @@ -9122,26 +9122,22 @@ "ro/foreignFull/givenFirst": { "given_names": { "expected": "Eva-Eleonora Karl Hans", - "got": "Chirurgie dentară Eva-Eleonora Karl Hans" + "got": "MD Chirurgie dentară Eva-Eleonora Karl Hans" }, "suffix": { "expected": "MD Chirurgie dentară Jr.", "got": "Jr." }, - "text": "Prof. dr. MD Chirurgie dentară Eva-Eleonora Karl Hans von der Ruhr Jr.", - "title": { - "expected": "Prof. dr.", - "got": "Prof. dr. MD" - } + "text": "Prof. dr. MD Chirurgie dentară Eva-Eleonora Karl Hans von der Ruhr Jr." }, "ro/foreignFull/surnameFirst": { "family": { "expected": "von der Ruhr", - "got": "Chirurgie" + "got": "MD" }, "family_base": { "expected": "Ruhr", - "got": "Chirurgie" + "got": "MD" }, "family_particles": { "expected": "von der", @@ -9149,7 +9145,7 @@ }, "given_names": { "expected": "Eva-Eleonora Karl Hans", - "got": "dentară von der Ruhr Prof. dr. Eva-Eleonora Karl Hans" + "got": "Chirurgie dentară von der Ruhr Prof. dr. Eva-Eleonora Karl Hans" }, "suffix": { "expected": "MD Chirurgie dentară Jr.", @@ -9158,7 +9154,7 @@ "text": "MD Chirurgie dentară von der Ruhr Prof. dr. Eva-Eleonora Karl Hans Jr.", "title": { "expected": "Prof. dr.", - "got": "MD" + "got": "" } }, "ro/foreignG/sorting": { @@ -9343,11 +9339,11 @@ "sc/foreignFull/surnameFirst": { "family": { "expected": "von Brühl González Domingo", - "got": "DDS" + "got": "MD" }, "family_base": { "expected": "Brühl González Domingo", - "got": "DDS" + "got": "MD" }, "family_particles": { "expected": "von", @@ -9355,7 +9351,7 @@ }, "given_names": { "expected": "Maria Giusepa Juana Teresa", - "got": "Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa" + "got": "DDS Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa" }, "suffix": { "expected": "MD DDS", @@ -9364,7 +9360,7 @@ "text": "MD DDS Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa", "title": { "expected": "Dut.ra Prof.ra", - "got": "MD" + "got": "" } }, "sc/foreignG/sorting": { @@ -11797,17 +11793,13 @@ "tr/foreignFull/givenFirst": { "given_names": { "expected": "Ada Cornelia César Martín", - "got": "DDS Ada Cornelia César Martín" + "got": "MD DDS Ada Cornelia César Martín" }, "suffix": { "expected": "MD DDS Jr", "got": "Jr" }, - "text": "Prof. Dr. MD DDS Ada Cornelia César Martín von Brühl Jr", - "title": { - "expected": "Prof. Dr.", - "got": "Prof. Dr. MD" - } + "text": "Prof. Dr. MD DDS Ada Cornelia César Martín von Brühl Jr" }, "tr/foreignFull/surnameFirst": { "family": { @@ -12368,11 +12360,11 @@ "vi/foreignFull/surnameFirst": { "family": { "expected": "von Brühl", - "got": "DDS" + "got": "MD" }, "family_base": { "expected": "Brühl", - "got": "DDS" + "got": "MD" }, "family_particles": { "expected": "von", @@ -12380,17 +12372,13 @@ }, "given_names": { "expected": "César Martín Ada Cornelia", - "got": "von Brühl César Martín Ada Cornelia" + "got": "DDS von Brühl César Martín Ada Cornelia" }, "suffix": { "expected": "MD DDS", "got": "" }, - "text": "MD DDS von Brühl César Martín Ada Cornelia", - "title": { - "expected": "", - "got": "MD" - } + "text": "MD DDS von Brühl César Martín Ada Cornelia" }, "vi/foreignG/sorting": { "family": { diff --git a/tools/differential/cldr_agreement_locales.json b/tools/differential/cldr_agreement_locales.json index 4882ae5a..0aa1e23c 100644 --- a/tools/differential/cldr_agreement_locales.json +++ b/tools/differential/cldr_agreement_locales.json @@ -399,17 +399,13 @@ "tr/foreignFull/givenFirst": { "given_names": { "expected": "Ada Cornelia César Martín", - "got": "DDS Ada Cornelia César Martín" + "got": "MD DDS Ada Cornelia César Martín" }, "suffix": { "expected": "MD DDS Jr", "got": "Jr" }, - "text": "Prof. Dr. MD DDS Ada Cornelia César Martín von Brühl Jr", - "title": { - "expected": "Prof. Dr.", - "got": "Prof. Dr. MD" - } + "text": "Prof. Dr. MD DDS Ada Cornelia César Martín von Brühl Jr" }, "tr/foreignFull/surnameFirst": { "family": { diff --git a/tools/differential/corpus_rules.jsonl b/tools/differential/corpus_rules.jsonl index 83187528..7bb3f3d1 100644 --- a/tools/differential/corpus_rules.jsonl +++ b/tools/differential/corpus_rules.jsonl @@ -308,7 +308,6 @@ "John Smith, Jr., van der Berg Prof." "John Smith, LEED AP" "John Smith, MA" -"John Smith, MD Jones" "John Smith, MD, Bart" "John Smith, MD, Ma" "John Smith, MD, Ms" @@ -327,6 +326,7 @@ "John Smith, PhD X.Y." "John Smith, PhD XYZ" "John Smith, PhD vd DO" +"John Smith, RA Jones" "John Smith, V." "John Smith, X.Y. P.Q." "John Smith, X.Y.Z." @@ -392,7 +392,6 @@ "Lloret Palol II, JA" "Lloret Palol II, Josep Antoni" "Lord Chancellor" -"MD DDS" "MD PhD Jr., John" "MSc Dr. med. univ." "Mai Le née Nguyen" @@ -444,6 +443,7 @@ "Prof. Smith, John, PhD" "Pérez, Juan Ó." "QC MP" +"RA DDS" "Rashid, Harun al-" "Rev. John Smith" "Rev. Sr. Smith, Mary" @@ -516,7 +516,6 @@ "Smith, Jr." "Smith, MA" "Smith, MD PhD" -"Smith, MD PhD Ma" "Smith, Ma" "Smith, Ma. John" "Smith, Major. John" @@ -530,6 +529,7 @@ "Smith, PhD" "Smith, PhD MEng" "Smith, PhD Ma" +"Smith, RA PhD Ma" "Smith, RN - CRNA" "Smith, Sir" "Smith, Sr." diff --git a/tools/differential/corpus_shapes.jsonl b/tools/differential/corpus_shapes.jsonl index d1419ebc..18f14540 100644 --- a/tools/differential/corpus_shapes.jsonl +++ b/tools/differential/corpus_shapes.jsonl @@ -103,9 +103,12 @@ {"name": "Luo Si", "shape": 1} {"name": "Maria Kowalska z domu Nowak MA", "shape": 1} {"name": "Md Abdul Karim", "shape": 1} +{"name": "Md Abu Sayed", "shape": 1} +{"name": "Md Rahman", "shape": 1} {"name": "Min Chi", "shape": 1} {"name": "Minjun KIM", "shape": 1} {"name": "Mr MA", "shape": 1} +{"name": "Mr Md Karim Uddin", "shape": 1} {"name": "Nguyen Van Lac", "shape": 1} {"name": "Sir Bob Andrew Dole", "shape": 1} {"name": "Wang M.Eng.", "shape": 1} @@ -219,14 +222,15 @@ {"name": "Smith, John V, Jr.", "shape": 2} {"name": "Smith, John, Extra, Jr.", "shape": 2} {"name": "Smith, MA", "shape": 2} -{"name": "Smith, MD PhD Ma", "shape": 2} {"name": "Smith, MEng", "shape": 2} {"name": "Smith, Ma", "shape": 2} -{"name": "Smith, Ms MD Ma", "shape": 2} {"name": "Smith, Ms Ma", "shape": 2} +{"name": "Smith, Ms RA Ma", "shape": 2} {"name": "Smith, PhD MEng", "shape": 2} {"name": "Smith, PhD Ma", "shape": 2} +{"name": "Smith, RA PhD Ma", "shape": 2} {"name": "Smith, meng", "shape": 2} +{"name": "Uddin, Md Karim", "shape": 2} {"name": "Van Buren, Ed", "shape": 2} {"name": "Wang, Si", "shape": 2} {"name": "de la Cruz, Ma", "shape": 2} diff --git a/tools/differential/snapshot.jsonl b/tools/differential/snapshot.jsonl index bb378e58..8d8c4cd7 100644 --- a/tools/differential/snapshot.jsonl +++ b/tools/differential/snapshot.jsonl @@ -1504,8 +1504,8 @@ {"name": "John Doe, CLU, CFP, LUTC", "given": "John", "family": "Doe", "suffix": "CLU, CFP, LUTC", "ambiguities": ["COMMA_STRUCTURE"], "initials": "J. D.", "capitalized_force": "John Doe CLU, CFP, Lutc"} {"name": "John Doe, Jr.", "given": "John", "family": "Doe", "suffix": "Jr.", "initials": "J. D."} {"name": "John Doe, Jr.,,", "given": "John", "family": "Doe", "suffix": "Jr.", "initials": "J. D."} -{"name": "John Doe, MD - PhD - FACS", "title": "MD", "given": "-", "family": "John Doe", "suffix": "PhD - FACS", "initials": "-. J. D.", "capitalized_force": "Md - John Doe PhD - FACS"} -{"name": "John Doe, MD - PhD, FACS", "title": "MD", "given": "-", "family": "John Doe", "suffix": "PhD, FACS", "initials": "-. J. D.", "capitalized_force": "Md - John Doe PhD, FACS"} +{"name": "John Doe, MD - PhD - FACS", "given": "John", "family": "Doe", "suffix": "MD - PhD - FACS", "initials": "J. D."} +{"name": "John Doe, MD - PhD, FACS", "given": "John", "family": "Doe", "suffix": "MD - PhD, FACS", "initials": "J. D."} {"name": "John Doe, MD, PhD", "given": "John", "family": "Doe", "suffix": "MD, PhD", "initials": "J. D."} {"name": "John Doe, MD-PhD-", "given": "MD-PhD-", "family": "John Doe", "initials": "M. J. D.", "capitalized_force": "Md-PhD- John Doe"} {"name": "John Doe, Msc.Ed.", "given": "John", "family": "Doe", "suffix": "Msc.Ed.", "initials": "J. D."} @@ -1629,7 +1629,6 @@ {"name": "John Smith, Jr., van der Berg Prof.", "title": "Prof.", "given": "John", "family": "Smith", "suffix": "Jr., van der Berg", "ambiguities": ["COMMA_STRUCTURE"], "initials": "J. S.", "capitalized_force": "Prof. John Smith Jr., Van Der Berg"} {"name": "John Smith, LEED AP", "given": "John", "family": "Smith", "suffix": "LEED AP", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} {"name": "John Smith, MA", "given": "John", "family": "Smith", "suffix": "MA", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} -{"name": "John Smith, MD Jones", "title": "MD", "given": "Jones", "family": "John Smith", "initials": "J. J. S.", "capitalized_force": "Md Jones John Smith"} {"name": "John Smith, MD MEng", "given": "John", "family": "Smith", "suffix": "MD MEng", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S.", "capitalized_force": "John Smith MD MENG"} {"name": "John Smith, MD, Bart", "given": "John", "family": "Smith", "suffix": "MD, Bart", "ambiguities": ["COMMA_STRUCTURE"], "initials": "J. S."} {"name": "John Smith, MD, Ma", "given": "John", "family": "Smith", "suffix": "MD, Ma", "ambiguities": ["COMMA_STRUCTURE"], "initials": "J. S.", "capitalized_force": "John Smith MD, MA"} @@ -1654,6 +1653,7 @@ {"name": "John Smith, PhD X.Y.", "given": "John", "family": "Smith", "suffix": "PhD X.Y.", "initials": "J. S."} {"name": "John Smith, PhD XYZ", "given": "John", "family": "Smith", "suffix": "PhD XYZ", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} {"name": "John Smith, PhD vd DO", "given": "John", "family": "Smith", "suffix": "PhD vd DO", "initials": "J. S.", "capitalized_force": "John Smith PhD VD DO"} +{"name": "John Smith, RA Jones", "title": "RA", "given": "Jones", "family": "John Smith", "initials": "J. J. S.", "capitalized_force": "Ra Jones John Smith"} {"name": "John Smith, RAI", "given": "John", "family": "Smith", "suffix": "RAI", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} {"name": "John Smith, V Jr.", "given": "John", "family": "Smith", "suffix": "V Jr.", "initials": "J. S."} {"name": "John Smith, V MD", "given": "John", "family": "Smith", "suffix": "V MD", "initials": "J. S."} @@ -2105,11 +2105,11 @@ {"name": "M. Jean-Nicolas Louis Marcel de Bouchart fils", "given": "M.", "middle": "Jean-Nicolas Louis Marcel", "family": "de Bouchart fils", "initials": "M. J. L. M. B. f.", "capitalized_force": "M. Jean-Nicolas Louis Marcel de Bouchart Fils"} {"name": "M. de Bouchart Jean-Nicolas Louis Marcel fils", "order": "FAMILY_FIRST", "given": "de Bouchart Jean-Nicolas Louis Marcel fils", "family": "M.", "initials": "d. B. J. L. M. f. M.", "capitalized_force": "De Bouchart Jean-Nicolas Louis Marcel Fils M."} {"name": "MANƉELA Rolanɖ Alfɔns", "order": "FAMILY_FIRST", "given": "Rolanɖ", "middle": "Alfɔns", "family": "MANƉELA", "initials": "R. A. M.", "capitalized_force": "Rolanɖ Alfɔns Manɖela"} -{"name": "MD - DO - DDS", "title": "MD", "given": "-", "family": "DO -", "suffix": "DDS", "initials": "-. -.", "capitalized": "Md - do - DDS"} -{"name": "MD Chirurgie dentară von der Ruhr Prof. dr. Eva-Eleonora Karl Hans Jr.", "order": "FAMILY_FIRST", "title": "MD", "given": "dentară", "middle": "von der Ruhr Prof. dr. Eva-Eleonora Karl Hans", "family": "Chirurgie", "suffix": "Jr.", "initials": "d. R. P. d. E. K. H. C.", "capitalized_force": "Md Dentară von der Ruhr Prof. Dr. Eva-Eleonora Karl Hans Chirurgie Jr."} -{"name": "MD DDS", "title": "MD", "family": "DDS", "initials": "D.", "capitalized": "Md Dds"} -{"name": "MD DDS Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa", "order": "FAMILY_FIRST", "title": "MD", "given": "Dut.ra", "middle": "Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa", "family": "DDS", "initials": "D. P. B. G. D. M. G. J. T. D.", "capitalized_force": "Md Dut.Ra Prof.Ra von Brühl González Domingo Maria Giusepa Juana Teresa Dds"} -{"name": "MD DDS von Brühl César Martín Ada Cornelia", "order": "FAMILY_FIRST", "title": "MD", "given": "von Brühl César Martín Ada Cornelia", "family": "DDS", "initials": "v. B. C. M. A. C. D.", "capitalized_force": "Md Von Brühl César Martín Ada Cornelia Dds"} +{"name": "MD - DO - DDS", "given": "MD", "middle": "-", "family": "DO -", "suffix": "DDS", "initials": "M. -. -.", "capitalized": "Md - do - DDS"} +{"name": "MD Chirurgie dentară von der Ruhr Prof. dr. Eva-Eleonora Karl Hans Jr.", "order": "FAMILY_FIRST", "given": "Chirurgie", "middle": "dentară von der Ruhr Prof. dr. Eva-Eleonora Karl Hans", "family": "MD", "suffix": "Jr.", "initials": "C. d. R. P. d. E. K. H. M.", "capitalized_force": "Chirurgie Dentară von der Ruhr Prof. Dr. Eva-Eleonora Karl Hans Md Jr."} +{"name": "MD DDS", "given": "MD", "suffix": "DDS", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "M.", "capitalized": "Md DDS"} +{"name": "MD DDS Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa", "order": "FAMILY_FIRST", "given": "DDS", "middle": "Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa", "family": "MD", "initials": "D. D. P. B. G. D. M. G. J. T. M.", "capitalized_force": "Dds Dut.Ra Prof.Ra von Brühl González Domingo Maria Giusepa Juana Teresa Md"} +{"name": "MD DDS von Brühl César Martín Ada Cornelia", "order": "FAMILY_FIRST", "given": "DDS", "middle": "von Brühl César Martín Ada Cornelia", "family": "MD", "initials": "D. B. C. M. A. C. M.", "capitalized_force": "Dds von Brühl César Martín Ada Cornelia Md"} {"name": "MD PhD Jr., John", "given": "John", "family": "MD", "suffix": "PhD Jr.", "initials": "J. M.", "capitalized_force": "John Md PhD Jr."} {"name": "MD, DO, DDS", "given": "DO", "family": "MD", "suffix": "DDS", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "D. M.", "capitalized": "Do Md DDS"} {"name": "MD, PHD", "family": "MD", "suffix": "PHD", "initials": "M.", "capitalized": "Md PhD"} @@ -2266,7 +2266,9 @@ {"name": "Mañé i Miravet, Teresa", "given": "Teresa", "family": "Mañé i Miravet", "initials": "T. M. M."} {"name": "Maǧrīṭī, Abu-'l-Qāsim Maslama Ibn-Aḥmad al-", "given": "Abu-'l-Qāsim", "middle": "Maslama Ibn-Aḥmad", "family": "al- Maǧrīṭī", "initials": "A. M. I. M.", "capitalized_force": "Abu-'L-Qāsim Maslama ibn-Aḥmad al- Maǧrīṭī"} {"name": "Mc Donald", "family": "Mc Donald", "initials": "D.", "capitalized_force": "mc Donald"} -{"name": "Md Abdul Karim", "title": "Md", "given": "Abdul", "family": "Karim", "initials": "A. K."} +{"name": "Md Abdul Karim", "given": "Md", "middle": "Abdul", "family": "Karim", "initials": "M. A. K."} +{"name": "Md Abu Sayed", "given": "Md", "family": "Abu Sayed", "initials": "M. S.", "capitalized_force": "Md abu Sayed"} +{"name": "Md Rahman", "given": "Md", "family": "Rahman", "initials": "M. R."} {"name": "Md,, Phd", "family": "Md", "suffix": "Phd", "initials": "M.", "capitalized_force": "Md PhD"} {"name": "Md. Abdul Karim", "title": "Md.", "given": "Abdul", "family": "Karim", "initials": "A. K."} {"name": "Medici, Clarice de'", "given": "Clarice", "family": "de' Medici", "initials": "C. M."} @@ -2349,6 +2351,7 @@ {"name": "Mozart Wolfgang Amadeus", "order": "FAMILY_FIRST", "given": "Wolfgang", "middle": "Amadeus", "family": "Mozart", "initials": "W. A. M."} {"name": "Mozart, Wolfgang Amadeus", "given": "Wolfgang", "middle": "Amadeus", "family": "Mozart", "initials": "W. A. M."} {"name": "Mr MA", "title": "Mr", "family": "MA", "initials": "M.", "capitalized_force": "Mr Ma"} +{"name": "Mr Md Karim Uddin", "title": "Mr", "given": "Md", "middle": "Karim", "family": "Uddin", "initials": "M. K. U."} {"name": "Mr XXX", "title": "Mr", "family": "XXX", "initials": "X.", "capitalized_force": "Mr Xxx"} {"name": "Mr. & Mrs. John Smith", "title": "Mr. & Mrs.", "given": "John", "family": "Smith", "initials": "J. S."} {"name": "Mr. Bertram Wilberforce Henry Robert Wooster Jr, MP", "title": "Mr.", "given": "Bertram", "middle": "Wilberforce Henry Robert", "family": "Wooster", "suffix": "Jr, MP", "initials": "B. W. H. R. W."} @@ -2587,7 +2590,7 @@ {"name": "Prof. Dr. Adama Coulibaly Camara Massamba won Baar Dominique Goudiaby MD DDS", "title": "Prof. Dr.", "given": "Adama", "middle": "Coulibaly Camara Massamba won Baar Dominique", "family": "Goudiaby", "suffix": "MD DDS", "initials": "A. C. C. M. w. B. D. G.", "capitalized_force": "Prof. Dr. Adama Coulibaly Camara Massamba Won Baar Dominique Goudiaby MD DDS"} {"name": "Prof. Dr. Anna Cornelia Eva Sophia van den Wolf jr. M.D. Ph.D.", "title": "Prof. Dr.", "given": "Anna", "middle": "Cornelia Eva Sophia", "family": "van den Wolf", "suffix": "jr. M.D. Ph.D.", "initials": "A. C. E. S. W.", "capitalized_force": "Prof. Dr. Anna Cornelia Eva Sophia van den Wolf Jr. M.D. Ph.D."} {"name": "Prof. Dr. Brühl, Ada", "title": "Prof. Dr.", "given": "Ada", "family": "Brühl", "initials": "A. B."} -{"name": "Prof. Dr. MD DDS Ada Cornelia César Martín von Brühl Jr", "title": "Prof. Dr. MD", "given": "DDS", "middle": "Ada Cornelia César Martín", "family": "von Brühl", "suffix": "Jr", "initials": "D. A. C. C. M. B.", "capitalized_force": "Prof. Dr. Md Dds Ada Cornelia César Martín von Brühl Jr"} +{"name": "Prof. Dr. MD DDS Ada Cornelia César Martín von Brühl Jr", "title": "Prof. Dr.", "given": "MD", "middle": "DDS Ada Cornelia César Martín", "family": "von Brühl", "suffix": "Jr", "initials": "M. D. A. C. C. M. B.", "capitalized_force": "Prof. Dr. Md Dds Ada Cornelia César Martín von Brühl Jr"} {"name": "Prof. Dr. Mary Lou Cesare Martín von Brühl Jr., Dr. Ph.D", "title": "Prof. Dr. Dr.", "given": "Mary", "middle": "Lou Cesare Martín", "family": "von Brühl", "suffix": "Jr., Ph.D", "initials": "M. L. C. M. B."} {"name": "Prof. Dr. Mary Sue Marie von Miller II, MD DDS", "title": "Prof. Dr.", "given": "Mary", "middle": "Sue Marie", "family": "von Miller", "suffix": "II, MD DDS", "initials": "M. S. M. M."} {"name": "Prof. Dr. María Florencia Martina Cristina von Brühl Jr., MD DDS", "title": "Prof. Dr.", "given": "María", "middle": "Florencia Martina Cristina", "family": "von Brühl", "suffix": "Jr., MD DDS", "initials": "M. F. M. C. B."} @@ -2599,7 +2602,7 @@ {"name": "Prof. Smith, Dr. John", "title": "Prof. Dr.", "given": "John", "family": "Smith", "initials": "J. S."} {"name": "Prof. Smith, John, PhD", "title": "Prof.", "given": "John", "family": "Smith", "suffix": "PhD", "initials": "J. S."} {"name": "Prof. dr. Cornelia Margarita Sofia Aletta van den Berg jnr., MBChB PhD", "title": "Prof. dr.", "given": "Cornelia", "middle": "Margarita Sofia Aletta", "family": "van den Berg", "suffix": "jnr., MBChB PhD", "initials": "C. M. S. A. B.", "capitalized_force": "Prof. Dr. Cornelia Margarita Sofia Aletta van den Berg Jnr., MBChB PhD"} -{"name": "Prof. dr. MD Chirurgie dentară Eva-Eleonora Karl Hans von der Ruhr Jr.", "title": "Prof. dr. MD", "given": "Chirurgie", "middle": "dentară Eva-Eleonora Karl Hans", "family": "von der Ruhr", "suffix": "Jr.", "initials": "C. d. E. K. H. R.", "capitalized_force": "Prof. Dr. Md Chirurgie Dentară Eva-Eleonora Karl Hans von der Ruhr Jr."} +{"name": "Prof. dr. MD Chirurgie dentară Eva-Eleonora Karl Hans von der Ruhr Jr.", "title": "Prof. dr.", "given": "MD", "middle": "Chirurgie dentară Eva-Eleonora Karl Hans", "family": "von der Ruhr", "suffix": "Jr.", "initials": "M. C. d. E. K. H. R.", "capitalized_force": "Prof. Dr. Md Chirurgie Dentară Eva-Eleonora Karl Hans von der Ruhr Jr."} {"name": "Prof. Ɖr. MƉ Samʊwɛl fan ƉAM Fostɛɛŋ", "title": "Prof. Ɖr.", "given": "MƉ", "middle": "Samʊwɛl fan ƉAM", "family": "Fostɛɛŋ", "initials": "M. S. f. Ɖ. F.", "capitalized_force": "Prof. Ɖr. Mɖ Samʊwɛl Fan Ɖam Fostɛɛŋ"} {"name": "Prof. Ɖr. MƉ fan ƉAM ƉOMIŊGO Fostɛɛŋ Samʊwɛl", "order": "FAMILY_FIRST", "title": "Prof. Ɖr.", "given": "fan", "middle": "ƉAM ƉOMIŊGO Fostɛɛŋ Samʊwɛl", "family": "MƉ", "initials": "f. Ɖ. Ɖ. F. S. M.", "capitalized_force": "Prof. Ɖr. Fan Ɖam Ɖomiŋgo Fostɛɛŋ Samʊwɛl Mɖ"} {"name": "Prop. Dr. Ada Cornelia César Martín von Brühl González Domingo MD DDS", "title": "Prop. Dr.", "given": "Ada", "middle": "Cornelia César Martín", "family": "von Brühl González Domingo", "suffix": "MD DDS", "initials": "A. C. C. M. B. G. D."} @@ -2622,6 +2625,7 @@ {"name": "Quertenmont, Andreas Bernardus de", "given": "Andreas", "middle": "Bernardus", "family": "de Quertenmont", "initials": "A. B. Q."} {"name": "Quincy Adams", "given": "Quincy", "family": "Adams", "initials": "Q. A."} {"name": "Quinquela Martín, Benito", "given": "Benito", "family": "Quinquela Martín", "initials": "B. Q. M."} +{"name": "RA DDS", "title": "RA", "family": "DDS", "initials": "D.", "capitalized": "Ra Dds"} {"name": "RONALD MACDONALD", "given": "RONALD", "family": "MACDONALD", "initials": "R. M.", "capitalized": "Ronald MacDonald"} {"name": "RONALD MCDONALD", "given": "RONALD", "family": "MCDONALD", "initials": "R. M.", "capitalized": "Ronald McDonald"} {"name": "Rabbi Cohen", "title": "Rabbi", "family": "Cohen", "initials": "C."} @@ -2937,16 +2941,15 @@ {"name": "Smith, Jr., PhD", "family": "Smith", "suffix": "Jr., PhD", "initials": "S."} {"name": "Smith, LEED AP", "given": "LEED", "middle": "AP", "family": "Smith", "initials": "L. A. S.", "capitalized_force": "Leed Ap Smith"} {"name": "Smith, MA", "family": "Smith", "suffix": "MA", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "S."} -{"name": "Smith, MD - PhD - FACS", "title": "MD", "given": "-", "family": "Smith", "suffix": "PhD - FACS", "initials": "-. S.", "capitalized_force": "Md - Smith PhD - FACS"} +{"name": "Smith, MD - PhD - FACS", "family": "Smith", "suffix": "MD - PhD - FACS", "initials": "S."} {"name": "Smith, MD PhD", "family": "Smith", "suffix": "MD PhD", "initials": "S."} -{"name": "Smith, MD PhD Ma", "title": "MD", "given": "PhD", "middle": "Ma", "family": "Smith", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "P. M. S.", "capitalized_force": "Md PhD Ma Smith"} {"name": "Smith, MEng", "given": "MEng", "family": "Smith", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "M. S.", "capitalized_force": "Meng Smith"} {"name": "Smith, Ma", "given": "Ma", "family": "Smith", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "M. S."} {"name": "Smith, Ma. John", "title": "Ma.", "given": "John", "family": "Smith", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} {"name": "Smith, Major. John", "title": "Major.", "given": "John", "family": "Smith", "initials": "J. S."} {"name": "Smith, Ms Jane", "title": "Ms", "given": "Jane", "family": "Smith", "initials": "J. S."} -{"name": "Smith, Ms MD Ma", "title": "Ms MD", "given": "Ma", "family": "Smith", "initials": "M. S.", "capitalized_force": "Ms Md Ma Smith"} {"name": "Smith, Ms Ma", "title": "Ms", "given": "Ma", "family": "Smith", "initials": "M. S."} +{"name": "Smith, Ms RA Ma", "title": "Ms RA", "given": "Ma", "family": "Smith", "initials": "M. S.", "capitalized_force": "Ms Ra Ma Smith"} {"name": "Smith, Ms.", "family": "Smith", "suffix": "Ms.", "initials": "S.", "capitalized_force": "Smith MS."} {"name": "Smith, Ms. Jane", "title": "Ms.", "given": "Jane", "family": "Smith", "initials": "J. S."} {"name": "Smith, PSM I", "family": "Smith", "suffix": "PSM I", "initials": "S."} @@ -2962,6 +2965,7 @@ {"name": "Smith, PhD MEng", "family": "Smith", "suffix": "PhD MEng", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "S.", "capitalized_force": "Smith PhD MENG"} {"name": "Smith, PhD Ma", "family": "Smith", "suffix": "PhD Ma", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "S.", "capitalized_force": "Smith PhD MA"} {"name": "Smith, Prof.", "title": "Prof.", "family": "Smith", "initials": "S."} +{"name": "Smith, RA PhD Ma", "title": "RA", "given": "PhD", "middle": "Ma", "family": "Smith", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "P. M. S.", "capitalized_force": "Ra PhD Ma Smith"} {"name": "Smith, RN - CRNA", "family": "Smith", "suffix": "RN - CRNA", "initials": "S."} {"name": "Smith, Sir", "title": "Sir", "given": "Smith", "initials": "S."} {"name": "Smith, Sr.", "family": "Smith", "suffix": "Sr.", "initials": "S."} @@ -3011,7 +3015,7 @@ {"name": "Steinunn Sigurðardóttir", "given": "Steinunn", "family": "Sigurðardóttir", "initials": "S. S."} {"name": "Stephan Guðmundsson Stephansson", "given": "Stephan", "middle": "Guðmundsson", "family": "Stephansson", "initials": "S. G. S."} {"name": "Steven Hardman", "given": "Steven", "family": "Hardman", "initials": "S. H."} -{"name": "Steven Hardman, MD - DO - DDS", "title": "MD", "given": "-", "middle": "DO -", "family": "Steven Hardman", "suffix": "DDS", "initials": "-. -. S. H.", "capitalized_force": "Md - do - Steven Hardman DDS"} +{"name": "Steven Hardman, MD - DO - DDS", "given": "Steven", "family": "Hardman", "suffix": "MD - DO - DDS", "initials": "S. H."} {"name": "Steven Hardman, MD, DO, DDS", "given": "Steven", "family": "Hardman", "suffix": "MD, DO, DDS", "initials": "S. H."} {"name": "Steven Hardman, RN - CRNA", "given": "Steven", "family": "Hardman", "suffix": "RN - CRNA", "initials": "S. H."} {"name": "Stober Sasilia Hamis", "order": "FAMILY_FIRST", "given": "Sasilia", "middle": "Hamis", "family": "Stober", "initials": "S. H. S."} @@ -3129,6 +3133,7 @@ {"name": "UNILEVER LTD", "given": "UNILEVER", "family": "LTD", "initials": "U. L.", "capitalized": "Unilever Ltd"} {"name": "US Magistrate Judge T Michael Putnam", "title": "US Magistrate Judge", "given": "T", "middle": "Michael", "family": "Putnam", "initials": "T. M. P.", "capitalized_force": "Us Magistrate Judge T Michael Putnam"} {"name": "US Magistrate-Judge Elizabeth E Campbell", "title": "US Magistrate-Judge", "given": "Elizabeth", "middle": "E", "family": "Campbell", "initials": "E. E. C.", "capitalized_force": "Us Magistrate-Judge Elizabeth E Campbell"} +{"name": "Uddin, Md Karim", "family": "Uddin", "suffix": "Md Karim", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "U.", "capitalized_force": "Uddin MD Karim"} {"name": "Ueshiba, Morihei", "given": "Morihei", "family": "Ueshiba", "initials": "M. U."} {"name": "Uexküll, Jakob von", "given": "Jakob", "family": "von Uexküll", "ambiguities": ["PARTICLE_OR_GIVEN"], "initials": "J. U."} {"name": "Uffenbach, Zacharias Konrad von", "given": "Zacharias", "middle": "Konrad", "family": "von Uffenbach", "ambiguities": ["PARTICLE_OR_GIVEN"], "initials": "Z. K. U."} From fd07040fccafe41ada84d2da20a921344e2cf832 Mon Sep 17 00:00:00 2001 From: Derek Gulbranson Date: Fri, 23 Oct 2026 21:29:16 -0700 Subject: [PATCH 2/2] Address the design-docs review of #853's md change - decisions/C13's #603 entry gains its supersession note: #853 moves `John Smith, MD Jones` and the `MD - PhD - FACS` rows it had kept. - Four rule examples still used MD as the title/suffix dual and no longer exercised their clause (C9, C11, C12, H6); respelled to RA, as is AGENTS.md's `is_title_piece` sentence. - The accepted comma cost reaches a given part with titles in front (`Uddin, Mr Md Karim`); recorded, pinned as a case row, and the "reads as Mohammad" claim scoped to names with no comma. - The release bullet stated that comma reading as unchanged; it now names it as the limit it is. The Standing line names the `Md.` exception. - The PSC and corpus figures are restated against a written recipe (1,246 alone, 54 with more words, 18 families lost, 37 `Md.`, 16 committed entries moved), and the en.toml reason follows. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- ...-credential-opening-the-part-after-the-comma.md | 2 +- docs/design/decisions/indic-honorifics.md | 13 +++++++------ .../rules/C11-what-the-comma-decision-reports.md | 2 +- ...suffix-word-ending-the-name-before-the-comma.md | 2 +- ...ne-character-suffix-word-inside-a-comma-part.md | 2 +- .../design/rules/H6-title-before-a-family-comma.md | 2 +- docs/release_log.rst | 2 +- nameparser/vocab/en.toml | 2 +- tests/v2/cases.py | 14 +++++++++++++- tests/v2/test_cases.py | 7 ++++--- tools/differential/corpus_rules.jsonl | 4 ++-- tools/differential/corpus_shapes.jsonl | 1 + tools/differential/snapshot.jsonl | 5 +++-- 14 files changed, 38 insertions(+), 22 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 55f2cad4..2c71c238 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -183,7 +183,7 @@ The library has two layers: `nameparser/config/` (data) and `nameparser/parser.p **Sweep the forms a change can reach: comma shapes, then `name_order`.** No comma, a FULL name before the comma, a ONE-WORD name before the comma and NOTHING before the comma are four paths, and the last two are the misses — #429, #430 and #432 are all the one-word path disagreeing with the full-name path on inputs the full-name path reads correctly, and #613's PR review found `, PhD` and `, Jr.` (an empty surname field, as CSV data writes it) read as given and title by a decision that returned early on an empty head, after two review rounds whose every grid had a name before the comma. Put an empty head and a nickname-only head (`(Bob), PhD`) in every comma grid. Count the one-word path the way C3 counts, which is neither tokens nor pieces: a particle run and the one word it attaches to are one word (#575), a connective join and a bound given-name pair count as their words (#613, so `Ortega y Gasset, Dr.` is a whole name), and a suffix beside the surname counts as no name word at all. #603's review found the suffix half (`Vega y Lopez Jr., PhD Jones` lost its family where `Vega y Lopez, PhD Jones` kept it); #613 then reversed the connective half, and both now read given 'Vega', middle 'y', family 'Lopez' — so test a particle surname, a connective surname and a surname with a suffix, each before a comma. For orders the sweep already exists (`tests/v2/test_cases.py` runs every row under all three) but carries one assertion, R2's family partition, so it checks nothing a new change moves; coverage there has been vacuous before (PR #394's review found the suite passed with `name_order` discarded from grouping). Parse your change's names in each comma shape and each order, and read the ones you did not predict. And read a generated grid's moves by SHAPE, not by corpus membership: #614's first design passed the suite, the five gates and a fingerprint of every corpus name, because its grid moves were sorted into 'corpus name moved' and 'garbage', and the review found the moves that mattered among the 'garbage' -- a bare salutation (`Mr. and Mrs.` lost its connective to the family name) and a name with one trailing word (`Freiherr von Berg X.Y.Z.`). Pull out the head-plus-one-word slice of the grid and a salutation with nothing after it, and read every move there. Two more were the second review's (#614): no grid put TWO words that are both a title and a particle at the head (`Freiherr St van Berg MA`, where an index into the pieces was applied to a merged view of them -- `TITLES ∩ particles_ambiguous` is `{freiherr, st}`, so put both in every head list, each in its bare and its period-marked spelling -- #637's grid had `St` and `Freiherr` alone and missed that `St.` and `Freiherr.` are declined by the bound join and then attached by P6), and the grids compared ambiguity KINDS, so a report telling the caller the wrong field (`Freiherr von Berg Ma`, 'read as a given name' of a word in the family) passed every pin. #701's review added one: put a title/suffix dual (`Ms`, `Sr.`, `Cpl`) SECOND in a title run, behind an ordinary title. A stop that asks only "is this word suffix vocabulary" passes every row that opens with the dual and fails `Rev. Sr. Smith, Mary`; it was found by comparing `T Smith, John` against `T John Smith` over every title word, and a comma-versus-comma-less comparison is the cheap check for any rule that moves a word between the two slots. Compare each report's `detail` against the field its tokens land in -- since #626 `test_a_report_names_the_field_its_word_lands_in` (tests/v2/test_cases.py) does it over every non-locale case row under all three orders (a row carrying its own policy only as declared), for the phrasings its `_FIELD_CLAIMS` lists -- so a row with the shape is the test only where the report's wording is listed: a new emitter's wording goes in that list, and segment's report on a part past the second comma names no field (#629). #626's own first grid then missed an emitter for want of one comma shape (`, Ma Dr.`: an empty head, an ambiguous word, a trailing title, which H1 moves to the family): a trailing title belongs in every empty-head comma grid too. And #620's: an exclusion list in code being replaced is a set of rules, not noise -- #614's merged copy excluded every word the trailing read weighs from its units, the first #620 prototype dropped the list as a symptom of the copy, and its review found the list had been keeping positional counting's rule that a member in front counts as a word (`Freiherr von Berg MA X.Y.Z.` lost its suffix). Before deleting one, mutate each entry out and read what moves. The converse holds for replacing a hand copy WITH the shared predicate: it is a behavior change wherever the two disagreed, and #735's /simplify shipped one as "no reading moves" because the suite and the snapshot were its only check (`St Sir de Mesnil` family-first, a token test replaced by P1's piece test); diff the two predicates' answers over a grid before calling the swap a refactor. -**Then sweep the spellings: a class reached by vocabulary is also reached by SHAPE.** A test that uses only the listed spelling of a word class walks only the vocabulary path, and the shape path is a separate branch that can be missing while every test passes. Roman numerals are the sharpest case: `ii`, `iii` and `iv` are suffix vocabulary; `i` and `v` are vocabulary too but initial-shaped, so `is_suffix_piece` vetoes them and only the numeral fork reads them; and `vi` through `x` are in no list at all, read by the fork's shape test alone (checked 2026-10-04 against `Lexicon.default()`) -- which stops at `x`: `_vocab._ROMAN` matches I to X, so `John Smith XI` reads `XI` as a name word (re-checked 2026-10-07, #614's /simplify, which found a docstring claiming "any length"). And the fork counts a ONE-letter numeral besides, since #718 (rules/S10): it is the suffix only where the name has a word to spare, so `John V` and `John Smith V` take different branches of one fork, a one-case name being counted like capitals whatever the letter -- since #721 a one-case one-letter numeral carries the `initial` tag its capital does, and since #723 every one-case bare letter does (rules/T4), and a bare letter in front of the numeral is initial-shaped as its capital is, so `john v` reads as `JOHN V` does over `test_a_one_case_name_reads_as_its_capitals`'s grid (comma shapes, orders, a maiden clause, a particle chain, a bare letter in front, a trailing title, a non-ASCII letter in front: 315 of its then 1,872 pairs disagreed at 2bfa9533, before #721, and none after). Its first version claimed "every shape" with no bare letter in front and no trailing title in the grid, and #721's reviews found both broken; a grid's claim is the grid -- put a head-plus-numeral row beside every words-to-spare row. So a change that handles `V` and `III` can still miss `VI` — #610 found exactly that, the maiden take's candidate loop admitting a shape-only numeral in one position where the peel reads it in two (`Jane Doe nee Smith VI Prof.` kept maiden `Smith VI` while `V` and `III` worked). #656's sweep found the same class from the other side: C12 takes the word ending the name before a comma as EVIDENCE, and its first draft asked the suffix-word tag, so `John Smith VI, AD` missed the rule `III` reached, and then counted the whole part, so `Smith VI, AD` fired with `VI` as its own name word and `Smith V, AD` with a `V` that S10 reads as the family. A rule that weighs a numeral as a generation must ask the predicate that READS it as one (the trailing fork and its words-to-spare count), not the vocabulary that lists some of its spellings. The same split runs through the credentials (`MA` listed, `X.Y.Z.` by the dotted shape, `XYZ` by the caps shape, and `Ph. D.` merged by group into one flagged piece the peel's walk never holds -- #603 found #602's run-start predicate refusing it too, so `Smith, John Ph. D. Jones` kept middle 'Jones' where `Smith, John PhD Jones` read suffix 'PhD Jones') and the titles (`Prof.` period-marked and read at the end of a name, `Prof` bare and a name word there, and `Mr. and Mrs.` a run P3 joins, carrying a piece tag a one-word vocabulary test never sees -- PR #735's review found C12's hand copy of C13's title test (C8's until its 2026-10-19 split) reading `John Smith Jr., Mr. and Mrs. AD` as all suffix; ask C13's predicates in C13's order, `not is_suffix_piece` then `is_title_piece`, not the token tags, since `is_title_piece` alone reads the duals `MD` and `Sr.` as titles). When a change touches a word class, test one spelling from each path. The same holds for a separator's SPACING: a comma (or any punctuation) can stand alone, glue to the word before it or glue to the word after, and each is a different token stream. #653's first fix swept `(née, Jones)` and missed `(née , Jones)`, where the kept comma became a word after the marker and M3 read the clause as maiden ', Jones'; its review found it. Put all three spacings in any grid around a separator. A full stop INSIDE a word has four spellings of its own: initials run together (`J.R.R.`), joined words each closed by a stop (`Lt.Gov.`, `Msc.Ed.`), a joined word or name closed by none (`St.John`, `J.Smith`), and an abbreviation's own ending, closed or not (Italian `Sig.ra`, Latvian `Dr.philol.`). Shape tells the last from none of the others, so a rule over the shape decides which it costs; #838 found the ending only through the snapshot's CLDR row `Dut.ra Prof.ra`, and its first narrowing traded `St.John` for `Sig.ra` while the snapshot listed the `Dr.philol.` move among moves read as intended, until the design-docs review named it. +**Then sweep the spellings: a class reached by vocabulary is also reached by SHAPE.** A test that uses only the listed spelling of a word class walks only the vocabulary path, and the shape path is a separate branch that can be missing while every test passes. Roman numerals are the sharpest case: `ii`, `iii` and `iv` are suffix vocabulary; `i` and `v` are vocabulary too but initial-shaped, so `is_suffix_piece` vetoes them and only the numeral fork reads them; and `vi` through `x` are in no list at all, read by the fork's shape test alone (checked 2026-10-04 against `Lexicon.default()`) -- which stops at `x`: `_vocab._ROMAN` matches I to X, so `John Smith XI` reads `XI` as a name word (re-checked 2026-10-07, #614's /simplify, which found a docstring claiming "any length"). And the fork counts a ONE-letter numeral besides, since #718 (rules/S10): it is the suffix only where the name has a word to spare, so `John V` and `John Smith V` take different branches of one fork, a one-case name being counted like capitals whatever the letter -- since #721 a one-case one-letter numeral carries the `initial` tag its capital does, and since #723 every one-case bare letter does (rules/T4), and a bare letter in front of the numeral is initial-shaped as its capital is, so `john v` reads as `JOHN V` does over `test_a_one_case_name_reads_as_its_capitals`'s grid (comma shapes, orders, a maiden clause, a particle chain, a bare letter in front, a trailing title, a non-ASCII letter in front: 315 of its then 1,872 pairs disagreed at 2bfa9533, before #721, and none after). Its first version claimed "every shape" with no bare letter in front and no trailing title in the grid, and #721's reviews found both broken; a grid's claim is the grid -- put a head-plus-numeral row beside every words-to-spare row. So a change that handles `V` and `III` can still miss `VI` — #610 found exactly that, the maiden take's candidate loop admitting a shape-only numeral in one position where the peel reads it in two (`Jane Doe nee Smith VI Prof.` kept maiden `Smith VI` while `V` and `III` worked). #656's sweep found the same class from the other side: C12 takes the word ending the name before a comma as EVIDENCE, and its first draft asked the suffix-word tag, so `John Smith VI, AD` missed the rule `III` reached, and then counted the whole part, so `Smith VI, AD` fired with `VI` as its own name word and `Smith V, AD` with a `V` that S10 reads as the family. A rule that weighs a numeral as a generation must ask the predicate that READS it as one (the trailing fork and its words-to-spare count), not the vocabulary that lists some of its spellings. The same split runs through the credentials (`MA` listed, `X.Y.Z.` by the dotted shape, `XYZ` by the caps shape, and `Ph. D.` merged by group into one flagged piece the peel's walk never holds -- #603 found #602's run-start predicate refusing it too, so `Smith, John Ph. D. Jones` kept middle 'Jones' where `Smith, John PhD Jones` read suffix 'PhD Jones') and the titles (`Prof.` period-marked and read at the end of a name, `Prof` bare and a name word there, and `Mr. and Mrs.` a run P3 joins, carrying a piece tag a one-word vocabulary test never sees -- PR #735's review found C12's hand copy of C13's title test (C8's until its 2026-10-19 split) reading `John Smith Jr., Mr. and Mrs. AD` as all suffix; ask C13's predicates in C13's order, `not is_suffix_piece` then `is_title_piece`, not the token tags, since `is_title_piece` alone reads the duals `Ms` and `Sr.` as titles). When a change touches a word class, test one spelling from each path. The same holds for a separator's SPACING: a comma (or any punctuation) can stand alone, glue to the word before it or glue to the word after, and each is a different token stream. #653's first fix swept `(née, Jones)` and missed `(née , Jones)`, where the kept comma became a word after the marker and M3 read the clause as maiden ', Jones'; its review found it. Put all three spacings in any grid around a separator. A full stop INSIDE a word has four spellings of its own: initials run together (`J.R.R.`), joined words each closed by a stop (`Lt.Gov.`, `Msc.Ed.`), a joined word or name closed by none (`St.John`, `J.Smith`), and an abbreviation's own ending, closed or not (Italian `Sig.ra`, Latvian `Dr.philol.`). Shape tells the last from none of the others, so a rule over the shape decides which it costs; #838 found the ending only through the snapshot's CLDR row `Dut.ra Prof.ra`, and its first narrowing traded `St.John` for `Sig.ra` while the snapshot listed the `Dr.philol.` move among moves read as intended, until the design-docs review named it. **And the encoding: a letter class written `[^\W\d_]` or tested with `isalpha()` is blind to a combining mark.** Python's `\w` and `isalpha()` do not match Mn, so a letter written as base plus accent (NFD) ends a word early; the vocabulary path is immune because `_normalize` NFC-folds, the shape path is not. #652: `Dr. inž. Janez Novak` read `inž.` as the given name when the ž was decomposed, and `Janez Novak, ŽŠČ` reversed, in two families that share the blind spot — the letter-shape regexes (`period_abbreviation`, `_DOTTED`, `_CHUNKED`, `_PAIRED_INITIALS`), now spelled with `_vocab._LETTER` (a letter plus the general combining blocks; `test_regex_sync` compares the two `_MARKS` strings and pins `period_abbreviation` against config's copy), and the caps predicates, which NFC-compose a non-ASCII word before `isalpha()` (`is_caps_word`, behind C-level `isalpha()`/`is_normalized` tests, so already-composed text pays no frame — measured 2026-10-12, `Smith, José` was 193 frames before and after; the first version called it for every non-ASCII word and cost one frame each, and no guard saw it because the reference name is ASCII). A width test must count letters, not code points (`composed_len`): admitting a decomposed word to the caps set put `John Smith, MÁ` on the pair exception's wrong side, and the review's grid found it where the rows (all three letters long) did not — put a two-letter word in every caps grid. The initial shape (`_INITIAL`, `\w\.`, v1-verbatim and public) is not widened but asked of the COMPOSED word, in `is_initial_shaped` and in `_render`'s two copies; and the single-letter connective tests count composed letters, since a decomposed `й` is two code points (#665, where `É. y É. Smith` and `John й Smith` read differently decomposed). Compose the WHOLE predicate, not one half: `is_initial` is a shape ANDed with the #320 script veto, and composing the shape alone made a decomposed `씨.` an initial, since decomposed hangul is conjoining jamo outside every script range (#665's first version; `in_initialless_script` composes too now). Not covered, deliberately: the marks only some scripts write (Indic vowel signs, Arabic and Hebrew points; rules/H2's Accepted clause). The guard is `test_a_decomposed_name_renders_as_its_composed_twin` over the CLDR corpus plus `test_a_decomposed_abbreviation_reads_as_its_composed_twin` (rows × three orders × EVERYWHERE); the corpus guard reaches a shape only if some corpus name does, which is how the caps shape was missed until review, so when adding a shape rule add a row there. Four of #652's rows and two of #665's pass with every fix reverted (measured) and are parity pins, and the test compares ambiguity kinds and `initials()` too, since #665's `John й Smith` differed in its report; `_DOTTED` has no observable input and is pinned by a direct call. diff --git a/docs/design/decisions/C13-credential-opening-the-part-after-the-comma.md b/docs/design/decisions/C13-credential-opening-the-part-after-the-comma.md index 355e31c8..7b7cf3b0 100644 --- a/docs/design/decisions/C13-credential-opening-the-part-after-the-comma.md +++ b/docs/design/decisions/C13-credential-opening-the-part-after-the-comma.md @@ -13,7 +13,7 @@ Open: none in this file. ## Log -- 2026-10-04 (Derek), #603 (decided at group's head since #613, decisions/C1) — A CREDENTIAL OPENING THE PART AFTER THE COMMA MAKES IT THE POSTNOMINAL PART, WHATEVER FOLLOWS; AND A TITLE WORD PAST THE SECOND COMMA IS A TITLE. One word that is neither suffix nor title vocabulary used to flip the whole comma reading: `John Smith, PhD Jones` read family 'John Smith', given 'PhD', where `John Smith, MD FACS` and `Eric H. Holder, Jr. Attorney General` were already read as a name and its postnominals. Now the first suffix word of the part, with only titles in front of it, opens the part when it starts S2's run (`_pieces.starts_a_credential_run`, #602's own predicate, so the two runs cannot disagree about what starts one), and every word after it reads as that run reads: a title word as a title, any other as a suffix. The mechanism was the one #296 built for a part holding no name word, extended rather than added beside; where this entry had segment deciding it, #613 (decisions/C1) decides it once at group's head. Every word the opened part takes that would otherwise have made it the given part is reported (`suffix-or-name`), as #602's run reports what it absorbs. +- 2026-10-04 (Derek), #603 (decided at group's head since #613, decisions/C1; its `John Smith, MD Jones` and `MD - PhD - FACS` readings superseded by #853, decisions/indic-honorifics, `MD` being no title there) — A CREDENTIAL OPENING THE PART AFTER THE COMMA MAKES IT THE POSTNOMINAL PART, WHATEVER FOLLOWS; AND A TITLE WORD PAST THE SECOND COMMA IS A TITLE. One word that is neither suffix nor title vocabulary used to flip the whole comma reading: `John Smith, PhD Jones` read family 'John Smith', given 'PhD', where `John Smith, MD FACS` and `Eric H. Holder, Jr. Attorney General` were already read as a name and its postnominals. Now the first suffix word of the part, with only titles in front of it, opens the part when it starts S2's run (`_pieces.starts_a_credential_run`, #602's own predicate, so the two runs cannot disagree about what starts one), and every word after it reads as that run reads: a title word as a title, any other as a suffix. The mechanism was the one #296 built for a part holding no name word, extended rather than added beside; where this entry had segment deciding it, #613 (decisions/C1) decides it once at group's head. Every word the opened part takes that would otherwise have made it the given part is reported (`suffix-or-name`), as #602's run reports what it absorbs. THE ONE-WORD PATH, DECIDED (Derek): the whole part is suffixes. `Doe, PhD Jones` reads family 'Doe', suffix 'PhD Jones', no given name, as `Smith, PhD` already reads. DECLINED: reading the credential as a suffix and the next name word as the given name (`Smith, PhD Jones` → given 'Jones'), proposed by analogy with a title at the head of the given part. Asked whether that is a real format, it is not -- nobody writes `Family, Credential Given` on purpose -- and the one realistic input it served, a misplaced generation (`Smith, Jr. John` for `Smith Jr., John`), is garbage in, garbage out. One reading for both comma paths was the deciding ground: the one-word path disagreeing with the full-name path is the shape #429, #430 and #432 each found. Accepted cost: `Smith, Jr. John` loses 'John' to the suffix, where 2.3 read given 'John' with 'Jr.' misfiled as a title. A TITLE/SUFFIX DUAL OPENS NOTHING (Derek). A word in both vocabularies (`md`, `ms`, `sr`, the ranks `lt`, `cpl` and the rest) is the listing form's title in front of a given name, `Smith, Ms Jane`, and a credential behind it opens nothing either (`Smith, Ms PhD Jones` keeps given 'PhD', as `Smith, MD PhD Ma` keeps given 'Ma' under #544). DECLINED after building it: a dual opening the part behind two or more words before the comma. Derek first chose it for `John Smith, MD Jones`; the reviews found what the count cannot see -- a surname of two words (`García Márquez, Ms Gabriela`, `Smith Jones, Sr. Maria`), a title in front of a one-word surname (`Dr. Smith, Ms Jane`, the count taking the title as a word), and spaced initials (`García Márquez, Ms G. J.`) each lost the given name -- and with the word explained (a dual is a word in both lists, decided by position), Derek chose that duals never open. So `John Smith, MD Jones` and the `MD - PhD - FACS` delimiter rows keep their 2.3 reading. An ambiguous-class member opens nothing either (`John Smith, Ma Jones` keeps given 'Ma'), nor does a suffix word S2's run start refuses (`John Smith, vd Jones`). STRICT MODE: the veto refuses an initial-shaped word the suffix READING, not the run. `Smith, PSM I.` under `lenient_comma_suffixes=False` reads suffix 'PSM I.' (2.3: given 'PSM', suffix 'I.'), reporting the absorbed 'I.', which matches #602's no-comma run (`John Smith PhD V.` reports; its given-part run does not, a disagreement #602 left and this does not touch). diff --git a/docs/design/decisions/indic-honorifics.md b/docs/design/decisions/indic-honorifics.md index bd094a92..90e24d5e 100644 --- a/docs/design/decisions/indic-honorifics.md +++ b/docs/design/decisions/indic-honorifics.md @@ -14,7 +14,7 @@ Closes #346, #344 and #343 as one bundle: each CLASS is decided once, and each s - An abbreviation carrying a mark is spelled with it, and the Bengali ones ship as the visarga form and the bare stem while the Devanagari ones ship as the bare stem alone, the dotted form listed in neither script — 2026-09-06 #346. - Title vocabulary beats the one-letter-plus-period initial shape, measured and pinned by rows and not a rule: `ড. মুহাম্মদ ইউনূস` reads title 'ড.' while `র. কে. নারায়ণ`, with no entry behind it, keeps its initials — 2026-09-06 #346. - An unlisted abugida honorific stays a name word, and rules/H2's executable example of it is `প্রকৌশলী. Sen`, so whoever ships প্রকৌশলী moves the example first; the Tier 4 candidates in the Indic scripts (transliterated military ranks, Advocate, Engineer, Marhum, the kinship forms, मां/माता/মা, the hafiz/syed/shahid forms) are skipped pending a corpus check and not declined — 2026-09-06 #346. -- Latin `md` is a credential and no title: a leading `Md` is the given name it abbreviates (`Md Abdul Karim` → given 'Md', middle 'Abdul'; `Mr Md Karim Uddin` → title 'Mr', given 'Md', middle 'Karim'), at two accepted costs, a particle behind it starting the family (`Md Abu Sayed` → family 'Abu Sayed') and the given part of a family comma it opens reading as credentials (`Uddin, Md Karim` → suffix 'Md Karim'); the Bengali মোঃ/মো entries stay titles — 2026-10-23 #853. +- Latin `md` is a credential and no title: a leading `Md` is the given name it abbreviates (`Md Abdul Karim` → given 'Md', middle 'Abdul'; `Mr Md Karim Uddin` → title 'Mr', given 'Md', middle 'Karim'), at two accepted costs, a particle behind it starting the family (`Md Abu Sayed` → family 'Abu Sayed') and the given part of a family comma it opens, titles in front or not, reading as credentials (`Uddin, Md Karim` and `Uddin, Mr Md Karim` → suffix 'Md Karim'); the period-marked `Md.` is still a title by rules/H2's leading-abbreviation shape (`Md. Abdul Karim` → title 'Md.'), and the Bengali মোঃ/মো entries stay titles — 2026-10-23 #853. Elsewhere: the bundle's class-level refusals are recorded under decisions/vocabulary-exclusions, not here: the Latin forms of the Indic titles belonging to the opt-in packs of #345 (2026-09-06, #344), the native spelling shipping alone where a transliteration collides with real names (sri/shri, sant, pandit, mst), the ban on the spaced trailing set becoming glued honorifics, and the per-word title refusals (ঠাকুর/ठाकुर, कुमारी/কুমারী, बेगम/বেগম, শেখ/शेख, आचार्य, राजा/रानी), each an `excluded` entry (`uv run python tools/vocab/query.py --excluded title`). @@ -29,14 +29,15 @@ Open: #514 (whether the Tibetan prefix titles Tulku, Khenpo, Geshe, Kyabje and J - **Rinpoche is postpositional** ("Sogyal Rinpoche", "Lama Zopa Rinpoche"), so it joins SUFFIX_WORDS and no title set. The Tibetan -la particle is vocative and glued and needs no entry. The Tibetan PREFIX titles (Tulku, Khenpo, Geshe, Kyabje, Jetsun) are out of this bundle — each re-asks the surname-retention question for a different tradition — and are deferred to a follow-up issue (#514). - **The trailing set ships spaced only.** rinpoche, जी/साहब/साहिब/साहेब/महाराज and সাহেব/বাবু/মহারাজ are SUFFIX_WORDS entries and none is a honorific_tails entry. The standing prohibition (now in decisions/vocabulary-exclusions; corrected 2026-10-21, #762, from "below", since #713 split the file) is what generalizes: it was recorded for जी alone and now covers the set, on the same evidence and with the same accepted cost. - **Bengali বাবু is trailing where Devanagari बाबू is leading** ("অমল বাবু" against a leading बाबू). Different codepoints, so the two cannot interact; the divergence is stated in both wordlist comments so that a sweep "harmonizing" them has to argue with the data module rather than discover the split afterwards. -- **The Latin md history, corrected.** md entered TITLES in 8cd3123 (2015-09-05, #32) as the medical degree written before the name, alongside do, which #296's audit later took out (#vocabulary-collisions). The Muhammad reading is a 2026 reinterpretation, recorded in the case row audit_md_leading_stays_a_title; both readings agree on the field, which is why the entry survived #296's audit as the one deviation from that audit's table. Recorded here so the record stops implying md was added for Muhammad. +- **The Latin md history, corrected.** md entered TITLES in 8cd3123 (2015-09-05, #32) as the medical degree written before the name, alongside do, which #296's audit later took out (#vocabulary-collisions). The Muhammad reading is a 2026 reinterpretation, recorded in the case row audit_md_leading_stays_a_title (renamed audit_md_leading_is_the_given_name by #853, which reverses its reading; pointer corrected 2026-10-23); both readings agree on the field, which is why the entry survived #296's audit as the one deviation from that audit's table. Recorded here so the record stops implying md was added for Muhammad. - **মোঃ/মো and মোসাঃ/মোছাঃ go into TITLES**, mirroring Latin md. Semantically they are religious name prefixes rather than titles. TITLES is the FUNCTIONAL home because `given` must stay the name the person is addressed by — মোঃ আবদুল করিম is called আবদুল করিম — and the parser has no name-prefix field. The bound-given-name set is the wrong home for the same kind of reason it is the right one for Arabic abdul: abdul is an inseparable half of one name, মোঃ is detachable. Latin Mst waits for the opt-in pack (#345); the most already in TITLES is the English "Most Rev." word and is unrelated. - **Abbreviation marks and periods.** An entry carrying an abbreviation mark is spelled with it: the Bengali visarga ঃ (U+0983) is a spacing combining mark (Unicode category Mc, not a letter and not punctuation), and `_lexicon._normalize` lowercases, composes NFC for a non-ASCII word and strips edge whitespace and the four full stops of `FULL_STOPS` — no NFKC, no casefold anywhere in that module — so the mark is simply never touched and মোঃ reaches the lexicon and matches intact (corrected 2026-09-10, #cjk-full-stops: the fold was ASCII periods only and no NFC when this was written; the visarga argument is unchanged, ঃ having no canonical decomposition, so NFC leaves it exactly where it was). Recompute with `Lexicon.default().add(titles={"মোঃ"}).titles & {"মোঃ"}`, which is non-empty iff the mark survived, and `unicodedata.category("ঃ")`, which is `Mc`. Period spellings (মো., মোসা., ডা., प्रो., पं.) match the bare stem through the same edge-period strip, so every BENGALI abbreviation carrying the mark ships as two entries, the visarga form and the bare stem (মোঃ and মো, মোসাঃ and মোসা, মোছাঃ and মোছা, মিঃ and মি), while the Devanagari abbreviations carry no such mark and ship as the bare stem alone (प्रो, पं, प्रा, डा); the dotted form is never listed in either script. - **Vocabulary beats the initial reading.** A one-letter-plus-period Bengali abbreviation is is_initial-shaped and the title entry wins: "ড. মুহাম্মদ ইউনূস" reads title ড., given মুহাম্মদ, family ইউনূস, while "র. কে. নারায়ণ" — real initials with no entry behind them — is unchanged. #343 verified the same fork at 2.1.0; it is re-verified here on the shipped set. This is MEASURED behavior pinned by rows, not a normative rule — no rule in rules.md states that title vocabulary outranks the one-letter-plus-period initial shape, rules/H2 speaking only to the bare-initial and the shape-versus-suffix cases — so a future shape veto must re-check "ড. মুহাম্মদ ইউনূস" before it lands. - **rules/H2's Accepted clause keeps its claim and loses its example.** The clause says an unlisted abugida honorific stays a name word, because the shape rule cannot see an unbroken run of letters through combining vowel signs; that is still true. Its example used প্রফেসর, which this bundle lists, so the example is now "প্রকৌশলী. Sen" → given="প্রকৌশলী." — Engineer, a loanword deliberately not shipped. The example is a contract-tier name in corpus_rules.jsonl, so the swap landed in the Bengali commit rather than in the records commit; leaving it would have exited the gate 1 there. - **Not recorded as excluded:** the Tier 4 candidates — transliterated military ranks, Advocate, Engineer, Marhum, and the kinship forms — are skipped pending a corpus check, not declined, and मां/माता/মা and the hafiz/syed/shahid forms are left alone the same way. An Excluded entry is a standing prohibition, and none of these has been argued to that standard. One of them carries a duty anyway: প্রকৌশলী (Engineer) is rules/H2's executable example of an unlisted abugida honorific, so whoever ships it must move the example to another unlisted word first, or the doc test and the contract-tier gate fail together. - **Measurement (2026-09-06).** Four corpus names move, all under the renunciate fold, all in one direction (the family empties and the given fills): Swami Vivekananda, Guru Nanak, Baba Ramdev and Lama Zopa, every one from the radar-tier corpus_issues.jsonl. Recompute by parsing every name in the tools/differential/corpus*.jsonl glob twice — once with the shipped lexicon, once with `Lexicon.default().remove(given_name_titles={"swami","guru","baba","lama"})` — and diffing the seven name fields. The multi-name spellings are the boundary and do not move: Swami Vivekananda Saraswati and Guru Gobind Singh keep families Saraswati and Singh, rules/H1's fold requiring exactly one name word. The Devanagari and Bengali additions move no corpus name at all, the corpora holding no name written in either script apart from rules.md's own H2 example, which this bundle rewrote. The gate's intentional counts rose by four at every baseline; read today's from the `corpus:` line of `uv run python tools/differential/compare.py --baseline X`. -- 2026-10-23 (Derek), #853 — LATIN MD IS A CREDENTIAL AND NO TITLE: A LEADING MD IS THE GIVEN NAME IT ABBREVIATES. SUPERSEDES: the Latin md half of the 2026-09-06 #346/#344/#343 bundle (its Standing line and the "md history, corrected" bullet's conclusion that both readings agree on the field) and decisions/C8's 2026-08-23 #296/#325 entry's `md` dual. The bundle had kept `md` a title on the ground that `given` holds the name a person is addressed by, and `Md. Karim Uddin` is addressed as Karim; the filers who write these names put `Md` in the given name. The `psc-individuals` local corpus (UK people with significant control, the 1-in-8 sample of the 2026-10-22 snapshot, #849) holds 1,300 names whose forename box opens with `Md`: 1,209 typed it there alone with the rest of the given name in the middle box, 49 typed `Md Karim` together, and 38 names elsewhere typed `Md` as the whole title box. Scored against the boxes, the title reading put the forename box in `given` for none of the 1,300 and left `given` empty for 94; the credential reading puts it there for 1,207 and leaves 3 empty. A bound given name like `abdul` (given `Md Karim`) was the third reading measured and declined: it matched the forename box 152 times, and it would make `Md` bind to the next word where the spelled-out `Mohammad Karim Uddin` (given 'Mohammad', middle 'Karim') does not. With the title gone, `Md` reads as `Mohammad` reads. `md` keeps its credential reading, so `John Smith MD` and `John Smith, MD` are unchanged, and its `excluded` academic entry in en.toml carries the reason. - WHAT MOVES, measured 2026-10-23 on this tree against the same tree with `Lexicon.default().add(titles={"md"})` as the comparator (the title reading restored and nothing else), over every name holding the word `md` in the local corpora fetched that day (`tools/differential/local_corpora.py`; a fetch of another day moves the counts): psc-individuals 1,283 moved, 1,280 of them `Md` from title to given; ctgov-officials 417 moved, 379 from title to suffix, almost all the shape `John Smith, MD Jones`, whose `MD` the title reading had taken for the given part's title and whose whole name before the comma it had read as the family, plus 21 from title to given, 15 of them a Bangladeshi `Md`, two a degree written first (`MD John Smith`-shaped) and four no person's name at all; ukec-individuals 4, all to given; fec-individuals 10. The committed corpora move 19 names (`tools/differential/corpus*.jsonl`, the same comparator), 5 of them fictional CLDR renderings that write `MD` as a prefix title. - ACCEPTED COSTS (Derek). (1) A particle behind `Md` starts the family: `Md Abu Sayed` reads given 'Md', family 'Abu Sayed', where the title reading left `Abu` the leading name word and read given 'Abu', family 'Sayed'. 17 of the 1,300 take this shape, with `Abu`, `Al` or `Bin`, the filer's surname box holding the last word alone. It is the spelled-out name's reading (`Mohammad Abu Sayed` reads the same), so the gap is P2's and P5's for any given name before `abu`, not Md's. (2) The given part of a family comma that `Md` opens reads as a credential run (rules/C13): `Uddin, Md Karim` reads family 'Uddin', suffix 'Md Karim' with a `suffix-or-name` report, where the title reading gave given 'Karim'. 9 of the 10 FEC moves are this shape, FEC writing `FAMILY, GIVEN`. Marking `md` ambiguous (`may_be_name`) would fix it, measured: `Uddin, Md Karim` reads given 'Md', and every `John Smith MD` then reports `suffix-or-name`, so it was declined for the commonest credential there is. - NOT CHANGED: the period-marked `Md.` is still a title by rules/H2's leading-abbreviation shape (36 of the 1,300 PSC names), which is #853's question 3 and not this entry's; and the Bengali মোঃ/মো and মোসাঃ/মোছাঃ entries stay titles, no filed data reaching Bengali script. The case rows and rule examples that used `MD` as their title/suffix dual (rules/H3, H4, S7 and C13; decisions/H3, H4, H6, S7 and C9's Standing lines; the cases.py dual rows) are respelled with `RA`, which reads as `MD` did at 1.4.0, 2.3.0 and the parent; their dated log entries keep `MD` as written. +- 2026-10-23 (Derek), #853 — LATIN MD IS A CREDENTIAL AND NO TITLE: A LEADING MD IS THE GIVEN NAME IT ABBREVIATES. SUPERSEDES: the Latin md half of the 2026-09-06 #346/#344/#343 bundle (its Standing line and the "md history, corrected" bullet's conclusion that both readings agree on the field) decisions/C8's 2026-08-23 #296/#325 entry's `md` dual; and, in part, decisions/C13's 2026-10-04 (Derek), #603 entry, which kept `John Smith, MD Jones` and the `MD - PhD - FACS` delimiter rows at their 2.3 reading because `MD` was a dual, and a dual opens nothing. The bundle had kept `md` a title on the ground that `given` holds the name a person is addressed by, and `Md. Karim Uddin` is addressed as Karim; the filers who write these names put `Md` in the given name. The `psc-individuals` local corpus (UK people with significant control, the 1-in-8 sample of the 2026-10-22 snapshot, #849) holds 1,300 names whose forename box opens with `Md`: 1,246 typed it there alone (37 of them as `Md.`), most with the rest of the given name in the middle box, and 54 typed it with more of the given name; across the whole sample, 38 names typed `Md` as the title box. Scored against the boxes, the title reading put the forename box in `given` for none of the 1,300 and left `given` empty for 94; the credential reading puts it there for 1,207 and leaves 3 empty. A bound given name like `abdul` (given `Md Karim`) was the third reading measured and declined: it matched the forename box 152 times, and it would make `Md` bind to the next word where the spelled-out `Mohammad Karim Uddin` (given 'Mohammad', middle 'Karim') does not. With the title gone, `Md` reads as `Mohammad` reads in a name with no comma; after a family comma the two part (cost 2 below). `md` keeps its credential reading, so `John Smith MD` and `John Smith, MD` are unchanged, and its `excluded` academic entry in en.toml carries the reason. + WHAT MOVES, measured 2026-10-23 on this tree against the same tree with `Lexicon.default().add(titles={"md"})` as the comparator (the title reading restored and nothing else), over every name holding the word `md` in the local corpora fetched that day (`tools/differential/local_corpora.py`; a fetch of another day moves the counts): psc-individuals 1,283 moved, 1,280 of them `Md` from title to given; ctgov-officials 417 moved, 379 from title to suffix, almost all the shape `John Smith, MD Jones`, whose `MD` the title reading had taken for the given part's title and whose whole name before the comma it had read as the family, plus 21 from title to given, 15 of them a Bangladeshi `Md`, two a degree written first (`MD John Smith`-shaped) and four no person's name at all; ukec-individuals 4, all to given; fec-individuals 10. 16 entries of the committed corpora move (`snapshot.corpus_entries()` over this tree's `tools/differential/corpus*.jsonl`, each entry under its own order, the seven name fields compared, the same comparator), 5 of them fictional CLDR renderings that write `MD` as a prefix title. + ACCEPTED COSTS (Derek). (1) A particle behind `Md` starts the family: `Md Abu Sayed` reads given 'Md', family 'Abu Sayed', where the title reading left `Abu` the leading name word and read given 'Abu', family 'Sayed'. 18 of the 1,300 lose the family name the title reading gave them this way, each with `Abu`, `Al` or `Bin`, the filer's surname box holding the last word alone. It is the spelled-out name's reading (`Mohammad Abu Sayed` reads the same), so the gap is P2's and P5's for any given name before `abu`, not Md's. (2) The given part of a family comma that `Md` opens reads as a credential run (rules/C13), and so does one with only titles in front of it, the run starting behind titles alone: `Uddin, Md Karim` reads family 'Uddin', suffix 'Md Karim' with a `suffix-or-name` report, where the title reading gave given 'Karim', and `Uddin, Mr Md Karim` reads title 'Mr', suffix 'Md Karim', where it gave title 'Mr Md', given 'Karim'. 9 of the 10 FEC moves are this shape, FEC writing `FAMILY, GIVEN`. Marking `md` ambiguous (`may_be_name`) would fix it, measured: `Uddin, Md Karim` reads given 'Md', and every `John Smith MD` then reports `suffix-or-name`, so it was declined for the commonest credential there is. + NOT CHANGED: the period-marked `Md.` is still a title by rules/H2's leading-abbreviation shape (37 of the 1,300 PSC names write it so), which is #853's question 3 and not this entry's; and the Bengali মোঃ/মো and মোসাঃ/মোছাঃ entries stay titles, no filed data reaching Bengali script. The case rows and rule examples that used `MD` as their title/suffix dual (rules/H3, H4, S7 and C13; decisions/H3, H4, H6, S7 and C9's Standing lines; rules/C9, C11, C12 and H6; the cases.py dual rows) are respelled with `RA`, which reads as `MD` did at 1.4.0, 2.3.0 and the parent; their dated log entries keep `MD` as written. + RECIPE for the figures above. PSC: in `~/.cache/nameparser-tools/local/psc-individuals/raw.jsonl`, the records whose `forename` field, split on whitespace, opens with a word that is `md` once lowercased and stripped of edge periods (1,300); "alone" is a one-word forename, `Md.` one ending in a period; the title-box count is over every record, its `title` folded the same way; a given name matches the box where `given` and `forename` agree lowercased with whitespace runs collapsed; a family is lost where the comparator's `family` equals the `surname` field lowercased and the tree's does not. Local corpora moves: every record of each `corpus.jsonl` matching `(^|[\s,])md\.?([\s,]|$)` case-insensitively, a move being any difference in the seven name fields between the comparator and the tree; "title to suffix" (or "to given") is `md`, periods removed, a word of the comparator's `title` and of the tree's `suffix` (or `given`). diff --git a/docs/design/rules/C11-what-the-comma-decision-reports.md b/docs/design/rules/C11-what-the-comma-decision-reports.md index 88db3b7b..f2704d56 100644 --- a/docs/design/rules/C11-what-the-comma-decision-reports.md +++ b/docs/design/rules/C11-what-the-comma-decision-reports.md @@ -8,7 +8,7 @@ ``` "John Smith, A.B." → ambiguities=("suffix-or-name",) ``` -- **A run the count leaves in the listing form reports as S7 reads the words in it**: a word of the class read as the credential because a credential in front speaks for it reports, and one its own capitals made the credential does not unless it opens the part, where it is the first word after the comma and reports as that word always does ('Doe, MA PhD', 'John Smith, MA MA'). So a run whose every such word is written in capitals reads whole in silence only behind another credential ('John Smith, PhD MA', 'Smith, PhD MA'), as does a part read as titles before a lone given name ('Smith, Ms MD Ma'). +- **A run the count leaves in the listing form reports as S7 reads the words in it**: a word of the class read as the credential because a credential in front speaks for it reports, and one its own capitals made the credential does not unless it opens the part, where it is the first word after the comma and reports as that word always does ('Doe, MA PhD', 'John Smith, MA MA'). So a run whose every such word is written in capitals reads whole in silence only behind another credential ('John Smith, PhD MA', 'Smith, PhD MA'), as does a part read as titles before a lone given name ('Smith, Ms RA Ma'). ``` "Doe, MA PhD" → ambiguities=("suffix-or-name",) "John Smith, PhD DO DO" → ambiguities=() · boundary diff --git a/docs/design/rules/C12-suffix-word-ending-the-name-before-the-comma.md b/docs/design/rules/C12-suffix-word-ending-the-name-before-the-comma.md index 795968b8..0d557de1 100644 --- a/docs/design/rules/C12-suffix-word-ending-the-name-before-the-comma.md +++ b/docs/design/rules/C12-suffix-word-ending-the-name-before-the-comma.md @@ -17,7 +17,7 @@ ``` - **In that part a title reads as a title**, whether one word or a run of title words a connective joins (P3, C7), a dual inside such a run included, and any other word, a title and suffix dual among them, as a suffix. ``` - "John Smith Jr., AD MD" → suffix="Jr., AD MD" + "John Smith Jr., AD RA" → suffix="Jr., AD RA" "John Smith Jr., Sir AD" → title="Sir" "John Smith Jr., Sir AD" → suffix="Jr., AD" "John Smith Jr., Mr. and Mrs. AD" → title="Mr. and Mrs." diff --git a/docs/design/rules/C9-one-character-suffix-word-inside-a-comma-part.md b/docs/design/rules/C9-one-character-suffix-word-inside-a-comma-part.md index b1d96fa4..a887cb1d 100644 --- a/docs/design/rules/C9-one-character-suffix-word-inside-a-comma-part.md +++ b/docs/design/rules/C9-one-character-suffix-word-inside-a-comma-part.md @@ -20,7 +20,7 @@ **Precedence:** -- **Under the strict comma-suffix switch** ("Smith, MD I"): C14 governs a part after the comma that is nothing but suffix words; the given part's walk does not consult the switch. +- **Under the strict comma-suffix switch** ("Smith, RA I"): C14 governs a part after the comma that is nothing but suffix words; the given part's walk does not consult the switch. **Accepted:** diff --git a/docs/design/rules/H6-title-before-a-family-comma.md b/docs/design/rules/H6-title-before-a-family-comma.md index 37ad75f7..b19a0d96 100644 --- a/docs/design/rules/H6-title-before-a-family-comma.md +++ b/docs/design/rules/H6-title-before-a-family-comma.md @@ -32,7 +32,7 @@ ``` - **The run stops where nothing but post-nominals stands behind it** (H3). ``` - "MD PhD Jr., John" → family="MD" · boundary + "RA PhD Jr., John" → family="RA" · boundary ``` - **The run always leaves the family at least one word.** ``` diff --git a/docs/release_log.rst b/docs/release_log.rst index 1b921aba..cacd01f2 100644 --- a/docs/release_log.rst +++ b/docs/release_log.rst @@ -17,7 +17,7 @@ Release Log - **Fix case repair lowering a capital after a full stop or an ayn.** ``j.r.r. tolkien`` repairs to ``J.R.R. Tolkien`` and ``AHMAD IBN-ʿALĪ`` to ``Ahmad ibn-ʿAlī``, where 1.4.0 through 2.5.0 gave ``J.r.r.`` and ``ʿalī``. A joined word is written the same way (``st.john`` gives ``St.John``), so an unlisted abbreviation's ending is capitalized too: ``dr.philol.`` gives ``Dr.Philol.``. :issue:`838` - **Add the Italian abbreviated salutations such as Sig.ra and Dott.ssa as titles.** ``Dott.ssa Anna Bianchi`` gives title ``Dott.ssa``, first ``Anna``, where 1.4.0 through 2.5.0 gave first ``Dott.ssa``, middle ``Anna``, and ``SIG.RA MARIA ROSSI`` repairs to ``Sig.ra Maria Rossi``. A bare ``Sig`` or ``Ing`` before a name is still read as a first name. :issue:`840` - **Add Júnior as a suffix.** ``José Silva Júnior`` gives last ``Silva``, suffix ``Júnior``, as ``José Silva Junior`` does, where 1.4.0 through 2.5.0 gave middle ``Silva``, last ``Júnior``. ``Filho``, ``Neto`` and ``Sobrinho`` still read as the last name. :issue:`843` - - **Read a leading Md as the given name it abbreviates, Muhammad, rather than a title.** ``Mr Md Karim Uddin`` gives title ``Mr``, first ``Md``, middle ``Karim``, where 1.4.0 through 2.5.0 gave title ``Mr Md``, first ``Karim``; ``John Smith, MD Jones`` gives last ``Smith``, suffix ``MD Jones`` where it gave title ``MD``, last ``John Smith``. ``MD`` after a name is still the degree, and so is ``Md`` opening the part after a family comma: ``Uddin, Md Karim`` gives suffix ``Md Karim``. :issue:`853` + - **Read a leading Md as the given name it abbreviates, Muhammad, rather than a title.** ``Mr Md Karim Uddin`` gives title ``Mr``, first ``Md``, middle ``Karim``, where 1.4.0 through 2.5.0 gave title ``Mr Md``, first ``Karim``; ``John Smith, MD Jones`` gives last ``Smith``, suffix ``MD Jones`` where it gave title ``MD``, last ``John Smith``. ``MD`` after a name is still the degree, and ``Md.`` with a full stop is still a title. One limit: ``Md`` opening the part after a family comma reads as a credential, so ``Uddin, Md Karim`` gives suffix ``Md Karim`` where it gave title ``Md``, first ``Karim``. :issue:`853` * 2.5.0 - October 19, 2026 diff --git a/nameparser/vocab/en.toml b/nameparser/vocab/en.toml index 02b7dd4f..3e7e4c30 100644 --- a/nameparser/vocab/en.toml +++ b/nameparser/vocab/en.toml @@ -4192,7 +4192,7 @@ credential = {} [[word]] text = "md" credential = {} -excluded = { academic = { reason = "a leading Md abbreviates Muhammad, a given name: 1,209 of 1,300 UK PSC filers with Md first in the forename box typed it there alone (2026-10-23), and a title reading took it from them", issue = 853 } } +excluded = { academic = { reason = "a leading Md abbreviates Muhammad, a given name: 1,246 of 1,300 UK PSC filers with Md first in the forename box typed it there alone (2026-10-23), and a title reading took it from them", issue = 853 } } [[word]] text = "mdiv" diff --git a/tests/v2/cases.py b/tests/v2/cases.py index 08e90a82..46aba6c4 100644 --- a/tests/v2/cases.py +++ b/tests/v2/cases.py @@ -9968,7 +9968,8 @@ def _check_cjk_shape_purity(self) -> None: "left 'abu' the leading name piece and the bound join " "read given 'Abu' (1.4.0 read family 'Abu Sayed' " "too, behind title 'Md'). The filer's surname box holds " - "'Sayed' (17 of #853's 1,300 PSC names take this shape, " + "'Sayed' (18 of #853's 1,300 PSC names lose their family " + "name this way, " "with 'Abu', 'Al' or 'Bin'); the gap is the spelled-out " "name's, not Md's (decisions/indic-honorifics)", shape=1), @@ -9986,6 +9987,17 @@ def _check_cjk_shape_purity(self) -> None: "Jones' shapes) the title reading had misread " "(decisions/indic-honorifics)", shape=2), + Case("md_behind_a_title_opening_a_family_comma_given_part", + "Uddin, Mr Md Karim", + {"title": "Mr", "family": "Uddin", "suffix": "Md Karim"}, + classification="fix(#853)", + ambiguities=("suffix-or-name",), + notes="the same accepted cost behind a title: C13's run " + "starts behind titles alone, so the title in front " + "does not stand 'Md' down. The title reading gave " + "title 'Mr Md', given 'Karim' (decisions/indic-" + "honorifics)", + shape=2), Case("audit_md_after_comma_is_the_degree", "Smith, MD", {"family": "Smith", "suffix": "MD"}, classification="fix(#296)", diff --git a/tests/v2/test_cases.py b/tests/v2/test_cases.py index 05c05b77..6a95392b 100644 --- a/tests/v2/test_cases.py +++ b/tests/v2/test_cases.py @@ -218,9 +218,10 @@ def test_the_field_sweep_sees_the_claims_it_checks() -> None: # review's 'Novak, Ing.' row; 1356 -> 1359 the same day: the second # review's 'Novak, ING.' row; 1359 -> 1362 the same day: #853's # 'Uddin, Md Karim' row, one suffix-or-name claim under the three - # orders. - assert claims == 1362, ( - f"the field sweep checks {claims} claims, recorded as 1362 on " + # orders; 1362 -> 1365 the same day: the review's 'Uddin, Mr Md + # Karim' row, the same claim behind a title. + assert claims == 1365, ( + f"the field sweep checks {claims} claims, recorded as 1365 on " f"2026-10-23") diff --git a/tools/differential/corpus_rules.jsonl b/tools/differential/corpus_rules.jsonl index 7bb3f3d1..d362aa32 100644 --- a/tools/differential/corpus_rules.jsonl +++ b/tools/differential/corpus_rules.jsonl @@ -255,7 +255,7 @@ "John Smith J.u.n.i.o.r." "John Smith Jr." "John Smith Jr. Prof." -"John Smith Jr., AD MD" +"John Smith Jr., AD RA" "John Smith Jr., AD Sr. and Mr." "John Smith Jr., J." "John Smith Jr., M.J." @@ -392,7 +392,6 @@ "Lloret Palol II, JA" "Lloret Palol II, Josep Antoni" "Lord Chancellor" -"MD PhD Jr., John" "MSc Dr. med. univ." "Mai Le née Nguyen" "Malcolm X" @@ -444,6 +443,7 @@ "Pérez, Juan Ó." "QC MP" "RA DDS" +"RA PhD Jr., John" "Rashid, Harun al-" "Rev. John Smith" "Rev. Sr. Smith, Mary" diff --git a/tools/differential/corpus_shapes.jsonl b/tools/differential/corpus_shapes.jsonl index 18f14540..ebcb14d3 100644 --- a/tools/differential/corpus_shapes.jsonl +++ b/tools/differential/corpus_shapes.jsonl @@ -231,6 +231,7 @@ {"name": "Smith, RA PhD Ma", "shape": 2} {"name": "Smith, meng", "shape": 2} {"name": "Uddin, Md Karim", "shape": 2} +{"name": "Uddin, Mr Md Karim", "shape": 2} {"name": "Van Buren, Ed", "shape": 2} {"name": "Wang, Si", "shape": 2} {"name": "de la Cruz, Ma", "shape": 2} diff --git a/tools/differential/snapshot.jsonl b/tools/differential/snapshot.jsonl index 8d8c4cd7..ce7f4f8e 100644 --- a/tools/differential/snapshot.jsonl +++ b/tools/differential/snapshot.jsonl @@ -1558,7 +1558,7 @@ {"name": "John Smith Jr", "given": "John", "family": "Smith", "suffix": "Jr", "initials": "J. S."} {"name": "John Smith Jr.", "given": "John", "family": "Smith", "suffix": "Jr.", "initials": "J. S."} {"name": "John Smith Jr. Prof.", "title": "Prof.", "given": "John", "family": "Smith", "suffix": "Jr.", "initials": "J. S."} -{"name": "John Smith Jr., AD MD", "given": "John", "family": "Smith", "suffix": "Jr., AD MD", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} +{"name": "John Smith Jr., AD RA", "given": "John", "family": "Smith", "suffix": "Jr., AD RA", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} {"name": "John Smith Jr., AD Sr. and Mr.", "title": "Sr. and Mr.", "given": "John", "family": "Smith", "suffix": "Jr., AD", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "J. S."} {"name": "John Smith Jr., J.", "given": "J.", "family": "John Smith", "suffix": "Jr.", "initials": "J. J. S."} {"name": "John Smith Jr., M.J.", "given": "M.J.", "family": "John Smith", "suffix": "Jr.", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "M. J. S."} @@ -2110,7 +2110,6 @@ {"name": "MD DDS", "given": "MD", "suffix": "DDS", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "M.", "capitalized": "Md DDS"} {"name": "MD DDS Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa", "order": "FAMILY_FIRST", "given": "DDS", "middle": "Dut.ra Prof.ra von Brühl González Domingo Maria Giusepa Juana Teresa", "family": "MD", "initials": "D. D. P. B. G. D. M. G. J. T. M.", "capitalized_force": "Dds Dut.Ra Prof.Ra von Brühl González Domingo Maria Giusepa Juana Teresa Md"} {"name": "MD DDS von Brühl César Martín Ada Cornelia", "order": "FAMILY_FIRST", "given": "DDS", "middle": "von Brühl César Martín Ada Cornelia", "family": "MD", "initials": "D. B. C. M. A. C. M.", "capitalized_force": "Dds von Brühl César Martín Ada Cornelia Md"} -{"name": "MD PhD Jr., John", "given": "John", "family": "MD", "suffix": "PhD Jr.", "initials": "J. M.", "capitalized_force": "John Md PhD Jr."} {"name": "MD, DO, DDS", "given": "DO", "family": "MD", "suffix": "DDS", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "D. M.", "capitalized": "Do Md DDS"} {"name": "MD, PHD", "family": "MD", "suffix": "PHD", "initials": "M.", "capitalized": "Md PhD"} {"name": "MP Pēteris Kārlis Ozoliņš", "given": "MP", "middle": "Pēteris Kārlis", "family": "Ozoliņš", "initials": "M. P. K. O.", "capitalized_force": "Mp Pēteris Kārlis Ozoliņš"} @@ -2626,6 +2625,7 @@ {"name": "Quincy Adams", "given": "Quincy", "family": "Adams", "initials": "Q. A."} {"name": "Quinquela Martín, Benito", "given": "Benito", "family": "Quinquela Martín", "initials": "B. Q. M."} {"name": "RA DDS", "title": "RA", "family": "DDS", "initials": "D.", "capitalized": "Ra Dds"} +{"name": "RA PhD Jr., John", "given": "John", "family": "RA", "suffix": "PhD Jr.", "initials": "J. R.", "capitalized_force": "John Ra PhD Jr."} {"name": "RONALD MACDONALD", "given": "RONALD", "family": "MACDONALD", "initials": "R. M.", "capitalized": "Ronald MacDonald"} {"name": "RONALD MCDONALD", "given": "RONALD", "family": "MCDONALD", "initials": "R. M.", "capitalized": "Ronald McDonald"} {"name": "Rabbi Cohen", "title": "Rabbi", "family": "Cohen", "initials": "C."} @@ -3134,6 +3134,7 @@ {"name": "US Magistrate Judge T Michael Putnam", "title": "US Magistrate Judge", "given": "T", "middle": "Michael", "family": "Putnam", "initials": "T. M. P.", "capitalized_force": "Us Magistrate Judge T Michael Putnam"} {"name": "US Magistrate-Judge Elizabeth E Campbell", "title": "US Magistrate-Judge", "given": "Elizabeth", "middle": "E", "family": "Campbell", "initials": "E. E. C.", "capitalized_force": "Us Magistrate-Judge Elizabeth E Campbell"} {"name": "Uddin, Md Karim", "family": "Uddin", "suffix": "Md Karim", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "U.", "capitalized_force": "Uddin MD Karim"} +{"name": "Uddin, Mr Md Karim", "title": "Mr", "family": "Uddin", "suffix": "Md Karim", "ambiguities": ["SUFFIX_OR_NAME"], "initials": "U.", "capitalized_force": "Mr Uddin MD Karim"} {"name": "Ueshiba, Morihei", "given": "Morihei", "family": "Ueshiba", "initials": "M. U."} {"name": "Uexküll, Jakob von", "given": "Jakob", "family": "von Uexküll", "ambiguities": ["PARTICLE_OR_GIVEN"], "initials": "J. U."} {"name": "Uffenbach, Zacharias Konrad von", "given": "Zacharias", "middle": "Konrad", "family": "von Uffenbach", "ambiguities": ["PARTICLE_OR_GIVEN"], "initials": "Z. K. U."}