The title-run bundle: a run addresses by its last title (#489), a trailing period-marked title is a title (#316) - #520
Merged
Merged
Conversation
…t be The leading peel runs before the trailing suffix peel and its only floor was "leave one piece", so `Dr King Jr` peeled `Dr King` and left `Jr` to be the name -- title 'Dr King', family 'Jr', no suffix. Derek's question put it plainly: Jr is a recognized suffix, so it could not count as a following name. leading_titles gains a second floor. Where everything behind the run is suffix pieces and the run's last word is not itself one, the run gives that word back. The word given back must be a name CANDIDATE, which is what leaves the all-suffix inputs alone -- `MD DDS` and `Jr. Ph. D.` are unchanged because `md` and `jr` are suffix vocabulary -- and the all-title ones too, there being no rest for the floor to read. It lands in the leaf rather than in assign because leading_titles is the one predicate for the leading run (mechanisms.md#ONE-PREDICATE-PER-QUESTION): assign sets the roles and group's chain reads the same count. `Dr King Jr` now reads title 'Dr', family 'King', suffix 'Jr' -- what v1 wanted, what rules.md#S2's descriptive note predicted, and what the comma form `King, Dr Jr` has always given. `Dr. King MD` moves the same way. Both report `title-or-name`, `king` being title vocabulary and now being the word left standing. One corpus name moves, measured over all 1123. Accepted edges: `Dr Jr` reads given 'Dr', suffix 'Jr' and `Sir Jr` given 'Sir', suffix 'Jr' -- with the title given back there is no title left to make either reading H1's. Two things the measurement forced, beyond the shape above. The floor's gate is an inline tag read rather than is_suffix_piece: leading_titles runs four times per parse, and asking the predicate first cost 8 frames against a band with room for two, so the two tag tests -- the cheapest NECESSARY condition for the next piece to be a suffix piece at all -- keep the ordinary titled name out of the branch entirely. Frame delta is now zero on both entry points. And under test_parser's overlap lexicon `Dr. Do Jr.` moves the same way `Dr. King MD` does, so the no-op-prefix-chain test names the fork it is about (PARTICLE_OR_GIVEN) instead of asserting silence: the report it now sees is H4's, about the word the floor left standing. Review round. The give-back is gated to a ONE-WORD piece: without it `Prince of Wales Jr` gave up its title entirely (given `Prince of Wales`, no title), where 1.4.0 and the pre-commit tree both read title `Prince of Wales`, family `Jr` -- a new parity row, `title_run_floor_keeps_a_joined_title_unit`, and a leaf test pin it, and the frame count is unmoved at 416 parse / 453 facade on 3.11. The docstring and the comment now say "its last piece, when that piece is one word" rather than "its last word", claim only that the piece given back is a name candidate, and name two residuals: a run whose last word IS suffix vocabulary is not given back (`Dr King MD PhD` keeps title `Dr King MD`), and the floor asks is_suffix_piece, which vetoes a bare initial-shaped numeral, so `Dr King V` still reads family `V`. The 8-call history is marked as measured on the plan's ordering, the committed shape having since hoisted the pieces[n-1] test into the guard. Three test notes were wrong: the `Sir Jr` row is renamed `title_run_floor_gives_back_a_given_name_title_run` and says it carries rules.md#S2's provenance rather than a second mechanism, the `MD DDS` row no longer calls itself the bare-suffix carve-out (that is `DDS MD`; `MD DDS` reads title `MD` plus H1's fold and reports nothing), and test_king drops its duplicated "v1 aspired" sentence for what `king` being title vocabulary still buys. test_parser's no-op chain test pins full silence on five rows and the exact `TITLE_OR_NAME` tuple on `Dr. Do Jr.` -- superseding the paragraph above, which describes the filter-one-kind-out shape it replaced. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`Her Majesty Queen Elizabeth` read family 'Elizabeth' because the given-name-title lookup was keyed on the whole run -- 'her majesty queen' is no shipped entry. A run is written as several titles and addresses the way its final one does, so the run's folded key is now matched whole OR by its last word. The whole-run ARM is what keeps a caller's multi-word phrase entry working: `lt col` is stored and matched as one key. Its order carries nothing -- the `or` short-circuits but decides nothing, since for a one-word run the key IS its last word. Every shipped given-name title is a single word, now asserted in test_lexicon.py, so that arm is dead for the default set: it can only ever match a one-word run, which the last-word arm reads the same way. Both sites move together, through one predicate in _lexicon beside _title_key, because the 2026-08-22 #369 decision rests on H1 and P5's licence never reading one run two ways. That invariant holds under last-word keying, and 'mr sir' -- #369's own example of a run neither site treats as a given-name title -- now IS one to both. The leaf test that pinned the old reading of that example, test_group's test_the_title_run_is_one_key_as_h1_reads_it, is renamed test_the_title_run_is_read_as_h1_reads_it and pins the invariant rather than the key's shape: 'mr sir abdul rahman' now joins. `Reverend Mother Teresa`, `Dr. Sir John`, `Mr Sir John` and `Xyz. Sir John` move with it, and through P5's licence `Sir Sheikh abdul rahman` reads given 'abdul rahman' with no family. What does not move: `His Excellency Lord Duncan` and `Her Royal Highness Princess Anne` -- `lord` and `princess` are not given-name titles, and whether they should be is a vocabulary question with its own frequency argument, deliberately left alone. One corpus name moves, measured over all 1123. The v1 suite's #489 xfail is dropped; xfail_strict would fail on the XPASS otherwise. Frame delta is zero on both entry points (416 parse / 453 facade on 3.11, unmoved): each site's read sits inside the branch it serves, and the reference name `Dr. Juan0000 de la Vega III` enters neither -- H1's guard wants an unoccupied family, P5's a bound given-name word. Review round. The predicate is one fold and one pair of lookups over that key, no list and no empty-run guard, measured unmoved on both counts. The docstrings drop the ordering language and say what the two arms reach instead: the last word is the last word of the FOLDED key, so a run token that folds away (the lone '.' the conjunction merge can leave, `Sir and . John`) cannot empty that arm; and an unlisted abbreviation can never MATCH as a last-word key, but may sit inside a caller's phrase entry, since given_name_titles is deliberately not validated against `titles`. _title_key's own claim that a lone '.' is not a title token was false and now says the fold drops it. post_rules' two-level `if` collapses to one condition and cites decisions.md#P5, where the #369 entry lives; group's comment is re-wrapped; test_lexicon's phrase-entry test folds into the per-word fold test it duplicated; and test_group's conjunction test is renamed for the mutation it catches, keyed over every token of the piece. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`John Smith Prof.` read family 'Prof.' and lost the surname, while `Smith, Prof.` read title 'Prof.' -- the two comma paths disagreed, and #316 is the question. They agree now: after the trailing suffix peel has taken its run, successive single words that wear the period-abbreviation shape AND are title vocabulary chain into the title from the end, leaving one name word standing. A RUN, mirroring H3, so `John Smith Prof. Dr.` reads title 'Prof. Dr.' rather than making `Prof.` a name word. The title view joins TITLE tokens in token order, so `Dr. John Smith Prof.` reads title 'Dr. Prof.' with no rendering change. The doctrine, stated rather than left to be inferred: the LEADING slot has a shape rule that outranks vocabulary (H2 -- `Esq. Smith` is a title though `esq` is post-nominal vocabulary, and that is unchanged); the TRAILING slot has no shape rule and reads vocabulary only. So `John Smith Xyz.` keeps family 'Xyz.', and a BARE trailing title word is a name word -- `John Smith Sir` and `Mary Jane King` are untouched, TITLES holding ordinary surnames being exactly why. The walk uses is_title_piece, never is_leading_title: that predicate carries H2's inference and would take `Xyz.`. No ambiguity is reported: under the input-is-a-name premise a period-marked title word is not a fork a reader would hesitate over. Two decisions taken with the spec's questions open. A trailing title is TRANSPARENT to the suffix peel -- `X Prof. Y` reads exactly as `X Y` reads, plus the title. The peel ahead of the walk is PROVISIONAL: all it settles is where the walk starts, because the walk can remove the very word that stopped it. So the pieces the walk takes are spliced out and ONE peel then runs over what stands, in original order, and that peel alone places a piece or reports a fork. `John Smith Jr. Prof.` reads suffix 'Jr.' where a single pass promoted a generational suffix to the family name; `John Prof. MA` reads the family 'MA' that `John MA` reads, S2's reserve keeping a bare ambiguous acronym the family of a two-word name, where a second peel laid over a first read family 'John', suffix 'MA'; and `John Smith V Prof. VI` reads what `John Smith V VI` reads -- middle 'Smith V', family 'VI', nothing reported -- where two peels each reporting their own last piece read suffix 'V VI' and reported the numeral fork twice. And the family-comma segment-1 walk ships: the comma gate does NOT route `Smith, John Prof.`, which read middle 'Prof.' at every baseline -- the `Smith, Dr.` family of rows that already route go through the no-name gate, a different mechanism. The same principle governs its candidates, which are the pieces that segment does not read as a SUFFIX, asked of the segment's own predicate rather than of a copy of half of it: `Smith, John Prof. V` is `Smith, John V` plus a title because the #144 lenient tail claims the numeral, and `Smith, John V Prof.` is too, because the walk moved where this segment's name ends and the lenient test follows it. Both are corpus-neutral, measured. Five corpus names move, measured over all 1123 -- the four `John Smith <Title>.` rows the issue planted, and `Andrew Perkins (Mgr.)`, which measurement found: its trailing period keeps the parenthetical out of nickname parsing and `mgr` is title vocabulary. One PINNED row moves that is not a corpus name, and is named here rather than left to be found: `Smith, PSM Dr. I` (family_comma_title_resets_the_credential_run) reads title 'Dr.' where it read middle 'Dr.', which is `Smith, PSM I` plus a title. The reset that row exists to pin still fires and the row still fails without it. Its parity claim went with the move and had outlived the audit anyway -- 1.4.0 reads suffix 'Dr., I', `dr` still being post-nominal vocabulary before #296. Two consequences worth naming. `titled` in the lone-name-word emitter is now true for a name whose only title is a trailing one, so `Smith Prof.` reports nothing where an untitled lone name word reports `given-or-family` -- H1 decides the field, the same answer `Dr. Smith` gets. And H4's JOIN clause gains one reach: `John of Prince Prof.` reports `title-or-name` because the walk left the unit `John of Prince` standing alone, which is what that unit already reports on its own. H4's single-word half cannot be reached this way -- a lone title-vocabulary word in front of a trailing title is taken by the LEADING run first, so `King Prof.` still reads title 'King'. Neither call site is gated. The question is asked by its predicate and by nothing else (mechanisms.md#ONE-PREDICATE-PER-QUESTION), so the walk's floor and its vocabulary read are the only things that answer it and a mutation removing either is visible. That costs the reference name one frame on each entry point -- inside the plan's design target of two -- and one frame comes back beside it: the group-flagged suffix scan collected a list it never read again, and a comprehension is a frame of its own on 3.11. Measured delta is 0 on both entry points, cold (416 parse / 453 facade on 3.11) and in a full pytest session (416 / 455). The give-back is not optional: 455 of the 455.94 allowed facade calls is what MASTER already costs in a full session, so the walk's frame alone measured 456 and failed the band -- the 453/454 pair a module-only run reports is not the number the test sees. Review round: both inline necessary conditions are gone, the walk is called directly at both sites, the mixed-provenance `Peel._replace` and the double numeral report with it, and `_report_numeral` is inlined back at the one site left. Six rows pin the transparency readings and each fails under its own mutation of the two peels or of the comma candidates; the seven-name corpus sweep against a0b93f0 is unchanged by any of it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… are asserted disjoint Esquire is a contraction, not an initialism. The SUFFIX_ACRONYMS entry arrived in the 2019 bulk Wikipedia post-nominal import (af5bdab, #93) and was never reviewed; its only unique coverage is the spelling "E.S.Q.", which nobody writes. Same criterion as the rai/cha decision: does the entry describe the WORD or the machinery. `John Smith E.S.Q.` now reads family 'E.S.Q.' where every release since 1.4.0 read suffix. `Esq` and `Esq.` are untouched -- the SUFFIX_WORDS membership carries them, and it stops being inert -- and so are `Smith, Esq.` and `Esq. Smith`. One corpus name moves. What that buys is an invariant: SUFFIX_ACRONYMS and SUFFIX_WORDS are now asserted disjoint at import. The two sets normalize differently, so a word in both is matched by two rules and which one fired is unreadable from outside. 'esq' was the only overlap and the reason the assert could not exist; the two comment blocks defending it, the AGENTS.md gotcha and the `deliberately not asserted disjoint` note all retire with it. A behavior change, not a fix: a 2.x parity break, classified on all four ledgers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
recorded rules.md gains H5, the trailing title run: after the suffix peel, successive single period-marked title-vocabulary words chain into the title from the end, floor one name piece, and the title is TRANSPARENT to the suffix reading. The H Background states the doctrine the two slots split on — shape at the front, vocabulary at the back, a bare title word a name word because TITLES holds surnames. H1 gains the run clause (a run addresses as its last title does), H3 the peel floor, H4's Accepted clause is rewritten now that `Dr. King MD` reports, P5's "one key" sentence and S2's descriptive note are replaced by the argument each asked for, and the code citations are upgraded to verbatim excerpts. decisions.md gains `### H3` and `### H5` and amends five entries: H1 (the last-word keying, #489), P5's 2026-08-22 #369 entry ('mr sir' IS a given-name-title run to both sites now, the invariant kept), H2's `Open: #316` hook closed, H4's #491 population corrected from six to ten with seven contract and three radar, the trailing-position prohibition scoped to BARE words, the esq Excluded block replaced by the drop and its criterion, and the v1-xfail triage's `Dr King Jr` bullet moved from NOT FIXED to FIXED with #489 out of FIX CANDIDATES. Each measured population is scoped to the tree it was taken on, with today's figure beside it, since this commit's own examples grow the corpus. mechanisms.md re-checks TWO-LAYER-ASSIGN's one named exception against H5 (still one) and adds `trailing_titles` to ONE-PREDICATE-PER- QUESTION's census. release_log.rst gains three fix bullets and the esq behavior change. AGENTS.md's "runs in one direction only" line becomes the doctrine, the esq gotcha retires with the assert that replaced it, and 694 → 746, measured. usage.rst and customize.rst follow. corpus_rules.jsonl regenerated: 23 rows, 15 new names. Design-docs review of this commit (2026-09-09), six findings folded in by fixup. decisions.md#P5's trailing-position bullet RETRACTS half its own argument: a period-marked trailing word is not one nobody writes as a surname, and the collision it was said to prevent is simply accepted there -- `Mary Jane King.` reads title 'King.', given 'Mary', family 'Jane', `John Smith Judge.` reads title 'Judge.', and 627 of the 746 titles in no suffix set read as a trailing title once a period is written behind them, the 119 that do not being held out by the abbreviation SHAPE alone (a digit, a hyphen, an apostrophe, a script with combining marks), no plain ASCII-letter title missing. rules.md#H5 carries the cost as an Accepted clause with `Mary Jane King.` as its executable example. H5's transparency sentence is SCOPED: it claims what it claims where two or more name words stand, and where the chain leaves ONE, H1 decides that word's field -- `Smith Prof.` reads family 'Smith' where `Smith` alone reads given 'Smith' and reports given-or-family. `_assign.py`'s verbatim excerpt follows the new wording. H1 gains the TRAILING REACH it always had in the code: a title run standing BEHIND the one name word decides its field the same way, so `Smith Sir.` and `Smith Queen.` read given 'Smith' while `Smith Dr.` reads family. `"Smith Sir." -> given="Smith"` is an example line, H1 and H5 cross-link in `interacts:`, and no code moved for it. Three counts corrected. release_log's "Ten names ... gain a report" is EIGHT distinct names in ten corpus rows, measured at a0b93f0, and decisions.md#H4's "these ten names" says eight, twelve after this bundle, with the recompute beside it, so the entry's two TENs stop naming two quantities. decisions.md#P5's renamed group test pins P5's side alone, the two-site agreement resting on the shared `_run_addresses_by_given` and on test_parser.py::test_the_p5_licence_and_h1_read_a_title_run_the_same_way rather than on that leaf. And #H5's A2 "moves NO corpus name" is scoped to the fix: `Smith, John Prof.` enters the corpus here, and the rule's population is FIVE at the fix and FOURTEEN here, re-measured with the walk stubbed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Twenty-one corpus names move: the eight the parser changes reach, plus the thirteen the rules-doc corpus gained when Task 5 regenerated it from rules.md's own examples -- eleven at first and two more from the 2026-09-09 design-docs review of that commit, `Mary Jane King.` and `Smith Sir.`. Before, on this branch with the corpora already regenerated: corpus: 1135 names; intentional diffs: 367; unexplained: 16; radar unclassified: 5 corpus: 1142 names; intentional diffs: 297; unexplained: 16; radar unclassified: 5 corpus: 1142 names; intentional diffs: 211; unexplained: 16; radar unclassified: 5 corpus: 1142 names; intentional diffs: 74; unexplained: 16; radar unclassified: 5 After: corpus: 1135 names; intentional diffs: 388; unexplained: 0; radar unclassified: 0; 283 of 388 changed names are Latin-only corpus: 1142 names; intentional diffs: 318; unexplained: 0; radar unclassified: 0; 218 of 318 changed names are Latin-only corpus: 1142 names; intentional diffs: 232; unexplained: 0; radar unclassified: 0; 218 of 232 changed names are Latin-only corpus: 1142 names; intentional diffs: 95; unexplained: 0; radar unclassified: 0; 85 of 95 changed names are Latin-only Exit 0 at all four. Every baseline rises by the same 21, which is the before-run's unexplained plus its radar unclassified: at the older baselines the names do not arrive, they move from unclassified to explained. 'John Smith Dr.' is the one that changed RULES rather than arriving there, which the fix(#296) paragraph below records. FOUR rules, one per argument, in all four ledgers and last in each file. Anchored alternations of names, so the reach IS the mover list: fix(#489) a title run addresses by its last title 2 names change(suffix-acronym-collisions) esq leaves the acronym set 1 name fix(#489) the title peel leaves a name word a suffix cannot be 4 names fix(#316) a trailing period-marked title word reads as a title 14 names Last in the file is narrow-first rather than a preference: every rule already present that REACHES one of the nineteen declares a strict subset of the bundle rule claiming it, and a wide rule ahead of a narrower one it shares a name with is an order-decided contest the run refuses (#382). Measured: at 1.4.0 fix(#296)'s trailing `dr` reaches two of the trailing-title names, the two comma routings reach 'Smith, John Prof.', and the two-token `jr` rule reaches two of the floor's four; at 2.0.0 and 2.1.0 only fix(#296) reaches any; at 2.2.0 none does. None of them ADMITS the diff it reaches, so no rule lost a name it was explaining and no [[change.precedes_narrower]] block was needed for the bundle itself. `_initials` is in none of the four field lists. The plan predicted it on six of the eight movers; the run puts it on none, because the derived view enters a diff only where every role and ambiguity kind agrees, and all nineteen move roles. validate_rules refuses it beside another field in any case. THE fix(#296) `dr` RULE, reconciled by widening nothing. 'John Smith Dr.' moves {title, suffix} at the three older baselines and {title, middle, family} at 2.2.0, neither a subset of that rule's fields, so it falls through to the #316 rule below it -- narrow-first placement means the DIFF decides the handover, not file order. The rule's closing sentence, which said no trailing title-only word routes to title on the no-comma path and called #316 the open question for the class, is retracted in the same edit; the bare spelling ('Smith Dr', the 'dr ... dr' names) stays with it, #316(b) being out of scope. The gate then called that rule OVER-DECLARED: with 'John Smith Dr.' gone, none of the three names it still explains moves a middle name, so `fields` narrows {middle, family, suffix} -> {family, suffix}. That narrowing made it a strict subset of the glued-CJK-honorific rule's three roles in the 2.0.0 ledger, where both regexes reach '田中さん, Dr.' -- a name that produces no diff at that baseline at all. The honorific rule keeps its place with a [[change.precedes_narrower]] block declaring the LATENT pair: it describes the compound the dr rule knows nothing about. ROSTERS in tests/v2/test_ledger_guards.py, every number taken from `_claim` rather than typed. _CORPUS_CLAIMS gains the four rules per ledger (2 / 1 / 4 / 14, identical digests across the four files, only the floor rule's roles differing -- `_ambiguities` at the 2.x baselines, absent at 1.4.0). Five existing claims moved with the corpus and each says which name arrived: fix(#296) `dr` 11 -> 12 ('John Smith Prof. Dr.'), the two comma routings 288 -> 289 ('Smith, John Prof.'), the two-token `jr` rule 5 -> 7 ('Dr Jr', 'Sir Jr') and the connective-run initials rule 96 -> 97 ('Prince of Wales Jr'). _NOT_A_VOCABULARY_COPY gains three alternations, _MUST_NOT_MATCH four keys with eighteen probes, and _ORDER_EXEMPTION_EFFECT its first 2.0.0 row. The last two members of the #316 rule, 'Mary Jane King.' and 'Smith Sir.', arrived with the 2026-09-09 review of the docs commit. Both are new rules.md examples and both diff identically at all four baselines. 'Smith Sir.' sits HERE rather than on the fix(#489) run rule because the DIFF decides: it moves {title, family}, the walk taking 'Sir.' out of the name, while its given/family reading is what every baseline already gave -- and the #489 rule declares {family, given}, which does not reach a title role at all. Neither takes a _WATCHED_DIFFS row: a rules.md example line is a test literal, executed by test_rules_doc.py, so neither is that roster's population shape. The #316 rule spells 'Andrew Perkins (Mgr.)' with \x28 and \x29. A literal parenthesis inside an alternation body, escaped or not, hides the whole group from test_ledger_guards._alternations, and a twelve-member alternation should not be invisible to the pass that demands every alternation declare what it copies. _WATCHED_DIFFS: the two rows this bundle could have moved, 'Smith, Prof.' at 1.4.0 and 'Esq. van Gogh' at 1.4.0 and 2.0.0, are unchanged and no run reports a moved shape. None of the nineteen sits at a shape two rules could admit, so none belongs there on that ground. One does belong on the roster's own POPULATION clause: 'John Smith Rev.' is the only one of the nineteen named by no test literal anywhere, so a ledger rule is its only watcher, and it takes a row at each baseline. That addition moves the row counts and nothing else -- the population paragraph was not re-derived. Full suite: 7536 passed, 177 skipped, 4 xfailed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #520 +/- ##
==========================================
- Coverage 98.68% 98.67% -0.02%
==========================================
Files 45 45
Lines 3262 3309 +47
==========================================
+ Hits 3219 3265 +46
- Misses 43 44 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
P5's bound-given reserve counted a trailing period-marked title word as a name word to spare, so `Prof. abdul rahman Prof.` joined the bound pair and read family 'abdul rahman' where `Prof. abdul rahman` reads given 'abdul', family 'rahman'. The reserve now subtracts the H5 run from both views -- assign's second peel runs over the pieces that walk LEFT, so the reserve has to read the same list -- and the same-suffix comparison is read over the shortened lists too, which is what stops `Sir abdul Prof.` joining a title word into the given name. No corpus name moves: the 21-mover list against a0b93f0 is unchanged. Frames are unchanged at 416 parse / 453 facade on py3.11; the reference name never enters the reserve branch, having no bound given word, so its two new calls cost nothing. rules.md#P5 names the H5 walk in the sentence the code cites; H5 gains the P2 and M2 boundaries (`John van der Berg Prof.` keeps family 'van der Berg Prof.', `Mary Smith née Jones Prof.` maiden 'Jones Prof.') and the P5 clause; H5/P2/M2/P5 gain each other's `interacts:`. Tests: the script-order half of "set before the positional read" (`毛 泽东 Dr.`), the conjunction-merged unit that actually pins the one-word gate in `trailing_titles`, `Dr. Do Jr.`'s own test kept out of the no-op-chain parametrization's silence claim, and case rows for the two bound-given readings, `Smith, E.S.Q.`, the two maiden orderings, `Smith, John, Prof.` and `John Smith Prof. and Dr.`. Stale counts and false comments, all re-measured: 12/nineteen -> 14/twenty-one for the title-run bundle in the four ledgers and the guard, the twelve-member alternation is fourteen, the release log's bullet names the accepted `Mary Jane King.` cost, and the ledgers' role paragraph says which baseline has three two-role movers and which has two. `John Prof. MA` does report an ambiguity, so the "none of the fourteen" sentence is corrected to the one that matters (`title-or-name`, still none). The first suffix peel is provisional only where the walk takes something; `trailing_titles` is entered by every parse with a name word to place and not by all 1289 corpus parses (52 return first); its `rest` is the caller's name pieces, and group is now a third caller, which mechanisms.md's census records. Two defensive branches are marked measured-inert over 191,146 generated inputs -- the folded-vs-raw last word and the titled-piece skip -- and the third the review named, `walkable` starting at `n`, is NOT inert: dropping it moves 24 of those inputs, so it is marked load-bearing with the shape that moves. compare.py's RECOMPUTE paragraph: the strict row counts are 38/34/33/8 over 52 names, 50 of them in corpus_issues.jsonl. The every-file pair recorded on 2026-09-05 is retracted rather than bumped -- it exceeded the strict pair, which is impossible -- and replaced with a derivation a reader can run without a baseline wheel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…w classified H1 collected every `Role.TITLE` token into ONE run, and with #316(a)'s trailing run shipped a name can carry titles at both ends, so the run behind the name decided the field of the word in front of it. `Sir John Prof.` keyed 'sir prof' -- addressed by neither word -- and read family 'John' where `Sir John` reads given 'John'; `Queen Elizabeth Prof.` lost its given name the same way, and `Dr. Smith Sir.` moved the other direction, the trailing 'sir' making the leading 'dr' address by given name. The rule is #H5's transparency principle applied to the one-word name: `X Prof.` is `X` plus a title, so adding the title cannot change how the words in FRONT of the name are read. The run standing before the one name word addresses; a run standing behind it decides only when none stands before, which is what keeps `Smith Sir.` reading given 'Smith'. Found by this PR's own review of P5: group's licence has always read only the pieces ahead of the bound word (`range(fk)`), so the 2026-08-22 #369 invariant -- H1 and P5 cannot read one run two ways -- had been broken by the composite keying, and `_group.py` now records that beside the licence. The split is at the NAME WORD. Splitting at the first token of another role -- the first shape written -- is defeated by a nickname in front of the titles: `'Smitty' Sir Jones Prof.` found no leading run and read family 'Jones', and `Dr. 'Smitty' Sir John` lost the run it had. H1's rationale decides it, a nickname beside the name word not deciding this reading, so a run written around one is one run. The design review's two-input invariant is what caught it. `Sir abdul rahman Prof.`'s case row flips to given 'abdul rahman': the P5 reserve fix was right AND this one was needed, the join firing for the right reason while the field stayed wrong. Rows added for `Sir John Prof.`, `Queen Elizabeth Prof.` and `Dr. Smith Sir.`; `Smith Sir.` and `Dr. King Sir.` (title 'Dr. King', given 'Sir.', title-or-name) are unmoved. The invariant test's trailing-title half now requires the titled spelling to give the same given and family as the spelling without it, over the nickname spellings too: restoring the whole-titles key fails six of its eleven rows and so does the first split, where comparing the two spellings' "no family" to each other passed under both. rules.md#H1 states the ordering and gains `"Sir John Prof."` and `"Dr. Smith Sir."`; #H5's one-name-word clause is conditioned the same way. Its P5 clause is scoped to the comma-less writing -- after a family comma the reserve reads no peel, so `Berg, abdul Prof.` takes the title into the given name, which is 1.4.0 parity and is recorded with a case row rather than an example, tracked as part of #316. `毛 泽东 Dr.` printed UNCLASSIFIED (radar) at all four gates and now has its own literal rule, `fields` differing per baseline -- it is the one bundle rule that cannot join an alternation, a script-classified member belonging to the honorific pin. The trailing-title alternation goes to sixteen, the bundle to twenty-four corpus names, and the four ledgers, the guard, the release log, decisions.md#H5's population (SEVENTEEN by its own recipe, the CJK name having been missing from it) and compare.py are re-measured. Twenty-four movers against a0b93f0; the only two this commit moves are the two rules.md examples it adds. All four gates read `unexplained: 0; radar unclassified: 0`. Frames unchanged at 416 parse / 453 facade on py3.11 -- the run is built inside H1's guard and the reference name has a family. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ound on PR #520 The trailing peel (S2) and the trailing title chain (H5) were derived twice: assign ran peel -> chain -> splice -> re-peel inline, and group's bound-given reserve modelled that as the peel's count minus the chain's take on each of the two views the join compares. The two disagreed at S2's bare-ambiguous reserve -- `abdul rahman MA` declined the join while `abdul rahman MA Prof.` took it, contradicting H5's stated clause that a bound given-name word behind a trailing title joins as it joins with the title absent. And assign's re-peel ran once, so `John Prof. MA Prof.` un-peeled the acronym and re-exposed the first title, reading family `Prof.` where `John Prof. MA` reads family `MA`. _pieces.tail_reading is now the one function: peel, chain, splice, peel again until the chain takes nothing, returning the walk, the chained pieces and the final peel. Assign calls it in place of its inline sequence; the reserve calls it on both views and compares the suffix lists, both subtractions deleted, with the join's refusal to take a chained piece said as the rule it is rather than left to the arithmetic. trailing_titles returns the count it LEAVES STANDING, the way peel_trailing counts, so the two compose without arithmetic at all. The two readings above are the only behaviour that moves: all seven fields, the ambiguity kinds and the recorded order over every corpus name and every cases.py literal are identical, 2674 names on both trees, none moved. Both take case rows. The helper costs one frame on the common path, paid by _effective_order taking the name-piece indices instead of a list built for it; the reference name stays at 416 parse / 453 facade on 3.11. Also in this round: leading_titles' floor as a for/else; the family-comma walk's `titled` as a parameter rather than a closure over a rebound local, one "previous kept piece" helper for both `prev` and the name's end, and the placement pass reading the first pass's own memo where the chain took nothing (-7 frames on `Smith, John Quincy`); _lexicon's fold factored so "the last word of the FOLDED key" is structural; suffixes.py's unreachable third assert folded into the disjointness one; duplicated prose in _group, _post_rules, compare.py and test_ledger_guards reduced to one copy; and the `Sir Jr` example and case row dropped, `Dr Jr` reading by the same branches -- which moves two ledger rosters (7 -> 6, 4 -> 3). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eads it CI's 3.12-3.15 jobs failed the facade band at 452.0 against a ceiling of 451.86 after the /simplify commit: the frame it traded for the shared tail reading was a list comprehension in _effective_order, which is a frame on 3.11 alone (PEP 709 inlines comprehensions from 3.12), so on the newer interpreters the wrapper went unpaid. `has_nickname` was computed at the top of the positional read as `any(t.role is Role.NICKNAME for t in tokens)`, a generator that every interpreter resumes once per token -- seven call events on the reference name -- and read by exactly one branch, the one-piece segment beside a nickname (rules.md#N3). The scan now sits last in that branch's condition, so an ordinary name never starts it. Measured with tools/perf/call_count.py on `Dr. Juan0000 de la Vega III`: py3.11 416/453 -> 409/446 (baseline 410/447); py3.12 395/432 -> 388/425 (baseline 388/425); py3.14 413/450 -> 406/443 (baseline 406/443). Every interpreter is back at or one under its recorded row; no row moves. Parses are unchanged: the branch's answer is the same, only when it is computed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mes tail_reading Six review findings on the pushed branch, all re-measured on this tree rather than carried over. decisions.md#H5's class bullet stated one width for two defects and named the wrong shape for it. The 88 shapes the fix closes PARTITION: 56 carry one period-marked title word and a bound given-name word besides -- the subtraction model -- and 32 carry two, the truncated fixed point. The 156 movers over the 82,712-input sweep split 60/96 the same way, and the two-title population is 784, of which 164 differed before and 132 still do. The population bullet's disabling stub was wrong the moment the same commit made `trailing_titles` return the count it LEAVES STANDING: a 0 stub now takes everything and raises IndexError on 'John Smith'. The recipe says `lambda rest, *a: len(rest)`, bound over both the `_pieces` name and the one `_assign` imported, and says why the fix's own tree needs the other stub. Re-run that way the five and the seventeen hold. mechanisms.md's ONE-PREDICATE-PER-QUESTION census was one round out of date: an AST call-site census over `_pipeline/*.py` says the shared predicate is `tail_reading`, that `peel_trailing` has no caller outside `_pieces.py` at all, and that `trailing_titles` has exactly one. #H4's rosters lose `Sir Jr`, which left corpus_rules.jsonl in the same round: eleven names in fourteen rows, the peel half nine, six contract and three radar -- with the tier rule spelled as the README states it, contract files sorted first. The release bullet counts the set once, three corpus names plus `Sir Jr` as an input `Dr Jr`'s row pins, and the four ledgers' peel-floor comment no longer claims a reach of four. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s reach Co-Authored-By: Claude Fable 5.1 <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.
Four measured changes to how a title run is read, in one bisectable order, then the docs and the ledgers.
1. The leading title peel leaves a name word a suffix cannot be.
Dr King Jrreads titleDr, familyKing, suffixJr— what v1 wanted, what rules.md#S2's descriptive note predicted, and what the comma formDr. King, Jr.has always given. The peel's only floor was "leave one piece"; it now also refuses to leave a word that is nothing but suffix vocabulary standing as the name, giving back its last title word when that word is one word and a name candidate.MD DDS,Jr. Ph. D.,Marquess of BathandPrince of Wales Jrare untouched. One corpus name moves;Dr. King MDandDr Jrmove with it. Two residuals recorded in decisions.md#H3: a run ending in a dual-vocabulary word (Dr King MD PhD) and the bare-numeral fork (Dr King V).2. A title run addresses by its LAST title (#489).
Her Majesty Queen Elizabethreads givenElizabeth; so doReverend Mother TeresaandDr. Sir John. The lookup was keyed on the whole run joined, and the shipped vocabulary has no multi-word entry, so no two-word run could ever be exempted from the fold. Both sites that ask — H1's retag and P5's join licence — move together through one predicate in_lexicon.py, so the 2026-08-22 #369 invariant holds; only its'mr sir'example flips. The test is the whole run's key OR the last word's key, so a caller's phrase entry (lt col) still matches.Sir John Smith,His Excellency Lord DuncanandHer Royal Highness Princess Annedo not move;prince/princess/lord/ladyare deliberately still not given-name titles (#519).3. A trailing run of period-marked title words is a title (#316a, new rule H5).
John Smith Prof.reads titleProf., familySmith, and the two comma paths agree at last. A RUN mirroring H3 (John Smith Prof. Dr.→ titleProf. Dr.;Dr. John Smith Prof.→Dr. Prof.in input order), TRANSPARENT to the suffix peel:X Prof. Yreads asX Yreads plus the title, soJohn Smith Jr. Prof.keeps suffixJr.andJohn Prof. MAkeeps S2's bare-acronym reserve. Where the chain leaves one name word, H1 decides its field, and H1 now reaches a run standing BEHIND the word (Smith Sir.→ givenSmith). The family-comma segment path gets the same walk (Smith, John Prof.). The doctrine, now stated in rules.md's H Background: the LEADING slot has a shape rule that outranks vocabulary (H2 unchanged —Esq. Smithis still a title); the TRAILING slot has no shape rule and reads vocabulary only, soJohn Smith Xyz.keeps its family name, a BARE trailing title word is a name word (Mary Jane King), and a period-marked surname-collision word at the back is an accepted cost under the input-is-a-name premise (Mary Jane King.→ titleKing.). No ambiguity is reported. Five corpus names move at the fix, including one the drafting did not expect:Andrew Perkins (Mgr.).4.
esqleavesSUFFIX_ACRONYMS. A behavior change.John Smith E.S.Q.stops reading a suffix;Esq,Esq.andEsquireare unchanged through the word set. Esquire is a contraction rather than an initialism, the entry came in with the 2019 bulk import (#93), and the multi-dot spelling was its only unique coverage. What it buys isSUFFIX_ACRONYMS ∩ SUFFIX_WORDS == ∅, asserted at import, retiring two comment blocks, a gotcha and a case row.Review rounds (two commits after the six).
/pr-review-toolkitfound one real defect and its root: P5's bound-given reserve counted a trailing title piece as a name word to spare, and H1 read the leading and trailing title runs as ONE run, soSir John Prof.folded John to the family whileSir Johnkept it given. Both fixed: the reserve subtracts the H5 run, and H1 addresses by the run standing BEFORE the name word — the first name word, not the first non-title token, since a nickname in front must not decide (Dr. 'Smitty' Sir John); a run behind the word decides only when none stands before it (Smith Sir.→ given,Dr. Smith Sir.→ family). P5 already read the leading run, so the two sites agree again. Also: the W4 script-order interaction pinned (毛 泽东 Dr.keeps family 毛), the one-word gate on the trailing walk pinned (John Smith Prof. and Dr.), H5's particle-chain and maiden-run boundaries recorded,esqpinned on the comma path, and the stale counts swept. One pre-existing gap recorded as a parity row, not fixed: after a family comma the bound join fires over a trailing title (Berg, abdul Prof.→ givenabdul Prof.)./simplify round (one commit). Assign and the P5 reserve now share one tail reading in
_pieces.py: peel, chain the trailing titles, splice, re-peel, to a fixed point. The reserve had modelled assign's splice-and-re-peel as "peel then subtract", which differed exactly at S2's bare-acronym reserve (abdul rahman MA Prof.joined whereabdul rahman MAdid not), and the fixed point closesJohn Prof. MA Prof.. The wrapper's frame is paid for by_effective_ordertaking indices instead of building a list. Mechanical:trailing_titlesreturns the kept count, one "previous kept piece" helper in the comma walk, the first pass reused when the walk took nothing (−7 frames onSmith, John Quincy), a redundant import-time assert dropped, duplicated comment blocks folded, two case rows that pinned already-pinned forks dropped. That commit's frame trade was 3.11-only (a list comprehension; PEP 709 inlines them from 3.12), so CI's 3.12-3.15 jobs sat one call over the band; the follow-up commit moves the nickname scan, a generator resumed once per token, into the one branch that reads it, which brings every interpreter back to its recorded baseline (3.11 409/446, 3.12 388/425, 3.14 406/443).Measured. Eight corpus names move against
a0b93f0out of 1123, 23 on the grown corpus once the rules.md examples entercorpus_rules.jsonl. Gate: 392 / 320 / 234 / 97 intentional,unexplained: 0,radar unclassified: 0at all four baselines. Frame budget: at or one under the recorded baseline on every interpreter; two inline gates written to match the walk's predicate were removed in review (mechanisms.md#ONE-PREDICATE-PER-QUESTION) and the walk's one frame is paid for by dropping a dead list comprehension, because master's facade count already sits at the band's in-session ceiling. Every case row classified against the 1.4.0 wheel. Six review rounds (two per code commit, one onesq, the design-docs reviewer on the docs) folded in by amendment, then the two PR review-round commits above.Out of scope by choice. #316(b), the bare-safe subset (
Smith Dr) — it needs #348's census, and the corpora show the bare trailingdrswallowed by P2's particle chain, a different mechanism. #348, #490 and #514 likewise.prince/princess/lord/ladyas given-name titles is #519.Closes #489
Closes #316
🤖 Generated with Claude Code