Repository navigation
Commit the per-word and per-change measurement tooling for #913 (word_report.py, --jobs, alike, measure.py) - #917
Merged
Merged
Conversation
derek73
force-pushed
the
feat/913-measurement-tools
branch
from
October 27, 2026 20:25
ed883ad to
61f08a0
Compare
derek73
force-pushed
the
feat/913-measurement-tools
branch
from
October 27, 2026 20:42
61f08a0 to
50e157d
Compare
…nce and usage in one run Phase 2j (#896 through #912) rewrote about ten probe scripts each batch: a shape grid, corpus incidence counts, a usage lookup. #913 lists about 22 more words for rules/H9 (the title phrase), so the probes become a committed tool. - grid: the word in 25 shapes (a head, behind a title or a style, after a family comma, after a full name and a comma, past a second comma, trailing with a period, the article epithet, its own title phrase), each in three cases and under all three name orders, folded where cases or orders read alike; with --base only what moved. For sheikh against its pre-#912 base that is 120 lines. - corpora: in every fetched local corpus, the names opening with the word or opening the part after a comma with it, counted by reading (title, given, other). In psc-individuals, the names opening with the word are also counted by the box the filer typed it into. Counts only: no name is printed. A corpus not fetched is named, and a damaged one ends the run. - usage: name_usage.py --no-wikidata --json's rows. - --rank, the triage #913 asks for, with no head change: the title readings at both positions, beside the PSC forename-box and title-box filings and the forename box over the two (28/34), ordered by the forename box's count, since a count cannot be rebuilt from a share. A title-box filing is a real title, which the move would cost. A forename-box filing is a given name the title reading takes, which the move gives back. On 2026-10-27: sir 277 / 8, sheikh 13 / 32, the 13 being the agreement records #912 moved away from agreeing. Each tree is read in a child process of its own, the two run at once, and each refuses a nameparser imported from outside its tree (snapshot.check_source). snapshot.read's spawn pool becomes snapshot.parallel, shared by the child and, in the next commits, the agreement score and pairs views. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… alike view local_agreement.py's score and local_corpora.py's pairs --agreement and groups views parsed every name in one process. They now read through snapshot.parallel with --jobs (default every CPU), and its workers abort the run where one imported another nameparser. Measured 2026-10-27 on 12 CPUs, each output byte-identical before and after: psc-individuals 88.9s -> 15.4s, groups jrc-names 55.6s -> 11.8s, pairs tse-donors --agreement 52.1s -> 15.8s. New `alike SOURCE --base <checkout>` view: whether each person's spellings read alike (jrc-names persons, nepal-candidates romanized and Devanagari pairs, tse-donors filed and registry pairs), and how many now read alike and now apart against the base. That replaces phase 2j's scratch pairs script, which parsed the base again for every commit. The base's flags are kept under alike/, keyed by the fetch, the base package's digest, the interpreter and the view's code (alike_fingerprint, its hand-kept list guarded like _FOLD_CODE). They are published only once whole and once the digest, taken again, is unchanged. A key names no one. plant_corpus now carries the real source's corpus_keys, so a nepal corpus planted for a --base child passes the child's key check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sequence, with one summary
Each phase 2j batch spent about ten minutes on a hand-written
measure.sh. measure.py --base <checkout> runs the same steps one at a
time, each but cldr_agreement.py parallel inside itself:
- the committed snapshot and cldr_agreement.py, against the records
committed on the branch;
- snapshot.py --local --base for every fetched corpus;
- local_agreement.py psc-individuals --base;
- local_corpora.py alike --base for every fetched source that pairs
spellings.
The summary labels each step with what it compared against, names the
local corpora that are not fetched, and fails a run in which no step
read --base.
Each step's whole output goes to a file under --out (by default the
tools' cache, since a local snapshot lists moves by name). summary.txt
keeps each step's unindented lines: the first few, the last ones (where
the verdicts are) and any "not measured" line. It never keeps the
indented ones, where the tools list names. Output is read as bytes and
cut at "\n" only, and snapshot.grouped escapes a line break inside a
name, so no part of a name reaches the left edge.
Every tool refuses with SystemExit("message"), which exits 1 like a
move, so each step runs through measure's --step wrapper. The wrapper
turns a message exit or an uncaught exception into exit 2, and prints a
crash's traceback indented below a line naming only its type.
On 2026-10-27, against a base whose package differed only in a README,
it ran 14 steps in 368s on 12 CPUs, each base recorded for the first
time. AGENTS.md's "Adding a word to the vocabulary" and the vocabulary
README point at word_report.py and measure.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
derek73
force-pushed
the
feat/913-measurement-tools
branch
from
October 27, 2026 20:54
50e157d to
7b7fc5f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Each phase 2j batch (#896 through #912) spent about ten minutes on a hand-written
measure.shand rewrote about ten probe scripts: shape grids, corpus incidence counts and a usage lookup. #913 lists about 22 more words, so this PR commits the tooling. It changes no parser behavior. There are three commits.1.
tools/vocab/word_report.py WORD... [--base <checkout>] [--tree <checkout>] [--rank]grid: the word in 25 shapes, each in three cases and under all three name orders. The shapes:
X of Yphrase;A shape's three case spellings fold into one block where they read alike once lowercased, and a text's orders fold into one
allline where they agree. With--base, only what moved is printed, folded the same way; an order-specific move still prints for its order alone. Forsheikhagainst a pre-Read the name Sheikh and its spellings as names outside a title phrase (#752 phase 2j, #853) #912 checkout the report is 120 lines; unfolded, it ran to about 330.corpora: in every fetched local corpus, the names that open with the word, and the names whose part after the first comma opens with it, counted by reading: title, given or other. In psc-individuals, the names opening with the word are also counted by the box the filer typed it into (title or forename). The report names the corpora it could not read, and a damaged corpus ends the run. Counts only; a test checks that no corpus name reaches the output.
usage:
name_usage.py --no-wikidata --json's rows, with each local corpus's credit trimmed.ranking, alone with
--rank, the triage Should Baron, Bishop, Judge and the other title words borne as names read as names outside a title phrase? (Baron Davis→ title 'Baron', no given name) #913 asks for: the title readings at both positions, beside the PSC forename-box and title-box filings and the forename box over the two (28/34). Words are ordered by the forename box's count, then by its share, then by titles; a count can't be rebuilt from a share. It needs no change to the tree.Each tree is read in a child process of its own. The two children run at once, and each refuses a
nameparserimported from outside its tree (snapshot.check_source).snapshot.read's spawn pool is nowsnapshot.parallel, which this child and commit 2 share, worker check included.#913's 22 words, ranked on 2026-10-27, with
dean,king,princeandsheikh(already moved) as controls:titlescounts every name that reads the word as a title, real titles included. The PSC boxes separate the two:The forename count orders the table, with the title box beside it. So
sir(8/285) andlady(5/165) rank abovequeen(3/3) on count, while their title boxes, 277 and 160, show the cost of moving them.sheikh's 13 title-box filings are the 13 PSC agreement records #912 moved away from agreeing, so the split predicts a batch's cost before it is cut. A-means no filer typed the word in either box, and the titles column is all there is.2.
--jobsfor the agreement score and the spelling views, and analikeview with a kept baselocal_agreement.pyscores throughsnapshot.parallel(--jobs, default every CPU), and its--basechild does too. Under jobs,score()refuses a non-defaultParser, since a worker cannot be sent one.local_corpora.py'spairs --agreementandgroupsparse each distinct name once, in workers.local_corpora.py alike SOURCE [--base <checkout>]view. It reads jrc-names persons, nepal-candidates romanized/Devanagari pairs and tse-donors filed/registry pairs. With--baseit prints the "now alike / now apart" counts each 2j PR quoted, replacing the scratchpairs.py, which parsed the base again for every commit.alike/, keyed by the fetch, the base package's digest, the interpreter andalike_fingerprint._FOLD_CODEhas.plant_corpusnow carries the real source'scorpus_keys, so a nepal corpus planted for a--basechild passes the child's key check.Measured 2026-10-27 on 12 CPUs, load average 3 to 15 from other sessions. Each output was byte-identical before and after (the report and the record's entries):
local_agreement.py psc-individuals --recordgroups jrc-namespairs tse-donors --agreement(--jobs 1vs default)3.
tools/differential/measure.py --base <checkout>The steps, run one at a time, each but
cldr_agreement.pyparallel inside itself:snapshot.py, the checktest_snapshot.pymakes);cldr_agreement.py;snapshot.py --local --basefor every fetched corpus;local_agreement.py psc-individuals --base;alike --basefor each source that pairs spellings.--base. Steps 1–2 compare against the records committed on the branch. The summary labels each step with what it compared against. It also names the local corpora that are not fetched, and a run where no step read--baseexits 1.--out, by default under the tools' cache, since local moves are listed by name.summary.txt: keeps each step's status, its time and its unindented lines: the first few, the last ones (where the verdicts are) and any "not measured" line. It never keeps the indented lines, where the tools list names.On 2026-10-27 it ran 14 steps in 368s against a base copy of master, recording every base for the first time. The base differed only in
nameparser/vocab/README.md, which the package digest hashes.Review fixes (folded into the commits)
Two review rounds, the second with four reviewers (code, tests, comments, silent failures). Each fix has a test, and the first round's were mutation-checked against the old code.
SystemExit("message"), which exits 1 like a move. Each step now runs through measure's--stepwrapper, which turns a message exit or an uncaught exception into exit 2.\r, U+2028 or U+0085 could have reached summary.txt. Output is now read as bytes and cut at\nonly.--rankwith corpora unfetched or damaged printed zeros. The report now says which corpora it read, and a damaged one ends the run.--base.name_usage.pycrashed word_report with a TypeError, because its note was a list. It now prints the reason.--base, alike counts pairs as well as persons, the parallel floor applies to every caller, and the--rankand--basewording.Docs
tools/differential/README.mdnow covers--jobs, thealikeview, the dated timings and a new "Measuring a change" section.nameparser/vocab/README.mdhas aword_report.pyparagraph and points atmeasure.pyin step 6. AGENTS.md's "Adding a word to the vocabulary" points at both.Notes
snapshot.pyand the scorer changes the kept-baseline and scorer keys. Every kept local baseline and kept agreement record is recorded once more on its next--baserun.word_report.py's child reads throughsnapshot.parallelonly above 5,000 texts. That path is tested in process, with the floor lowered, since a child's floor cannot be patched.alikecounts flips between alike and apart only. A person whose spellings all change their reading but still agree is no move there; the local snapshot shows it.Baron Davis→ title 'Baron', no given name) #913 (a title borne as a surname,Graf Steffi).--rankreads the default order only; the grid shows the family-first orders per word.Tests:
uv run pytest31,979 passed; serial 43 passed; the touched modules pass on 3.15;ruffandmypyare clean.🤖 Generated with Claude Code