Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Open: #774 — a roman numeral S10 reads is lost into the family before dotted p

- 2026-10-18 (Derek), [#656](https://github.com/derek73/python-nameparser/issues/656) (its declined `mbchb` superseded by #797, decisions/suffix-acronym-collisions) — A SUFFIX WORD ENDING A WHOLE NAME BEFORE THE COMMA IS EVIDENCE THAT THE COMMA IS A SUFFIX COMMA, AND THE PART AFTER IT DECIDES ONLY WHETHER IT HOLDS A NAME. Until this rule only the part after the comma decided (C1), so an unlisted credential there that no shape rule admitted reversed the whole name into the listing form: `John Smith II, AD` read given 'AD', family 'John Smith'. Derek's ground, in the #720/#656 session: a generation is part of the name, more so than a credential, and is commonly written before the comma that starts the credentials (`John J Ingram III, MD`), and `II` counts as such a word though it is also initial-shaped. The evidence is the suffix WORD (`vocab:suffix-word`: every member of `suffix_words`, 170 on 2026-10-18 by `len(Lexicon.default().suffix_words)` — the generations, `esq`, `ret`, `vet`, `2`, and the honorifics, `rinpoche`, जी, 先生, 씨 among them), not an acronym, because a listing with a long surname puts an acronym there as readily (`von bergen wessels MD, pennie`, a corpus.jsonl row, keeps its given name).
DECLINED: the generation alone deciding. CLDR's own renderings hold the counter-case, a two-word surname with a generation before a listing comma: `Excm. Sr. Lloret Palol II, Josep Antoni Carles Joan` and `Excma. Dra. Van Dalen Hoogeveen II, Maria da Graça César Martín`, both keep the listing form and their given names before and after this rule (their title and family fields disagree with CLDR for other reasons, the title run left in the family). So the part after the comma still decides, by a narrower question than C1's: a word in it written as a name (`_vocab.written_as_a_name`, unlisted, no period) or an initial keeps the listing form. That needs the name's case contrast, which is C6's test (`_comma._name_contrast`) reused rather than restated; without it a given name written in capitals is indistinguishable from `AD`, so a one-case name and a caseless script decline (the Arabic, Gujarati, Kannada and Malayalam CLDR renderings of this shape stay as they read).
THE NUMERALS, found by the sweep before review. The evidence is reached by vocabulary AND by shape: `ii`–`iv`, `v` and `i` carry the suffix-word tag, but `VI` to `X` are numerals by shape alone, so a tag-only test read `John Smith III, AD` and left `John Smith VI, AD` in the listing form, #610's class. The numeral path asks `_vocab.is_trailing_numeral_suffix`, the trailing fork's own predicate, behind a C-level `ROMAN.match` filter in `decide` so the listing form pays no frame. Both paths then count the words IN FRONT of the generation as C3 does (`_whole_name(seg0[:-1])`): `Smith VI` is one word and a numeral, as `Smith III` is, where counting the whole part had made the shape-only numeral its own name word. And a one-letter numeral is evidence only where S10 reads it as the generation, a name word to spare in front of it (`name_word_count`, a title being none): `Smith V, AD` and `St. Berg V, AD` read the V as a name word and keep the listing form.
THE NUMERALS, found by the sweep before review. The evidence is reached by vocabulary AND by shape: `ii`–`iv`, `v` and `i` carry the suffix-word tag, but `VI` to `X` are numerals by shape alone, so a tag-only test read `John Smith III, AD` and left `John Smith VI, AD` in the listing form, #610's class. The numeral path asks `_vocab.is_trailing_numeral_suffix` (`_vocab.reads_as_generation` since #752), the trailing fork's own predicate, behind a C-level `ROMAN.match` filter in `decide` so the listing form pays no frame. Both paths then count the words IN FRONT of the generation as C3 does (`_whole_name(seg0[:-1])`): `Smith VI` is one word and a numeral, as `Smith III` is, where counting the whole part had made the shape-only numeral its own name word. And a one-letter numeral is evidence only where S10 reads it as the generation, a name word to spare in front of it (`name_word_count`, a title being none): `Smith V, AD` and `St. Berg V, AD` read the V as a name word and keep the listing form.
THE HONORIFICS, found by the design-docs review: the tag reaches every suffix word, so `Tenzin Gyatso Rinpoche, AD` reads suffix 'Rinpoche, AD' where the parent read given 'AD'. Kept as stated rather than narrowed to generations: an honorific written after the name stands in the same place a generation does, and no vocabulary set separates the generations from the rest. Dotted paired initials were the review's other find: `Smith Jones Jr., J.A.` read suffix 'Jr., J.A.', since the veto asked only the `initial` tag; `is_paired_initials` joins it, as C5 keeps such a pair the given name.
DECLINED: adding `mbchb` to SUFFIX_ACRONYMS, approved in the session before the rule was measured. `MBChB` ends in a capital, so it is not written as a name and the rule reads `van den Berg jnr., MBChB PhD` without it; a vocabulary member would also reach `Smith, MBChB`, which nothing here measured.
Measured 2026-10-18 on this change against its parent: five corpus names move, every one a CLDR rendering of the shape and each to the agreeing reading on family, given, suffix and title where CLDR states them (`Sig. Fabio Massimo Carlo Alberto Martini II, AD` in both orders, `prof. dr. Ada Cornelia César Martín von Brühl Jr, univ. bacc`, `Prof. dr. Cornelia Margarita Sofia Aletta van den Berg jnr., MBChB PhD`, `Skeggjason Hr. Geir Jón Magnús Brynjar yngri, þingmaður`); the last still disagrees on its given names, CLDR's `Geir Jón Magnús Brynjar` against `Hr. Geir Jón Magnús Brynjar`, a title inside a surname-first name, which is not this rule. No other corpus name moves, read off `snapshot.py`'s report. The issue's comment counted 25 disagreements of the shape on 2026-10-13; the `Jr, MP` renderings among them read correctly on the parent already, MP being listed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Open: [#819](https://github.com/derek73/python-nameparser/issues/819) (whether r
- 2026-08-25 #430 / #432 (its accepted `Smith, John PSM I I.` reading superseded by #602, decisions/S8) — one boundary, fixed as one: inside a comma segment a ONE-CHARACTER suffix word — the only kind a reader could take for an initial — is read by WHAT STANDS BEFORE IT. Behind another suffix it describes that suffix and continues the run, period included — `Smith, PSM I` and `Smith, PSM I.` are suffix 'PSM I'/'PSM I.' where they read given 'PSM' plus suffix 'I', an initial in that slot being no shape anyone writes. Behind a name word the period is decisive, marking an abbreviation and so name material — `Smith, John V.` is middle 'V.' where it read suffix 'V.', which is what `Smith, John B.` always did; the only thing that made V differ is that V is also suffix vocabulary. The bare form is the boundary and does not move: `Smith, John V` stays suffix 'V' (v1 parity, #144), which is why the test is the PERIOD and not the numeral. SCOPE, stated as the code reads rather than as the intent was argued: the class is one-character SUFFIX VOCABULARY, not "a roman numeral". It sweeps in `2` (`Smith, John 2.` is middle '2.'), misses `X` and `IX` (roman, but not vocabulary — `Smith, PSM X` is unchanged), and never touched `II`/`III`/`IV` at all, which are two characters and so were never initial-shaped and never vetoed. Anyone adding a one-character suffix in a script that has initials extends it. And the strict knob is consulted: under `lenient_comma_suffixes=False` the initial veto stands and the run ends at the numeral, which the first draft silently overrode by reading no policy at all.
#430's cause was rules/S2's initial veto in `is_suffix_piece`, which keeps a numeral out of a suffix reading — right after a name, wrong inside a credential run — so the segment did not look like a run at all and fell to the given-and-suffix walk. Two wrong answers came out of that one cause, and a fix verified on `Smith, PSM I` alone would have left the other: where the run's first word is TITLES vocabulary too (`Smith, MD I`, and `Smith, Jr. I` through the period-abbreviation inference) the leading-title peel took it instead, giving title 'MD' plus given 'I'.
THREE readers, one reading, which is the part worth copying. The gate that says a segment is a credential run, assign's router that assigns its roles, and group's one-entry join that renders it must agree PIECE FOR PIECE, and #429 shipped the inverse of its own fix by deriving that agreement twice. `segment_suffix_reading` returns the per-piece verdicts and all three read it (mechanisms.md#ONE-PREDICATE-PER-QUESTION); the intermediate state where only the gate and the router had been converted rendered `Smith, PSM I` as 'PSM, I', the render splitting a run whose roles were already right — the same tell as #429, one predicate along. TWO readers since 2026-09-06: #436/#437 took the render join out of group, and `segment_suffix_reading`'s remaining readers are assign's gate and assign's router, one call site between them. The predicate is not moved — one call site is no reason to relocate what two sites will contest again — and what is worth carrying away is unchanged, since the reason the three had to agree is the reason the two still do.
Declined, and the v1 suite caught it: asking `is_trailing_numeral_suffix` for #432's half. It answers the period question — `_ROMAN` is anchored and admits no period — but it also refuses a numeral standing behind an initial-shaped piece, which is a NO-COMMA rule and the opposite of this path's parity: `Chang, Andy C I` is first 'Andy', middle 'C', suffix 'I', and asking that predicate here made the numeral a middle. decisions.md's #401/#421 entry records the same fork declining to transfer to this walk under LENIENT. The half that applies is the period alone.
Declined, and the v1 suite caught it: asking `is_trailing_numeral_suffix` (`_vocab.reads_as_generation` since #752) for #432's half. It answers the period question — `_ROMAN` is anchored and admits no period — but it also refuses a numeral standing behind an initial-shaped piece, which is a NO-COMMA rule and the opposite of this path's parity: `Chang, Andy C I` is first 'Andy', middle 'C', suffix 'I', and asking that predicate here made the numeral a middle. decisions.md's #401/#421 entry records the same fork declining to transfer to this walk under LENIENT. The half that applies is the period alone.
BLAST RADIUS, over the 1069 corpus names against 56fb505c (PR #443): seven carry a one-character numeral behind a word inside a comma segment, every one bare, and none moves; the only movers are the three names this change added as examples, so the case table is the whole coverage. `Berg, abdul V` cannot reach the walk at all, the bound-given join making `abdul V` one piece, which is why the LENIENT mirror #401/#421 declined does not collide here.
Two things the /simplify round settled by measurement rather than argument. The walk asks `is_suffix_piece(pieces[m - 1])` — a VOCABULARY test — where the reading carries the verdict it just recorded, and unifying them looks obviously right until you run it: the walk assigns `PSM` to GIVEN in `Smith, PSM I.`, so a reading-style flag says "behind a name" and sends the numeral to middle, losing the strict parity restored two commits earlier. The two sites ask different questions on purpose, and the vocabulary answer is the load-bearing one. The cost is a reading the walk cannot reach: `Smith, John PSM I I.` moves from middle 'I' + suffix 'PSM, I.' to middle 'I I.' + suffix 'PSM', two consecutive one-character numerals behind a credential — accepted, both readings being defensible for a shape nobody writes, and recorded here rather than chased at the price of two correct pins.
The walk is also strict-blind, pre-existing and unchanged: it calls is_suffix_lenient unconditionally, so `Smith, John V` reads suffix 'V' under `lenient_comma_suffixes=False` as under the default. The reading consults the knob and the walk does not, which is now conspicuous where it was merely unexamined. Not this change's to fix.
Expand Down
Loading
Loading