← Analysis Reports

Page generated: 2026-08-27
Data (model runs) generated: 2026-07-23
Scoring inputs: ground truth 36c390d2, evaluate.py 5c52b890, rules 355f2e77

Name matching and field conflation

How a traveler name is scored, why two different similarity measures give different answers, and where models put the right value in the wrong field. Every figure on this page is computed from the run checkpoints and the ground-truth CSVs by build_scoring_doc.py; none of it is typed by hand.


1. What the name score is, and what it is not

The name is the matching key, not a scored field. It never appears in SCORED_FIELDS, so its accuracy contributes to no published figure directly. It enters the results in exactly one way:

1. A similarity is computed for each candidate (model row, GT row) pair.
2. Pairs below 0.7 are rejected; the rest are eligible to match.
3. Rows are paired, and then the similarity value is discarded.
4. Matched rows are counted: precision = matched / rows produced, recall = matched / GT travelers, F1 = 2PR/(P+R).

So a pair at 0.71 and a pair at 1.00 are indistinguishable in every published number, and a pair at 0.69 is counted twice against the model — once as a false positive and once as a missed traveler.

2. The two similarity measures

Both run on the reduced readings of the model name, not the raw string. evaluate.name_variants generates every reading the EQUIVALENCES.md §1 rules allow — the untouched name, the name with a leading article and title removed, the name with an appended place of origin removed — and the best-scoring one wins. The unreduced form is always among the candidates, so a reduction can reveal a name that was buried but never lower a score. The §1 removed column in the tables below reports which reduction won.

Gate (evaluate.name_match, applying name_similarity to each reading) — the larger of two candidates:

CER-based (analyze_name_precision.pair_sim) — 1 − CER, where CER = levenshtein(gt, model) / len(gt), a true minimum edit distance normalized by the reference length. This is the measure already used for the eight scored fields, via evaluate.char_accuracy.

They agree on plain substitutions and diverge on length: the combined-length denominator absorbs appended text, the reference-length denominator does not.

3. Worked cases

gate, no §1 is the similarity on the untouched string — what this page reported before the §1 rules were wired into the scorer. gate is what evaluate.match_rows actually applies. The difference between the two columns is the rules doing their job. title score and origin score are the scorer's own per-field results for the same row.

Conflation no longer costs a match by itself: wherever §1 removed is non-empty, the added title, article or origin was taken off before the comparison, and what is left is the reading of the name. Two things the reduction does not fix are visible below — a short name misread is still rejected on its own merits, and a higher gate score lets some genuinely wrong readings clear 0.7 that previously fell short.

Conflation removed, and the row is still lost — to the reading, not the title

Page 39 — GT traveler Baissen. GPT reports title correctly as Barone and also prefixes it to the name. §1 takes the title back off, so the comparison is Bassa against Baissen: three wrong characters in a seven-character name. The row is rejected on the reading alone. This case used to be presented as conflation breaking the match; the title was never the reason it would fail today.

SourceRunsnametitleplace_of_origingate, no §1gatename CER§1 removedtitle scoreorigin score
Ground truth—BaissenBaroneSassonia——————
GPT A7Il Barone BassaBaroneSassonia0.3640.6670.571title/article: il barone1.001.00
GPT N3Il Barone Bassa diBaroneSassonia0.3200.6670.571title/article: il barone; origin: di1.001.00
GPT N3Il barone Bassa diIl baroneSassonia0.3200.6670.571title/article: il barone; origin: di1.001.00
GPT N3Il Barone Bassa diIl BaroneSassonia0.3200.6670.571title/article: il barone; origin: di1.001.00
Gemini 3.1 N10BaissenIl BaroneSassonia1.0001.0001.000(blank)1.001.00
Claude B10BaissenBaroneSassonia1.0001.0001.000(blank)1.001.00

Place of origin conflated into the name, and now absorbed

Page 28 — GT traveler Israel Bassan. GPT fills place_of_origin correctly and also appends it to the name. The raw string falls below threshold; with the appended origin removed the same row matches. Nothing about the model's reading changed — only whether the scorer could see it.

SourceRunsnametitleplace_of_origingate, no §1gatename CER§1 removedtitle scoreorigin score
Ground truth—Israel Bassan(blank)Verona——————
GPT N6Israel Baysan di Verona(blank)Verona0.6670.9230.923origin: di verona1.001.00
GPT A2Israel Baysan di Verona(blank)Verona0.6670.9230.923origin: di verona1.001.00
GPT N1Israel(blank)Verona0.6320.6320.462(blank)1.001.00
Claude N10Israel Bassan(blank)Verona1.0001.0001.000(blank)1.001.00
Gemini 3.1 N10Israel Bassan(blank)Verona1.0001.0001.000(blank)1.001.00

