Repository navigation
Add Token.reading, the reading the parse took for each word (3.0, 4a) - #942
Merged
Merged
Conversation
Every parsed token now carries a Reading: what the word was read as, the field it landed in, and why. Assemble reads it from the final role and the tags classify wrote, through the predicates that already name Ambiguity.alternative (_vocab.suffix_kind and name_source), so the reading taken and the reading not taken share one predicate. Neutral: no field of any parse moves and the committed snapshot is unchanged. ReadingKind gains PATRONYMIC, which no reading takes until 4c. Token.reading carries ADDED_IN 3.0.0 and compare=False, so a 2.x pickle loads with None (a 2.8.0 wheel fixture is added) and equality keeps its 2.x meaning. replace() and revise() build tokens without one; capitalized() carries each through. Reading.languages waits for 3a. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 4a review) - name_source loses the bound-given arm, which moved Ambiguity.alternative for a bound given name read as a name (Smith, John Abu: declared -> vocabulary); assemble names the bound given name's reading itself, and _EXPECTED_ALTERNATIVES holds both shapes at their master values. - A surname reads 'vocabulary' only where the segmentation split it off an unspaced name, matched on its folded text; spaced, the order placed it. - A word a suffix run takes stays a credential by position; only the second word of a split credential (Ph. D.) takes the first's reading. - Token rejects a reading of another field. - Strict xfail for Jr and Sr reading as a generation, the 4b follow-up. - Docs: the tags assemble reads are the deciding stages', not classify's alone; 280 shared readings, not 252; 4b needs 3a for a listed suffix word's kind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#754, 4a review 2) - A word that is a bound given name and a particle ('abu') reads 'particle' where the chain took it into the family (John Abu Smith, Smith, John Abu), and P5's given name only in the given part (Abu Bakar Salim); before, the bound-given arm ran first and read every one 'name', so Smith, John Abu's taken reading shared its kind with the report's alternative. - A connective in a suffix run reads 'conjunction' (John Smith, PhD and MD), as in a title or name part, and skips the vocabulary lookup. - The per-token frame costs in assemble's comment are the measured ones. - The decisions entry says a listed bound given name P5 did not join still reads 'vocabulary'; _pipeline/AGENTS.md's capitalized() sentence reads right again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew 2) The role gate on the bound-given arm read 'abu' as P5's given name in any given part, including one the particle chain made under FAMILY_FIRST_GIVEN_LAST (Salim Ali abu Bakar Hassan), where 'de' in the same place reads particle. No tag tells P5's join from the chain's, so a word in both sets reads particle wherever it joins, and name, vocabulary where it joins nothing (Abu Smith). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Classify tags every listed suffix word, bracketed or not, so only an
acronym written with an interior period ('M.A' in 'John Smith (M.A)')
reaches the check untagged; codecov/patch flagged the dead arm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eview 3) f001e7e read every joined abu as a particle, P5's given-name join included (Abu Bakar Smith, Smith, Abu Bakar, , Abu Bakar), where its twin Abdul Rahman Smith reads name. A word in both sets is P5's given name in the given part as the first word of its segment past the titles, which is where P5 joins, and a particle where the chain took it, in either part (John Abu Smith; Salim Ali abu Bakar Hassan under FAMILY_FIRST_GIVEN_LAST). Also: a particle in a nickname reads as a name ('"Van" Smith'), the unjoined marks being written in the name parts only; the frame comment says three for an untagged suffix word, as measured; the decisions entry says a connective in the suffix reads conjunction trailing as well as joining. Co-Authored-By: Claude Opus 5.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.
3.0 sub-phase 4a of the vocabulary work order (#754): every parsed token now carries
Token.reading, the reading the parse took for it.ReadingKindgainsPATRONYMIC.Reading.languagesis out of scope. 3a's reading index (#941) has merged, and a follow-up adds the field together with its values from that index. Adding it unfilled now would be wrong data, since an emptylanguagessays no file holds the reading.What it adds
Token.reading: Reading | None, a field withcompare=FalseandADDED_IN = "3.0.0".Reading(kind, role, source)type thatAmbiguity.alternativeuses for the reading not taken.reading.roleis always the token's own role, andTokenraisesValueErrorotherwise.Nonefor a token thatreplace()orrevise()builds, and for a token loaded from a 2.x pickle.capitalized()keeps each token's reading.ReadingKind.PATRONYMIC. Nothing writes it yet; 4c's rules will.Where it is filled, and why there
The reading is read in
_pipeline/_assemble.py, from each token's final role and the tags the deciding stages left:joinedmark that group and post_rules write;It is read there rather than written at each decision site because roles are only final at assemble. post_rules still moves words after assign (rules/P1's fold, P6's attachment, the policy folds), so a reading written earlier would need re-writing at every later move. That is the stale-mark class
_types._remarkedexists to recompute.The suffix and name sources go through
_vocab.suffix_kindand_vocab.name_source. These were split out ofsuffix_readingandname_reading, so the reading taken andAmbiguity.alternativeuse one predicate there.Which reading each role takes is in decisions/vocabulary-model's new 2026-10-29 #754 entry, item 2, and
_EXPECTED_READINGSintests/v2/pipeline/test_assemble.pyholds one row per branch.Neutral
tools/differential/snapshot.jsonlis byte-identical, alternatives included.Smith, John Abuwent fromdeclaredtovocabulary. Review caught it. Both shapes are now rows of_EXPECTED_ALTERNATIVESat their master values.Cost
Measured with
tools/perf/call_count.pyon the reference row:suffix_kindfor the row'sIII._CALL_BASELINE(307 and 288) is not moved.git archiveof master, Python 3.11:John Smith149 → 149,Juan de la Vega231 → 231John Smith PhD199 → 200name_sourcevan Gogh161 → 162김민준212 → 213John Smith, PhD and MD298 → 302, with +1 each forPhDandMDsuffix_kind,_listed_suffix,_normalize), +4 in the whole-parse deltaJohn Smith PhD Jones267 → 272,... Jones Brown327 → 336Readings are built once at import and shared, so no token pays to construct one.Open questions
Jr,SrandEsq.all readpost-nominal. The TOML givesjragenerationreading, butSUFFIX_WORDScompiles both kinds into one set, and classify tags them identically.Ambiguity.alternativehas given it since 2.7. The snapshot's one post-nominal alternative isRinpoche's.test_a_suffix_word_reads_as_its_vocabulary_kindmarksJrandSras strict expected failures, with a reason naming 4b. The RFC's 4b row and its interleaving paragraph now say 4b reads a listed suffix word's kind from 3a's index.Lexiconfield, was set aside: the 2026-10-28Lexiconsurface entry makes the compiled sets internal.Reading.languages. A comment in assemble's loop marks where it joins. 3a's merged_reading_index.readings_of(text)returns the vocabulary's own kinds, so 4a's follow-up (or 4b) maps them ontoReadingKind, and it also gives the generation/post-nominal split above. This branch merges master (3a) and corrects 3a's RFC sentence, which said Add Token.reading, the reading the parse took for each word (3.0, 4a) #942 adds the field unfilled.abu,أبو) reads P5's given name (name/vocabulary) where P5 joins it: in the given part, as the first word of its segment past the titles (Abu Bakar Smith,Smith, Abu Bakar,, Abu Bakar). Where the particle chain took it, it readsparticle, in either part (John Abu Smith, and the given part ofSalim Ali abu Bakar HassanunderFAMILY_FIRST_GIVEN_LAST). No tag tells the two joins apart, so position is the discriminator, matching where P5 joins. A tag written at P5's join would be the precise alternative, but changing a tag is not neutral.revise()builds tokens with no reading, since the caller forced their role. The alternative is to keep the sub-parse's reading re-pointed at the forced role; I left it out because the kind can then contradict the role (a title forced intofamily).Token's repr is unchanged. Readings are not shown, so reprs and doctests elsewhere hold still. Showing them is a later choice.Docs
docs/usage.rst: a new "The reading each word took" section with doctests.AGENTS.md: the pickling and decision-site bullets, and assemble'sReads:line.nameparser/_pipeline/AGENTS.md: a new "The reading taken is read at assemble" section.The design-docs reviewer's six findings and a code review's four are fixed in the second commit. The orchestrator's review of 22afb30 is fixed in the last two commits: a joining particle reads
particle, a suffix connective readsconjunction, the per-token costs above are measured, and the decisions entry covers a bound given name P5 did not join. Each fixed row was run against 22afb30 and fails there. A third round restores P5's given-name reading ofabu(by position, the guard rows failing on 1e2ad95), reads a particle in a nickname as a name, sets the frame comment to the measured three, and words the suffix connective as the code reads it.Checks
uv run pytest: 32322 passed, 5 xfailed (after merging master).uv run pytest -n0 -m serial: 41 passed.uv run --python 3.15 pytest: green.uv run mypy,uv run ruff checkand the Sphinx doctest build (0 failures): clean.🤖 Generated with Claude Code