CJK full stops: honorifics and names written with a period of any width (#322, #323) - #522
Merged
Merged
Conversation
FULL_STOPS names the four characters the parser treats as a period at a word's edge; _normalize strips them and NFC-composes non-ASCII words, so 김민준 씨./씨。/씨。 reach the suffix entry. No shipped vocabulary string changes under NFC (measured 2026-09-09). Adding the three tolerated CASE rows widened what "fix(#271/#272/#298) native-script CJK..." claims in the corpus-claims ledger (109->112 names in expected_since_1.4.0.toml and expected_since_2.0.0.toml) and regenerated corpus_cjk_tolerated.jsonl; both are included here since the full gate does not pass without them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… edge full stop _normalized_for_script strips FULL_STOPS before classifying, so a period glued to a CJK token no longer removes it from the surname site, the order rule and the peel's other-script test; and the surname site matches its head on the token's core, so the stop rides with the remainder instead of being it. 양. 지훈 and 김. 민준 keep their given names; 양 지훈. keeps family-first. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…l stop _peel_site matches on the token with trailing FULL_STOPS removed and cuts before the tail, so 김민준씨. and 田中さん. divide where their stop-less spellings do and the stop rides with the honorific. The W3 reading these two carried was measured 2026-09-05 and pinned by nothing; both are case rows now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
is_leading_title refuses a period-marked word in a _NO_INITIALS script, the veto is_initial already carries since #320: a CJK word wearing a period is a name word, so 김민준. divides as 김민준 does. Latin is untouched (Smith. John keeps title Smith.). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…note rules.md W Background names the four full stops; H2 takes the initialless-script veto as an Accepted clause; W3's glued-period sentence records the new reading. decisions.md#cjk-full-stops carries the NFKC decline, the NFC measurements, the corpus population and the parse-cost numbers, and corrects the indic-honorifics, W1 and cjk-comma-demotion entries it supersedes. One ledger rule per baseline, fix(#322/#323), last in each file: nine members at 1.4.0 and 2.0.0 where the native-script CJK rule already declares three of the twelve movers, twelve at 2.1.0 and 2.2.0. Its members are the first CJK alternation in any ledger that is a list of NAMES, so the honorific pin now reads _NOT_A_VOCABULARY_COPY as its Latin twin already did, and the comments that said every classified alternation belongs to that pin are amended. 田中. 太郎 stays tolerated with its three #323 siblings, and every one of the twelve movers is radar-tier: H2's Accepted clause is illustrated in W3's tolerated example block rather than by promoting the row, per the 2026-09-05 precedent recorded in compare.py -- marking the row alone would leave the name enforced and documented as demoted. Two reviewers' findings applied. H2's clause is scoped to what the script table classifies (halfwidth katakana is outside it: タナカ. John still reads title, which the limits bullet now records and nothing pins); the population claims name their criterion beside each number (17 names under the edge-stop shape, five of them non-movers; 20 distinct names on 21 rows under the looser ASCII-period reading); R1's revise round-trip is re-measured at 372 of 1155 with the same single failure; the corpus floor comments carry measured line counts; and the roster comment says why a member-shape test was rejected and what a declared non-copy forgoes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #522 +/- ##
=======================================
Coverage 98.67% 98.67%
=======================================
Files 45 45
Lines 3309 3320 +11
=======================================
+ Hits 3265 3276 +11
Misses 44 44 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ops only; review-round pins The script-classification fold stripped BOTH edges, so a token wearing a leading full stop classified as its script, became a surname site, was declined by the head match (which rstrips) and fell through to a configured segmenter -- which answering offset 1 divided '.김민준' into the stop and the name. It now rstrips, matching the two division sites: such a token classifies as no script, reaches neither site, and reads given '.김민준' as d37b8ec does. The surname site hands the segmenter the CORE rather than the raw token for the twin reason on the other side, and bounds its answer by the core's length; the ja pack's _wholly_japanese therefore accepts '山田太郎.' and divides it 山田 + 太郎. where it declined before. Three sentences that said '.김민준' takes the hangul order rule, and one that recorded the raw-token hand-off as a limit, are corrected. Pins the review round found missing: the peel's rstrip ('.김민준씨'), the surname site's whole-token guard on the core ('남궁.'), the NFC-not-NFKC fold ('M.B.A.'), three stop-bearing FAMILY_COMMA spellings with notes naming which conjunct of the gate the stop moved, the bracketed '(김민준.) John Smith' degradation, and the stop set's unasked-for Latin reach ('Jr。' suffix, 'V。' suffix where 'V.' is a middle initial) -- which the FULL_STOPS docstring had denied. Movers against d37b8ec: 13 -> 17. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The peel site returns the CUT OFFSET rather than a length the one arithmetic caller had to undo, and the shared rstrip invariant moves to the module docstring so the three sites keep only their own halves. The segmenter bounds message names what the segmenter was given; the H2 veto declines on an ASCII test before the regex search. The CJK half of _NOT_A_VOCABULARY_COPY becomes a DERIVED criterion: an alternation is a hand copy when a member folds into the honorific vocabulary, bare or wearing one full stop, which pins the adversary a member-shape test would have waved through (2 of 3 members fold) and skips the bundle's name lists (0 of 11, 0 of 17). The NFC roster reads _VOCAB_FIELDS, the round-trip exemption gains its characterization, and the floor and ledger comments lose their round-by-round history. No behavior change: full suite, the four differential gates (exit 0, unexplained 0, radar unclassified 0) and parse=409 facade=446 on py3.11 are all unmoved. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…r NFD entry no longer loads Derek's decision 2026-09-10: a release-note line with the remedy (rebuild the Lexicon from its source), not a forgiving re-fold on load. The decisions entry records the two remedies weighed and why the note won. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 11, 2026
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
The parser knew one full stop (ASCII) at one site (the lexicon's edge strip). It now knows four --
.,.U+FF0E,。U+3002,。U+FF61 -- through one constant,_lexicon.FULL_STOPS, read at four sites plus a fifth that reads the same no-initials repertoire. The period never leaves the text: it stays on the token it arrived with.김민준 씨.) #322 --김민준 씨./씨。/씨。and NFD김민준, 씨.reach the suffix vocabulary. The lookup fold strips the stop set at word edges and NFC-composes non-ASCII words. NFKC was declined: it does not fold U+3002 at all (NFKC("。") == "。"), and it would rewrite stored vocabulary. No shipped entry changes under NFC (1735 strings, 0 changed; pinned by a test). The NFC half reaches Latin too: a decomposedSeñoris a title now.양. 지훈splits the given name in half #323 -- a period glued to a CJK token no longer removes it from script classification, so양. 지훈keeps its given name,김. 민준too (the issue's first candidate fix would have missed it), and양 지훈.keeps family-first. The surname site and the honorific peel match on the stop-stripped core so the stop rides with the remainder:김민준씨.and田中さん.peel as their stop-less spellings do. rules.md#H2's abbreviation shape refuses a word carrying a no-initials script (the vetois_initialhas carried since A trailing period stops a CJK honorific being recognized ("김민준, 씨." → given "씨.") #320), so a lone田中.is the family name; hangul never reaches that test because hangul segmentation runs first, so its witness is Han. Latin and Cyrillic openers are untouched.Commits
Five, bisectable, each green on its own: lookup fold; classification fold plus the surname site; the honorific peel; the H2 veto; docs, ledgers and release note.
Measurements (py3.11, 2026-09-10)
fix(#322/#323)rule per ledger, 11 names at 1.4.0/2.0.0 and 17 at 2.1.0/2.2.0; every baseline exits 0 with nothing unexplained and nothing unclassified.parse=409 facade=446calls (tools/perf/call_count.py --against master); an H2-shaped opener pays the veto,Xyz. John Smith304 to 308.毛 泽东 Dr.,田中さん V.), which is the boundary.Decisions worth a glance
田中. 太郎stays a tolerated row rather than becoming H2's contract-tier example line, following the 2026-09-05 precedent recorded incompare.py(the example moved into tolerated W3 instead)._NOT_A_VOCABULARY_COPYroster so a literal-name alternation can be declared not-a-vocabulary-copy; the declaration forgoes two reach checks (the The #274 maiden rule matches "born", which is not a maiden marker, so a regression on "Max Born" reads as intended #350 precedent) and_CORPUS_CLAIMSplus_MUST_NOT_MATCHstand in for them.タナカ. Johnstill reads a title by shape; a bracketed ASCII-period CJK credential(김민준.) John Smithis unwrapped and divided (was a title); the stop set reaches Latin and Cyrillic too (Dr。 John Smithreads title,Smith, John V。reads suffix whereV.stays a middle initial)..김민준into.and the name); the segmenter receives the stop-stripped core, so the ja pack now divides山田太郎.; the honorific peel is not script-gated and still reads a leading-stop token (.김민준씨peels). Decided (Derek, 2026-09-10): aLexiconpickled by 2.1.x/2.2.x with a caller-added wide-stop or NFD entry no longer loads (the load guard re-runs the widened fold; shipped vocabulary is unaffected) — carried as a release-note line with the remedy (rebuild theLexiconfrom its source), not a forgiving re-fold on load; the decisions entry records both remedies weighed.isascii()short-circuit before the H2 veto; the ledger guard's enumerated exemption sets replaced by a derived vocabulary-fold criterion; call counts unchanged.decisions.md#cjk-full-stopscorrects three earlier entries it supersedes (indic-honorifics' "no NFC in that module", W1's 2026-07-29 "matching stays raw", cjk-comma-demotion's five-parse summary).Closes #322, closes #323.
🤖 Generated with Claude Code