Skip to content

abdul Smith Jr Ma loses the family name — P5's reserve does not mirror S2's bare-acronym fork #425

Description

@derek73

The bound-given reserve now asks what assign will read as a suffix (#401), but only for the roman-numeral fork. S2's other positional fork — a bare ambiguous acronym is consumed only with words to spare — is not mirrored, and with a suffix word between the join and the acronym it loses the family the same way #401 did:

abdul Smith Ma        ->  given='abdul Smith'  family='Ma'                     no family lost
abdul Smith Jr Ma     ->  given='abdul Smith'  family=''      suffix='Jr, Ma'  family lost
John Smith Jr Ma      ->  given='John'         family='Smith' suffix='Jr, Ma'
abdul Rahman PhD MA   ->  given='abdul Rahman' family=''      suffix='PhD, MA'
abu Bakar Jr Ed       ->  given='abu Bakar'    family=''      suffix='Jr, Ed'

Why

The reserve counts Ma as a name word (it is vocab:suffix-ambiguous, not a suffix piece), sees three, and joins. assign then peels from the end: with the joined pair plus Jr plus Ma there are three pieces, so its k >= 3 branch peels the bare acronym as a credential, then Jr, and one piece remains — the given. Where the join leaves exactly two pieces the same branch keeps the acronym as the family (abdul Smith Ma), which is why decisions.md#P5 records that shape as lossless; the three-piece case is where the fork and the reserve disagree.

Scope

Pre-existing, and byte-identical before #423. Every bound word, every bare ambiguous acronym (do, ed, jd, ma by default), with any suffix word between. A review sweep of the STRICT path during #423 found 132 family-losing shapes, every one ending in a bare ambiguous acronym — this is the whole of what remains of #401's shape on the main walk. No differential corpus name has it, so the gate cannot see a fix; tests are the verification.

Fix

Share the peel rather than mirror a second fork by hand. #423's reserve re-derives assign's trailing peel (the flagged-credential drop, the walk's start, the numeral fork's conditions, the preceding piece) and holds each seam with a pin; a second hand-mirrored fork would double the seams. The deeper mechanism, weighed at #423's review and deferred: extract assign's peel into a pure function over (pieces, ptags, tokens, start) that returns the boundary (and the ambiguity picks for assign to report), have assign call it, and have the reserve build the post-join viewpieces[:k] + [pieces[k] + pieces[k+1]] + pieces[k+2:], ptags unioned the same way — and call the same function. Three things fall out: the prev == first_name_k + 1 remap disappears (the view already has the bound word first), the k == first_name_k carve-out in the count dissolves (the joined piece is two tokens and never a suffix piece), and this fork is mirrored automatically, because the post-join piece count is just len(view).

Costs to price in: it is a behaviour change (the 132 shapes move, so a release-log bullet, ledger claims at three baselines and rules examples), BoundJoin's "the value IS the threshold" trick has to be restated as post-join counts, and placement is awkward — the peel is assign's rule, but assign imports from group, so the function lives beside _is_suffix_piece in group or in a new _pipeline/_peel.py. The cheap alternative — count the acronym out exactly when the joined layout leaves three or more pieces — fixes this shape alone and adds a seam.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions