Repository navigation
Do the CJK comma ledger rules still earn their place now that nothing demands them? (fix(cjk-comma-compound) reaches 23 radar names and 0 contract) #495
Description
Correction (2026-09-02): two figures below were measured with a hand-built
file→tier map that wrongly markedcorpus.jsonlas contract; it is radar. The
split is 326 contract / 790 radar, not 786/330, and the jr rule's reach is
2 contract / 3 radar, not 3/2. The argument and the five radar-only pairs are
unaffected — see the correction comment for the recompute.
Rationale
#488 demoted every composed comma/Latin-wrapper form around a CJK name to
tolerated input and split the projection: corpus_cjk_tolerated.jsonl is
radar tier, where an unmatched diff is "reported, never fatal" and
"cannot fail the run or demand a rule" (_CORPUS_TIERS, #468).
The two fix(cjk-comma-*) ledger families were kept — #488's verification
records them "still classify 11 + 7, now radar-sourced". That is a
measurement, not an argued decision: decisions.md#cjk-comma-demotion
records the format-purity line and the C1→W3 move, and says nothing about
whether the rules that classify the demoted names should stay.
Measurement (2026-09-02, c1a98a9)
Of the 1116 distinct corpus texts, 786 are contract tier and 330 radar.
Scanning expected_since_1.4.0.toml for same-tier rule pairs whose fields
are strictly nested and whose regexes reach a common corpus name gives 11
pairs; 5 of them are contested only over radar names:
| earlier (wide) → later (narrow) | co-matched |
|---|---|
fix(#296) credential-only comma → fix(comma-family) lone post-comma |
2 |
fix(#296) credential-only comma → fix(comma-precomma-family) |
2 |
fix(cjk-comma-compound) → fix(cjk-glued-honorific-peel) |
17 |
fix(cjk-glued-honorific-peel) → fix(suffix-routing) numeral |
1 |
fix(cjk-glued-honorific-peel) → fix(suffix-routing) jr |
1 |
Sharper, and the number that makes this actionable: three of the five rules
can never classify a contract name at all, because their name_regex
reaches none. Regex reach is an upper bound on what a rule explains — a rule
cannot explain a name it does not match — so this is sound without a run:
| rule | contract reach | radar reach |
|---|---|---|
fix(#296) a credential-only comma string... |
0 | 2 |
fix(cjk-comma-compound) |
0 | 23 |
fix(suffix-routing) roman numeral |
0 | 4 |
fix(cjk-glued-honorific-peel) |
16 | 21 |
fix(suffix-routing) jr |
3 | 2 |
Recompute: load the ledger with tomllib, take each rule's regex reach over
the distinct name values in corpus*.jsonl, and split by _CORPUS_TIERS
(a text held by both tiers reads contract, matching main()'s dedup).
The question
A rule nothing demands is not automatically a rule to delete — it still
classifies a diff rather than leaving it merely reported, and the radar tier
exists so those diffs stay watched. But nothing has re-argued the case since
the demotion, and the rules carry ongoing cost: _CORPUS_CLAIMS entries,
_CROSS_RULE_WINNERS rows, and — once #382 lands — a precedes_narrower
exemption each, whose why today says only "radar-only since #488, kept so
the diff stays explained".
Options
- Keep them, and say so. A
decisions.md#cjk-comma-demotionbullet
arguing that a radar diff is worth classifying rather than merely
reporting. Cost: none, beyond writing the argument. - Delete the three rules with zero contract reach. They are the clean
case: nothing they could ever classify can fail a run. Cost: each deletion
moves a_CORPUS_CLAIMSentry and any_CROSS_RULE_WINNERSrows, the
dormancy check has to be re-reasoned for whatever survives, and the
demoted diffs start reporting as UNCLASSIFIED on every run — visible
noise that a reader has to learn to skip. - Case by case, which the reach table argues for.
fix(cjk-comma-compound)
has 23 radar names and no contract reach, and is the classifier-of-record
for shapes_CROSS_RULE_WINNERSpins — the strongest keep-or-delete
argument either way.fix(cjk-glued-honorific-peel)and
fix(suffix-routing)jr still reach contract names and are not candidates
for deletion at all; they appear here only as the narrower half of a
contested pair.
Not in scope
Rule ORDER. #382 settles that separately: all five pairs get a
precedes_narrower exemption there, so this issue is only about whether the
rules should exist, not about which one wins while they do.
Blocked by
#382 — its exemptions are what make the current state legible, and its
_ORDER_EXEMPTION_EFFECT roster is what would have to be re-recorded here.
Activity
김, 민준씨is claimed by whichever of two rules comes first) #382derek73 commentedon Sep 3, 2026
Correction — two figures in the issue body were measured wrong. The argument
is unaffected; the digits are not.
What was wrong
The body says "Of the 1116 distinct corpus texts, 786 are contract tier and 330
radar." Measured against
compare._CORPUS_TIERS, it is 326 contract and 790radar.
Root cause: the measuring script hand-wrote the file→tier map instead of reading
_CORPUS_TIERS, and markedcorpus.jsonlas contract. It is radar —compare.py's roster says so, anddecisions.md's #468 bullet records why("
corpus.jsonl(scraped from v1's test banks) andcorpus_issues.jsonl(harvested from the tracker) became RADAR"). It is also the largest corpus at 486
distinct names, which is the intuition that misleads: the biggest file reads like
the contract one and is the demoted scrape.
That is
docs/design/AGENTS.mdaxis 2 — "your detector is a second, unreviewedimplementation; it must read the same vocabulary and boundaries the rule reads."
The reach table also has one wrong row. Recomputed with the real roster:
fix(#296) a credential-only comma string...fix(cjk-comma-compound)fix(suffix-routing)roman numeralfix(cjk-glued-honorific-peel)fix(suffix-routing)jrOnly the last row moved (was 3 / 2). Recompute:
What is unaffected
contract-tier regex reach, so they can never classify a name that could fail a
run. Same three rules, same zeros.
the real roster: pairs 1, 4, 5, 6, 7, 8 are contract-backed and 2, 3, 9, 10, 11
are radar-only — identical to what the body lists.
fix(cjk-glued-honorific-peel)andfix(suffix-routing)jr still reach contractnames and are still not deletion candidates.
Filed under #497 as well
This is a fourth instance of that issue's class, and the first where the wrong
value reached a tracker rather than a comment. Worth noting for whoever picks
#497 up: the sibling roster comment in
tests/v2/test_ledger_guards.py(
_ORDER_EXEMPTION_EFFECT) states the same division as an ARGUMENT with nodigits — "some reach contract-tier names, the rest only radar" — and is therefore
still correct. That is the convention
AGENTS.mdprescribes, working.docs(design): the seventeen are radar by two warrants, not one
derek73 commentedon Sep 3, 2026
Closing as no — the radar-only rules earn their place, and "nothing demands
it" is not a deletion criterion.
The measurement that settles it, and why this issue asked the wrong question
This issue named five rules, reached from the five radar-only contest pairs of
#382. That was the wrong population. Measured over
expected_since_1.4.0.tomlat baseline 1.4.0, 19 of the 72 explaining rules explain only radar names —
48 of the 352 intentional diffs:
fix(cjk-comma-compound) comma routing compounds with the CJK order flipfix(cjk-comma-honorific-peel) glued honorific peels off a post-comma given namefix(#296) dr is not postnominal vocabulary, so a trailing Dr. is a name wordfeat(#273) typographic nickname delimiters recognized by defaultfix(#367) a title no longer displaces a leading particle out of the leading runfeat(#269) Arabic بن prefix chains onto family (non-Latin new-recognition)fix(#296) a credential-only comma string reads a name and its postnominalfix(#296) a dropped prenominal takes the name position it occupiesfix(comma-family) a comma followed only by titles keeps the given/family splitfix(suffix-routing) a two-token name ending in a credential acronymfix(suffix-routing) a two-token name ending in a roman numeralfix(#325),fix(#342),fix(#360),fix(#367)inferred-title,fix(#397),fix(#424),fix(credential-pair-order),fix(suffix-routing)M.A.Applied as a deletion criterion, "nothing demands it" removes 26% of the rules
that explain anything — including:
fix(#342)andfix(#397), the two NOT WANTED rules. Those exist toclassify a reading nobody wants so the gate stays green while the bug is open,
and they carry a delete-when-fixed instruction the dormancy check enforces once
the fix lands (
decisions.md#differential-ledger, the fields-only arc). Deletingthem would make two open bugs report as UNEXPLAINED on a radar tier that cannot
fail, i.e. silently.
feat(#269)andfeat(#273), records of new recognition behavior.fix(#360), the record ofstemoving into the never-given particles.Why the tier does not carry the argument the issue assumed
The corpus-tier arc states what radar is for: "the tier split gave the
differential somewhere to WATCH a name without promising it"
(
decisions.md#differential-ledger, the corpus-tier arc). A rule explaining aradar name is what watching looks like. Delete it and the diff does not
disappear — it reports as UNCLASSIFIED, which is watched-but-unexplained, strictly
less information for strictly no gain.
The vocabulary already distinguishes the case this issue was reaching for.
dormantmarks a rule that explains nothing, and requires a written reasonprecisely because an exemption nobody can justify should be deleted instead. A
rule explaining eleven radar names is not dormant; it is doing its job on names
the contract does not answer for.
So the criterion that would justify deleting a ledger rule is "it explains
nothing" — which
dormantalready covers and the dormancy check alreadyenforces — not "the names it explains cannot fail the gate".
What this leaves standing from #382
Nothing changes for the five
precedes_narrowerexemptions that cite this issue.Their claim was always about the NAMES being radar, never about the rules being
deletable; #496 was closed on the same measurement and its exemption text says so.
The reach figures in the correction comment above are unaffected.
Recompute
Read the
## <issue> (N)headings and their name lists, then take each name'stier from
compare._CORPUS_TIERS(read the roster; do not rebuild the file→tiermap —
corpus.jsonlis the largest corpus and it is radar). Note the reporttruncates each rule's list at ten names (
names[:10], with no "and N more"line), so a rule explaining more than ten cannot be tiered from the printed
output — that truncation is what produced a wrong count of 20 on the first pass
here. Take the full lists from
by_issue, or raise the slice in a scratch copy.Recorded in
docs/design/decisions.mdso the next reader does not re-derive it.Merge pull request #500 from derek73/claude/decisions-radar-only-rules
tooling(differential): declare the eleven wide-first pairs
tooling(differential): stop concluding a rule's tier from its names'
docs(design): declaring the contest, and why narrow-first is only a d…
docs(design): derek73#496 is weighed and declined, not an open gap
docs(design)+tooling(differential): rebuild the derek73#495 decline o…