Skip to content

Commit 2bfa953

Browse files
derek73claude
andauthored
Read a lone V, I or X after a first name as the family name (#718) (#719)
* Read a lone one-letter numeral by S4's words-to-spare count (#718) A capital V, I or X ending a name with no word to spare for it is the family name: 'John V' and 'Malcolm X' read family V and X, as 1.4.0 read them, where 2.0.0 through 2.4.2 gave suffix V and X and no family. 'John Smith V' keeps the suffix. A numeral in lower case in a mixed-case name is settled by its writing: still the generation, and no longer reported ('John Smith vi'). States the comma-less numeral as rules/S10 (the trailing roman numeral), with its decisions entry; amends P2's chain stop, P5's words-to-spare list and M2's examples, which the count moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Report a declined numeral both ways, as S4's fork reports (#718) 'John V' now reports suffix-or-name while reading family V, as 'Jack Ma' reports keeping its family. The declined numeral is a pick of the trailing walk, and three sites take its branches, so three report it: assign (a lone name word), the particle chain ('Freiherr von Richthofen V'), and a maiden clause that keeps it ('abdul nee Smith V'). A letter classify already reported as an initial reports nothing new (INV10 found 138 one-case rows, 'HENRY I', carrying both kinds). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix the docs the numeral count moved, from the design-docs review (#718) S10's Accepted clause named every listed lowercase letter where only 'v' bypasses the count ('john i' is counted, as P3's connective read as an initial), and left out that 'john smith v' reports nothing. P3's "is the generation it also spells" now defers to S10; O5's example is 'John III, Dr.', 'John V, Dr.' no longer being a lone word; the H3 contrast comment and the release bullet ('Dr. Smith V' moves the surname into first) catch up. The case row that lost its H1 reading is renamed for what it pins, and says that nothing pins H1 behind a comma. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the numeral count out of the given part after a family comma (#718) Code review found the chain's bound in a family comma's given part asking the count through tail_reading, which assign's walk there never asks (S4: "read without it"), so the chain took the V of 'Smith, Freiherr von Berg V' into the given name, unreported. The bound now reads with count_numeral=False and the four shapes read as on master. Two case rows pin it; AGENTS.md's group-side-predictor gotcha records the from-the-peel side of the class. Also: assign uses the shared _AMBIGUOUS_CREDENTIAL_TAGS rather than a second spelling of it, and group's Produces: line lists the chain's and clause's reports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document peel_trailing's and tail_reading's count_numeral (#718) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address the PR review: one numeral report site, case-aware clause (#718) Comment review: fix labels that contradict 1.4.0 ('John III' is no parity row, 'John III, Sir' is one, 'abdul Doe nee Smith V' is #274's), and comments this change made stale (assign's deferral note and module docstring, group's 'abdul V' reserve example, _vocab's claim that X is suffix vocabulary). Test review (mutation of each emitter in a copy): the particle chain's numeral report only chose a sentence -- assign reports the same kind from the final role -- so it is gone, as is the maiden clause's stand-down, which could never fire. The clause's report on a numeral it gives up now asks the case, so 'Jane Doe nee Smith vi' is silent as every other path is. New rows pin the chain-taken numeral, its already-an-initial stand-down, 'John VI', family-first 'John V', the lowercase pair 'John x'/'john x' and the maiden-clause case contrast; five rows restoring 1.4.0 field for field are relabelled parity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Simplify the numeral fork: decide the case once, in the walk (#718) /simplify pass over PR #719, behavior-neutral (snapshot unchanged): - a numeral its lower case settles is taken by the walk like any suffix word and recorded as no call, so assign and the maiden take stop re-asking numeral_settled_by_case -- the drift #718's review found in the clause can no longer recur - the one-letter test asks is_single_letter_numeral rather than a hand `len(text) > 1` - reported_as_initial moves into _assign, its one caller, and _pieces drops the two ambiguity imports it needed - flatter control flow in assign's numeral pick; one duplicated comment sentence and a long case note trimmed; the given-slot bound's comment says only the numeral half is uncounted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 958d252 commit 2bfa953

25 files changed

Lines changed: 497 additions & 87 deletions

‎AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

‎docs/design/decisions/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Entry conventions:
4343
- [S7 — the company of an unambiguous credential](S7-company-of-an-unambiguous-credential.md)
4444
- [S8 — the credential's trailing run](S8-credentials-trailing-run.md)
4545
- [S9 — unlisted words that join the ambiguous class by shape](S9-unlisted-words-that-join-the-ambiguous-class-by-shape.md)
46+
- [S10 — the trailing roman numeral](S10-trailing-roman-numeral.md)
4647

4748
## Nicknames & quoted names (N)
4849

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# S10 — the trailing roman numeral
2+
3+
- 2026-10-17 (Derek), #718 — A ONE-LETTER NUMERAL NEEDS WORDS TO SPARE, AND THE RULE IS WRITTEN DOWN. Until this entry no rule stated the comma-less numeral at all: the behavior was pinned in tests/v2/cases.py and credited in code comments to rules/S2, whose statement covers the suffix vocabulary and never the shape fork that reads `VI` to `X`, nor the initial veto that sends `V`, `I` and `X` there. The extraction found one reading worth changing. `John V` and `Malcolm X` read suffix with an EMPTY family in every 2.x release (2.0.0 through 2.4.2 measured), alongside 2.0.0's `Johnson PhD` fix; 1.4.0 read family `V` and `X`. Derek: a generational V is mostly archaic, X rarer still and I hardly ever written without a comma (Sr. and Senior are what people write), so a capital letter standing alone behind the given name is far likelier an initial or a one-letter surname. The numeral was never what S2's Accepted clause (an unambiguous suffix is consumed even when that leaves no family) covers, since the fork has always reported itself ambiguous; S4's count is the analogue, and the numeral now asks it.
4+
WHAT MOVES. The count is S4's own, in name UNITS, so a particle chain is one word (P2) and a title none. Measured on the shipped change against origin/master at 958d252e, the snapshot moves four corpus names: `John V` and `Mohamad X` (family V and X, as 1.4.0 read them), `John née Jones Smith V` (the clause-free name is `John V`, so the V is no suffix and the clause, which M2's statement takes up to the trailing run the clause-free name reads, keeps it: maiden 'Jones Smith V'), and `Freiherr von Richthofen V`, family 'von Richthofen V': behind a title the chain is the one name word, so the V has no word to spare and the chain takes it as P2 takes any name word, as it already took `Freiherr von Richthofen Ma`. That last is the reading a scratch prototype counting raw pieces did not show, and the one most worth a second look. Four case rows moved with it (`strict_comma_reads_the_bare_numeral_into_the_run`: `Smith V, Ma` under the strict knob reads family V, suffix Ma, as 1.4.0 did; and the rows whose purpose needed a suffix kept their ids on a spelling that still has one — `abdul Doe nee Smith V`, `John III, Dr.`, `John III, Sir` — with the old inputs re-pinned as new rows: `abdul nee Smith V` maiden 'Smith V', `John V, Dr.` and `John V, Sir` given John, family V). `Sir abdul V` reads given 'abdul V': the V is a name word, and P5's licence behind a given-name title joins it. Nothing with a word to spare moves: `John W. Ingram V`, `King John Alexander V`, `Larry James Johnson I` (a v1 test) keep the suffix and the report. A title is no word to spare, so `Dr Smith V` reads given Smith, family V, which is 1.4.0's reading too, where `Dr. von Richthofen V` keeps suffix V, the particle and the surname being two words there.
5+
THE LOWERCASE HALF. A numeral in lower case in a mixed-case name is not how anyone writes an initial, so it is the generation and reports nothing (Derek). Measured before the change, the reports had no rationale: `John Smith v` and `John Smith i` were silent because the listed letters reach the vocabulary path, `John Smith vi` and `John Smith x` reported because the shape fork reports everything it takes, and the one-case `john smith i` reported because the connective tag (P3, #479) makes a one-case `i` an initial. Now the mixed-case lower case is uniformly silent and a one-case name is counted like capitals, `v` excepted (the ACCEPTED paragraph below). `john smith i` keeps its report, by Derek's call.
6+
Declined: dropping the shape fork for single capital letters outright. Prototyped in a scratch edit, it moved 26 readings and cost the real generational names the count keeps: `John W. Ingram V` read middle 'W. Ingram', family 'V', and `John van der Berg V` folded the numeral into the particle chain. A variant that dropped the fork for I and X only, on top of the count, moved four more, among them the v1 test `Larry James Johnson I`.
7+
ACCEPTED, and recorded in rules/S10: a one-case lowercase `v` is read as the vocabulary reads it, so `john v` still loses its family and `john smith v` reports nothing, where `JOHN V` and `john x` keep their family and `JOHN SMITH V` reports — the listed lowercase letter is taken as a suffix word before the numeral fork is reached, and closing that means touching the suffix-vocabulary path, which is not this change. Not `i`, though it is listed too: a one-case `i` is P3's connective, which classify reads as an initial, so it reaches the fork and is counted (`john i` → family 'i'; `john smith i` reports). And a one-letter numeral with a credential behind it is not the last word and is read as a name word (`John Smith V PhD` → family V, 1.4.0 parity), where `John Smith III PhD` reads suffix 'III PhD'.
8+
Recompute: the moved names are this commit's diff of tools/differential/snapshot.jsonl (or `uv run python tools/differential/snapshot.py` on the parent tree with this commit's code). The Declined paragraph's figures are a prototype's, over every cases.py row and snapshot name parsed before and after the patch in one process, on master at 958d252e: its 26 replace `_pieces.is_trailing_numeral_suffix` with a wrapper returning False for a text that is one capital `I`, `V` or `X` (periods stripped) and the original's answer otherwise; its "four more" apply that wrapper to `I` and `X` alone on top of the shipped count. Both are dated snapshots by Counting claims; nothing re-runs them.
9+
- 2026-10-18 (Derek), #718 — THE DECLINED NUMERAL REPORTS TOO, BOTH DIRECTIONS AS S4'S FORK DOES. The count's first form reported only the generation, so `John V` read family V in silence while `Jack Ma` reports keeping its family; Derek: report both, as `Jack Ma` does. A declined numeral is a pick of the walk, and its branches are taken in three places, so three sites report it (mechanisms.md#AMBIGUITY-AT-THE-DECISION-SITE): assign where it stays a lone name word (`John V` — "read as a family name rather than a generational suffix"), the particle chain where it joins the run (`Freiherr von Richthofen V`), and a maiden clause that keeps it as its last word (`John née Jones Smith V`, `abdul nee Smith V`). Each wording is its own, since the acronym pick's ("both a post-nominal and an ordinary name") misdescribes a numeral.
10+
ONE STAND-DOWN, FOUND BY INV10 rather than designed. In a one-case name the connective `i` reaches the fork as a letter classify has already reported as `conjunction-or-initial`, read as an initial, and the new report put a second report on the same token: 138 rows of the connective grid (measured on this branch with the `reported_as_initial` check out of assign's numeral report, a dated snapshot) in tests/v2/test_properties.py (`AND ROVIRA I`, `HENRY I`) tripped INV10's kind pair. The two reports did not contradict each other — both read the letter as a name word — but they named one reading twice, so the numeral's report stands down where the initial reading is already reported, the shape of rules/S7's "a member its own capitals already made the credential reports nothing new". The guard was left untouched: its kind pair still means "two readings" everywhere else, and narrowing it to suit this change would have retaken its recorded control. `HENRY I` therefore reports `conjunction-or-initial` alone, and moved in test_assemble's contrast list from the withdrawn side to the side where the fork resolved to a name word.
11+
- 2026-10-18, #718 review — THE COUNT IS NOT ASKED IN THE GIVEN PART AFTER A FAMILY COMMA. The code review's grid (family-comma and empty-head shapes behind a title-particle, about 558,000 parses) found 2,880 parses where the first form moved a numeral from the suffix into the given or family name with no report: `Smith, Freiherr von Berg V` read given 'von Berg V', `, Freiherr von V` family 'von V', `Smith, St van V` given 'van V', where `Smith, von V` and `Smith, Freiherr John V` kept suffix V. The cause was not a choice: assign's walk over that part never counts the numeral (rules/S4: the given part's trailing slot "is read without it (S5)"), but the particle chain's bound there asks `tail_reading`, which the count had reached, so the chain took a word assign would have read as the suffix. The bound now reads with the count off (`trailing_run_start`, `count_numeral=False`), and all four read as they did on master (2.2.0 onward, since #424; 1.4.0 read the numeral into the name). The class is the one the AGENTS.md gotcha on group-side predictors of the given part names, met from the other side: the predictor did not change, the peel it asks did. The S4 analogue, `Smith, Freiherr von Berg Ma` → given 'von Berg Ma', is master's reading and is left alone here.
12+
- 2026-10-18, #718's PR review — TWO OF THE THREE REPORT SITES ARE ONE, AND THE CLAUSE ASKS THE CASE. The test-coverage review mutated each new emitter out in a copy of the tree. Removing the particle chain's numeral report moved no field and no kind — assign's pick loop reported the same `suffix-or-name`, worded from the final role ('read as a family name') — so the chain's branch was only choosing a sentence, and it is gone: a declined numeral stays one of the read's picks, and assign alone reports it, the decision having been the read's and not the chain's. The maiden clause's stand-down on a letter already reported as an initial could not fire at all (classify reads no connective inside a clause), so that call is gone too. And the clause's report on a numeral its take gave up did not ask the case: `Jane Doe nee Smith vi` and `x` reported where every other path had gone silent, the drift mechanisms.md#ONE-PREDICATE-PER-QUESTION names; the take now asks `numeral_settled_by_case` as assign does, `VI` still reporting beside them. Five rows the first draft labelled `fix(#718)` read field for field as 1.4.0 does and are `parity`, as are the two Freiherr rows added here; a restored 1.4.0 reading is parity by the table's own definition (rules/S4's `bare_surname_is_not_a_credential` is the precedent).

‎docs/design/rules/M2-maiden-marker-rule.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ M2. Rationale: a maiden marker announces that what follows it is the
6363
"Jane Smith née Jones" → maiden="Jones"
6464
"Maria López née García Pérez" → maiden="García Pérez"
6565
"Jane Smith née Jones PhD" → suffix="PhD"
66-
"John née Jones Smith V" → maiden="Jones Smith"
67-
"John née Jones Smith V" → suffix="V"
66+
"Jane Doe née Jones Smith V" → maiden="Jones Smith"
67+
"Jane Doe née Jones Smith V" → suffix="V"
68+
"John née Jones Smith V" → maiden="Jones Smith V" · boundary
6869
"Doe nee Smith, Jane" → maiden="Smith"
6970
"Doe, Jane nee Smith" → maiden=""
7071
"Doe, Jane nee Smith" → middle="nee Smith"

‎docs/design/rules/O5-lone-name-words-convention-and-reporting-it.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ O5. Rationale: O4 reads a name by comparing where its words stand,
3131
nothing after it names no family and silences nothing, so
3232
`سلمان،` and `Smith,` report where `Smith, Andrew` does not. A
3333
comma with only a TITLE after it names no family either, but the
34-
title still decides the field wherever it stands, so `John V, Dr.`
34+
title still decides the field wherever it stands, so `John III, Dr.`
3535
is silent for H1's reason and not the comma's. A word with no
3636
letter or digit in it is no name word and
3737
reports nothing (A2). Where the one name unit carries title

‎docs/design/rules/P2-particles-join-forward.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ P2. Rationale: a particle is written as part of the surname it
77
a trailing suffix begins — the trailing run S2 reads, once,
88
before the chain is made and counting the chain's own run as the
99
one name word it will be, so the chain cannot change what it
10-
counts: a trailing roman numeral, or a
11-
bare acronym with words to spare, ends the chain as a suffix word
12-
does, and so does a bare ambiguous acronym written in capitals in
10+
counts: a trailing roman numeral S10 reads as the generation, or
11+
a bare acronym with words to spare, ends the chain as a suffix
12+
word does, and so does a bare ambiguous acronym written in capitals in
1313
a mixed-case name, which needs no words to spare; the same
1414
acronym written Title-case in a mixed-case name ends nothing and
1515
joins the chain as any name word does, words to spare or not,
@@ -48,7 +48,7 @@ P2. Rationale: a particle is written as part of the surname it
4848
"John van der J. V" → family="van der J. V" · boundary
4949
"Freiherr von Berg MA" → family="von Berg"
5050
"Freiherr von Berg MA" → suffix="MA"
51-
"Freiherr von Richthofen V" → suffix="V" · boundary
51+
"Freiherr von Richthofen V" → family="von Richthofen V" · boundary
5252
"SMITH VD MA, JOHN" → family="SMITH VD MA"
5353
"SMITH VD, JOHN" → suffix="VD" · boundary
5454
"John van der Berg née Jones" → family="van der Berg"
@@ -76,5 +76,5 @@ P2. Rationale: a particle is written as part of the surname it
7676
(#132's ask) has it as the surnames view rather than the
7777
family field.
7878
"Vincent van Gogh van Beethoven" → surnames="van Gogh van Beethoven"
79-
history: decisions/P2 · interacts: P1, P4, H5, M2, S2, C2, S4 · implemented: nameparser/_pipeline/_assign.py, nameparser/_pipeline/_group.py, nameparser/_pipeline/_pieces.py, nameparser/_pipeline/_post_rules.py
79+
history: decisions/P2 · interacts: P1, P4, H5, M2, S2, C2, S4, S10 · implemented: nameparser/_pipeline/_assign.py, nameparser/_pipeline/_group.py, nameparser/_pipeline/_pieces.py, nameparser/_pipeline/_post_rules.py
8080

‎docs/design/rules/P3-connectives.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ P3. Rationale: connective words ("y", "of the") bind name words into
3838
connectives standing between. A connective with nothing to its right is connecting
3939
nothing, and a word of that vocabulary ending a name, or standing
4040
before the credential a name ends with, is the generation it also
41-
spells.
41+
spells wherever S10 reads one there.
4242
A separator the caller declared, standing in a trailing suffix
4343
part, ends the search there as a comma would, so a connective
4444
never joins across it (C1); anywhere else it is a word.

‎docs/design/rules/P5-bound-given-names.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ P5. Rationale: some given-name words are incomplete alone — "abdul"
4444
each read over what the other leaves until neither takes
4545
anything more, are read once, before the join is tried, and the
4646
name words they leave are the words to spare. A trailing
47-
roman numeral, or a bare acronym the run takes, or a trailing
47+
roman numeral the run takes (S10), or a bare acronym it takes, or a trailing
4848
title word the run takes, is no
4949
word to spare. The join joins two name words into one and, read
5050
after the trailing run, changes no suffix reading: a word the

‎docs/design/rules/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ The marker's unit is the whole rule, so a clause that reaches past the tolerated
5959
- [S7 — the company of an unambiguous credential](S7-company-of-an-unambiguous-credential.md)
6060
- [S8 — the credential's trailing run](S8-credentials-trailing-run.md)
6161
- [S9 — unlisted words that join the ambiguous class by shape](S9-unlisted-words-that-join-the-ambiguous-class-by-shape.md)
62+
- [S10 — the trailing roman numeral](S10-trailing-roman-numeral.md)
6263

6364
## [Nicknames & quoted names (N)](N-nicknames-and-quoted-names.md)
6465

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# S10 — the trailing roman numeral
2+
3+
S10. Rationale: a roman numeral ending a name is its generation,
4+
but one letter is also how an initial is written, and the
5+
generation it would name is rare; a name of two words has no
6+
word to spare for it.
7+
A roman numeral ending a name written without a comma reads as
8+
the generation — the numerals the vocabulary lists, and those
9+
up to X it does not, by their shape alone — unless the word in
10+
front of it is written as an initial, which makes it a name word
11+
too. A single letter written with a period is an initial, and a
12+
numeral past X is a name word. A numeral of one letter needs
13+
words to spare, as a bare ambiguous acronym does (S4): as the
14+
second of two name words it is the family name, a title in front
15+
being no word to spare, and either reading is reported, as
16+
either reading of an ambiguous acronym is. A numeral read by its
17+
shape alone reports too; a numeral the vocabulary lists in two
18+
or more letters is not in question and reports nothing, and a
19+
letter already reported as read as an initial reports nothing
20+
new. Written case
21+
settles the fork in one way only: a numeral written in lower
22+
case in a name of mixed case is the generation and reports
23+
nothing, since nobody writes an initial that way. A name written
24+
wholly in one case says nothing about any letter in it. The part
25+
before a suffix comma is read as the same name written without
26+
the comma (C1); the given part after a family comma is not
27+
counted, the comma having settled the count there (S5).
28+
"John Smith V" → suffix="V"
29+
"John Smith V" → ambiguities=("suffix-or-name",)
30+
"John W. Ingram V" → suffix="V"
31+
"John Smith VI" → suffix="VI"
32+
"John Smith VI" → ambiguities=("suffix-or-name",)
33+
"John Smith III" → ambiguities=()
34+
"John III" → suffix="III"
35+
"John V" → family="V"
36+
"John V" → ambiguities=("suffix-or-name",)
37+
"Malcolm X" → family="X"
38+
"JOHN V" → family="V"
39+
"Dr Smith V" → family="V"
40+
"John V, Dr." → family="V"
41+
"John Smith vi" → ambiguities=()
42+
"john smith i" → ambiguities=("suffix-or-name",)
43+
"John Smith V." → family="V." · boundary
44+
"John J. V" → family="V" · boundary
45+
"John Smith XI" → family="XI" · boundary
46+
Accepted: a lower-case `v` in a name written wholly in lower
47+
case is read as the suffix word the vocabulary lists, words to
48+
spare or not, and reports nothing, where the same letter in
49+
capitals is counted and reported; `i`, which is also a
50+
connective, is read as an initial there (P3) and counted.
51+
"john v" → family=""
52+
"john smith v" → ambiguities=()
53+
"john i" → family="i"
54+
"john i" → ambiguities=("conjunction-or-initial",)
55+
Accepted: inside a maiden marker's clause the connective is not
56+
read (M2), so a one-case `i` there is the suffix word the
57+
vocabulary lists, as `v` is.
58+
"john nee smith i" → suffix="i"
59+
Accepted: a numeral with another suffix word behind it is not
60+
the last word of the name and is read as any word there is, so a
61+
one-letter numeral before a credential is the family name.
62+
"John Smith V PhD" → family="V"
63+
history: decisions/S10 · interacts: S2, S4, S7, C1, P2, P3, M2, H1, H3 · implemented: nameparser/_pipeline/_assign.py, nameparser/_pipeline/_group.py, nameparser/_pipeline/_pieces.py, nameparser/_pipeline/_vocab.py

0 commit comments

Comments
 (0)