GT is the bare surname, so the raw string is mostly title

Page 36 — GT traveler Tornielli. GT records only the surname, so before §1 almost every row here was rejected for carrying a title the model also filed correctly. With the title removed all but the worst misreading now clear the threshold — including Barone Donizelli, where the surname is simply wrong. That is the cost side of the reduction: it raises every score, not only the deserving ones.

SourceRunsnametitleplace_of_origingate, no §1gatename CER§1 removedtitle scoreorigin score
Ground truth—TornielliBaroneMilano——————
Claude Thinking N1Barone BonniettiBaronedi Milano0.4000.5560.556title/article: barone1.001.00
Claude A7Barone DonizelliBaroneMilano0.5600.7780.667title/article: barone1.001.00
Claude N6Barone DonizelliBaroneMilano0.5600.7780.667title/article: barone1.001.00
GPT A5Barone TonelliBaronedi Milano0.6090.8750.778title/article: barone1.001.00
Claude N4Barone DonzelliBaroneMilano0.5000.7060.667title/article: barone1.001.00
GPT N2Il Barone TonelliBaroneAmbasciatore di Milano0.5380.8750.778title/article: il barone1.000.00
GPT N2Il Barone TonelliBaronedi Milano0.5380.8750.778title/article: il barone1.001.00
Claude A2Barone DonnielliBaroneMilano0.5600.7780.778title/article: barone1.001.00
Gemini 3.1 N10TornielliIl BaroneMilano1.0001.0001.000(blank)1.001.00
Claude B10TornielliBaroneMilano1.0001.0001.000(blank)1.001.00

Three travelers in one row

Page 36 — GT traveler Francesco Mazuchelli. The document writes the three brothers on one line and GT expands them to three rows. A model that transcribes the line faithfully produces one row. §1 does not help here, and it hurts once: stripping Conte lifts Conte Francesco Alberti over the threshold, matching a traveler it is not.

SourceRunsnametitleplace_of_origingate, no §1gatename CER§1 removedtitle scoreorigin score
Ground truth—Francesco MazuchelliConteBrescia——————
GPT N1Conte Francesco AlbertiConteSavoia0.6050.7030.650title/article: conte1.000.00
GPT N1Conte Francesco di ... MarchettiConteS. ... / Grecia0.6670.7620.650title/article: conte1.000.00
Gemini 2.5 A3Francesco MariscottiConteBologna0.6500.6500.650(blank)1.000.00
Claude Thinking N1Archiciato Malucelli(blank)Leccia0.6500.6500.550(blank)0.000.00
Claude B10Francesco MazuchelliConteBrescia1.0001.0001.000(blank)1.001.00
Claude Thinking B10Francesco MazuchelliConteBrescia1.0001.0001.000(blank)1.001.00

Control: a long name absorbs the same number of errors

Page 39 — GT traveler Giovanni Battista Maffoni. Nothing is conflated here, so §1 removed is empty throughout and the two gate columns agree. Included to show what the same character error rate does to a long reference.

SourceRunsnametitleplace_of_origingate, no §1gatename CER§1 removedtitle scoreorigin score
Ground truth—Giovanni Battista Maffoni(blank)Brescia——————
Claude N10Giovanni Batta Maffoni(blank)Brescia0.9360.9360.880(blank)1.001.00
Gemini 3.1 N10Giovanni Batta Massoni(blank)Brescia0.8510.8510.800(blank)1.001.00

Several travelers in one row

The register writes family groups on a single line (f.lli = fratelli, brothers) and the ground truth expands them to one row per named traveler, which is what the prompt asks for. A model that transcribes the line as written produces one row where GT has two or three, so it is charged one false positive plus a missed traveler for each name it did in fact read.

PageModelCondRunsthe single name producedtitleplace_of_originGT travelers in that one row
36Gemini 3.1N10Francesco Filippo e Federico f.lli MazzuchelliLi Co:Brescia3 — Francesco Mazuchelli; Filippo Mazuchelli; Federico Mazuchelli
36Gemini 2.5N3Giuseppe Palmarini, Girolamo Bouillino e Antonio Zivati(blank)di Genova2 — Giuseppe Palmarino; Girolamo Borellino

The definite article halves a short title score

Not conflation, but the same arithmetic. title is scored by character accuracy against a short reference, so writing the article the document uses costs half the score:

