Skip to content

The one fields-only ledger rule absorbs any diff of its shape, and no guard can see it grow #451

Description

@derek73

tools/differential/expected_since_1.4.0.toml's fix(suffix-routing) two-token name with unambiguous trailing suffix stays suffix is the only rule in any of the three ledgers with no name_regex:

# tomllib over all three ledgers
expected_since_1.4.0.toml: 59 rules, 1 with NO name_regex
expected_since_2.0.0.toml: 58 rules, 0
expected_since_2.1.0.toml: 51 rules, 0
issue = "fix(suffix-routing) two-token name with unambiguous trailing suffix stays suffix"
# 'Johnson PhD' / 'Smith Jr.' / 'John V' / 'QC MP': ... Four corpus names,
# and the prose fits all four.
fields = ["given", "family", "suffix"]

It explains 14 names today, not four.

Why this shape absorbs silently

compare.py admits a rule when diff_fields <= set(fields). With no name_regex, this rule claims any name whose diff fits {given, family, suffix} — a very common shape — so a newly-moved name lands under it and the gate stays green with nothing to read.

The guard cannot see it either. _CORPUS_CLAIMS records names as "corpus names the name_regex reaches; the whole corpus when a rule has none" — so this rule's entry is _Claim(1090, ('family', 'given', 'suffix'), …), the entire corpus. It is already at its maximum, so arrivals never move it. test_every_rule_claims_the_recorded_share_of_the_corpus passes whether the rule explains 4 names or 40.

It has absorbed real movements twice

So the ledger's central promise — that every behaviour change is classified in writing, by the rule that caused it — is not held for any diff of this shape at the 1.4.0 baseline.

Worth deciding

Whether a fields-only rule should be permitted at all. compare.py's validate_rules currently allows it (the docstring calls it "the most unbounded shape validate_rules permits"), and _CORPUS_CLAIMS's comment already names it as the shape "most worth writing down" — the recording exists precisely because the risk was understood.

Options, roughly:

  1. Give it a name_regex. It explains 14 names; an anchored alternation would bound it and make _CORPUS_CLAIMS meaningful. Cheapest, and it matches what fix(post_rules): H1 counts name words, not annotations (#410) #444 and Ledger rules classify diffs they do not describe, and a rule that stops explaining anything is invisible #372 already did piecemeal.
  2. Forbid fields-only rules in validate_rules. Structural, and there is exactly one to migrate. Would need a rule for any legitimately open-ended class.
  3. Make the guard see reach rather than regex coverage. Record what a rule explains, not what its regex matches, so a growing catch-all fails the guard. Larger change; would make every rule's claim tighter.

Its comment says "Four corpus names, and the prose fits all four" — that has been wrong since at least #414's rules-corpus harvest, and nothing recomputes it. Whatever is decided, that figure should either become a recompute one-liner or go.

Raised from #450's review, where the same shape was found still absorbing after a targeted narrowing.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions