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
rules.md#P1states that a leading never-given particle makes theparticle 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
name_orderdeliberately has no effect on this input class, andthat 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 readsfamily-first under every declared order because the script settles it.
decisions.md#O4draws the line the same way: "Words no vocabularyhas 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_ordernames the position — is Declined at
decisions.md#P1: it makesde la Vegareadgiven='de la Vega'with no family underGIVEN_FIRST, breaking a corpus name and v1 parity, unless a furtherrule 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(the2026-08-16 #364entry). #364asked the question and closed once it was answered; this issue is the
implementation, so the two
deviates: #364markers onrules.md#P1have 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:
Two lookalikes do not move:
de la Vegais one group start tofinish, and
de Mesnil Jr.has two name words becauseJr.is asuffix.
Measurement trap, from
mechanisms.md: the particle run chainsthrough any particle, not only the never-given ones. A detector
walking only the never-given run splits
de la Vegaafterde laandreports 50 false movers.
Implementation note
The fold is in
nameparser/_pipeline/_post_rules.pyand today readsfor i in givens + middles: _retag(tokens, i, Role.FAMILY)— everyremaining name word. It needs the particle's own group instead, which
state.piecesalready carries. TheDEVIATION #364comment above itcomes out in the same diff.
Sibling
#365 is the same rule reached under
FAMILY_FIRST, where the particlestrands in
middle. Both close together: once grouping isorder-independent, the middle position needs no third fold site.
Verification
tools/differentialat all three baselines, with a ledger entry forde Mesnil Garciadeviates: #364markers removed fromrules.md#P1in the samePR (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 thatissue asks for