Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion docs/customize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,9 @@ listed below.
glued-honorific peel asks before crossing a family comma
(#319), so the setting reaches CJK names too: ``"田中さん,
V."`` gives family ``田中``, suffix ``さん`` when ``True``, and
family ``田中さん``, given ``V.`` when ``False``.
family ``田中さん``, given ``V.`` when ``False`` — though a
comma around a CJK name is tolerated input
(``rules.md#W3``) and this reading can change.
* - ``strip_emoji``
- ``bool``
- Excludes emoji from tokenization — they appear in no field or
Expand Down
26 changes: 26 additions & 0 deletions docs/design/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,29 @@ Excluded (SUFFIX_ACRONYMS / SUFFIX_WORDS — the esq dual membership, deliberate

- esq is in BOTH sets and must not be "deduplicated". The load-bearing membership is the acronym one (it carries the multi-dot spellings: removing it costs "John Smith E.S.Q." its family name); the word membership is inert as shipped but is what keeps "Esq" matching for a caller who edits suffix_acronyms themselves. esq is the ONLY member of SUFFIX_ACRONYMS ∩ SUFFIX_WORDS — that singleton is why the two sets cannot carry a disjointness assert, which is the standing cost this entry defends. Deliberately no changed-parse count — the count is a property of the measuring grid, not of the code.

### cjk-comma-demotion — the script shapes are pure, the wrappers are tolerated (2026-09-01, #469)

Closes #469, and continues the corpus-tier arc below rather than standing apart from it: the tier split gave the differential somewhere to WATCH a name without promising it, and this is the first doctrine narrowing to spend that. No parser behavior moves anywhere in it. Counts are this session's and every one is recomputable from the checked-in tree — `wc -l` over tools/differential/, `uv run python tools/differential/build_shapes_corpus.py --coverage`, and one `uv run python tools/differential/compare.py --baseline X` run per baseline, read off its `corpora:` and `corpus:` lines.

- 2026-09-01 #469 — THE LINE IS FORMAT PURITY, and not comma class. Shapes 6 ("Family Given [Honorific]") and 7 (the interpunct/katakana transcription in source order) admit wholly classified-script text and refuse a comma and every ASCII letter; each composed form around a CJK name — the family-comma listing W3 reads, the honorific-comma probe zoo, a leading Latin title, a trailing credential comma — is TOLERATED input instead: parsed best-effort, contract-exempt, changeable without notice. What the inventory is FOR is the whole argument. It tabulates written ARRANGEMENTS, and a comma-wrapped or Latin-wrapped CJK name is not an arrangement any of the three writing systems produces — it is a listing convention from elsewhere with a CJK name inside it. So the admission test is a property of the FORMAT, and a rule reading such input describes rather than promises.
Supersedes an in-session fork of this same day's design, recorded because it is a line the next reader would redraw the same wrong way. The first cut kept trailing CREDENTIAL commas contract while demoting the family comma, on the theory that the two comma classes differ in kind — a credential comma being a Latin convention wrapped around anything, where a family comma makes a claim about the name's own division. The shape lens replaced it: both are wrappers, neither is a form the script writes, and a doctrine promising one while tolerating the other has to keep explaining a distinction its own admission test cannot state. Purity states the line in one sentence and needs no comma taxonomy at all.

- 2026-09-01 #469 — SCOPES the FAMILY_COMMA bullet under 3-0-reevaluations below, which reads "inherited from v1's lastname-comma but correct on its own terms — an explicit comma is stronger evidence than script". It stands unchanged for LATIN script, which is the script it was argued about. For CJK script it is narrowed to a description: an explicit comma still beats script and the parser still reads it that way, but every input carrying one is a form no East Asian writing system produces, so what the parser makes of it is current behavior and not a promise. The bullet is not edited — it was right about what it was reasoning over, and this entry is the scope it never had to state while Latin was the only script in the room.

- 2026-09-01 #469 — the 2026-08-23 (PR #428) snapshot under decisions.md#C1 below attributes `田中さん, Dr.` keeping its honorific glued to "(C1 Accepted)". That clause now lives in rules.md#W3's `tolerated:` note: the BEHAVIOR is unchanged and only its home moved, W3 being where the CJK comma forms are set down now that they are described rather than promised. The snapshot stays as written — it was true when it was taken, and this is the note that says where to read the clause instead.

- CLDR, PULLED VERBATIM 2026-09-01 — #469 asked for the citation and asked that it be a pull rather than a recollection, so the failures are recorded with the findings. The paths named in the plan (`https://raw.githubusercontent.com/unicode-org/cldr/main/common/personNames/ko.xml`, `.../zh.xml`, `.../ja.xml`) all 404: there is no `common/personNames/` directory in the repository, and the data is a `<personNames>` element inside `common/main/<locale>.xml`. Read there instead, from unicode-org/cldr at `main`, commit `8e4fb0fa1e287c4612b512cf2ef77e890467da28`; the grammar from https://www.unicode.org/reports/tr35/tr35-personNames.html. The grammar first, verbatim, because it decides whether absence is evidence: "A namePattern is composed of a sequence of field IDs, each enclosed in curly braces, and separated by zero or more literal characters (eg, space or comma + space)." A comma between surname and given is therefore expressible, and its absence below is a choice the locale data makes rather than a limit of the format. The `order="surnameFirst" usage="referring" formality="formal"` patterns, verbatim, long then medium then short:
ko {surname} {given} {given2} {credentials} · {surname} {given} {credentials} · {surname} {given}
zh {generation}{surname} {given} {given2} {credentials}{title} · {generation}{surname} {given} {given2-initial}{credentials} · {surname} {given} {given2}
ja {surname} {given2} {given}{title} · {surname} {given}{title} · {surname} {given}{title}
Counted rather than eyeballed: each of the three files holds 42 `namePattern` elements, and not one of the 126 contains a comma of any width (U+002C, U+FF0C, U+3001) or a semicolon. The delimiter between surname and given is a single space throughout.
THE ONE COMMA IN REACH, recorded because the record must not overclaim. It is in the locale-neutral root, whose sorting pattern reads verbatim `{surname} {surname2}, {title} {given} {given2} {credentials}`. `sorting` is a list-ordering format and not a referring one — UTS #35 verbatim: "Used to format names for a sorted list. example: “Brown, William” [medium, informal]" — and ko and zh override all six of their sorting slots comma-free (`{surname} {given}` and kin), while ja overrides five and leaves `order="sorting" length="medium" usage="referring" formality="formal"` as `↑↑↑`, inheriting root's comma-bearing pattern. So the honest reading, and it is the stronger one for being stated with its exception: no native pattern in any of the three writes a comma between surname and given, and the sole comma any of them can reach is INHERITED from the locale-neutral default, in the format whose entire job is sorted lists. That is the "listing convention carried in from elsewhere" the W Background already named, arriving from CLDR rather than from this project's own reasoning — the pull CONFIRMS the writing-system rationale, and complicates it only by showing the exact seam where a comma does get in.
Two findings worth keeping past the comma question. zh writes `{surname}{title}` with no delimiter at all (and ja `{surname} {given}{title}`), which corroborates rules.md#W2's glued honorific from the formatter's side, independently of the peel's own vocabulary argument. And ja declares `<foreignSpaceReplacement xml:space="preserve">・</foreignSpaceReplacement>` — a foreign name's spaces become the nakaguro — which is shape 7's divider reached from the formatting direction rather than the parsing one.

- 2026-09-01 — the SHIPPED 2.2.0 release notes are left untouched, deliberately, and this bullet is the record of that choice. docs/release_log.rst describes the comma-crossing as a fix in two 2.1.0 entries ("Fix a comma or a 间隔号 stopping the glued-honorific peel", and #319's `田中さん, V.` entry). Both were true statements about what those releases did, made while the crossing was contract and #312 had argued it. Demoting the rule does not make a released note false about its release, and editing shipped notes to agree with a later doctrine would cost the log the one property it exists for. The demotion is 2.3's news and belongs in 2.3's notes; the log is a history, not a mirror of the current contract.

- COUNTS, 2026-09-01, with the recompute beside each. 29 case rows carry `tolerated=True` over 26 distinct texts — the flag is per ROW and the corpus is per TEXT, which is why the two numbers differ (recompute: read `CASES` and count `c.tolerated`). The 26th text arrived with the W3 demotion itself: the given-side listing `지훈, 남궁민수` was a rules.md example with no case row, and would have left the harness altogether when the rules corpus stopped carrying W3, so it was given a tolerated row rather than dropped. `corpus_cjk.jsonl` went 98 → 73 and the radar-tier `corpus_cjk_tolerated.jsonl` was created at 25, then 26; the identity is the check worth keeping, since 73 + 25 = 98 exactly says the split MOVED names and dropped none, and the 99th is that one rescued row. `corpus_rules.jsonl` went 252 → 248: six comma-bearing example texts left (`田中さん, Dr.`, `田中さん, PhD`, `남궁민수, 지훈`, `지훈, 남궁민수` when the builder skipped W3 whole, plus `김, 민준씨` and `田中さん, V.` from W2's swap) and two pure ones arrived (`김민준씨`, `田中さん 様.`). `corpus_shapes.jsonl` stands at 37, with the new shapes covered 5 names (shape 6) and 2 (shape 7). And the stop condition held: intentional diffs are UNMOVED at 226/205/113/0 across 1.4.0/2.0.0/2.1.0/2.2.0, with unexplained 0, radar unclassified 0 and exit 0 at each — 1113 names compare at 1.4.0 (7 skipped, shapes 4/5) and 1120 above it. That the counts do not move is the substance and not a formality: a classified diff counts identically on either tier, so demoting a name changes which file it loads from and nothing about what the gate makes of it.

### P5 — bound given names

- 2026-06-30 (first-name-prefix-join design; v1-era, carried into the v2 port) — the join is vocabulary-driven and deliberately tiny.
Expand Down Expand Up @@ -702,6 +725,9 @@ Decisions that landed:
- 2026-09-01 #486 (later the same day, so read this bullet as superseding the arithmetic in the one above rather than the two figures disagreeing) — the shape 1-3 variation matrix filled the given-first half of the inventory, which the feature work that authored shapes 4 and 5 had left at whatever the pipeline PRs happened to tag. 14 existing case rows gained a tag and 3 rows were authored for slots no row instantiated, taking `corpus_shapes.jsonl` from 13 entries to 30 and shapes 1/2/3 from 1/4/1 names to 9/11/3. The arithmetic moves with it and the recipe is unchanged — run `uv run python tools/differential/compare.py` and read its `corpora:` and `corpus:` lines, adding `--baseline 1.4.0` for the skip figures: 30 shape entries, of which 14 dedupe into names already compared under the default order, for 1120 comparisons; shapes 4 and 5 still declare `min_baseline` 2.0.0, so 7 entries are skipped at `--baseline 1.4.0` and 1113 names compare there. The dedupe count is the half worth reading, because it is what the tier promotion looks like from this side: 8 of the newly tagged names were in a RADAR corpus only, so the tag moved them into the contract tier without adding a comparison, and 2 more were already contract through the rules corpus. None of the 17 names #486 tagged or authored needed a ledger rule — the seven of them that were in no corpus at all diff at no baseline, and intentional stayed 226/205/113/0 across the change. Not a claim about all 30 entries: the seven shape 4/5 entries are classified by ledger rules today, and scoping exactly those rules is what the `orders` bullet three above is about.
- 2026-09-01 #469 — the CJK arrangement is deliberately OUTSIDE the shape inventory. Whether an unspaced CJK name is a third family-first shape is the open question on #469, and `corpus_cjk.jsonl` — contract-tier already, generated from the case table already — covers that ground meanwhile. Tagging it in would have to ANSWER the question first, since a shape row cannot be written without a `name_order` and a `min_baseline` for it, and the inventory is a table of arrangements rather than where that argument belongs.

- 2026-09-01 #469 (later the same day, so read this bullet as SUPERSEDING the one above rather than the two disagreeing) — the CJK arrangement is IN the inventory after all, as shapes 6 and 7, and the bullet above is right about why it could not be until the question was answered: a shape row cannot be written without settling what it admits. What settled it was splitting the question the bullet treats as one. There is no single "unspaced CJK name" shape — there is a pure family-first arrangement (6) and a source-order transcription listing (7), and the composed forms are not arrangements at all. Both take `order=None`, which is the part that had looked impossible: the family-first reading is SCRIPT-carried rather than declared, so a pure shape 6/7 string already parses correctly under the DEFAULT policy and there is nothing for `order` to name. `min_baseline` is then documentary rather than a skip trigger, an asymmetry with shapes 4/5 that tools/differential/shapes.py's docstring states so it reads as designed. The doctrine half is its own entry (cjk-comma-demotion above); what belongs here is that `corpus_cjk.jsonl` no longer "covers that ground meanwhile" — the ground is split between it and a radar file now.
- 2026-09-01 #469 — the `tolerated` flag on a case row is the demotion's VEHICLE, and it is recorded here rather than as a mechanisms.md entry. It is an explicit declaration, reviewed one row at a time exactly as a `shape=` tag is, mutually exclusive with one, and restricted to rows whose text bears a classified codepoint; `build_cjk_corpus.py` partitions on it, and a text marked on one row and not another is a hard error, the flag being per-text in effect. Why not a mechanisms entry: that catalog is keyed by RECURRING problem shape and its contract statements are citable verbatim from code comments, and this is one arc's device with no second instance and no comment needing to cite it — the tier mechanics it rides on are already owned by tools/differential/README.md, so an entry would restate a source rather than say something the source does not (the #473 lesson under decisions.md#review-agent-single-source). If a second arc ever needs "record the behavior, promise nothing", that is the point to promote it and this bullet is the first instance to cite.

### comma-suffix-arc — #291/#296/#316 (2026-07-26 → 2026-08-01)

#291 was filed 2026-07-26 out of the 2.0 vocabulary cleanup, with
Expand Down
Loading