Let the glued-honorific peel cross a comma and a 间隔号 (#312) - #318
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #318 +/- ##
=======================================
Coverage 98.46% 98.46%
=======================================
Files 41 41
Lines 2803 2807 +4
=======================================
+ Hits 2760 2764 +4
Misses 43 43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Every family-comma input in the suite has exactly two segments, so segments[:2] was only ever exercised as "more than one run", never as "and not three". Five one-line narrowings of the site scan passed the whole suite: segments[:3] '김, 민준씨, 박씨' ... and len(state.segments) == 2 '김, 민준씨, Jr.' ... and nothing was extracted '김, 민준씨 (Jimmy)' ... and not state.interpunct_offsets '威廉·莎士比亚, 太郎さん' ... and state.segments[0] ', 민준씨' Each named input now has a pin, and each mutant fails exactly the pin that names it. The first two are the hazard the site comment already argues against, unwitnessed on the side #312 opened: 씨 stranded in the given name, or the junk tail's 씨 peeled instead of the person's own. The nickname one is a case row rather than a stage test because extract_delimited runs before this stage in the pipeline and not in the stage harness; ko_honorific_glued_given_nickname pins only the NO_COMMA half, where the two runs are one and the choice is invisible. The empty-run one is separate from ", , 씨" below it, where BOTH runs are empty and the scan short-circuits before the crossing is reached. Also rename test_family_comma_is_inert: the stage is no longer inert under a family comma once the lexicon carries honorific_tails. Its assertion is sound -- it runs on _LEX, which carries none -- but the name states a general claim #312 falsified. It pins the surname split; say so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ja_honorific_glued_family_comma's expectation bakes in two deviations from 1.4.0 -- the peel, and the comma-family first -> family flip -- and the old note said "the only deviation is first -> family", which the flip to fix(#312) correctly removed and replaced with nothing. A reader now has to infer that the classification claims the whole diff. Name the other half and its Latin witness instead: 1.4.0 reads 'Smith, Dr.' as first Smith where family_comma_lone_title expects family Smith, so no script-conditional rule is reaching that half. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
segments[1] is not always name text. segment picks FAMILY_COMMA whenever the pre-comma part is a single word, including when the post-comma part is entirely suffix-shaped, and it admits that run on is_suffix_lenient where the peel's site scan asks is_suffix_strict. The initial-shaped suffix words fall in that gap, so the scan lands on a token that is no name and ends in no tail, and the peel is abandoned: 田中さん, PhD -> family 田中, suffix さん peels 田中さん, V. -> family 田中さん, given V. does not 田中さん, Ph. D. -> family 田中さん, suffix 'Ph. D.' does not Same credential, three spellings, two answers. Not a regression: both of the two that do not peel are parity with 1.4.0 (measured against 1.4.0-on-PyPI, first V. / last 田中さん), and the strict/lenient gap predates #312. Fixing it wants segment's own suffixy closure extracted into a predicate the peel can share, which is a bigger change than this branch; record the behavior with its reason instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both are claims about code outside this stage, and both were written
from reasoning rather than from a run.
The site comment said every non-family-comma structure "puts
post-nominals after" segments[0]. That holds for the SUFFIX comma
only. Under NO_COMMA segment returns exactly one run and a trailing
post-nominal sits inside it -- '김민준씨 Jr.' is NO_COMMA with
segments ((0, 1),) -- which is the fact the scan-back paragraph in
the docstring above already rests on, so the two paragraphs
contradicted each other on the more common structure.
The ASCII bail said a glued Latin post-nominal "gets neither
treatment and simply stays in the name". It gets the period-joined
treatment: period_joined_vocab('Smith.Jr.') is 'title', 'jr' being
title vocabulary as well as suffix vocabulary, and where position
allows the treatment wins -- parse("Smith.Jr. Anderson") gives title
'Smith.Jr.', family 'Anderson'. The example the comment chose
("John Smith.Jr.") only loses to the last-name reservation. The
sentence needs no theory of that function anyway; what it has to say
is that the bail returns above all of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The maiden paragraph was written entirely in NO_COMMA terms ("which
under NO_COMMA reaches into a maiden clause"). #312 extended that
reach to FAMILY_COMMA along with the rest of the crossing:
'김, 민준 née 박씨' gives suffix 씨 / maiden 박 here where master gives
maiden '박씨'. Same behavior, one structure wider.
The "would strand" argument at the site scan reads as a
counterfactual about a mutation nobody would ship.
Policy(lenient_comma_suffixes=False) reaches it without touching the
code: it drops segment's post-comma test to the strict one, which
reads 'Dr 김민준씨, V.' as FAMILY_COMMA, so the scan crosses to 'V.'
through the sanctioned two-run reach and strands 씨 in family
'Dr 김민준씨'.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"No comma structure and no dot gates the peel any more" and "A comma and a 间隔号 both leave the peel alone" are universal negatives, and both have counterexamples. Measured: 김, 민준 지훈씨 -> suffix 씨 peels (two runs) 김, 민준, 지훈씨 -> suffix 지훈씨 does not (third run) 김,, 민준씨 -> suffix 민준씨 does not (later run) 田中さん, V. -> family 田中さん does not (second run is not name text) One fact with four faces: the peel reaches the two runs around a family comma and nothing beyond, presuming the second of them is name text. Say that instead, in one sentence rather than as a list of caveats, in the release log, in usage.rst and in AGENTS.md's architecture paragraph (which named the slice but not its limit). Also fix the scope of "likewise" in the release log. "gives ... the same as the spaced 김 민준씨 -- and likewise 田中, 太郎さん, 田中さん, PhD and 威廉·莎士比亚さん" holds for two of the three: 田中さん PhD gives suffix 'さん, PhD' while 田中さん, PhD gives title PhD. cases.py states that difference; the release log left it inferable and wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both describe the branch's own mechanism, and both were checked against origin/master afterwards rather than before. The maiden clause said 씨 "stayed in maiden 박씨 on the unscoped segments[0]". It did stay there, but not for that reason: before #312 the family-comma gate sat ABOVE the peel and the stage returned without running it at all. The comma-and-dot test comment said the 间隔号 gates the surname split and the family comma "gates it too". Both stand over the split, but the family comma returns first, so the dot's gate is never reached on this input -- which is the only reason the test is a composition and not two facts. 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.
Summary
#308's glued-honorific peel sat behind two gates it never asked for — the family comma and the 间隔号 — inherited purely from where it was placed in
script_segment. Both gates answer where does a name divide into surname and given. The peel asks something else: does this token end in a word that can never end a name? A comma or a dot elsewhere in the string does not change that answer.This moves the peel in front of both, and changes its site from
segments[0]to the name-bearing runs, so it reaches the given-name side of a family comma:김, 민준씨민준씨田中, 太郎さん太郎さん田中さん, PhD田中さん威廉·莎士比亚さん莎士比亚さん田中さん, 太郎田中さんEach now agrees with its spaced spelling. The surname split still stands down for both marks, unchanged — a comma still means the writer said where the family name ends, and the 间隔号 still marks a transcription. Default-on, reaches
HumanName.The stage is also split into two siblings (
_peel_honorific_tail,_split_surname_site) with their own gates, so "these answer different questions" is visible in the code rather than only in prose. That is commit 1, deliberately behavior-free, so commit 2 is only the behavior.The issue was filed with a wrong example
#312 named
田中さん 太郎vs田中さん, 太郎as the motivating pair. They agree — neither peels, because the site is the last non-post-nominal token, which is 太郎 in the spaced form. The real disagreements were mostly on the other side of the comma, where the honorific is glued to the given name.That matters because the fix the issue proposed — move the peel above the gates, change nothing else — fixes one case of four, misses two, and makes
田中さん, 太郎peel, introducing a disagreement where none existed. The last row of the table above is pinned precisely because a naive fix breaks it. Issue body and comments corrected.A regression caught in review
The first draft flattened all segments.
segment()admits a post-comma run onis_suffix_lenientwhile the peel filters withis_suffix_strict, and the initial-shaped suffix words fall in that gap —V.,V,Iare lenient-True and strict-False. So:Scoping to
segments[:2]under a family comma andsegments[0]otherwise fixes both — a FAMILY_COMMA name can never span three runs. Now pinned at stage level and by a case row (ko_honorific_glued_given_suffix_comma_initial), the latter added only after simulating the broken scan against the whole case table to confirm no existing row caught it.Known limit
A doubled comma (
김,, 민준씨) does not peel —segment()puts민준씨in a suffix run, so it was never name text. Unchanged from before, and the docs are phrased to stay true of it.Test Plan
uv run --extra ja pytest -q— 2872 passed, 16 skipped, 11 xfaileduv run mypy— clean, 96 filesuv run ruff check nameparser/ tests/— cleansphinx-build -b html/-b doctest/python -m doctest README.rst— clean, 0 doctest failurestools/differential/compare.pyvs 1.4.0 — 734 names, 90 intentional, 0 unexplained; each new corpus row's classifying rule verified rather than assumedDr 김민준씨, Jr.and김민준씨 (Jimmy)byte-identical)Closes #312.
🤖 Generated with Claude Code
Known limit, recorded not fixed
The peel reaches the first two runs under a family comma and nothing beyond. One fact, several faces — all measured, all byte-identical to pre-#312 and to 1.4.0, so none is a regression:
田中さん, V.is the interesting one and has its own issue: #319. It needssegment()'ssuffixyclosure extracted into a shared predicate, which is out of scope here — the cheap substitute was measured and leavesPhDandPh. D.inconsistent instead. Pinned as aparitycase row so the limit is recorded rather than latent.Note for bisecting
Commit 2 (
c89536b, the behavior change) is red on its own: it invalidatesja_honorific_glued_family_comma's expectation, and that row is not updated until commit 3 (b54f692). Every other commit is independently green. This was a deliberate choice in the implementation plan — behavior in one commit, its pins in the next — and is left as-is rather than rewriting pushed history. Agit bisectfor a CJK-comma regression will stop there with two known failures; they are the row and its facade twin, nothing else.Whole-branch review
All seven original commits had per-task review; a three-agent whole-branch pass then found what slice-level review structurally could not:
FAMILY_COMMAinput had exactly two segments, sosegments[:2]was only ever exercised as "more than one run", never as "and not three". Asegments[:3]mutant survived the whole suite; its catching input김, 민준씨, 박씨peels the junk tail's 씨 and strands the person's own — verbatim the failure the code comment argues against. Now pinned, along with four sibling mutants (extracted content, interpunct-plus-comma, empty leading run,len(segments) == 2).period_joined_vocab— it does classifySmith.Jr.as a title, andparse("Smith.Jr. Anderson")swallows the surname.