Skip to content

CJK full stops: honorifics and names written with a period of any width (#322, #323) - #522

Merged
derek73 merged 9 commits into
masterfrom
claude/322-cjk-full-stop
Sep 11, 2026
Merged

derek73 merged 9 commits into
masterfrom
claude/322-cjk-full-stop

Conversation

@derek73

@derek73 derek73 commented Sep 10, 2026

Copy link
Copy Markdown
Owner

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.

  • Honorifics written with a fullwidth or ideographic full stop aren't recognized (김민준 씨.) #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 decomposed Señor is 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 veto is_initial has 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)

  • Corpus: 17 names move against master (after the review round's four new pins), all of them the bundle's own rows; the 20 pre-existing CJK names carrying an ASCII period (21 rows) are not moved by this bundle. One 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 cost: the reference name is unchanged at parse=409 facade=446 calls (tools/perf/call_count.py --against master); an H2-shaped opener pays the veto, Xyz. John Smith 304 to 308.
  • Two-input invariant over the corpora: 28 of 30 CJK names with an edge stop divide exactly as their stop-less spelling; the two that do not are Latin words wearing a Latin period (毛 泽东 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 in compare.py (the example moved into tolerated W3 instead).
  • The ledger guard's honorific-alternation pin gained a _NOT_A_VOCABULARY_COPY roster 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_CLAIMS plus _MUST_NOT_MATCH stand in for them.
  • Recorded limits, not fixed: halfwidth kana is outside the script table, so タナカ. John still reads a title by shape; a bracketed ASCII-period CJK credential (김민준.) John Smith is unwrapped and divided (was a title); the stop set reaches Latin and Cyrillic too (Dr。 John Smith reads title, Smith, John V。 reads suffix where V. stays a middle initial).
  • Review round (two commits on top): the classification fold reads TRAILING stops only, so a leading stop hides the token from the surname site as on master (a caller-configured segmenter could otherwise have divided .김민준 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): a Lexicon pickled 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 the Lexicon from its source), not a forgiving re-fold on load; the decisions entry records both remedies weighed.
  • /simplify round (one commit): the peel site returns the cut offset; the core/rstrip invariant is stated once; an 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-stops corrects 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

derek73 and others added 5 commits September 10, 2026 03:35
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>
@derek73 derek73 added the bug label Sep 10, 2026
@derek73 derek73 self-assigned this Sep 10, 2026
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.67%. Comparing base (d37b8ec) to head (d3f5597).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

derek73 and others added 2 commits September 10, 2026 19:15
…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>
@derek73 derek73 added this to the v2.3 milestone Sep 11, 2026
derek73 and others added 2 commits September 10, 2026 21:41
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>
@derek73
derek73 merged commit 555270e into master Sep 11, 2026
11 checks passed
@derek73
derek73 deleted the claude/322-cjk-full-stop branch September 11, 2026 05:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

양. 지훈 splits the given name in half Honorifics written with a fullwidth or ideographic full stop aren't recognized (김민준 씨.)

1 participant