Skip to content

Add config_diff.py: compare parser configurations in one tree, in parallel - #1025

Merged
derek73 merged 4 commits into
masterfrom
feat/config-diff-tool
Nov 3, 2026
Merged

derek73 merged 4 commits into
masterfrom
feat/config-diff-tool

Conversation

@derek73

@derek73 derek73 commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

What

tools/differential/config_diff.py, a general tool for the measurement several recent PRs each wrote a single-process throwaway script for (#1000, #1005, #1017, #1019): parse the corpora under two or more configurations of the same tree and report what moves. snapshot.py --local compares two trees under one configuration and local_agreement.py scores one configuration against filed boxes; neither compared configurations.

  • diff SPEC SPEC...: a SPEC is default, plain:<items>, strict:<items> or expr:<python>. plain: and strict: are parser_for over the items joined by +: a shipped pack in capitals such as ES or PT_BR, or a bare language tag in lower case. Region tags such as pt-BR and en-GB work, since a bare choice carries a constant code. expr: covers any other lexicon or policy. The first is the baseline.
    • Inputs: the committed corpus*.jsonl (each name under its shape's order) or any fetched local corpus (--local SOURCE / all).
    • --group is a fielded corpus's country or nationality, as the corpus writes it. It needs --local, and it refuses --committed and a group no record holds. With --local all it reads the fielded corpora only and says so.
    • Filters: --match REGEX (found anywhere in the name), --sample N (snapshot's hash sample).
    • Compares the seven role fields, the ambiguity kinds and 3.0's views (generation, credentials, patronymic, surname2). Reports moves per input, grouped by changed fields, and the words whose token changed role, ranked with an example.
    • --json writes the counts and the moved names. --readings writes every name's readings, one sorted line a name, so runs in two trees compare line by line.
  • gaps [tag...]: the per-language strict-gap report. For each language tag a choice can name, the plain choice against strict=True, over the records of the countries the language is written in, from ORCID, PatentsView and PSC. Both are scored against the filed boxes.
    • A broken record is laid to the words the two lexicons read differently whose own token moved. Where none did, it goes to those beside a token that moved. A beside column counts the broken records a word merely stood in.
    • The tables are country-keyed: LANGUAGES_OF (ISO 3166-1, plus ISO 3166-2 for the UK's nations) and NATIONALITY (PSC's spellings). They are a measurement aid only: decisions/vocabulary-model forbids a run-time country table.
    • The report closes with every group of 100 or more records the tables place nowhere (0 on the current caches). A test holds every choosable tag either listed under a country or named unmeasured (bo, yi).
  • Every parse goes through snapshot.parallel: spawned workers, each building a configuration once from its spec string, with the segmenter warning suppressed there too, and refusing a nameparser from outside the tree.

Docs: a paragraph and two examples in tools/differential/README.md ("Measuring a change"), a pointer from nameparser/vocab/README.md, and a root AGENTS.md paragraph telling the next measurement to reach for this instead of a script.

tests/v2/test_layering.py's recorded negative control moves from 16 tool reads to 19: gaps compares two lexicons set by set through _sets.

Review round (22234c1)

An independent review found five defects; each is fixed with a test.

  1. A bare region tag failed to build.
  2. Moves in the views went unreported. plain:RU's Ivan Ivanovich Petrov moves only in patronymic.
  3. gaps credited a break to every differing word in the name. pt's da was credited with 45 breaks, where its own token moved in 1; the Miss beside it broke the rest.
  4. --group was silently ignored or mixed with unfiltered names.
  5. The country table had large omissions.

Nits fixed: the README's example regex now has \b; an expr: that raises is a usage error; one test runs through main(). Three surviving mutants are now killed, each checked by mutating the file: search→match in gather, the per-name dedup in tally, and p.fields == s.fields→p == s in score_gaps.

Measured (2026-11-03, Python 3.11, 12 cores shared with other sessions)

  • gaps, all 83 tags, before the review fixes: 447 s wall, 956 s user, under load averages of 46 to 97.
  • diff of 162 configurations over the 29,097 local-corpus names holding ing or mba, about 4.7M parses: 271 s and 281 s in two trees.
    • Two single-process scripts doing a comparable job had not finished after 28 minutes when stopped.
  • Re-run of gaps after item 3. Credits change little where the word itself moved (fil de 474 → 473, ca de 636 → 626, si de 332 → 334, pt-BR jr 183 and junior 151) and fall where it only co-occurred (pt da 45 → 1, pt-BR da 23 → 0, pt do 6 → 0).
    • With the wider tables, counts grow for widely written words: ms breaks 38,117 records across tags, de's jr 48, ar's sultan 227.

Tests

tests/v2/test_config_diff.py (41 tests) covers the tool's logic, not its numbers. uv run pytest -q passes (35,276), as do uv run mypy, uv run ruff check and CI on 3.11 to 3.15. Not run: -n0 -m serial (the change touches no library code).

The vocabulary change this tool's report fed (ms, miss, mba) is #1028.

🤖 Generated with Claude Code

…allel

`config_diff.py diff` parses the committed corpora or any fetched local
corpus (filtered by country or nationality, a regex, a hash sample)
under two or more named configurations -- default, plain:<items>,
strict:<items> (a pack in capitals, a language tag in lower case) or
expr:<python> -- through snapshot.parallel, and reports the names that
moved against the first, grouped by the fields that changed, with the
words whose role changed ranked. --readings writes every name's
readings one line a name, so runs in two trees compare line by line.

`config_diff.py gaps` is the strict-gap report on top of it: each
language tag's plain choice against its strict one over the records of
the countries the language is written in (WRITTEN_IN, a measurement
aid only), scored against the ORCID, PatentsView and PSC boxes, with
the words strict reads differently ranked by records broken.

The layering test's recorded control moves from 16 tool reads to 19:
the new tool compares two lexicons set by set through `_sets`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73 derek73 added this to the 3.0 milestone Nov 3, 2026
@derek73 derek73 added the tooling label Nov 3, 2026
@derek73 derek73 self-assigned this Nov 3, 2026
…country tables)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
derek73 and others added 2 commits November 3, 2026 01:57
…p, hints)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@derek73
derek73 merged commit 31a9fca into master Nov 3, 2026
13 checks passed
@derek73
derek73 deleted the feat/config-diff-tool branch November 4, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant