fix(bound-given): count the reserve against the name that will remain - #416
Merged
Conversation
`abd Berg née Jones` read given 'abd Berg' with an EMPTY family, where `abd Berg` alone correctly reads given 'abd', family 'Berg'. P5 has always said the join "needs a name word to spare, so two name words alone do not join". The implementation counted while the maiden marker and the maiden name were still pieces, because group's marker pass runs later in the same stage -- so it answered the question about a name that would not exist. Four words counted, the join fired, and when the two departed nothing was left for the family. The count now excludes what the marker will take, through the same span helper the marker pass itself uses. One definition of what leaves, so the count and the removal cannot disagree -- the shape #399 settled for the marker predicate, and the reason that helper is shared rather than restated. Pre-existing: the marker-less spelling behaved this way at 2.1.0 too. What changed is reach -- #399's chain stop put the particle spellings in front of the reserve for the first time. It also closes one of the two join-swallows M2 recorded as accepted: with the reserve counted correctly the join declines on `van der Berg, abdul née Jones`, so the marker is never merged into 'abdul née' and M2's bound sees a lone marker piece. maiden 'Jones' where the name read given 'abdul née'. P3's connective join is the one that survives, and #412 narrows to it -- M2's Accepted clause and the case row that pinned the old reading move with it. The corpus name that demonstrates that side effect exists only because the rules-doc corpus (#414) added it a day earlier as an M2 example. #411 itself has no corpus name, so the ledger rule records the side effect rather than the defect, and says so. Closes #411 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #416 +/- ##
=======================================
Coverage 98.55% 98.56%
=======================================
Files 44 44
Lines 2981 2990 +9
=======================================
+ Hits 2938 2947 +9
Misses 43 43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Review found the reserve fix necessary but not sufficient, and two prose claims measurably false. Excluding the departing words is not enough on its own. Where a suffix sits INSIDE the name the marker walk stops early, so the excluded span is short, enough words survive it to clear the reserve, and the join fires and takes the MARKER -- after which nothing leaves at all and the count that authorised the join was reasoning about a name that never came to be. `abd née Jones Jr Smith Berg` read given 'abd née'. The join now declines outright when the piece it would absorb is a marker, which is what P5 meant all along: it joins the bound word to a name word, and a marker is not one. Tested against the marker directly rather than through the span, because the two differ exactly where it matters -- a marker with nothing but a suffix after it has no span at all, yet the join would still have absorbed it (`Berg, abdul née PhD` read given 'abdul née'). That closes the P5 half of #412 for real; the first cut only narrowed it while claiming it closed. The docstring's "the count and the removal cannot disagree" was the same overstatement this session already shipped once. Sharing the span helper settles what a maiden span IS; it does not settle what leaves, because the two calls run at different times on different piece lists and the join between them can invalidate the span. Forbidding the join to absorb a marker is what closes that -- the sharing only rules out disagreeing about the span itself. The pre-existence claim was backwards. `abd Berg née Jones` reads CORRECTLY at 2.1.0; `abd` acquired the defect only when #400 made it a bound given-name word this cycle, so for that spelling this fixes a regression introduced earlier in the same release. The pre-existing witness is `abdul`/`abdel`/`abdal`, which read the defect at 2.0.0 and 2.1.0 alike. The issue body carries the same error and needs it too. Six more rows, each closing a measured gap: - `Abd Berg née Mary Jones`. Capping the excluded span at two pieces reproduced #411 exactly with the whole suite green -- every existing row had a one-word maiden name, so none could tell the span from the marker plus one. A particle-led maiden name does not serve; P2 makes it a single piece. - The two shapes the new guard fixes, and `Berg, abd née Jones`, where a declining join after a family comma leaves S2's suffix reading and the name has no given name at all -- as `Berg, abd` alone always parsed. It reads alarmingly and was in no bullet and no row. - The marker immediately after the bound word, and the Arabic script. Plus a piece-level pin that the join never merges a marker, and the bound-given shapes added to the #409 chain invariant's inputs -- that invariant is keyed on the particle chain, and the distinction is now exercised rather than assumed. `Berg, abd née Jones` arrived UNEXPLAINED at 1.4.0 on four fields, the subset test correctly rejecting both nearby rules, and is read once here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A /simplify pass over the #411 work. No behavior change -- all three baselines still exit 0 and the suite is unmoved at 4463. - `marker(k)` joins `title`/`prefix`/`suffix`/`conj` as a closure, and both the chain stop and P5's guard route through it. "Is this piece a marker" was spelled three ways in one function. - The reserve gets a comment at its own site. It sat under a block entirely about #397 and read as covered by it, with its actual reason 300 lines up in a docstring. `leaving`/`going` collapse to one name for one concept, matching what `group()` already calls the same value. - The marker pass keeps only the half of its comment that still lives there; which pieces the span covers is `_maiden_span`'s to state, and it was being stated in two places 400 lines apart. Two prose corrections, both measured false by review: - The Arabic row claimed it "reaches the same arithmetic through script segmentation as well". It does not -- segments and effective script are identical to the Latin row's, so what it adds is vocabulary coverage, not a second code path. A note that names a mechanism gets believed. - decisions.md recorded the reorder's cost as "P3's rootname count dropping when the maiden words leave early". Under the principle this PR establishes that is not a cost, it is the second fix -- the branch was asserting a principle six lines below a sentence asserting its opposite. #418 now tracks the P3 count on its own, and the narrow reorder nobody had weighed (marker pass after P2/P3, before P5) is recorded beside the one that was rejected. The new piece-level test asserted more than its name: "no wide piece holds a marker" is false in general and contradicts the test directly above it, which pins the particle chain building exactly that. It held only because of which strings the loop listed, so extending the list -- the obvious next move -- would have produced a failure that misdescribed the defect. Keyed on the bound-given token now; verified to catch the same two violations when the guard is reverted. Two findings were behavior changes and are filed rather than applied: #417 (the chain stop restates the consumer's condition instead of sharing it, stranding a marker in 'Jane van der Berg nee Jr Jones') and #418 (P3's count has the identical defect this PR fixed in P5's, and unlike #411's own shape it moves 7 corpus names). Co-Authored-By: Claude Opus 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.
Closes #411.
The cause
rules.md#P5has always said the join "needs a name word to spare, so two name words alone do not join." The implementation counted the reserve while the maiden marker and the maiden name were still pieces —group()'s marker pass runs later in the same stage. So it answered the question about a name that would not exist: four words counted, the join fired, and when the two departed nothing was left for the family.The count now excludes what the marker will take, through the same span helper the marker pass itself uses. One definition of what leaves, so the count and the removal cannot disagree — the shape #399 settled for the marker predicate, and the reason the helper is shared rather than restated.
Pre-existing: the marker-less spelling behaved this way at 2.1.0 too. What changed is reach — #399's chain stop put the particle spellings in front of the reserve for the first time, which is how it was found.
It closes half of #412 as a side effect
With the reserve counted correctly the join declines on
van der Berg, abdul née Jones, so the marker is never merged intoabdul néeand M2's bound sees a lone marker piece:That was one of the two join-swallows
rules.md#M2recorded as accepted. P3's connective join is the one that survives, so #412 narrows to it — M2'sAccepted:clause and the case row that pinned the old reading move with it.What review changed
Excluding the departing words was necessary but not sufficient. Where a suffix sits inside the name the marker walk stops early, so the excluded span is short, enough words survive to clear the reserve, and the join fires and takes the marker — after which nothing leaves and the count that authorised the join was reasoning about a name that never came to be:
The join now declines outright when the piece it would absorb is a marker — which is what P5 meant all along: it joins the bound word to a name word, and a marker is not one. Tested against the marker directly rather than through the span, because the two differ exactly where it matters: a marker with nothing but a suffix after it has no span at all, yet the join would still have absorbed it (
Berg, abdul née PhDreadgiven='abdul née'). So the P5 half of #412 is genuinely closed now; the first cut only narrowed it while claiming it closed.My "cannot disagree" docstring was the same overstatement this session already shipped once. Sharing the span helper settles what a maiden span is; it does not settle what leaves, because the two calls run at different times on different piece lists and the join between them can invalidate the span. Forbidding the join to absorb a marker is what closes that — the sharing only rules out disagreeing about the span itself.
The pre-existence claim was backwards.
abd Berg née Jonesreads correctly at 2.1.0;abdacquired the defect only when #400 made it a bound given-name word this cycle. So for that spelling this fixes a regression introduced earlier in the same release. The pre-existing witness isabdul/abdel/abdal. Issue #411 carried the same error and has been corrected.Six more rows, each closing a measured gap. The sharpest: capping the excluded span at two pieces reproduced #411 exactly with the whole suite green, because every existing row had a one-word maiden name — nothing distinguished "the span" from "the marker plus one". Also
Berg, abd née Jones, where a declining join after a family comma leaves S2's suffix reading and the name has no given name at all (matching howBerg, abdalone has always parsed) — it reads alarmingly and was in no bullet and no row.Plus a piece-level pin that the join never merges a marker, and the bound-given shapes added to #409's chain-invariant inputs — that invariant is keyed on the particle chain, and the distinction is now exercised rather than assumed.
Berg, abd née JonesarrivedUNEXPLAINEDat 1.4.0 on four fields, the subset test correctly rejecting both nearby rules, and is read once in its own rule.A
/simplifypass, third commitNo behavior change — all three baselines still exit 0 and the suite is unmoved.
marker(k)now sits besidetitle/prefix/suffix/conj, with both the chain stop and P5's guard routed through it; "is this piece a marker" had three spellings in one function. The reserve gained a comment at its own site — it sat under a block entirely about #397 and read as covered by it — andleaving/goingcollapsed to one name.Two prose claims measured false and corrected: the Arabic row claimed it "reaches the same arithmetic through script segmentation" (segments and effective script are identical to the Latin row's — it adds vocabulary coverage, not a code path), and
decisions.mdrecorded the reorder's cost as "P3's rootname count dropping when the maiden words leave early", which under this PR's own principle is not a cost but the second fix.And the piece-level test asserted more than its name. "No wide piece holds a marker" is false in general and contradicts the test directly above it, which pins the particle chain building exactly that; it held only because of which strings the loop listed. Keyed on the bound-given token now, verified to catch the same violations when the guard is reverted.
Two findings filed rather than applied
Both are behavior changes, so out of scope here:
Jane van der Berg née Jr Jonesstrands the marker — the chain stop restates the consumer's condition instead of sharing it #417 — the chain stop restates the consumer's condition (any non-suffix piece anywhere) instead of sharing it (a contiguous walk halting at the first suffix). They diverge onJane van der Berg née Jr Jones, stranding the marker. The comment there claimed the two agree.juan y garcia nee jonesloses the family name — P3's word count includes the words the maiden name takes away #418 — P3's word count has the identical defect this PR fixes in P5's.juan y garcia nee jonesloses its family name, and unlikeAbd van der Berg née Jonesreports no family name — the bound-given join counts words the maiden name takes away #411's own shape it moves 7 shipped corpus names. Entangled withJane van der Berg née y Joneskeeps the marker in the surname — a maiden marker inside a joined piece is never seen #412 by necessity: hoisting one span computation across all three group-stage counts is only sound once no join can absorb a marker, which is the argument thatJane van der Berg née y Joneskeeps the marker in the surname — a maiden marker inside a joined piece is never seen #412 should be a guard atmerge()rather than another special case.Verification
Ten case rows, including a control (
abd Allah Smith née Jones, which has a word to spare and must still join), the multi-word maiden name that is the only shape distinguishing the span from the marker-plus-one, andBerg, abd née Jones, where a declining join after a family comma leaves the suffix reading and the name has no given name at all.All three baselines exit 0 over 868 names. The differential caught the #412 side effect as an
UNEXPLAINEDdiff and made me classify it, and later caughtBerg, abd née Jonesthe same way on four fields — the subset test correctly rejecting both nearby rules. No baseline shows a #411-attributable diff, since every released version already reads those names the way the fix does, so the ledger rules record the side effects rather than the defect and say so. (abd Berg née Jonesis incorpus_rules.jsonland goes unexplained at all three baselines if the fix is reverted — the guard working.)Full suite green: 4463 passed. ruff and mypy clean.
🤖 Generated with Claude Code