Rationale
test_the_recorded_rule_still_wins_each_contested_name feeds each _RECORDED_DIFFS shape to classify() and asserts the result startswith the pinned winner in _CROSS_RULE_WINNERS. A prefix was chosen so a row could name a rule family (fix(comma-family)) rather than one issue string, and so a pin survives a rule's issue being reworded past the prefix.
The cost is that a pin is satisfied by ANY rule whose issue extends it. Where two rules share a prefix, the guard cannot tell which one won, and that is the one thing the roster exists to say (mechanisms.md#CROSS-RULE-OUTCOME-PINS: "pin who wins the contest"). #507's completeness check borrows this guard's meaning outright — a key in _RECORDED_DIFFS counts as "a winner is pinned" because set(winners) == set(shapes) — so a pin that names two rules is a pin that owns nothing.
The measurement (2026-09-05, master at 449a7bc)
Over the 51 rows in _CROSS_RULE_WINNERS, three have a pin that is a prefix of more than one rule's issue in their ledger, all in expected_since_1.4.0.toml:
| name |
pin |
rules whose issue starts with it |
what classify() returns today |
Andrews, M.D. |
fix(comma-family) |
three: …a comma followed only by titles keeps the given/family split, …, the C1 example, …lone post-comma piece routes to suffix/title, not first |
the lone-post-comma rule |
Bob Jones, author |
fix(comma-family) a comma followed only by titles keeps the given/family split |
two: itself, and …, the C1 example, whose issue extends it by a suffix |
the all-titles rule |
Bob Jones, compositeur |
same |
same |
the all-titles rule |
The first is a deliberate short pin naming a family; the other two are FULL issue strings that happen to be a strict prefix of a sibling's. Bob Jones, compositeur's roster comment (added by #507) already says so and rests the pin on the C1 rule's name_regex — anchored ^john\s+smith,\s*mr\.?… — being unable to reach the string. That argument is true and nothing executes it: widen that regex and hoist the rule, and classify() returns the C1 rule, startswith still passes, and the pin has changed meaning with every guard green.
In the three 2.x ledgers no pin is ambiguous (measured over their 6 rows).
RECOMPUTE:
import importlib, tomllib
from pathlib import Path
g = importlib.import_module("tests.v2.test_ledger_guards")
for ledger, winners in g._CROSS_RULE_WINNERS.items():
issues = [r["issue"] for r in tomllib.loads(
(Path("tools/differential") / ledger).read_text())["change"]]
for name, pin in winners.items():
hits = [i for i in issues if i.startswith(pin)]
if len(hits) > 1:
print(ledger, repr(name), repr(pin[:40]), len(hits))
Options
- Exact match, with the three rows spelled out.
got == expected, and Andrews, M.D. pinned to the lone-post-comma rule's full string (which is what wins it). Simplest, and it ends the family-pin idiom: a rule reworded past its old issue string breaks its pins, which is arguably right — a reworded rule is a different claim. Cost: every existing prefix pin that is unambiguous today (most of the 51) must become the full string too, or the guard has two modes.
- Prefix match plus an ambiguity guard. Keep
startswith, and add a guard that refuses any pin whose prefix matches more than one rule in its ledger unless the row is listed in an explicit allowlist with a reason. Today that list is the three rows above. Cheap, keeps the idiom, and the allowlist is exactly the roster of "pins that mean a family" — but an allowlist is a second thing to keep honest.
- Prefix match on a delimiter. Treat the pin as matching only at a word boundary, so
…keeps the given/family split does not match …split, the C1 example. Fixes the two full-string rows and leaves Andrews, M.D.'s family pin as it is; but it does not answer whether a family pin should exist at all, and the comma in , the C1 example makes the boundary rule fiddly.
Option 1 is the honest one if the roster's job is "which rule wins"; option 2 is the cheap one if family pins are wanted. Either way Bob Jones, author and Bob Jones, compositeur deserve a guard that executes the argument their comment makes.
Where this is written down
mechanisms.md#CROSS-RULE-OUTCOME-PINS, the roster's docstring in tests/v2/test_ledger_guards.py, and decisions.md's #498 completeness check entry, which says a _RECORDED_DIFFS key means "a winner is pinned" on the strength of this guard.
Rationale
test_the_recorded_rule_still_wins_each_contested_namefeeds each_RECORDED_DIFFSshape toclassify()and asserts the resultstartswiththe pinned winner in_CROSS_RULE_WINNERS. A prefix was chosen so a row could name a rule family (fix(comma-family)) rather than one issue string, and so a pin survives a rule's issue being reworded past the prefix.The cost is that a pin is satisfied by ANY rule whose issue extends it. Where two rules share a prefix, the guard cannot tell which one won, and that is the one thing the roster exists to say (mechanisms.md#CROSS-RULE-OUTCOME-PINS: "pin who wins the contest"). #507's completeness check borrows this guard's meaning outright — a key in
_RECORDED_DIFFScounts as "a winner is pinned" becauseset(winners) == set(shapes)— so a pin that names two rules is a pin that owns nothing.The measurement (2026-09-05, master at
449a7bc)Over the 51 rows in
_CROSS_RULE_WINNERS, three have a pin that is a prefix of more than one rule'sissuein their ledger, all inexpected_since_1.4.0.toml:classify()returns todayAndrews, M.D.fix(comma-family)…a comma followed only by titles keeps the given/family split,…, the C1 example,…lone post-comma piece routes to suffix/title, not firstBob Jones, authorfix(comma-family) a comma followed only by titles keeps the given/family split…, the C1 example, whose issue extends it by a suffixBob Jones, compositeurThe first is a deliberate short pin naming a family; the other two are FULL issue strings that happen to be a strict prefix of a sibling's.
Bob Jones, compositeur's roster comment (added by #507) already says so and rests the pin on the C1 rule'sname_regex— anchored^john\s+smith,\s*mr\.?…— being unable to reach the string. That argument is true and nothing executes it: widen that regex and hoist the rule, andclassify()returns the C1 rule,startswithstill passes, and the pin has changed meaning with every guard green.In the three 2.x ledgers no pin is ambiguous (measured over their 6 rows).
RECOMPUTE:
Options
got == expected, andAndrews, M.D.pinned to the lone-post-comma rule's full string (which is what wins it). Simplest, and it ends the family-pin idiom: a rule reworded past its old issue string breaks its pins, which is arguably right — a reworded rule is a different claim. Cost: every existing prefix pin that is unambiguous today (most of the 51) must become the full string too, or the guard has two modes.startswith, and add a guard that refuses any pin whose prefix matches more than one rule in its ledger unless the row is listed in an explicit allowlist with a reason. Today that list is the three rows above. Cheap, keeps the idiom, and the allowlist is exactly the roster of "pins that mean a family" — but an allowlist is a second thing to keep honest.…keeps the given/family splitdoes not match…split, the C1 example. Fixes the two full-string rows and leavesAndrews, M.D.'s family pin as it is; but it does not answer whether a family pin should exist at all, and the comma in, the C1 examplemakes the boundary rule fiddly.Option 1 is the honest one if the roster's job is "which rule wins"; option 2 is the cheap one if family pins are wanted. Either way
Bob Jones, authorandBob Jones, compositeurdeserve a guard that executes the argument their comment makes.Where this is written down
mechanisms.md#CROSS-RULE-OUTCOME-PINS, the roster's docstring intests/v2/test_ledger_guards.py, anddecisions.md's #498 completeness check entry, which says a_RECORDED_DIFFSkey means "a winner is pinned" on the strength of this guard.