Make a title transparent to the leading-particle exception (#367) - #370
Open
derek73 wants to merge 9 commits into
Open
Make a title transparent to the leading-particle exception (#367)#370derek73 wants to merge 9 commits into
derek73 wants to merge 9 commits into
Conversation
A leading particle deliberately does not chain -- that is what makes
"Van Johnson" read as given Van, family Johnson rather than as one
surname. But the exception was keyed on piece index 0, so a TITLE
displaced the particle out of leading position and the chain fired.
Identical name text then parsed differently depending on whether a
title preceded it:
Van Johnson given='Van' family='Johnson'
Dr. Van Johnson given='' family='Van Johnson'
Sir Van Johnson given='Van Johnson' family=''
The last row compounded it: with a given-name title, post_rules rule
1's carve-out handed the whole chain to `given`, so the name came out
with no surname at all.
A title is not part of the name, so it cannot decide whether the NAME
begins with a particle. The chain loop's `k == 0` becomes "the first
piece of the name", computed once before the loop.
Not the plain "first piece that is not a title" the rule is stated as,
and the difference is not academic: `st`, `do` and `freiherr` are each
BOTH a title and an ambiguous particle, so a plain title test skipped
over the very piece the exception exists to protect. Measured, the
untitled "St John Smith" collapsed from title St, given John, family
Smith into one given name "St John Smith", and "Do John Smith" and
"Freiherr Blah Blah" with it -- none of which has a title in front of
it at all. It also broke test_constants.py::test_add_title, whose
"Te Awanui-a-Rangi Black" adds `te` to the titles while `te` ships as
an ambiguous particle, manufacturing the same overlap from config. So
the scan steps over a piece that can ONLY be a title and stops at one
that could be the name's own first piece; with that, every untitled
shape is byte-identical and test_add_title needs no change.
Suffix-flagged pieces are deliberately not skipped too. The shapes
that look like they need it -- "Jr. Van Johnson", "MD Van Johnson",
"PhD Van Johnson" -- classify their leading piece as a TITLE and are
already covered. What remains is a leading piece assign puts in GIVEN
("Ph.D. Van Johnson", "II Van Johnson", "Msc.Ed. Van Johnson"), which
is part of the name in the output and holds the leading name position
exactly as John does in "John Van Johnson"; skipping it also measured
"Ph. D. Van Johnson" losing its family name outright.
What moves, all of it a titled name whose first name-piece is a
particle:
Dr. Van Johnson family='Van Johnson' -> given='Van' family='Johnson'
Dr. Van Johnson Smith family='Van Johnson Smith'
-> given='Van' middle='Johnson' family='Smith'
Mr. Van Nguyen family='Van Nguyen' -> given='Van' family='Nguyen'
Sir Van Johnson given='Van Johnson' -> given='Van' family='Johnson'
Sir Van Johnson Smith given='Van Johnson Smith'
-> given='Van' middle='Johnson' family='Smith'
Sir de Mesnil given='de Mesnil' -> family='de Mesnil'
Sheik Abu Bakar given='Abu Bakar' -> given='Abu' family='Bakar'
The last one is a regression, tracked as #369: it worked before only
because `abu` happens to be a particle as well as a bound given name,
and "Sheik abdul salam" shows bound-given alone does not chain.
test_first_name_is_prefix_if_three_parts drops a v1-era xfail whose
docstring said "Not sure how to fix this without breaking Mr and Mrs".
It does not break Mr and Mrs -- a bare "Mr./Mrs. Surname" has no
particle to displace, so nothing about it reaches the rule that moved
-- and those shapes are now asserted in the test rather than only
asserted about.
cases.py's titled_ambiguous_particle_chains pinned the opposite and
cited v1 parity. Parity was real (1.4.0 gives last 'Van Johnson') and
still not the tiebreaker it looked like: "Dr. Van Johnson" and
"Mr. Van Nguyen" are the same shape, so v1 pinned one shape both as
correct and as broken. Resolved toward the xfail. The fork is still
reported either way, from assign rather than group -- the same place
the untitled "Van Johnson" reports it.
_group's PARTICLE_OR_GIVEN emitter is NOT dead, though every test that
used to reach it went through a plain title. It fires only when a
piece before the particle is both a title and a prefix, which is the
`st`/`do`/`freiherr` class again: "Freiherr von Richthofen" is the one
natural spelling of it, and the four tests that pinned the emitter
move onto it. The no-op-chain tests move to "Do Van Jr." for the same
reason -- with a plain title the loop now skips the particle before
the j > k + 1 guard is ever consulted, so they were passing without
exercising the branch they exist to pin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first 2.2 change to move the default-order corpus, so expected_since_2.1.0.toml gains its first rule and its header stops saying it is empty on purpose. Exactly one of 751 corpus names moves -- 'Mr. Van Nguyen', given '' -> 'Van' and family 'Van Nguyen' -> 'Nguyen' -- measured by running every corpus name through fd8dd8d and through the tree rather than by reading the harness's summary, so the count covers names that already diff from a baseline for other reasons. No field outside given/middle/family moves on any name. baseline intentional unexplained 2.1.0 1 0 2.0.0 90 0 1.4.0 108 0 The 1.4.0 copy has a second job. That run already exited 0 WITHOUT a rule, because the diff was landing on fix(suffix-routing), a fields-only rule about a trailing credential whose ["given", "family", "suffix"] is a superset of this diff's fields -- a rule silently absorbing a change it does not describe, which is the mis-classification the README warns about and is invisible in an exit code. A name_regex rule outranks every fields-only one, so the new rule claims it back: that class goes 26 -> 25 and #367 takes 1, with no other class moving at any baseline. The regex is deliberately narrower than the class it documents. The class is any title followed by a particle -- 'Dr. Van Johnson', 'Sir de Mesnil', 'Sheik Abu Bakar' all move -- and only `van` is written, because it is the only member the corpora exercise; a name that joins the class later should arrive UNEXPLAINED and be read once. The leading `\S+\.?` title slot is the loose half, since no regex over the raw string can ask whether a word is a title, and `fields` carries the tightness instead. _CORPUS_CLAIMS records the rule at 11 names in each ledger -- the corpus names the regex reaches, not the one it explains, which is what that roster measures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The four tests that pinned _group's PARTICLE_OR_GIVEN emitter moved to "Freiherr von Richthofen" in the first commit, which leaves the mechanism pinned only from the public API. Two more pins, one per half of the rule, at the stage that owns it: test_a_title_does_not_make_the_particle_behind_it_non_leading is the change itself in `pieces` -- "Mr. Van Johnson" groups as ["Mr.", "Van", "Johnson"], the untitled grouping with a title in front of it. test_a_title_that_is_also_a_particle_stops_the_scan is why the rule is not spelled "the first piece that is not a title", and it is written against a CONFIGURED overlap rather than against `st`/`do`/`freiherr`. That is the same shape from the other direction: this module's fixture lexicon is a small hand-built one, and adding `van` to its titles is exactly what tests/test_constants.py::test_add_title does with `te` -- which is how the plain-title spelling was caught, since it broke that test as well as the untitled "St John Smith". test_a_leading_ambiguous_particle_is_reported_once_and_only_once gains "Freiherr " to its lead tuple. It swept "", "Dr. " and "Dr. Ann " over all 39 ambiguous particles and four tails, and after #367 not one of those reaches _group's emitter -- the sweep of the coordination between two emitters would have been measuring one. The new lead is the shape that still reaches it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every claim below was re-measured by parsing rather than reasoned about, per AGENTS.md's rule for mechanism claims. docs/release_log.rst gains the Behavior Changes bullet, which has to say plainly what moves because it reverses documented behaviour. It names the shapes that move, the shapes that provably do not (bare Mr./Mrs., and the title-that-is-also-a-particle class), the detail-text change that follows from the fork moving stages, and the "Sheik Abu Bakar" regression tracked as #369. The 2.2 summary paragraph above it claimed no default-order parse changed at all; that was true through #354, #358 and #361 and is not true now. AGENTS.md, in the two places it asserted the old reading. The particles.py entry recorded "Sir de Mesnil" -> given "de Mesnil", no family, as an output believed wrong and tracked as #367; it is now the untitled reading, and #367 got there by removing the chain rather than by touching post_rules rule 1b, which is the part worth keeping. The ambiguity-emitter entry said _group reports "when a title shifts it off index 0", which was the whole of that emitter's reach and is no longer any of it -- and since "is this emitter now dead?" is the question a reader arrives with, the answer and the input that settles it are written down. nameparser/_types.py's PARTICLE_OR_GIVEN docstring, published API reference: the second shape it describes is no longer "a particle a title shifted off the front". nameparser/config/particles.py said the chain "skips the first piece unconditionally, membership in this set or any other never entering into it". The first half now reads "the first piece of the NAME", and the second half is false as written -- the titles vocabulary does enter into it, in exactly one way, which is the st/do/freiherr overlap and is worth naming where a reader adding a particle will see it. nameparser/_lexicon.py's particles_ambiguous docstring and _pipeline/_assign.py's module docstring each named the leading piece in a way the change makes ambiguous; both now say which "leading" they mean. _group.py's emitter comment is rewritten around the reachability argument rather than the example: `all(title(x) for x in range(k))` and `leading` can now both hold only if one of the leading titles is also a particle, which is what makes the branch narrow rather than dead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #370 +/- ##
=======================================
Coverage 98.51% 98.51%
=======================================
Files 44 44
Lines 2894 2895 +1
=======================================
+ Hits 2851 2852 +1
Misses 43 43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
_group's PARTICLE_OR_GIVEN emitter needs a leading piece that is both a title and an ambiguous particle, and since this branch that is the ONLY shape reaching it -- an ordinary title is now transparent to the leading-particle exception, so it takes _assign's branch. The five tests covering the emitter therefore lead with "Freiherr", and a comment names the class (freiherr/st/do) they depend on. A comment is the wrong medium for that. #360 is considering moving words between the particle sets, and freiherr and st are both on its candidate list. If either moves, those tests fail as five unrelated-looking parse mismatches and the reader has to find the comment to learn why. The precondition is now a test, and it distinguishes the two cases, which want opposite fixes. A missing WORD means pick another from the set -- the message prints what is left. An EMPTY intersection means the emitter is unreachable, every test of it is measuring nothing, and the right move is to delete the emitter rather than repoint its tests. Verified both branches by evaluating the guard against mutated sets: dropping 'freiherr' reports "need a lead from ['do', 'st']", dropping all three reports "remove the emitter rather than repointing". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The release log and all three ledgers claimed the change only ever redistributes given/middle/family and "never touches title, suffix, nickname or maiden". Two of those four are wrong. `title` moves: a word in BOTH vocabularies stops the transparency scan and stays a title piece instead of being chained onto the name, so "Dr. St John Smith" goes from title 'Dr.', family 'St John Smith' to title 'Dr. St', given 'John', family 'Smith'. `maiden` moves: un-chaining lets a marker standing behind the particle be seen at all, so "Mr. Van Johnson nee Brown" goes from family 'Van Johnson nee Brown' with no maiden name to given 'Van', family 'Johnson', maiden 'Brown'. Over a 20,979-name stress set of this class, `title` moved on 2,583 names (always growing, never shrinking) and `maiden` on 1,638 (always appearing, never lost); `suffix` and `nickname` moved on none. The ledgers keep fields = ["given", "middle", "family"], which is still what they should claim -- it is now stated as deliberately narrower than the change, so a title- or maiden-moving corpus name arrives UNEXPLAINED rather than being absorbed, the same posture the `van` literal already takes. Two more counts in the release log: * the release summary said the family-first orders move "the same seven names under each". Measured against 2.1.0 over the 751 corpus names it is eight under each -- the seven from #359 plus "Mr. Van Nguyen", which the title fix moves in every order. * the #367 bullet stated every worked example in default-order fields without saying so. Under both family-first orders "Dr. Van Johnson" reads family 'Van', given 'Johnson'; the #359 bullet below already names its orders explicitly, so this one now does too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three sites still described #367 as an open bug. The prose sweep caught AGENTS.md and missed these: * nameparser/config/particles.py -- "'Sir de Mesnil' gives given 'de Mesnil' and no surname at all -- that is an open bug (#367)". Measured on HEAD: title 'Sir', family 'de Mesnil'. The remaining case where a chain IS reported as the given name is the one this set means to draw -- FAMILY_FIRST "Juan de la Vega", given 'de la Vega' -- so that is what it now cites. This is a `#:` comment and renders into the published API docs. * _post_rules.py's 1b preamble -- same claim, and the guard now fires. * _post_rules.py's worked example for 1b's single-token invariant was wrong end to end. "Mr. de Mesnil" is not "three tokens in two pieces"; it is three tokens in THREE pieces, [['Mr.'], ['de'], ['Mesnil']]. Both sites are one token long, so 1b FIRES rather than declines, and rule 1 cannot be producing the family reading -- rule 1 is gated on `not families` and 'Mesnil' is already the family. Output is unchanged either way, which is why no test caught it. "Exactly one shape" / "the only shape" reaches _group's PARTICLE_OR_GIVEN emitter was asserted at five sites (_group.py, tests/v2/cases.py, tests/v2/test_parser.py twice, AGENTS.md) and is false. Measured over a 20,979-name stress set: 2,520 hits, and "Freiherr von Richthofen", "St Van Johnson", "Do St Johnson" and "Dr. Do van Johnson" are four structurally different ways in. The last one matters most, because _group.py said "a plain title no longer reaches here at all" and it has one. The real reach is: any number of plain title pieces, then a piece in BOTH vocabularies, then any number of further titles, then an ambiguous particle whose chain claims something. The reachability argument itself is sound and is now stated the way it actually works. `leading` is STRICTLY before k (the loop skips k == leading), so it is one of the titles ahead of k and must also be a prefix -- a word in both vocabularies. The conjunction merge is the only other way a piece acquires those tags and cannot manufacture the pair: it derives from one neighbor, the left one whenever there is one, and its right operands are always fresh pieces. 243,280 conjunction-bearing probes produced no title+prefix piece without a both-vocabulary word. tests/test_first_name.py's docstring stated the rule as "the first piece that is not a title" -- precisely the naive rule this PR shows is wrong. Measured: that predicate collapses "St John Smith" into one given name and fails 15 tests including test_add_title. The shipped predicate is `not title(k) or prefix(k)`; the Mr/Mrs conclusion holds under either, which is why no test caught the docstring. _group.py's suffix rationale had the sign backwards. Skipping suffix pieces does not cost "Ph. D. Van Johnson" its family name -- shipped gives given 'Van Johnson', family '', and the skipping variant gives it given 'Van', family 'Johnson'. The real reason is the other half: "Ph.D. Van Johnson", "II Van Johnson" and "Msc.Ed. Van Johnson" put their leading piece in `given` and read family 'Van Johnson', and skipping moves Van out of the family into the middle name. The pointer to the 2.2.0 release log, which says nothing about suffixes, is replaced by the reasoning inline. Minors, all measured: _lexicon.py said "Dr. de Mesnil" groups into two pieces (it is three; the NAME is two); the `, 0` fallback now says it is inert by construction, since no piece being a prefix means the loop merges nothing; and cases.py's `titled_ambiguous_particle_chains` is renamed to `..._does_not_chain`, its id having come to contradict its own expectation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ries (#367) The fix(#367) rule shipped with name_regex = "(?i)^\S+\.?\s+van\b". The title slot matched any first word at all, so the rule reached 11 corpus names and explained 1. The other ten include 'Vincent van Gogh', 'VINCENT VAN GOGH' and 'Alex van Johnson' -- the "Van Johnson" family AGENTS.md names as a standard regression canary. Since name_regex rules sort ahead of fields-only ones and `fields` matches by subset, a future regression on those names was labelled fix(#367) and exited 0. At the 2.1.0 baseline that is a strict loss: the ledger was empty before this PR. It is the same defect this PR found in fix(suffix-routing). Narrowed to "(?i)^mr\.\s+van\b", matching the comment's own deliberately-narrower-than-the-class posture. `\.?` went with it, being inert -- `\S+` is greedy, so it had already taken the period; both spellings reach the same 11 names. Demonstrated rather than argued. With a simulated chain regression in a scratch tree, the wide regex absorbs 7 corpus names under fix(#367) -- 'Alex van Johnson', 'Vincent van Gogh', 'VINCENT VAN GOGH', 'Vincent van Gogh van Beethoven', 'Charles van der van der Berg', 'Mike van der Velt' and the one it explains -- while the narrow one absorbs 1 and every canary arrives UNEXPLAINED. The three _CORPUS_CLAIMS entries go from _Claim(11, ..., "b9d738c0e73b") to _Claim(1, ..., "dce0ae6df4be"), read off the guard rather than predicted. All three baselines still exit 0 and still classify 'Mr. Van Nguyen'. Four things the prose asserted and nothing tested: * The suffix decision. "Also skip suffix pieces" survives all 3169 tests while changing output, so test_a_suffix_shaped_leading_piece_is_not_stepped_over pins the two shapes that decide it. Kill proven: with the mutation applied to a scratch copy of this tree, that test is the only failure (1 failed, 3169 passed). * Four shapes the release log names with zero assertions anywhere get cases.py rows: "Dr. Van Johnson Smith", "Sir Van Johnson", "Sir de Mesnil" and "Jr. Van Johnson", each classified fix(#367) against measured 1.4.0 output. The existing "Dr. Van Johnson" row is reclassified from the default "parity" for the same reason: 1.4.0 gives last 'Van Johnson', so parity stopped holding when this PR changed the row's expectation. * #369's regression. "Sheik Abu Bakar" lived only in ledger comments and is in no corpus, so nothing would notice it moving again. A strict xfail now asserts the DESIRED post-#369 output; verified strict by flipping the assertion to today's output in a scratch tree and watching it fail XPASS(strict). * test_chained_particle_detail_is_order_invariant covered all three orders for "Freiherr von Richthofen" -- the shape #367 did NOT move. It now also pins "Dr. Van Johnson" against the bare "Van Johnson" in each order, including family 'Van', given 'Johnson' under both family-first ones. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This PR respelled titled_ambiguous_particle_no_op_chain from "Dr. Van
Jr." to "Do Van Jr." so it would still reach the chain loop's j > k + 1
guard -- a plain title is transparent now, so the old spelling never
gets there. The row kept its default `parity` classification through
the respelling, and the new text does not hold parity:
1.4.0 first='Do Van' last='Jr.'
this branch title='Do' given='Van' suffix='Jr.'
cases.py's own header says changing a row means changing its
classification, and the respelling is this PR's doing even though the
divergence is not: 'do' being a title and 'Jr.' routing to suffix are
both 2.0-era, so neither belongs to #367. Classified `fix` rather than
a specific slug because there are two independent causes, and the note
now records what they are so the next reader does not have to
re-measure 1.4.0 to find out.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #367.
A leading particle deliberately does not chain — that is what makes
Van Johnsonread as givenVan, familyJohnson. But the exception was keyed on piece index 0, so a title displaced the particle out of leading position and the chain fired. Identical name text parsed differently depending on whether a title preceded it:A title is not part of the name, so it should not change whether the name begins with a particle. The exception now skips leading title pieces.
What moves
Unchanged:
Mr. Smith,Mrs. Smith,Mr. Nguyen,Mr. John Smith,Sir Ian,Sir Ian McKellen,King Henry, and every untitled shape.It fixes a v1-era xfail that said it couldn't be fixed
It passes now, and it does not break Mr and Mrs — every bare
Mr./Mrs. Surnameshape is byte-identical, because those have no particle to displace. The marker is removed.It also reverses the case row
titled_ambiguous_particle_chains, which pinnedDr. Van Johnson→family='Van Johnson'as intended v1 parity. That row and the xfail describe the same shape — the repository pinned it simultaneously as correct and as broken. This resolves it toward the xfail.The naive fix is wrong, and the suite caught it
Skipping pieces that are titles breaks names that never had a title:
st,doandfreiherrare each both a title and an ambiguous particle, so a plain "not a title" test steps over the very piece the exception protects.That is also what failed
test_add_title: it addsteto the titles, andteships as an ambiguous particle. The shipped test isnot title(k) or prefix(k)— step over a piece that can only be a title, stop at one that could be the name's own first piece. With it, every untitled shape is byte-identical andtest_add_titleneeds no change.Suffix pieces are deliberately not skipped, by measurement: the shapes that look like they need it (
Jr. Van Johnson) classify their leading piece as a title already, and skipping the rest costPh. D. Van Johnsonits family name._group's PARTICLE_OR_GIVEN emitter is still liveIt fired only when a title displaced the particle, so it looked like dead code. It isn't: the emitter needs
all(title(x) for x in range(k)), which after this change requires a leading piece that is a title and a prefix — exactly thest/do/freiherroverlap.The five tests that covered it are re-pointed there. Two others —
test_no_op_prefix_chain_is_not_a_forkandtest_a_fork_is_reported_by_exactly_one_stage— turned out to be passing without exercising their branch once a plain title stopped reaching it, and now use inputs that do.Differential
Exactly one corpus name moves,
Mr. Van Nguyen, and only itsgiven/family. All three baselines exit 0:A ledger integrity finding came out of this. The 1.4.0 run exited 0 before any rule was added — the diff was being absorbed by
fix(suffix-routing), a fields-only rule about a trailing credential whose["given","family","suffix"]is a superset of this diff's fields. A rule was silently explaining a change it does not describe, and the exit code showed nothing. The newfix(#367)rule claims it back.Review round
A four-agent review found the mechanism correct and the prose wrong in nine places. Five follow-up commits.
Verified sound, at depth.
leadingis computed once before a loop that mutatespieces; a variant recomputing it every iteration is byte-identical over 29,105 names. The, 0fallback is provably inert, not merely unexercised. And the best evidence the change is right: over 268 corpus names, comparingparse(N)againstparse("Dr. " + N), the base tree has 30 non-transparent names and this branch has 19 — a strict subset. Nothing becomes newly non-transparent; 11 are fixed.What was wrong:
titlemoves (Dr. St John Smith→ titleDr. St) andmaidenmoves (Mr. Van Johnson nee Brown→ maidenBrown, because un-chaining exposes the marker). The claim appeared in the release log and, load-bearingly, as the justification for the ledger rule'sfieldsin all three ledgers.suffixandnicknamegenuinely never move.(?i)^\S+\.?\s+van\breached 11 corpus names and explained 1 — the others includingVincent van Gogh, whichAGENTS.mdnames as a standard canary. Sincename_regexrules outrank fields-only ones, a future regression there would have been labelledfix(#367)and exited 0. This PR discovered that exact defect infix(suffix-routing)and then reproduced it; now narrowed to(?i)^mr\.\s+van\b, reaching 1. (\.?was inert —\S+is greedy.)Dr. Van Johnsonreads as one surname #367 an open bug, one a#:comment rendered into the published API docs. A fourth was wrong end to end: it describedMr. de Mesnilas two pieces with rule 1b declining; it is three pieces and 1b fires. The output is unchanged, which is why no test caught it.Dr. Do van Johnson, where a plain title is present. The reachability argument itself is sound and survives the conjunction-merge path; only the framing was wrong.Ph. D. Van Johnsonhas no family name under the shipped rule; skipping suffixes gives it one. The real reason is the other half — skipping movesVaninto the middle forPh.D./II/Msc.Ed.shapes. Corrected and inlined; the citation pointed at a release-log section that never existed.Sir Van Johnson,Sir de Mesnil,Jr. Van Johnson,Dr. Van Johnson Smith) had zero assertions; theSheik abdul salamsplits the bound given name thatabdul salam ahmedjoins #369 regression had none and is not in the corpus. Both now covered, the latter by a strict xfail so fixingSheik abdul salamsplits the bound given name thatabdul salam ahmedjoins #369 announces itself.test_first_name.py's docstring named the rule this PR rejected.Filed rather than fixed here: #371 (a leading suffix displaces the particle exactly as titles used to —
Ph. D. Van Johnsonhas no surname) and #372 (pre-existing ledger rules classifying diffs they do not describe, plus a rule that stops explaining anything being invisible to the harness).Known regression, tracked
Sheik Abu Bakar→ givenAbu, familyBakar. It read as one given name only becauseabuhappens to be a particle as well as a bound given name —Sheik abdul salamshows bound-given alone never chained. #369 covers giving that reading a path of its own.🤖 Generated with Claude Code