Skip to content

Read any of the pipeline's hyphens as a hyphen, and set a maiden name's particles aside (#1264) - #1344

Merged
derek73 merged 5 commits into
masterfrom
fix/issue-1264-hyphens-and-maiden-particles
Nov 12, 2026
Merged

derek73 merged 5 commits into
masterfrom
fix/issue-1264-hyphens-and-maiden-particles

Conversation

@derek73

@derek73 derek73 commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Settles the two gaps #1341 left open as decisions on #1264, both now decided yes. The other three rows of #1264's table were settled earlier (#1289, #1296, #1341), so this closes it.

Read first

  • The hyphen claim holds where the parser reads a name alike. The parser itself reads some names differently with another hyphen, and the match compares the fields it is given. This limit was already present at the merge base, and this PR does not change the parser.
    • A credential or generation joined by another hyphen becomes a name word: John Smith MD‐PhD gets family MD‐PhD; John Smith, NP‐C, , OTR‐L and , M.D.‐Ph.D. put the credential in the given name; John Smith Jr.‐III gets family Jr.‐III. No suffix written with a hyphen parses alike under the other hyphens.
    • A trailing title the vocabulary lists with the hyphen-minus becomes a name word too: Hans Meier Priv.‐Doz. gets family Priv.‐Doz..
    • A spaced hyphen between credentials stays inside the suffix: John Smith, MD ‐ PhD keeps suffix MD ‐ PhD, where the hyphen-minus gives MD, PhD.
    • These pairs stay apart. They conflict, except where the family-first order lands the word in the middle name, where it reads one-sided.
    • _PARSER_HYPHEN_LIMITS pins them under all four parsers, so a parser fix will show up. Titles at the head, at the end and after a comma do read alike (Dipl.‐Ing. Hans Meier, Hans Meier Dipl.‐Ing., John Smith Lt.‐Col., Hans Meier, Dr.‐Ing.).
  • The maiden name does not get the family chain whole. It asks particles-aside and then part-of-other on its two bases, ahead of the outcomes it read before.
    • Measured cost of the whole chain on the same draw: 126 maiden rows initial → family-initial, 21 initials-agree → part-of-other, 21 hyphenated-part → conflict, and 42 more conflict → part-of-other.
    • In verdicts, that is 81 consistent → inconsistent (e.g. Ana Ruiz (née V.) / Ana Ruiz (née Vega)) and 41 more inconsistent → consistent.
    • As built, no verdict moves to inconsistent under any setting. Overrule here if "the way a family name is" was meant literally.
  • Two maiden texts of initials alone skip the new leniencies, following joined's recorded exception. So (née J.-P.) / (née J. P.) stays initials-agree, which is matching.rst's example of it, and (née J. P.) / (née P. J.) stays a conflict.
    • The family name reads J. P., Ann / P. J., Ann as part-of-other, consistent, and did before this PR too. That is now an Open: question in the decisions file.
  • The maiden name departs from the family comparison in two more places, both stated in the entry and held by a test:
    • Particles alone. A maiden particle carries no unjoined tag, so the maiden bases are empty and (née de la) / (née de) conflicts (it conflicted at the merge base too). The family pair de la, Ana / de, Ana reads part-of-other.
    • A particle as a hyphen half, present at the merge base. (née al-Rashid) / (née al) reads hyphenated-part, consistent under that opt-in, where the family pair conflicts.
  • Changes outside _match.py and its tests: one row in tests/v2/test_layering.py's table (_match → nameparser._pipeline._vocab, for _HYPHEN_CHARS and _HYPHENS), the matching sentence in AGENTS.md's layering bullet, and _match's module docstring.
  • The maiden name was measured on test-written names only. No corpus match_diff.py draws from holds maiden names. 12 corpus pairs (jrc-names) hold a hyphen other than the hyphen-minus.

What changes

Any of the pipeline's hyphens is a hyphen, wherever the parser reads the name alike.

  • _match._fold is the lexicon's fold, then every character of _pipeline/_vocab._HYPHEN_CHARS written as the hyphen-minus (rules/O6's set: U+2010, U+2011, figure dash, en dash, minus sign, and the small and fullwidth hyphen-minus).
  • A written word is cut at any of them in _part_agrees, in _family_words (which the check and the keys share), and in the keys' joined and whole forms of a word.