GT titlemodel titlecharacters wrongreference lengthtitle score
BaroneIl Barone061.00
ConteSignor Conte750.00

4. Reference length decides how much error is survivable

CER divides by the length of the GT name, so the same number of wrong characters produces opposite verdicts depending on how long the name is:

The verdict column is the gate's, not CER's. An earlier version of this table compared 1 − CER against the gate's 0.70 threshold, which is the wrong measure for that cutoff — the gate is Ratcliff-Obershelp. Both are shown so the divergence is visible.

GT namemodel readingcharacters wrongreference lengthCERgateverdict
BaissenBassa370.5710.667rejected
Giovanni Battista MaffoniGiovanni Battesta Maffoni1250.9600.960matched

The shortest names on a page are the easiest to read and the hardest to match.

5. Rows returned, before any scoring

How many per-traveler rows each model actually returned for a page, as the lowest–highest range across its 10 runs, with the median and its distance from the ground-truth count beneath. Matching is ignored here: a model returning 20 rows for an 18-row page is reported as 20 even if only 10 of those rows correspond to a real traveler. This separates over- and under-generation from reading accuracy — a model can return exactly the right number of rows and still have read none of them correctly.

ModelCondPage 28 — GT 18Page 36 — GT 37Page 39 — GT 10All pages — GT 65
rows returnedmatchedname CERrows returnedmatchedname CERrows returnedmatchedname CERrows returnedmatchedname CER
ClaudeN18median 18 · +0 vs GT15.32.7 unmatched13.6%291 of 2145 chars34–35median 35 · -2 vs GT27.57.4 unmatched19.6%829 of 4227 chars10median 10 · +0 vs GT7.92.1 unmatched16.5%212 of 1285 chars62–63median 63 · -2 vs GT50.712.2 unmatched17.4%1332 of 7657 chars
A19median 19 · +1 vs GT181 unmatched14.6%374 of 2570 chars34–35median 34 · -3 vs GT24.69.6 unmatched18.9%722 of 3818 chars10median 10 · +0 vs GT82 unmatched16.2%210 of 1300 chars63–64median 63 · -2 vs GT50.612.6 unmatched17.0%1306 of 7688 chars
B18median 18 · +0 vs GT171 unmatched3.9%92 of 2360 chars37median 37 · +0 vs GT370 unmatched0.2%10 of 5450 chars10median 10 · +0 vs GT100 unmatched0.6%10 of 1620 chars65median 65 · +0 vs GT641 unmatched1.2%112 of 9430 chars
GPTN16–23median 18 · +0 vs GT10.58.1 unmatched22.3%331 of 1484 chars26–34median 33 · -4 vs GT14.717.5 unmatched23.1%539 of 2337 chars9–10median 10 · +0 vs GT4.45.5 unmatched26.6%203 of 764 chars58–67median 60.5 · -4 vs GT29.631.1 unmatched23.4%1073 of 4585 chars
A24–29median 25 · +7 vs GT7.917.2 unmatched14.9%171 of 1148 chars31–34median 32.5 · -4 vs GT15.217.2 unmatched23.6%570 of 2418 chars10median 10 · +0 vs GT46 unmatched24.2%167 of 691 chars66–70median 67 · +2 vs GT27.140.4 unmatched21.3%908 of 4257 chars
B18median 18 · +0 vs GT180 unmatched4.3%110 of 2570 chars37median 37 · +0 vs GT370 unmatched0.4%20 of 5450 chars10median 10 · +0 vs GT100 unmatched0.0%0 of 1620 chars65median 65 · +0 vs GT650 unmatched1.3%130 of 9640 chars
Gemini 2.5N19–20median 20 · +2 vs GT163.6 unmatched11.5%269 of 2334 chars24–36median 32 · -5 vs GT16.913.8 unmatched19.6%456 of 2327 chars10median 10 · +0 vs GT6.73.3 unmatched25.9%284 of 1096 chars54–66median 61 · -4 vs GT39.620.7 unmatched17.5%1009 of 5757 chars
A20–21median 20.5 · +2 vs GT14.75.8 unmatched8.7%187 of 2160 chars35–37median 36 · -1 vs GT24.711.3 unmatched19.4%661 of 3403 chars10median 10 · +0 vs GT73 unmatched17.4%200 of 1150 chars66–68median 66 · +1 vs GT46.420.1 unmatched15.6%1048 of 6713 chars
B18–19median 18 · +0 vs GT180.1 unmatched0.8%20 of 2570 chars37median 37 · +0 vs GT370 unmatched0.3%19 of 5450 chars10median 10 · +0 vs GT100 unmatched0.0%0 of 1620 chars65–66median 65 · +0 vs GT650.1 unmatched0.4%39 of 9640 chars
Gemini 3.1N19median 19 · +1 vs GT181 unmatched10.2%262 of 2570 chars34median 34 · -3 vs GT295 unmatched11.3%460 of 4084 chars10median 10 · +0 vs GT100 unmatched12.3%200 of 1620 chars63median 63 · -2 vs GT576 unmatched11.1%922 of 8274 chars
A19median 19 · +1 vs GT181 unmatched7.4%191 of 2570 chars36median 36 · -1 vs GT324 unmatched15.5%736 of 4747 chars10median 10 · +0 vs GT9.60.4 unmatched13.2%206 of 1560 chars65median 65 · +0 vs GT59.65.4 unmatched12.8%1133 of 8877 chars
B18–19median 18 · +0 vs GT180.2 unmatched0.8%20 of 2570 chars37median 37 · +0 vs GT370 unmatched0.4%20 of 5450 chars10median 10 · +0 vs GT100 unmatched0.0%0 of 1620 chars65–66median 65 · +0 vs GT650.2 unmatched0.4%40 of 9640 chars
Claude ThinkingN17–19median 18 · +0 vs GT14.73.3 unmatched16.0%328 of 2053 chars30–35median 34 · -3 vs GT2310.4 unmatched21.2%754 of 3561 chars10median 10 · +0 vs GT8.51.5 unmatched19.5%268 of 1376 chars58–63median 62 · -3 vs GT46.215.2 unmatched19.3%1350 of 6990 chars
A18–19median 18 · +0 vs GT15.13 unmatched16.1%343 of 2131 chars34–36median 35 · -2 vs GT25.98.8 unmatched20.8%830 of 4000 chars10median 10 · +0 vs GT8.31.7 unmatched19.2%259 of 1351 chars62–64median 63 · -2 vs GT49.313.5 unmatched19.1%1432 of 7482 chars
B18median 18 · +0 vs GT17.80.2 unmatched2.5%64 of 2528 chars37median 37 · +0 vs GT370 unmatched0.3%14 of 5450 chars10median 10 · +0 vs GT100 unmatched0.1%2 of 1620 chars65median 65 · +0 vs GT64.80.2 unmatched0.8%80 of 9598 chars

