Skip to content

fix(maiden): a marker stops the particle chain that swallowed it - #409

Open
derek73 wants to merge 2 commits into
masterfrom
fix/399-maiden-marker-stops-chain
Open

fix(maiden): a marker stops the particle chain that swallowed it#409
derek73 wants to merge 2 commits into
masterfrom
fix/399-maiden-marker-stops-chain

Conversation

@derek73

@derek73 derek73 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Closes #399.

Ursula von der Leyen geb. Albrecht lost the maiden name that the same words one particle chain apart kept:

Ursula Leyen geb. Albrecht          ->  family='Leyen'          maiden='Albrecht'
Ursula von der Leyen geb. Albrecht  ->  family='von der Leyen'  maiden='Albrecht'   (was: family='von der Leyen geb. Albrecht', maiden='')

The fix

Grouping's prefix chain absorbs everything up to the next prefix or suffix piece, and the maiden marker is consumed after the chain merges — by which point there is no lone marker piece left to find. The chain now stops at a marker the way it already stops at a suffix.

Only a non-leading particle ever reached the marker, which is why a leading single particle always worked (P4 chains nothing) while a leading run of two did not — the second particle's own chain fired.

The stop is gated on the consumer actually taking

This is the substance of the change, and it came out of review rather than the first cut. Sharing one marker-piece predicate settles what a marker piece is; it does not settle whether the consumer takes, because the consumer also needs a non-suffix piece after the marker. Stopping without one left the marker standing as its own piece, and a lone trailing piece takes a role field:

Jane van der Berg née    ->  middle='van der Berg'  family='née'     <- ungated
                         ->  family='van der Berg née'               <- gated

The marker became the surname and the real surname was demoted — the defect the stop exists to prevent, one field over. The gated reading is also what M2 already said should happen: a marker with nothing after it "is just a word", exactly as Jones néefamily='née'.

Three measured consequences of the ungated form, each closed by the gate:

Two shapes fixed without a second change

Limits, now stated rather than implicit

The bound reaches only a marker standing as a word of its own, so P3's connective join and P5's bound-given join each still absorb one first — Jane van der Berg née y Jones and van der Berg, abdul née Jones are unchanged. The deleted Accepted: clause covered a class of which this fixes one instance and leaves two; both survivors are back in M2 with examples.

Docs

  • rules.md#M2: the stop and its gate are in the statement, ahead of the examples — the citation checker cuts a statement at its first example line, so a rule stated after them can never be cited from code.
  • rules.md#P2: its statement enumerated the join's stops and named neither the marker nor the suffix. Both are named now, and its two code citations move with it.
  • decisions.md#M2: the false invariant is corrected; the quoted removal is verbatim so git log -S finds it; and the recorded reason for preferring the stop over a stage reorder is rewritten, because the example it cited does not discriminate — Anna Müller geb. von der Berg is identical before Ursula von der Leyen geb. Albrecht loses the maiden name — a particle chain swallows the marker where a suffix stops it #399, after it, and under a reconstructed reorder.
  • docs/release_log.rst gets its Behavior Changes bullet, which the first commit omitted.

Verification

Tests carry it, and the class is now gate-visible for the first time. No corpus name had a particle ahead of a marker, so the harness was blind to this shape; the cross-script case row puts one in the CJK corpus, with its own ledger rule and recorded corpus claim. All three baselines exit clean (1.4.0, 2.0.0, 2.1.0), the 1.4.0 baseline verified genuine rather than the mislabelled cached tree.

20 case rows plus two piece-level tests in test_group.py. The piece level matters because that is where the two halves can disagree without any field looking wrong enough to fail — the misaligned version passed the entire suite. Removing the gate now fails 6 tests; dropping the marker predicate's lone-piece guard, previously an unkilled mutant, now fails 2.

Full suite green: 4293 passed. ruff and mypy clean, _group.py and _post_rules.py at 100% statement and branch coverage.

🤖 Generated with Claude Code

@derek73 derek73 added bug docs Documentation fixes and updates labels Aug 19, 2026
@derek73 derek73 self-assigned this Aug 19, 2026
`Ursula von der Leyen geb. Albrecht` lost the maiden name that
`Ursula Leyen geb. Albrecht` kept: grouping's prefix chain absorbed
everything up to the next prefix or suffix piece, and the marker is
consumed AFTER the chain merges, by which point there is no lone
marker piece left to find. rules.md carried that as an Accepted
limitation, but it was a consequence of stage order rather than a
judgment, so M2 now states the stop and the limitation is gone.