Pair Merge base Now
Ann Smith‐Jones / Ann Smith family conflict, no shared key part-of-other, shares smith;a
Ann Smith‐Jones / Ann Smith-Jones family conflict folded, identical keys
Ann Smith–Jones / Ann SmithJones family conflict joined
Jean‐Paul Smith / Jean-Paul Smith given conflict folded
Dipl.‐Ing. Hans Meier / Dipl.-Ing. Hans Meier title noted title folded
John Smith MD‐PhD / John Smith MD-PhD conflict conflict (the parser's limit)

A maiden name sets its particles aside.

Pair Merge base Now
Ana Ruiz (née de la Vega) / Ana Ruiz (née Vega) maiden conflict particles-aside
Ana Ruiz (née de la Vega Gómez) / Ana Ruiz (née Vega) conflict part-of-other
Ana Ruiz (née Vega-Gómez) / Ana Ruiz (née Vega) hyphenated-part (opt-in, inconsistent) part-of-other
山田 花子 (旧姓 田中 中) / 山田 花子 (旧姓 田中) conflict part-of-other
Ana Ruiz (née V.) / Ana Ruiz (née Vega) initial initial (unchanged)
Ana Ruiz (née J.-P.) / Ana Ruiz (née J. P.) initials-agree initials-agree (unchanged)
Ana Vega / Ana Ruiz (née de la Vega) under across-roles conflict conflict (cross-role still full names only)

The default keys hold no maiden word and do not move.

Tests

  • test_any_hyphen_reads_as_the_hyphen_minus. Runs under the default, FAMILY_FIRST, locales.ES and locales.ZH parsers, under the default setting and with every opt-in and other_readings on.
    • Input: 24 hyphenated names, each read with the seven other hyphens. The hyphens are written out from rules/O6, not read from _HYPHEN_CHARS.
    • The 24 names place a hyphen in a family name before and after a comma, a given name and a maiden name; beside a particle half (Al‐, al‐, Ter‐); in a title at the head, at the end and after a comma; doubled, leading, trailing and stray (- Pierre, "- Boy"); and spaced as a separator (Ann Smith – Jones).
    • Asserts the parse matches field for field.
    • Asserts the keys are equal.
    • Asserts every comparison reads the hyphen-minus pair's outcomes, in both argument orders: against the name itself, against 26 partners, and against every other hyphenated name written with the en dash.
    • Asserts a consistent pair shares a key.
    • Asserts every listed hyphen was read, so the test cannot pass on nothing.
  • test_no_other_character_reads_as_a_hyphen. The em dash, U+2015, the slash and the tilde each move some name's parse, under every parser, so the parity check discriminates.
  • _PARSER_HYPHEN_LIMITS and the spaced-credential test pin the parser's limit.
  • Shared grids. U+2010 and the en dash joined NAMES (3) and _SHAPE_NAMES (3), with maiden-particle names (4 and 2).
  • Maiden rows, both orders.
    • particles-aside, part-of-other, a hyphen half and a particle hyphen half.
    • A listed spelling inside a compound maiden name.
    • joined, folded, initial, initials-agree and one-side.
    • Particles alone, (née) with no name, and 旧姓.
    • Both departures from the family comparison.
    • The cells that held at the merge base (test_a_maiden_outcome_accepted_at_the_merge_base_stays_accepted, narrowed in review), and the cross-role non-match.
    • Default keys with no maiden word.
  • Every row reading a new outcome fails at the merge base.

Negative controls

Taken at the final code, each mutation applied and reverted.

  • _RECORDED_CONTROLS and _SHAPE_CONTROLS. Every earlier pair stands. The new keys-only mutation (the keys cutting a family word at the hyphen-minus alone) fails the main grid's guarantee under 6 settings and 24 of the 32 shape-grid runs.
Mutation Fails
The fold maps no hyphen the hyphen test under all 4 parsers, plus rows
_part_agrees cuts at the hyphen-minus alone the hyphen test under all 4 parsers
_family_words cuts at the hyphen-minus alone the hyphen test under all 4 parsers, plus the control rows
The keys' joined and whole forms read the hyphen-minus alone the hyphen test under all 4 parsers
_noted folds one side with the lexicon's fold (survived every test before this round) the hyphen test under all 4 parsers
_HYPHEN_CHARS = "-" 19 tests
The em dash added to _HYPHEN_CHARS the non-hyphen test under all 4 parsers
The maiden row given back its merge-base comparator 11 maiden tests
The initials exception removed its 3 rows

Measurement

uv run python tools/differential/match_diff.py --base <fresh detached worktree at the merge base 62bbdeb1>, run after the last test edit (its pairs come from this branch's tests).

  • 3,254 of 655,473 rows moved (31,213 pairs × 21 settings): 312 under each of the four settings that accept across-roles, and 118 under each of the other seventeen.
  • Key guarantee: 0 consistent pairs share no key.
  • Keys: 2,718 rows moved, every one a pair holding a hyphen other than the hyphen-minus. No maiden name moved a key.
  • Verdicts: only inconsistent → consistent, 501 (205 hyphen pairs, 296 maiden pairs). None moved to inconsistent.
Row move Count
family conflict → part-of-other 599
family conflict → joined 21
family conflict → across-roles 4
given conflict → hyphenated-part 38
given conflict → folded 21
maiden conflict → part-of-other 336
maiden conflict → particles-aside 105
maiden conflict → folded 42
maiden hyphenated-part → part-of-other 21
  • What the draw can see: 294 pairs with another hyphen (12 from jrc-names) and 236 with a maiden name on both sides.
  • Harness control: the whole-chain run above (3,464 rows) reports 81 verdicts moving to inconsistent, so the zero here is real.
  • The first round's figure (3,717 of 655,347) was taken on the earlier draw.

Docs

docs/design/decisions/name-matching.md:

docs/matching.rst, the outcome table:

  • folded: "...are folded away" → "...are folded away, and each hyphen the parser joins two names with (U+2010, the en dash and others) read as the hyphen-minus wherever the parser reads the name alike, which it does not for a credential joined by one (MD‐PhD)"
  • joined: "stripped of spaces and hyphen-minus signs" → "stripped of spaces and hyphens"
  • particles-aside: "the family names agree without their particles" → "the family names, or the maiden names, agree without their particles"
  • part-of-other: "one family name's words are among the other's" → "one family or maiden name's words are among the other's"

No release-log bullet. Parser.matching() has not shipped.

Checks (head 4af97b2)

  • uv run pytest: 45,436 passed, 38 skipped, 4 xfailed.
  • uv run mypy, uv run ruff check: clean.
  • sphinx-build -b doctest: 0 failures.
  • sphinx-build -W -b html: no warnings.
  • uv run pytest -n0 -m serial: not clean on this machine. The clock-timed scaling tests in test_benchmark.py fail at load averages of 27 to 53 from other sessions. They fail on the merge base too in the same minutes (a different set each run), and this PR touches no parser code. The frame-count guard passed. CI's runners are the judge here.

Timings, measured alternately with the merge base under load on 2026-11-11 and dated in the tests:

  • The code-point fold test: 10–16 s against the merge base's 6–10 s.
  • The shape grid: 17 s against 12 s.
  • The hyphen test: about 3.3 s per parser.

Closes #1264

🤖 Generated with Claude Code

@derek73 derek73 added this to the 3.1 milestone Nov 12, 2026
@derek73 derek73 added tests matching Name matching: Parser.matching(), check() and keys() maiden Maiden and married names (rules/M) labels Nov 12, 2026
@derek73 derek73 self-assigned this Nov 12, 2026
derek73 and others added 3 commits November 11, 2026 16:11
…'s particles aside (#1264)

The name match reads every hyphen of _pipeline/_vocab._HYPHEN_CHARS
(rules/O6's set) as the hyphen-minus: its fold writes them so, and a
written word is cut into its halves at any of them, on both sides of a
comparison and in the keys alike, so Ann Smith‐Jones (U+2010) reads
family part-of-other against Ann Smith and folded against Ann
Smith-Jones, with the same keys.

The maiden name asks particles-aside and part-of-other on its two bases
ahead of the leniencies it read before, outside the family chain, so
(née de la Vega) / (née Vega) reads particles-aside while an initial
against it still reads initial, two texts of initials alone stay
initials-agree's (joined's exception), and a hyphen half still reads
hyphenated-part where part-of-other is not accepted.

Tests: the hyphen invariant over every hyphen and four parsers, parse
parity included; the maiden rows; the shared grids joined; the negative
controls re-taken with a keys-only hyphen mutation added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
decisions/name-matching: a dated entry superseding #1261's Accepted
bullet in part and deciding the two Open bullets of the repr entry, the
Standing lines for the fold, part-of-other, joined and the maiden name,
and Open back to none. docs/matching.rst: the outcome table's folded,
joined, particles-aside and part-of-other rows. AGENTS.md: the layering
note names _match's import of _pipeline/_vocab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1264)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73
derek73 force-pushed the fix/issue-1264-hyphens-and-maiden-particles branch from fa93ce8 to 67b32a5 Compare November 12, 2026 00:13
derek73 and others added 2 commits November 11, 2026 17:26
…1264)

Review of #1344: the hyphen test's title and suffix names never parsed
as a title or a suffix, so `Dipl.-Ing.`, `Lt.-Col.` and `Dr.-Ing.` at the
head, end and after a comma replace them; no suffix written with a
hyphen parses alike under the other hyphens, so those and a trailing
`Priv.-Doz.` are pinned as the parser's limit. The other hyphens are
written out from rules/O6, every one must be read, and four non-hyphens
must each move some name's parse. The maiden test that claimed every
cell held at the merge base is narrowed to the cells that do, and the
two departures from the family comparison get a test of their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1344: the hyphen claim holds where the parser reads a name
alike, so the decisions entry, the Standing line and docs/matching.rst
say so with the credentials and the trailing title it does not; the
maiden name's departures from the family comparison (particles alone,
a hyphen half that is a particle) are stated as they are, the
whole-chain control is recorded in full, the superseded and decided
notes sit in the entry heads, and the family name of initials alone is
an Open question. AGENTS.md's layering note names _HYPHENS too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73
derek73 merged commit 970b54e into master Nov 12, 2026
14 checks passed
@derek73
derek73 deleted the fix/issue-1264-hyphens-and-maiden-particles branch November 12, 2026 02:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maiden Maiden and married names (rules/M) matching Name matching: Parser.matching(), check() and keys() tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Which smaller name-matching gaps should be fixed? (J.P. Smith / John Paul Smith, John Beth Smith / John Anne Beth Smith, and others)

1 participant