Skip to content

A tussenvoegsel after a family comma joins the family name - #403

Merged
derek73 merged 1 commit into
masterfrom
fix/379-tussenvoegsel-attaches
Aug 19, 2026
Merged

A tussenvoegsel after a family comma joins the family name#403
derek73 merged 1 commit into
masterfrom
fix/379-tussenvoegsel-attaches

Conversation

@derek73

@derek73 derek73 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

"Beethoven, Ludwig van" is how "Ludwig van Beethoven" is filed in a Dutch or Belgian alphabetized listing. The trailing particle run was read as a middle name; it now attaches to the family the comma has already named and renders before it.

Beethoven, Ludwig van   ->  given='Ludwig'  family='van Beethoven'    (was middle='van')
Berg, Jan van der       ->  given='Jan'     family='van der Berg'     (was middle='van der')
Jong, Piet de           ->  given='Piet'    family='de Jong'
Berg, Jan van Jr.       ->  given='Jan'     family='van Berg'  suffix='Jr.'

rules.md#P6 has stated this since the 2026-08-16 keystone, so the design was settled before the code — all three deviates: markers come out and P6's pointer moves from tracked: to implemented:. The derived views move with the parse: family_particles='van', family_base='Beethoven', where they were '' and 'Beethoven'. (#130 asked for those views and shipped them in 1.3.0; what was wrong until now was the values they reported for this listing.)

Read this first: an accepted cost

A Vietnamese name written in the Dutch listing convention loses its given name.

Nguyen, Thi Van    2.1.0:  given='Thi'  middle='Van'  family='Nguyen'
                   here:   given='Thi'  middle=''     family='Van Nguyen'

Nguyễn Thị Vân is family-middle-given, so Vân is the given name.

The cost is narrower than it first looks: vân is not particle vocabulary, so the correctly spelled name never reaches this rule.

Nguyễn, Thị Vân  ->  given='Thị'  middle='Vân'  family='Nguyễn'    unaffected

It is the ASCII transliteration that collides, and there the two traditions write the same string with no signal to separate them.

The colliding spelling has a format that reads correctly, and it is one order, not both — FAMILY_FIRST still sends the given name to middle:

Nguyen Thi Van   family-first-given-last  ->  family='Nguyen'  given='Van'   middle='Thi'
Nguyen Thi Van   family-first             ->  family='Nguyen'  middle='Van'  given='Thi'

No ambiguity is reported for the collision, which rules.md#A1 calls for — filed as #405, since emitting one needs post_rules added to the stage-ownership map and that is a decision rather than a fix.

That is also a retroactive argument for scoping the attachment to the comma form. Had #365's implied-comma reading been taken — treating a comma-less family-first name as if the comma were missing — Vietnamese would have had no working format at all.

P6 now carries this as an Accepted: block with executable examples, and tussenvoegsel_takes_the_vietnamese_reading pins it so it cannot move without someone deciding to move it. P6's rationale previously claimed the words-to-spare test adjudicated this; it does not, and that sentence is gone.

Two guards, both P6's

Words to spare. A name whose only given word is the particle keeps it — "Nguyen, Van" still reads given='Van'. The test asks for a given word specifically: _NAME_ROLES would include FAMILY, and P1's fold runs earlier in the same function and retags all of segment 1 to FAMILY, so the wider test passed on family text P1 had just produced and hoisted the particle in front of a base it never preceded.

Precedence over S2. The rule keys on the token's vocabulary, not its assigned role. vd, mc and do are the three words in both vocabularies; assign reads a trailing vd or mc as a post-nominal, so those two need the override — "Berg, Jan vd" reads family='vd Berg' where 1.4.0 and 2.1 alike gave suffix='vd', which closes #380. do is in the ambiguous acronym half, which already left it a name word, so it attaches by the plain rule.

Differential

One of 751 corpus names moves — "Vega, Juan de la" — at all three baselines, so each ledger needed a rule. Unlike the two preceding PRs the harness is informative here: this changes default-order output.

The ledger guards did real work. The first draft reused the neighbouring comma rules' bare-comma regex, which claimed 215 names and reached two protected exclusions; the guards refused both. A member des also had to come out — not because it matched a name, but because it is not in PARTICLES at all and the text it reached was "Designated". Review then found the narrowed regex still had a leading .*, so a Cyrillic comma name whose diff had another cause classified as fix(#379); it is now Latin-anchored over the whole name. fields gained suffix, because the #380 half moves a token out of suffix and the rule could not otherwise classify the shape it is named for.

Found in review, fixed here

  • The words-to-spare guard passed vacuously after P1's fold (above) — "Smith, de Mesnil van" gave family='van Smith de Mesnil'. 312 generated inputs hit it.
  • A post-nominal blocked the attachment entirely. "Berg, Jan van Jr." and "Berg, Jan van, Jr." parsed two ways on nothing but a comma. The backward walk now steps over a trailing piece holding no name — unless it is itself particle vocabulary, since vd arrives suffix-roled and is the run.
  • The MIDDLE arm of the guard was dead — 0 witnesses in 8,862 generated parses — and P6 says "given" only, so it is narrowed to match.
  • Six false claims in my own prose, all measured wrong and corrected: that the all-particle middle shape was "gone from the default order" (it is not — "Vega, Santa de la", "Berg, Van der" and "Nguyen, Van Le" still produce one), a decisions.md line stating as shipped the predicate three lines below it calls the defect, the des account, the #130 closes, do inheriting vd's adjudication, and a parity label on a row 1.4.0 does not agree with.