rows returned is the raw count of per-traveler rows the model produced, before any scoring — range across the 10 runs, with the median and its distance from the ground-truth count. matched is the mean number of those rows that paired with a real GT traveler, and unmatched the remainder. name CER is the character error rate of the traveler names on the matched rows, micro-averaged as total edits over total reference length so it composes across pages. Detection and transcription are separate questions, reported separately: a model can return the right number of rows, match all of them, and still have misread the names.

6. How often the right value lands in the wrong field

Counted over all rows a model produced, matched or not, and over the scorer's own pairing (evaluate.match_rows) rather than a separate one. also correct in its own field = the model filled that field correctly as well, so it duplicated the value rather than misfiling it. row lost = the name match failed, costing a false positive and a missed traveler.

Read row lost as a residue, not as the cost of conflation: §1 already removed the added title or origin before the match was attempted, so these are rows that failed on the reading of the name even after the reduction. The column stays because it is the honest denominator — a matched-only count cannot see the rows that never made it.

ModelCondRowsTitle in nameOrigin in namevalue also correct in its own fieldrow still matchedrow lost
ClaudeN62962 (9.9%)0 (0.0%)49 of 624616
ClaudeA63223 (3.6%)0 (0.0%)11 of 23221
GPTN60761 (10.0%)60 (9.9%)104 of 12110021
GPTA67534 (5.0%)15 (2.2%)44 of 493811
GPTB65010 (1.5%)0 (0.0%)5 of 10100
Gemini 3.1N6304 (0.6%)0 (0.0%)0 of 440
Gemini 3.1A6506 (0.9%)0 (0.0%)0 of 660
Gemini 3.1B6529 (1.4%)0 (0.0%)9 of 990
Claude ThinkingN61421 (3.4%)0 (0.0%)13 of 21183
Claude ThinkingA62812 (1.9%)0 (0.0%)2 of 1293
Claude ThinkingB6501 (0.2%)0 (0.0%)0 of 110

The title in name and origin in name counts are still worth reading on their own: they measure how often a model puts a value somewhere the schema did not ask for, which is a question about instruction-following rather than about transcription, and the §1 rules deliberately make it cost nothing.


Generated by build_scoring_doc.py from the phase N/A/B checkpoints. See eval_guidelines.html for the full scoring rules.