The chain's stop and the maiden handler's consumer share one
definition of a marker piece. A chain that stopped where the consumer
would not then take would strand the marker inside the family name --
the very defect the stop exists to fix.

Two shapes it fixes for free. Under a family-first order the marker
used to survive as an ordinary name word and take the leftover given
slot (`de la Cruz née Vega` -> given 'née'); dropped, it never reaches
the placement, which answers #399's open question with no second
change. And stopping the chain can leave a family of nothing but
particles, which #404's R2 reading already covers: `Jane de la née
Jones` reports family 'de la' with a non-empty base.

Tests are the whole verification here. No differential corpus name has
a particle ahead of a marker, so the gate is blind to this class and
its clean exit says nothing about it.

Closes #399

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@derek73
derek73 force-pushed the fix/399-maiden-marker-stops-chain branch from b633270 to 829de84 Compare August 19, 2026 08:36
@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.55%. Comparing base (c43d0a3) to head (105bba3).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #409   +/-   ##
=======================================
  Coverage   98.55%   98.55%           
=======================================
  Files          44       44           
  Lines        2977     2981    +4     
=======================================
+ Hits         2934     2938    +4     
  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.

@derek73 derek73 added this to the v2.2 milestone Aug 19, 2026
Review found the first cut of #399 traded one stranding for a worse
one. Sharing a marker-piece predicate between the chain's stop and the
maiden consumer settles what a marker piece IS; it does not settle
whether the consumer TAKES, because the consumer also needs a
non-suffix piece after the marker. Stopping without one left the marker
standing as its own piece, and a lone trailing piece takes a role
field:

    Jane van der Berg née    was middle 'van der Berg', family 'née'

The marker became the surname and the real surname was demoted -- the
defect the stop exists to prevent, one field over. The stop now tests
the consumer's own condition, so the two halves cannot disagree.

Three measured consequences, each fixed by the gate. 'Jane van der Nee'
moved a bearer of the attested surname Nee out of the family, which M1
already warns about. 'Ursula von der Leyen geb.', an ordinary truncated
record, reported the marker as the surname. And 'St St née' stopped
reporting its particle-or-given fork, because group's emitter is
guarded on the chain having merged something and an ungated stop made
it merge nothing for a different reason than the guard assumes -- an A1
violation of the shape #405 tracks, and worse, since it removes a
report callers already see.

Pinned six ways: four case rows and two piece-level tests in
test_group.py. The piece level is where the two halves can disagree
without any field looking wrong enough to fail -- the misaligned
version passed the entire suite.

Also from review:

- Two limits the deleted Accepted clause used to cover are back in M2,
  scoped: the bound reaches only a lone marker, so P3's connective join
  and P5's bound-given join each still absorb one first. #399 fixed one
  instance of the join-swallow and left two.
- P2's statement enumerated the join's stops and named neither the
  marker nor the suffix. Both are stated now, and its two code
  citations move with it.
- The new normative sentence moved ahead of M2's examples, because the
  citation checker cuts a statement at its first example line -- stated
  after them, it could never be cited from code.
- decisions.md: the false invariant is corrected, the quoted removal is
  verbatim so `git log -S` finds it, and the recorded reason for
  preferring the stop over a stage reorder is rewritten. The example it
  cited does not discriminate: 'Anna Müller geb. von der Berg' is
  identical before #399, after it, and under a reconstructed reorder.
- A release_log.rst bullet, which the first commit omitted.
- Three case notes corrected: one claimed only the maiden field moved
  when the family moved too, one credited R2 for what P4 and P1 do, and
  one promised a pin the partition invariant does not deliver.
- 'Jane née and Jones Smith' pins the marker predicate's lone-piece
  guard, which was an unkilled mutant: dropping it left 4229 tests
  green while changing behaviour through a conjunction merge.
- The cross-script row puts a name of this shape in the CJK corpus, so
  the class is gate-visible for the first time, with its own ledger
  rule and recorded corpus claim.

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

bug docs Documentation fixes and updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ursula von der Leyen geb. Albrecht loses the maiden name — a particle chain swallows the marker where a suffix stops it

1 participant