Repository navigation
de Mesnil Juan should read family de Mesnil plus given Juan — the fold takes the particle run, not the rest of the name #390
Description
rules.md#P1 states that a leading never-given particle makes the
particle run and the one name word it attaches to the family, with any
further name words read by position. The parser still folds every
remaining name word in, so the rule and the code disagree.
Behavior, all three orders
today intended
GIVEN_FIRST family='de Mesnil Juan' family='de Mesnil' given='Juan'
FAMILY_FIRST family='de Mesnil Juan' family='de Mesnil' given='Juan'
FF_GIVEN_LAST family='de Mesnil Juan' family='de Mesnil' given='Juan'
name_order deliberately has no effect on this input class, and
that is the decision rather than an oversight. A leading never-given
particle is evidence about how the name is written — the Latin-script
analogue of rules.md#W4, where a wholly-hangul name reads
family-first under every declared order because the script settles it.
decisions.md#O4 draws the line the same way: "Words no vocabulary
has claimed read by position", so the declared order governs the
unclaimed remainder, which is most inputs but not this one.
The alternative — particle group takes position one, name_order
names the position — is Declined at decisions.md#P1: it makes
de la Vega read given='de la Vega' with no family under
GIVEN_FIRST, breaking a corpus name and v1 parity, unless a further
rule forces a particle-headed lone group into the family, at which
point it produces this issue's reading anyway.
Where the decision lives
Decided 2026-08-16 in #386 as part of the order-precedence keystone,
recorded at decisions.md#P1 (the 2026-08-16 #364 entry). #364
asked the question and closed once it was answered; this issue is the
implementation, so the two deviates: #364 markers on rules.md#P1
have a live target.
"Takes everything" was never argued for — it was the shape of v1's
handle_non_first_name_prefix, not a decision.
Measured scope
One corpus name moves, over all 782 names of the three differential
corpora with no prefilter:
de Mesnil Garcia family='de Mesnil Garcia' → family='de Mesnil', given='Garcia'
Two lookalikes do not move: de la Vega is one group start to
finish, and de Mesnil Jr. has two name words because Jr. is a
suffix.
Measurement trap, from mechanisms.md: the particle run chains
through any particle, not only the never-given ones. A detector
walking only the never-given run splits de la Vega after de la and
reports 50 false movers.
Implementation note
The fold is in nameparser/_pipeline/_post_rules.py and today reads
for i in givens + middles: _retag(tokens, i, Role.FAMILY) — every
remaining name word. It needs the particle's own group instead, which
state.pieces already carries. The DEVIATION #364 comment above it
comes out in the same diff.
Sibling
#365 is the same rule reached under FAMILY_FIRST, where the particle
strands in middle. Both close together: once grouping is
order-independent, the middle position needs no third fold site.
Verification
tools/differentialat all three baselines, with a ledger entry for
de Mesnil Garcia- both
deviates: #364markers removed fromrules.md#P1in the same
PR (the runner asserts today's value, so the suite fails until they
go) Mesnil Garcia destrands the particle under FAMILY_FIRST but folds it under FAMILY_FIRST_GIVEN_LAST #365's family-first shapes come back with 0 diffs, the property that
issue asks for
Activity
derek73 commentedon Aug 17, 2026
First attempt closed unmerged — three routes still violate P1, and one regresses
PR #391 implemented the claim in
assign, before positions are handedout. That part works for the shape this issue names:
It passed 3459 tests,
tools/differentialexit 0 at 1.4.0/2.0.0/2.1.0,ruff and mypy. A
/pr-review-toolkit:review-prpass with three agentsthen found eight issues, all verified by measurement. Recording them
here so the next attempt starts from them rather than rediscovering
them.
Blocking:
de los Santosregresses to a wrong surnamelos,las,das,elare not in the particle vocabulary — onlyde,la,der,denare. So the claim takesde+losand handsthe real surname to
given.This is the important one, because it inverts the bundle's sequencing.
"Takes everything" is insensitive to vocabulary gaps; "takes the group"
is not. The old sweep was masking an incomplete particle set, and
narrowing the claim converts that gap into user-visible breakage on a
very common Hispanic name shape. #360 has to land first, with C-i
applied to the unexamined articles (
los/lasare articles, borne asno one's name, so C-i puts them in the never-given half).
The differential could not see it: no
de losname exists in any of thethree corpora. Corpus-blindness hiding a regression rather than a small
count.
Blocking: a multi-particle run still folds the whole name
Cause is
rules.md#P4: the leading particle chains nothing, sodeisits own piece and the next particle chains everything after it —
pieces = [['de'], ['la','Vega','Juan']]. Takingpieces[:2]takes thewhole name.
de Mesnil Juanworks only because it has no secondparticle. So the implementation takes the particle run plus one
piece, where P1 says one word; the two differ exactly when the run
spans more than one piece.
Blocking: the family-comma path never reaches the claim
_assign_mainis not called forFAMILY_COMMA, so the oldwhole-remainder sweep survives in
post_rulesfor segment 1.All three agents converged independently on the last two, which is the
strongest signal in the set.
#365 does NOT close with this
The attempt claimed it did. It does not — verified:
The surviving
post_rulessite is still ROLE-keyed, which is #365'sactual defect. Relocating the leading half does not reach it.
Other verified findings
PARTICLE_OR_GIVENemits a false detail.de Mesnil van MD Goghreports "leading 'van' may be a family-name particle" —
vanis thethird piece. Cause:
name_piecesis rebound to the leftover beforethe emitter reads
name_pieces[0], silently changing its meaning._ambiguitiesis a compared differential field, so this is a latentunexplained diff.
de Mesnil Juan Carlos→ FFGL swaps given/middle. The_name_positions(order, count + 1)/ drop-FAMILY line is pinned onlyin its degenerate one-leftover case, where all three orders coincide.
Dr. de Mesnilcannot exercise thetitle peel under the module's fixture lexicon (
titles={"mr","sir"}—drabsent), sode Mesnilchains into one piece, the claim neverfires, and the test passes with the feature disabled. Mutation-verified.
Mr.is the title that works here._CROSS_RULE_WINNERSneeds a row forde Mesnil Garcia: withoutthe new ledger rule it falls to the fields-only
fix(suffix-routing)catch-all with the gate still at 0 unexplained.
amendment rule puts them in the diff: no
release_log.rstbullet, theexisting particles_ambiguous has no effect on the parse under name_order=FAMILY_FIRST #359 bullet now false,
nameparser/config/particles.pydocstrings (autodoc-rendered API reference),
docs/customize.rst,docs/usage.rst,AGENTS.md:198,mechanisms.md#PIECESstill naming_post_rules.pyas the pieces reader, and both stage docstrings.Every doctest keeps passing because every example is a two-word name.
What the next attempt needs
Mc DonaldandSte Marieread the particle as the given name #360 first. The claim cannot be narrowed while the particle sethas gaps.
blocking routes need a chained piece SPLIT (
de la Vega Juan) andthe third needs the
FAMILY_COMMAbranch — neither is reachable fromwhere
assignhands out positions.deviates: #364markers onrules.md#P1until all threeroutes hold. Removing them while the comma and multi-particle routes
still sweep is what let the doc claim a rule the parser does not have.
Branch
fix/390-fold-takes-particle-groupis pushed if any of it isworth salvaging;
masteris untouched.The leading-particle claim takes its run plus one word (#390, retry)
derek73 commentedon Aug 18, 2026
Second attempt also closed unmerged (PR #394) — and the rework strategy is now clearer
#394 put the claim in grouping, which fixed the three routes #391 could
not reach. It still regressed four shapes and stated several things in
the normative docs that are false. A three-agent review found them; all
were verified by measurement against
master.What #394 got right, and should be kept
de la Vega Juan→family='de la Vega',given='Juan'(the routeThe leading-particle claim takes its group, not the rest of the name (#390, #365) #391 could not reach at all)
de la Cruz Maria,de Mesnil Juanlikewise, in all three ordersde los Santossurvives —Mc DonaldandSte Marieread the particle as the given name #360 landing first did its jobambiguous one does not, and mid-name runs stay greedy (P2's join
genuinely unchanged, verified)
Regressions from master
False statements no test could see
rules.md#P4: "A title before either changes nothing" — three TITLESmembers (
st,do,freiherr) change everything.rules.md#P2: "The final group reads as the family name" — false forevery name the PR moves (
de Mesnil van der Berg→family='de Mesnil',given='van der Berg'). P2 got no amendment and has nointeracts:line, so nothing points back at P1/P4.credited
not titleforst/do/freiherr— which are in theAMBIGUOUS half, so a different conjunct already excludes them.
family='Smith de Mesnil'" — v1.4.0 actually giveslast='de Mesnil Smith'. Wrong indecisions.mdand a test comment.fix(suffix-routing)being "below" wascopy-pasted into the 2.0.0 and 2.1.0 ledgers, which have no
fields-only catch-all at all.
The core new code was unpinned
Verified by mutation on a copy: replacing the
_name_positions(order, count + 1)minusFAMILYconstruction with ahardcoded
[GIVEN] + [MIDDLE]*(n-1)— discardingname_orderfor theleftover entirely — passes the whole suite. So does removing the
prefix(leading)conjunct, which is also inert (no input distinguishesit). The two-leftover shape (
de Mesnil Juan Carlos), where the ordersactually diverge, appears in no test, no case row and no rule example.
Also a THIRD fixture-trap casualty:
Dr. de MD Mesnilcannot exercisethe leading chain, because
dris absent fromtest_post_rules.py'sreduced
_LEX.The rework, corrected
My first proposal was to walk tokens and ignore piece boundaries. That
is wrong, and Derek's correction is why: the conjunction rule means a
"word" in other rules can be several words.
Vega y Santosis oneunit, so
de la Vega y Santos Juan→family='de la Vega y Santos',given='Juan'is CORRECT — a review finding against it was a falsepositive. Ignoring piece boundaries would split it.
The formulation that explains every case, including both regressions:
Both #394 regressions are this rule not being applied: it extended past
a piece that already had a name word, and it stopped on a piece that
had none. Stated this way the conjunction merge and the title overlap
stop being special cases.
Whatever lands next also needs, per the review:
name_ordermatters for the remainder)rules.md#P2amended or marked — its "final group is the family"sentence does not survive this change
DEVIATION #364comment in_post_rules.pyremoved_LEXBranch
fix/390-retry-claim-in-groupingis pushed if any of it isworth salvaging.
masteris untouched.derek73 commentedon Aug 18, 2026
Superseded by #395.
The direction changed after two failed attempts: the stopping point is now keyed on
name_orderrather than on position alone. Declaring a family-first order is precisely an assertion that what follows the family is not more surname, so the default order keeps the greedy reading (de Mesnil Juan→family='de Mesnil Juan', the same shape aspennie von bergen wessels) and only family-first orders stop at the first name word.That makes this issue's title false — it asserts the bounded reading for the default order.
decisions.md#P1carries a 2026-08-17 entry superseding the "no name_order moves that stopping point" sentence this issue was written under.Everything the two attempts established — the guard traps, the coverage gaps, the fixture trap, the docs needing amendment — is carried forward to #395.
docs(decisions): a family-first order may move the particle stop