fix(post_rules): the trailing particle is decided by its slot, not its spelling (#467) - #472
Merged
Conversation
…ohibition Three sites said a never-given particle "cannot be a name at all" or "can never be a given name", and used that to justify the readings built on it. It is false: NON_GIVEN_NAME_PARTICLES supplies the reading position leaves open and forbids nothing. Measured, all 33 members report given == themselves when parsed bare. The conclusions those sites draw are right; the reason given for them was not, and it is load-bearing. A particle OPENING the name has the rest of the name to join forward to -- that is evidence about the writing rather than about the word, which is why no declared name_order contradicts it. Stating it as a property of the vocabulary invites the opposite inference for a particle ENDING the name, where there is nothing to join forward to and the writing is all there is. This is the premise #466 was argued from, and the second correction of this same over-claim: decisions.md#P6 already records dropping "no particle is a name by itself" for the same reason. Behavior descriptions that the rest of #467 will change are left alone here; this commit moves no parse.
…s spelling (#467) A declared family-first order names the family as a comma does, so P6's attachment fires without a comma too -- keyed on the SLOT the trailing run landed in. MIDDLE is the one position that means nothing for a particle, middles being further given names, and FAMILY_FIRST is the only order that puts a trailing piece there. Jong, Anke de family 'de Jong' (unchanged) Jong Anke de FF family 'Jong' middle 'de' -> family 'de Jong' P1 loses its given-position site in the same change, and that half is a correction rather than a narrowing: that slot holds what the caller DECLARED to be the given name. `Menil de` now reports given 'de' under both family-first orders. No vocabulary test at the new site, and that is the design. A comma cannot separate the Dutch reading from the Vietnamese; the declared order can, and does both at once -- 'Beethoven Ludwig van' gives family 'van Beethoven' under FAMILY_FIRST while 'Nguyen Thi Van' keeps given 'Van' under FAMILY_FIRST_GIVEN_LAST, the same word in the same set. A never-given test would have excluded 37 ambiguous particles including von, di, da, del, le and `van` itself. No re-layout either, and that is a property of the order: the roles run family, given, middle, middle..., so dropping a trailing middle leaves every other piece where it was. One guard remains, the base -- attaching to an all-particle family reorders words R2 reads as ordinary names. The invariant this rests on: `Family, Given particle` under the default order and `Family Given particle` under FAMILY_FIRST parse the same. 0 of 216 constructed names agreed before, 216 of 216 after, including all 72 with an ambiguous particle. One example line was deleted rather than updated -- "Nguyen Thi Van" under FAMILY_FIRST -- because the name is not written in that format and its reading there pins nothing (#470 is the general question). 30 of 6594 parses move, twelve names, none under the default order. Gate green at all three baselines and blind to all of it. The leading fold's reach under the default order is #471, split out: that one is a 1.4.0 parity break where this is new-feature territory.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #472 +/- ##
=======================================
Coverage 98.65% 98.66%
=======================================
Files 45 45
Lines 3194 3214 +20
=======================================
+ Hits 3151 3171 +20
Misses 43 43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The slot criterion was right and the code did not implement it. Two defects, found independently by two reviewers each, and both invisible to the corpus. (i) THE SITE FIRED UNDER THE DEFAULT ORDER. The gate was `state.order is not None`, which every no-comma order satisfies, while the reasoning above it argued from the FAMILY_FIRST layout. Where a conjunction stops a particle's forward chain the particle stands in a middle under any order, so `Maria Luisa y de la Cruz` gave family 'de la Cruz' where master gives 'la Cruz' -- 52 such names, on plain HumanName() with no policy. Three shipped documents asserted the opposite in my words. (ii) IT FOLDED A PARTICLE THAT DOES NOT END THE NAME. Under FAMILY_FIRST_GIVEN_LAST the trailing MIDDLE is not the trailing name word -- the given name stands behind the middles -- so `de Anke van y` gave family 'van de Anke', hoisting a particle in front of a base it was written after. 366 folds. The repair is P6's own word: the run must END the name. That single test subsumes the order test a first repair added -- measured over 542,592 generated parses, an explicit order test never decides anything the ends-the-name test has not, so it is not in the code. The default order ends with the FAMILY and FFGL with the GIVEN name, so only FAMILY_FIRST can put a name's last word in a middle at all. Also repaired: a stale `families` list (O1/O2 retag without recomputing, 33,840 stale observations; no shipped input reaches a wrong answer through it, so it is recorded rather than tested), and the site now REPORTS the fork it decides -- decisions.md#P6 settled that in #405, and the state that decision fixed is the one this site recreated, the comma writing reporting while the comma-less writing decided the identical fork in silence. MY INVARIANT WAS MEASURED ON A RIGGED SAMPLE. It is 603 of 630 over the whole particle vocabulary, not 216 of 216: the script sliced `sorted(particles)[:14]` and excluded `vd`, `mc` and `do`, the three words that are also suffix vocabulary and the only ones where the two writings genuinely differ. AGENTS.md axis 2 exactly -- the detector agreed with the design because it inherited the design's blind spot. The rule now states that limit; the entry records the failure. Docs corrected with it: two paragraphs still described the removed given-position site as current, one in rendered API documentation; P6's Accepted block still scoped the rule to the comma form; P6's rationale predicted the opposite of its own example; "181 of 247" reproduces under no definition (219 of 304); and a boundary example asserted the one field its guard cannot move. Both regressions are now `· boundary` examples so the doc runner holds them. Six mutations verified. Reach unchanged at 30 of 6606, 12 names, none under the default order -- the corpus never saw either defect.
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.
A trailing surname particle stops being stranded as a standalone middle name, and the given slot is given back to the declared name order.
Closes #467
The rule
A particle ending the name has nothing to link forward to, so what it is doing there is decided by what the writing says — never by the word. After a family comma it joins the family the comma named. A declared family-first order names the family the same way, so the attachment fires there too.
The test is P6's own phrase: the run must END the name, and stand in a MIDDLE. A middle is a further given name, which a particle is not — so a particle that ends the name there is doing nothing, and belongs to the family.
Those two together name the order without asking it. The default order ends with the FAMILY and
FAMILY_FIRST_GIVEN_LASTends with the GIVEN name, so onlyFAMILY_FIRSTcan put a name's last word in a middle at all. An explicit order test was added during review and then removed: measured over 542,592 generated parses, it never decided anything the ends-the-name test had not already decided.In the same change, P1 loses its given-position site — a correction rather than a narrowing, since that slot holds what the caller declared to be the given name:
Why no vocabulary test appears
The comma path needs one because a comma cannot separate the Dutch reading from the Vietnamese. A declared order can — when the caller declares the right one for the name:
A never-given test here would have excluded every ambiguous particle —
von,di,da,del,le, andvanitself, the flagship word of the listing the rule is named for. #466 carried exactly that test and failed the Dutch case because of it.It is one order, not both.
Nguyen Thi Vanread asFAMILY_FIRSTgivesfamily 'Van Nguyen'and loses the given name — the same trade the comma form already makes, recorded as accepted inrules.md#P6and intests/v2/cases.py.The site also reports the fork it decides, matching its comma sibling.
decisions.md#P6settled that in #405, and the state that decision fixed is exactly what this site recreated: the comma writing reporting while the comma-less writing decided the identical fork in silence.The premise this replaces was false
Three sites said a never-given particle "cannot be a name at all" —
rules.md#P6's rationale,_lexicon.py,config/particles.py— and the readings built on them were justified by it. Measured: all 33 members reportgiven== themselves when parsed bare.NON_GIVEN_NAME_PARTICLESsupplies the reading position leaves open and forbids nothing.Second correction of the same over-claim;
decisions.md#P6already records dropping "no particle is a name by itself" for the identical reason. Commit 1 is that correction alone and moves no parse.What review found, because the corpus could not
Four agents. Two defects, each found independently by two of them, and the differential corpus reported neither.
The site fired under the DEFAULT order. The first draft's gate was
state.order is not None, which every no-comma order satisfies, while the reasoning above it argued from theFAMILY_FIRSTlayout. Where a conjunction stops a particle's forward chain, the particle stands in a middle under any order:52 such names. Three documents in the first draft asserted the opposite.
It folded a particle that does not end the name. Under
FAMILY_FIRST_GIVEN_LASTthe trailing MIDDLE is not the trailing name word — the given name stands behind the middles — sode Anke van ygavefamily 'van de Anke', hoisting a particle in front of a base it was written after. 366 folds.Of 1101 corpus names, six hold a conjunction-then-particle adjacency and none in the firing shape, so the reach recipe honestly reported 0 default-order movers throughout. The count was right; the population was blind. Both regressions are now
· boundaryexamples inrules.md, so the doc runner holds them.Also repaired: a stale
familieslist (O1/O2 retag without recomputing — 33,840 stale observations; no shipped input reaches a wrong answer through it, so it is recorded rather than tested), a boundary example that asserted the one field its guard cannot move, two paragraphs still describing the removed given-position site as current — one in rendered API documentation — and P6's Accepted block still scoping the rule to the comma form.The invariant, and how its first number was got wrong
Measured over all 70 particles × 3 families × 3 givens: 0 of 630 before, 603 of 630 after.
The 27 failures are
vd,mcanddo— the three words that are also suffix vocabulary. P6's precedence over S2 is stated inside the comma paragraph and justified by the comma, so the comma form givesfamily 'vd Berg'where the comma-less form givessuffix 'vd'. A real limit, now stated in the rule.An earlier draft of this PR claimed 216 of 216. That number came from a script that sliced
sorted(particles)[:14], excluding exactly those three words —AGENTS.mdaxis 2 in its purest form: the detector agreed with the design because it inherited the design's blind spot. Two other numbers were wrong the same way and are corrected:181 of 247reproduces under no definition (real: 219 of 304), and "nothing moves under the default order" was a property of the corpus.Reach
30 of 6606 parses, twelve names, none under the default order — this tree's 1101-name corpus × three
name_ordervalues ×middle_as_familyoff and on.The differential gate cannot see any of it:
compare.pyparses every name asHumanName(name)and sweeps no policy, so no non-defaultname_orderbehavior has ever been compared across versions. Gate output is unchanged at all three baselines apart from the corpus names this change's own example lines add. The counts carry a recompute recipe indecisions.md#P6, with the comparator named as a checkout of the parent commit reading the same corpus files.One example line deleted rather than updated
The name is written in
FAMILY_FIRST_GIVEN_LAST; itsFAMILY_FIRSTreading is wrong by construction, so the line pinned a value nobody wants. The reason is recorded in the rule. The general question — whether arules.mdexample should declare its format — is #470 and is not decided here.Split out deliberately
#471 is the leading fold's reach under the default order (
de Mesnil JuanreadsJuanas part of the surname). Prototyped in this branch and reverted: it takes the gate red at the 1.4.0 baseline with 5 unexplained diffs and fails a v1 parity test. That is a compatibility decision on the order every existing caller is on, where nothing in this PR has a v1 answer to break.Verification
unexplained: 0, counts unchanged.🤖 Generated with Claude Code