Skip to content

tooling(differential): every measured contest has an owner (#498) - #507

Merged
derek73 merged 4 commits into
masterfrom
claude/498-contest-pins
Sep 6, 2026
Merged

tooling(differential): every measured contest has an owner (#498)#507
derek73 merged 4 commits into
masterfrom
claude/498-contest-pins

Conversation

@derek73

@derek73 derek73 commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Every measured contest in the differential ledger now has an owner. A new run-time check, UNPINNED CONTEST, fails the gate where a diff two or more rules admit is won by neither narrow-first nesting nor a precedes_narrower declaration and no _RECORDED_DIFFS row pins it; the fourteen names that lacked one are adjudicated and pinned. Closes #498.

What was open

#382's undeclared_contests refuses a WIDER rule sitting ahead of a NARROWER one it shares a corpus name with, unless the wider rule declares why. Nesting is sufficient for an order-decided contest and not necessary: two rules whose fields merely intersect, or are equal, both admit any diff inside the intersection, classify() returns the first, and the static check looks past the pair in either arrangement. The docs said _CROSS_RULE_WINNERS "stays the instrument" for that class. Nothing checked that the instrument had been applied.

Measured 2026-09-05 by driving main() at all four baselines with classify() spied (recipe in the decisions entry):

baseline diffs two or more rules admit already pinned owed a pin
1.4.0 47 18 14
2.0.0 3 3 0
2.1.0 1 1 0
2.2.0 0 0 0

The fourteen come from seven (winner, loser) pairs over fifteen name-pair rows. Seven of the names sit in EQUAL-fields pairs — six under fix(#325) against the lone-post-comma rule, and Bob Jones, compositeur — the clearest case of the class the docs named the roster for, and none was pinned. Four are contract tier: the issue's three nakaguro names and Smith, Ph. D. Jr..

Why run-time, and what the 2026-09-02 decline declined

A pin is a shape beside a winner, and the shape is what a run measured. Without a diff the population is the static reach of every pair the check can report, 71 pairs (60 overlapping, 11 with equal fields), and the roster nobody writes; with a diff it is 14 names. So the check has no static form. The recorded decline of run-time contest detection was of REPLACING the static check, on coverage; this adds to it: the static check covers the hazard that has a declaration site, this covers the hazard that has none. Speed was never the reason — a whole gate run is a fraction of a second, and the "multi-minute" phrase was withdrawn 2026-09-03.

The commits

  1. The fourteen pins. A researcher adjudicated each of the fifteen name-pair contests against both rules' prose and the measured 1.4.0 old/new values; all fifteen current winners stand, none by accident. Each gets a _RECORDED_DIFFS row and a _CROSS_RULE_WINNERS winner with a per-pair argument that says what the pin buys (a reorder moves 7 / 3 / 9 / 15 / 2 / 3 / 8 of the 352 classifications, pair by pair). Five names move from _WATCHED_DIFFS; every 31-row figure in the tree becomes its 45-row successor, re-measured, with the dated records keeping their numbers and gaining a clause.
  2. unowned_contests, a pure function behind hermetic tests, with _UNOWNED_WHY rendering its kind tokens the way _DORMANT_WHY does. Mutation-tested: dropping the order narrowing, the sorted, any continue, or returning early each fails exactly one named test.
  3. UNPINNED CONTEST joins the exit code, fatal on both tiers, rows grouped by pair, after the MOVED SHAPE blocks; main()-level tests including that a _WATCHED_DIFFS row does not silence it and a radar name fails as a contract one does. Mutation control on the real ledger: deleting Smith, Ph. D. Jr.'s row in memory exits 1 naming the name, its winner and its loser; unmutated, no block prints at any baseline.
  4. Records. A dated decisions entry with the population figures, the seven verdicts, the scope decision (the predicate stops at Should file order decide between rules whose fields overlap without nesting? (マイケル・ジャクソン goes to whichever of two rules comes first, and the contest check never sees the pair) #498's class; the wider narrow-first one costs 9 more names, 23 in all, and is declined with Six contested diffs in the 2.x ledgers have no pin, and the empty rosters read as if none existed (MD, PHD goes to whichever rule comes first) #501's gap (ii) left open by choice), and the coverage argument; the two 2026-09-02 bullets each gain a sentence pointing here; README's "What counts as a contest" carries the second predicate; mechanisms.md's cross-rule pins section says the roster's completeness is now checked rather than stated.

Scope decision

The predicate stops where the winner beats a loser with equal or non-nested fields. A pin for every undeclared contest, narrow-first ones included, would also have forced the argument #501's regex accident lacked, at the cost of pins the declaration-free default already justifies. Recorded as declined, with the figure.

Verification

Intentional counts 352 / 247 / 155 / 14 byte-identical at every commit (a pin moves no classification; a check that fires on nothing prints nothing), 0 unexplained, 0 radar-unclassified, no finding block at any baseline. Full suite green; ruff and mypy clean. Every commit went through a spec-compliance review and a quality review; the review rounds found and fixed one stale present-tense roster total, two mutation survivors, a fail-open .get, and four comment sentences a measurement contradicted. A final five-reviewer pass over the whole branch then closed two more: the pin gate now applies only to default-order comparisons, since a recorded shape is a default-order shape and cannot own a declared-order contest, and the function sorts its rules internally as dormant_rules does.

🤖 Generated with Claude Code

@derek73 derek73 added docs Documentation fixes and updates tooling tests labels Sep 6, 2026
@derek73 derek73 self-assigned this Sep 6, 2026
@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.66%. Comparing base (fca8bf4) to head (24fad28).
⚠️ Report is 5 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #507   +/-   ##
=======================================
  Coverage   98.66%   98.66%           
=======================================
  Files          45       45           
  Lines        3219     3219           
=======================================
  Hits         3176     3176           
  Misses         43       43           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@derek73 derek73 added this to the v2.3 milestone Sep 6, 2026
derek73 and others added 4 commits September 5, 2026 22:32
…winners

Every 1.4.0 diff two or more rules admit, where the winner beats a
loser by neither narrow-first nesting nor a `precedes_narrower`
declaration and no roster row pins it: fourteen names over seven
(winner, loser) pairs, measured 2026-09-05 against the 1.4.0 wheel.
SEVEN sit in an EQUAL-`fields` pair -- PAIR A's six and 'Bob Jones,
compositeur' -- the clearest case of the class the docs say
_CROSS_RULE_WINNERS "stays the instrument" for, a class they scope to
merely-intersecting `fields` too; four are contract tier. Nothing is
owed at 2.0.0, 2.1.0 or 2.2.0.

Each name gets a shape in _RECORDED_DIFFS and a winner in
_CROSS_RULE_WINNERS with a per-pair argument. The five that were
_WATCHED_DIFFS rows at 1.4.0 move with their shapes unchanged and
keep their 2.x rows, where each has one admitter -- so the watched
roster falls 41 -> 36 at 1.4.0 (107 -> 102 rows) over the same 50
distinct names.

Re-derived: _RECORDED_DIFFS at 1.4.0 is 45 rows, 32 of them on
radar-tier names and 13 with a single admitter at their recorded
shape. Every count-carrying site for those figures is updated with
its recompute; dated records of what an earlier tree measured keep
their numbers and gain a "since #498" clause.

Gate at all four baselines: 352 / 247 / 155 / 14 intentional, 0
unexplained, 0 radar unclassified, exit 0, summary lines
byte-identical to master. A pin moves no classification.

Refs #498

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ration must be pinned

`unowned_contests` walks a run's own `diffing` list, re-asks
_entry_matches of every rule, and reports each (winner, loser) pair
where the winner beats a loser by neither narrow-first nesting nor a
`precedes_narrower` declaration and the name has no _RECORDED_DIFFS
row. Equal `fields` and a bare intersection are both reported;
narrow-first stays #382's declaration-free default and a declared
wide-first pair stays covered by its `why`. A wide-first UNDECLARED
pair is classified (kind `wide-undeclared`) rather than assumed
unreachable, because the tests hand this rule lists nothing
validated. The three kinds are TOKENS the caller renders, the shape
_Dormant set: `_UNOWNED_WHY` beside `_DORMANT_WHY` holds the wording.

Run-time by necessity: a pin is a shape beside a winner and the shape
is what a run measured, so there is no static form of the question --
without a diff the population is the 60-pair static reach and with
one it was 14. A _WATCHED_DIFFS row does not justify: it pins no
winner, and the `set(winners) == set(shapes)` guard is what makes a
_RECORDED_DIFFS key mean one exists. No vacancy half, and the
docstring says why a dissolved contest keeps its pin.

Pure and unwired: nothing calls it yet, so the gate is byte-identical
at all four baselines and the machinery bisects apart from the
severity.

Refs #498

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
main() calls unowned_contests over its own `diffing`, the sorted
rules, the exclusions, the ledger's _RECORDED_DIFFS keys and the
tier map, and prints a block grouped by (winner, loser) pair: one
lead carrying the repair (pin the name in both rosters with the
argument, or narrow the accident away) and the disclaimer (a contest
is not a defect, an unowned one is), then one line per name with its
order tag, tier, shape and the _UNOWNED_WHY clause its kind renders
to. The count is PAIR(S) and not diff(s), the rows being one per
(name, losing rule), so a name contesting two losers is two of them.
The declared-order limit prints only when a row carries an order, the
way NOT CHECKED names its own, and the lead says that the population
is the entries this run loaded -- a `--corpus` subset can only
under-report.

The block sits after the three MOVED SHAPE blocks and above the
Role-vocabulary legend, and `unowned` joins the exit expression
beside `shape_bad`: fatal on both tiers, because a contest row is
fatal on either tier for carrying an argument and an owed row is
fatal for carrying none yet.

MUTATION CONTROL on the shipped tree, in memory around main() so the
worktree is untouched: with 'Smith, Ph. D. Jr.' deleted from
_RECORDED_DIFFS['expected_since_1.4.0.toml'], a 1.4.0 run exits 1
with

  UNPINNED CONTEST expected_since_1.4.0.toml: 1 contested pair(s) ...

naming that row at [contract] ['suffix', 'title'] (equal `fields`,
file order the whole decision). Unmutated, no block prints at any of
the four baselines.

Gate at all four: 352 / 247 / 155 / 14 intentional, exit 0, summary
lines byte-identical to master.

Refs #498

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
decisions.md gains a dated `### differential-ledger, the #498
completeness check (2026-09-05)` entry after the #501 adjudication:
the four-baseline population figures with their recipe (47/18/14 at
1.4.0, 3/3/0, 1/1/0, 0/0/0), the equal-fields finding, the scope
decision with the 9-more-names / 23-in-all figure and that #501's gap
(ii) stays open by choice, the coverage argument that says what the
2026-09-02 decline declined and what this is, the seven verdicts with
their measured old and new values, and the mutation control.

The 2026-09-02 "nesting is sufficient, not necessary" bullet and the
run-time-detection Declined bullet each gain one sentence pointing
here, so neither reads as the last word on a class that has since
been closed.

README's "What counts as a contest" gains the second predicate, its
severity, and the no-vacancy-half asymmetry; the #498 paragraph goes
from "blind spot, filed" to "closed by", keeping the worked case and
the static figures. mechanisms.md#CROSS-RULE-OUTCOME-PINS says the
roster's completeness is now enforced rather than stated, and
#LEDGER-RULE-SEPARATION's "#498 carrying the worked case" clause says
how it was closed.

Refs #498

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation fixes and updates tests tooling

Projects

None yet

1 participant