Related, not fixed here

"Vega, Juan de la" was the input tests/test_initials.py used to build an all-particle middle name. That shape survives on other inputs, so the guard is repointed at "Nguyen, Van Le" rather than deleted. Whether such a middle should exist is #402, and #404 proposes the principle that subsumes it: a particle with no name word to attach to is not doing particle work, so it is positioned and rendered as ordinary text.

3557 tests pass, mypy clean, 0 unexplained at all three baselines.

Closes #379
Closes #380

🤖 Generated with Claude Code

@derek73 derek73 added the bug label Aug 19, 2026
@derek73 derek73 self-assigned this Aug 19, 2026
@derek73 derek73 added this to the v2.2 milestone Aug 19, 2026
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.54%. Comparing base (5b1d8b6) to head (0251a00).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #403   +/-   ##
=======================================
  Coverage   98.53%   98.54%           
=======================================
  Files          44       44           
  Lines        2942     2954   +12     
=======================================
+ Hits         2899     2911   +12     
  Misses         43       43           

☔ 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.

"Beethoven, Ludwig van" is how "Ludwig van Beethoven" is filed in a
Dutch or Belgian alphabetized listing, and the trailing particle run
was read as a middle name. It now attaches to the family the comma has
already named and renders before it, so the listing and the plain
spelling agree:

    Beethoven, Ludwig van   ->  given 'Ludwig', family 'van Beethoven'
    Berg, Jan van der       ->  given 'Jan',    family 'van der Berg'

rules.md#P6 has stated this since the 2026-08-16 keystone; both its
deviates: #379 markers come out, and its pointer moves from tracked:
to implemented:. The derived views move with the parse, which closes
the half of #130 that asked for them (family_particles 'van',
family_base 'Beethoven', where both were empty and 'Beethoven').

Keyed on the token's VOCABULARY, not its assigned role: that is what
gives P6's stated precedence over S2 its effect, since assign reads a
trailing `vd`/`mc`/`do` as a post-nominal. "Berg, Jan vd" therefore
reads family 'vd Berg' where 1.4.0 and 2.1 alike gave suffix 'vd',
which closes #380 on the same shape. The words-to-spare guard is a
piece test rather than a count, so "Nguyen, Van" keeps its only given
word.

One of 751 corpus names moves at all three baselines, and needed a
ledger rule in each. The ledger guards forced the regex to narrow: the
first draft reused the neighbouring comma rules' bare-comma pattern,
which claimed 215 names and reached two protected exclusions; the
member `des` came out too, being broad enough to match the name "Des".

Closes #379
Closes #380

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@derek73
derek73 force-pushed the fix/379-tussenvoegsel-attaches branch from 61e160e to 0251a00 Compare August 19, 2026 04:48
@derek73
derek73 merged commit 6aa3fe7 into master Aug 19, 2026
11 checks passed
@derek73
derek73 deleted the fix/379-tussenvoegsel-attaches branch August 19, 2026 05:33
derek73 added a commit that referenced this pull request Aug 19, 2026
Price every candidate fix before choosing one, by simulating each
in-process rather than implementing it. Recorded because the practice
paid twice and its absence cost once, all in one session.

#397 and #400 both turned on it: the obvious fix in #400 -- add the
word to the list -- provably changed nothing on the common shape, and
the two vocabulary alternatives each lost a credential spelling. The
winner was a fifth option the wordlist framing had hidden, and none of
that was visible until the candidates ran side by side.

The counterexample is #403's ledger `fields`, widened on one
reviewer's finding without pricing what else the widening claimed. It
took {family, suffix} from a neighbouring rule and two more shapes
from UNEXPLAINED -- diffs in which the rule's own subject does not
move -- and had to be reverted.

Filed here rather than in AGENTS.md because AGENTS.md's own rule sends
a new reusable pattern to mechanisms.md, and it already points at this
section twice.

Refs #400, #403

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
derek73 added a commit that referenced this pull request Aug 19, 2026
From this session's practice, kept terse because AGENTS.md is loaded
every session:

- review the fix commit too -- second-round passes on #398, #400 and
  #403 each found defects in the first round's fixes
- check the PR description before merging; a review that moves
  behavior leaves it stale, and a stale body reads as authoritative
- a scripted multi-edit that asserts as it goes discards everything
  when a late pattern misses, silently, since prose edits fail no test

mechanisms.md's "check the receiver" field note gains the other half:
ask what the old behavior was CONCEALING. #379's attachment removed
the input a test used to build an all-particle middle name (#402), and
#400's reserve fix exposed the count shape #397 names.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

1 participant