click anywhere, or press Esc, to close

OverviewOrientation

What drives desertion in a volunteer army: the question, the design, the evidence, how it is checked, and where it stands. · source: reference/OVERVIEW.md

In short

What drives desertion in a volunteer army? A conscript army can explain desertion as the exit of men who never chose to serve. The Czechoslovak Legions cannot: they were raised from volunteers, most of them former Habsburg soldiers, who joined a foreign army to fight the state they were born in. Of the 132,993 legionnaires in the record, 3,717 later deserted the Legions as well.

Every number on this page is computed when the page is built, from data/final as of 2026-10-29 19:01; none is typed. To correct one, fix its source and rebuild.

At a glance ⇱ Interactive ↕ Hover for values

Month of desertion or enrolment, where the record dates it to the month or the day. Men in service is the spell sample the hazard model will use — enrolment and exit month both known, 73.7 % of all records — and the rate is its deserters over the men at risk that month; months with fewer than 200 at risk are not drawn.

By 1910 political okres; the switch sets the denominator of the Matice, theatre and legionnaire maps — all 1910 inhabitants, or the Czech speakers among them — while desertion is per legionnaire either way. Provisional: built in data/interim/intensity/ and not yet joined to the persons (D-163). Grey is an okres without a value. The court and settlement views and the correlations are on Descriptives.

Page 256 of the Czech register

Podrobný seznam míst, IX. Čechy, printed page 256. The whole page, both editions and the symbol plate are on Digitising the 1910 register.

One of 300 complete records drawn at a fixed seed, half of them deserters. No name and no place below the district; dates to the month. The rank change is on the common scale of the rank ladders.

The design: one man, two desertions

Every legionnaire had already deserted once, from the Austro-Hungarian army, before he joined the Legions. A later desertion from the Legions (exit code D) shows the same man making two loyalty decisions in sequence, under different incentives: the first against a multinational empire that had conscripted him, the second against a national army he had volunteered for. Costa & Kahn (2003), the template for this study, observe one decision per Civil War soldier. Here the first decision is on record for every man, so the second can be read as revealed preference against it.

The record describes both sides of that choice. For 100,531 men it names the Habsburg unit he left, and for 91,561 it gives his rank in both armies, so the change in standing between the two decisions — the rank reset on entering the Legion — is measured man by man.

The headline fact

Desertion from the Legions was concentrated in Russia. Of the 87,734 legionnaires who served there, 4.1 % deserted, against 0.23 % in Italy and 0.05 % in France; Russia accounts for 97.0 % of all deserters in the record. The timing is as sharp: 86.1 % of the Russian desertions dated to the month or day fall after the Bolshevik coup of 7 November 1917. Whether that is the coup itself, the Legion's position in Russia after it, or the men who served there is the question the hazard model has to separate.

Russian theatre: enrolments and desertions by month
Figure 1 — Russian theatre, by month: enrolments (left axis) and desertions (right axis), with the February revolution, Zborov, the Bolshevik coup and the armistice marked. From notebooks/03_visualisation.ipynb.
Russia4.1 % · 3,607 of 87,734Italy0.23 % · 66 of 28,620France0.05 % · 5 of 10,627Unknown0.56 % · 26 of 4,666Serbia/Balkans1.2 % · 11 of 886Britain/Dominions0.46 % · 2 of 433Post-war (ČSA)0.00 % · 0 of 27
Figure 2 — Share of legionnaires who deserted, by theatre: rate, deserters and legionnaires. Computed from data/final when this page is built; the small Serbian and British formations rest on a handful of cases.

The data

  • 132,993 legionnaire service records, scraped from the Legion register (csol.cz): date and place of birth, schooling, occupation, religion, political affiliation, the Habsburg unit and rank at capture, the Legion unit and rank at enrolment, and the date and manner of leaving.
  • 186,192 fallen Habsburg soldiers from the Military Historical Archive (VÚA), the comparison population. 1,340 individuals appear in both files, each pair confirmed by an exact date of birth (1,201) or by an archive note naming the Legion (139); weaker coincidences of name and year are dropped, not down-weighted.
  • Birthplaces in 1910 districts. 84.6 % of legionnaires are placed in a 1910 district — 158 political okresy of the Czech lands or 192 districts and towns of Upper Hungary — through settlement crosswalks of 18,916 Czech and 3,511 Slovak places, so each man carries his home district's 1910 census.
  • Occupations in HISCO. The Czech job titles are coded to the historical international classification and its social classes (HISCLASS) through a Czech dictionary written for this project; 98.5 % of recorded occupations are coded.
  • The 1910 place register. The census's printed register of every settlement gives its population, language and religion, and in symbols under each table the institutions it had. It is being read by machine against a hand-made gold transcription. The certified reading is running; its tables enter data/final when it finishes, and nothing downstream uses the register before then.
  • Czech nationalism before the war, place by place. Two measures of the treatment: amateur theatre, from the national database of Czech companies (7,368 places, 1780–1913), and the member lists of the two Czech national book societies, the Matice česká and the Matice moravská (11,128 entries read from the scans). Both are built and mapped; neither is yet joined to the persons.

How it is checked

  • Against the reference work. Fidler (2025), the Encyklopedie československých legií, gives every Legion regiment's theatre. The theatre assigned here agrees on 99.6 % of the 71,854 Czechoslovak-Legion records it can check. It also caught two defects, both fixed: generals read as majors, and remarks about a rank scored as that rank.
  • Against other coders. OccCANINE, a neural coder trained on historical occupations and given the same German glosses, agrees on the social class of 89.1 % of coded records and on the HISCO major group of 96.8 %. The Czech statistical office's CZ-ISCO, which pairs a Czech and an English label under one code, agrees with the Czech reading on 99.1 % of the records it can compare.
  • Against the census. The commonest occupation, rolník, is coded as an own-account farmer rather than a hired hand. Across 100 okresy, the legionnaires' own-account share within agriculture moves with the census share of Selbständige (Spearman +0.40).
  • Against an outside transcription. Ostafin et al. (2020) typed every Silesian commune's 1910 census from the same printed volume. The census used here is identical in 12 of 12 political districts, and the machine reading of the register equals their population figure on 98.6 % of 441 communes.
  • On every build. 126 of 127 invariants pass on the current data. Each corresponds to a bug that was once real, and notebook 02 runs them as its last cell, so a build that breaks the data fails.
  • In writing. 188 judgement calls in a dated decision log, each with its status and a link to the argument, and 3 full code-and-logic audits with their findings and fixes.

Where it stands

Data construction is done and checked; the analysis has not begun. The next step is the person-period spell file: one row per legionnaire per period from enrolment to exit, the input the estimation needs. On it comes a discrete-time competing-risks hazard model following Costa & Kahn (2003), in which desertion competes with death, discharge and demobilisation. A static logit would condition on survival it does not model, and with a single spell per soldier individual fixed effects are not available. The first regressor of interest is the rank reset. Its main threat is that enrolment officers demoted men who had already shown signs of disloyalty; late-enrolment restrictions and balance tests are planned against it.

Two pieces remain open. The Habsburg order of battle of 1914, which ties a soldier's regiment to its recruiting district and its ethnic make-up and so carries the Costa–Kahn mechanism back to the first desertion, is built but not yet joined to the persons. The education ladder is checked against the Austrian school statistics, which cannot reach the Hungarian half of the monarchy. The Roadmap lists every open item.

Reading further

Three routes through the sheets, for the three questions readers arrive with. Click any box.

START HEREI want the numberswhat the data saysDescriptivesVariablesData dictionaryI want the methodhow it was builtPipelinePlace matchingRank laddersUnits & armiesI want to trust itwhy it holdsData QAExternal validation — units & ranksDecisions

Every sheet

SheetGroupContentsSource
OverviewOrientationWhat drives desertion in a volunteer army: the question, the design, the evidence, how it is checked, and where it stands.reference/OVERVIEW.md
DescriptivesOrientationLive figures and tables computed from data/final.generated from data/final
Data QAOrientationInvariant checks on the current build.generated from data/final
Roadmap / to-doOrientationWhat's done and what remains.reference/ROADMAP.md
What's newOrientationSite updates + the live-page link.reference/SITE_CHANGELOG.md
PipelineHow it worksWhat every script does.reference/PIPELINE.md
Place matchingHow it worksBirthplace → 1910 census district.reference/PLACE_MATCHING.md
Digitising the 1910 registerHow it worksOne page of the printed census register, annotated — and why the pictures under the table are the hard part.reference/LEXIKON_1910.md
1910 register — the reading routeHow it worksHow the register is read today, step by step and why; what was tried and dropped; the gold's results; certification; notebook 15.reference/LEXIKON_1910_ROUTE.md
1910 register — the municipal dataHow it worksThe register as analysis tables in data/final/register: every place linked to settlements_1910, the German volume through the Czech one, every column.reference/REGISTER_VARIABLES.md
Amateur-theatre measureHow it worksCzech amateur theatre per place and year, 1780–1913: sources, rulings, certain vs possible, where it stands.reference/THEATRE_MEASURE.md
Matice measureHow it worksMembers of the Matice česká and Matice moravská, read from the scans, placed in 1910 settlements and summed per okres and court; with an interactive map.reference/MATICE_MEASURE.md
Occupation → HISCOHow it worksCzech occupation string → HISCO → social class; §9 summarises the three checks.reference/OCCUPATION_CODING.md
Education ladderHow it worksThe 1869 Austrian school ladder — and why the apprentices' continuation school is not a rung on it.reference/EDUCATION_LADDER.md
Rank laddersHow it worksThe two rank systems and the reset.reference/ranks/rank_ladders.md
Rank recognition 1918–38How it worksLegal memo behind the common scale.reference/ranks/rank_recognition_1918_1938.md
Units & armiesHow it worksregiment vs unit_key, and which units Fidler confirms.generated from data/final
Habsburg army & recruitmentHow it worksErgänzung system: how a unit ties to a home district (+ regiment explorer).reference/units/ERGAENZUNG.md
Source provenanceHow it worksWhich source grounds which mechanism.generated from data/final
Architecture mapHow it worksWhich script imports which.generated from data/final
VariablesWhat's in the dataEvery constructed variable, with formulas.reference/VARIABLES.md
Data dictionaryWhat's in the dataEvery final column and its source.data/final/data_dictionary.csv
Census codebookWhat's in the dataEvery census covariate, EN ← DE/HU.reference/census/census_codebook.csv
Data sourcesWhat's in the dataProvenance and licences.reference/DATA_SOURCES.md
ReferencesWhat's in the dataLiterature cited, in APA — hover any citation to preview.generated from data/final
Symbol usageWhat's in the dataEvery public function and who calls it.generated from data/final
Party registerWhat's in the data55 parties of the Czech lands 1861–1918, with every merger and renaming (Akademický atlas, plate IV.37).reference/parties/atlas_iv37_parties.csv
Unit authorityWhat's in the dataLegion units → theatre, dates, lineage (Fidler 2025).reference/units/unit_authority.csv
Commits & versionsHistory & decisionsMachine record: commits, module versions, working-tree state.generated from data/final
DecisionsHistory & decisionsRegister of every judgement call, with a link to the argument.reference/DECISIONS.md
AuditsHistory & decisionsThree dated code+logic reviews (2026-10-24, 2026-09-22, 2026-07-16): findings, fixes, verification.reference/AUDIT_2026-10-24.md
Validation registerHistory & decisionsEvery check, and what each one is blind to.reference/VALIDATION.md
External validation — units & ranksHistory & decisionsChecked against Fidler 2025 — what agreed, what it caught.reference/VALIDATION_FIDLER.md
External validation — occupationsHistory & decisionsThe German bridge, OccCANINE and CZ-ISCO on the Czech→HISCO dictionary.reference/VALIDATION_OCCUPATIONS.md
External validation — educationHistory & decisionsThe Austrian school statistics of 1908/09 on the education ladder.reference/VALIDATION_EDUCATION.md

Running it yourself

python -m venv .venv && . .venv/Scripts/activate   # Windows
pip install -r requirements.txt

Then run the stage notebooks in order from notebooks/ (they resolve the project root as Path("..")):

  1. 01_data_collection.ipynb — scrapes both person databases into data/raw/ (long-running; resumable) and runs post-scrape QA.
  2. 02_data_cleaning.ipynb — the whole cleaning pipeline: person parsing (§1–§2), the 1910 geographic backbones + census panels + settlement crosswalks (§3), cross-dataset linkage + place standardisation (§4), wide analysis files (§4b), data dictionary (§5). Writes everything under data/final/.
  3. 03_visualisation.ipynb — descriptive figures and LaTeX tables from data/final/ only, into output/.

The person records are personal data and are not in the repository; processing rests on §90 zákon č. 121/2000 Sb. (the scientific-research exception) and GDPR Art. 89.

About this document

This page is generated from the repository by utils/docs/build_docs.py. The markdown files under reference/ are the canonical prose and every figure on the Descriptives and Data QA sheets is recomputed from data/final at build time — nothing here is transcribed by hand. To correct something, edit its source and rebuild; do not edit the HTML.

Chips sit at the end of a figure's own heading, one per affordance, and never anywhere else. ⇱ Interactive means the block has controls — click, toggle, drag or filter it. ↕ Hover for values means its marks carry tooltips; a block that does both shows both. The red dot in the sidebar is derived from the chips a sheet actually rendered, so the two cannot disagree. Long tables announce themselves with a visible search box instead. Everything else is a plain table or figure.

Module index

Every importable module, its version, and its purpose — read straight from the source files. Pipeline explains what each one does in context.

155 rows

moduleversionpurpose
utils/docs/build_docs.py—generate the single-file HTML documentation site
utils/docs/build_fonts.py1.0Subset the Libertinus faces the docs site embeds -> utils/docs/fonts/*.woff.
utils/lexikon_1910/bakeoff.py1.3.0The bake-off, as a module: the configuration and the run loop, in one place.
utils/lexikon_1910/build_contrast_sheet.py1.3.0The contrast sheet: the look-alike symbols the readers swap, side by side at printed size (D-99).
utils/lexikon_1910/build_de_page_map.py1.0.0Pair every Czech page of the 1910 register with the German pages that print it.
utils/lexikon_1910/build_glyph_bank.py—Cut the boxed glyphs out of the gold crops: the bank test A reads.
utils/lexikon_1910/build_lexikon_figure.py0.1.0Cut the docs-site figure out of the 1910 register — images plus derived geometry.
utils/lexikon_1910/build_mark_ledger.py1.7.0Fill the mark ledger (D-109) from what the r5 gold run left on disk, and report it.
utils/lexikon_1910/build_register_walkthrough.py1.0.0The docs site's Brzice walkthrough: from the printed line to the place authority (D-148, D-150).
utils/lexikon_1910/build_symbol_atlas.py—Cut the volume's own symbol legend into a labelled atlas of pictogram images.
utils/lexikon_1910/build_symbol_atlas_de.py1.2.0Cut the German edition's own symbol legend into a labelled atlas (D-92).
utils/lexikon_1910/build_token_review.py—Queue the suspect tokens of the gold sheets for a person to decide, one by one.
utils/lexikon_1910/check_transcription.py—Validate a human transcription worksheet, and turn it into a reader.
utils/lexikon_1910/draw_gold_sample.py—Draw the pages a person will transcribe, reproducibly.
utils/lexikon_1910/extract_prose_pages.py—The prose of the statutory towns' pages, from the text layer.
utils/lexikon_1910/lexikon_1910.py0.5.0Podrobný seznam míst / Spezialortsrepertorium 1910 — grid, blocks, invariants.
utils/lexikon_1910/lexikon_1910_attributes.py0.1.0What the register prints *beside* two glyphs: a school's classes and language,
utils/lexikon_1910/lexikon_1910_census.py0.2.0Reconcile transcribed 1910 register blocks against the okres-level census.
utils/lexikon_1910/lexikon_1910_courts.py0.1.0The court each section of the register belongs to: one 1910 backbone court per heading (D-122).
utils/lexikon_1910/lexikon_1910_crosswalk.py1.0.0lexikon_1910_crosswalk — the German Böhmen volume's places paired with the Czech volume's (D-150)
utils/lexikon_1910/lexikon_1910_de.py0.1.0Spezialortsrepertorium Böhmen 1910 — the German twin of the Czech volume.
utils/lexikon_1910/lexikon_1910_de_notes.py0.5.0The German edition's apparatus, read from the image alone (D-92).
utils/lexikon_1910/lexikon_1910_de_table.py0.1.0The German volumes' table, cut into blocks from the image and read by a model (D-160).
utils/lexikon_1910/lexikon_1910_editions.py0.2.0The two editions of the 1910 register as two measurements of one truth (D-58, D-92).
utils/lexikon_1910/lexikon_1910_image.py0.3.0Page geometry of the 1910 register from the rendered page alone — no text layer.
utils/lexikon_1910/lexikon_1910_institutions.py0.15.0Decode the ``Úřady, ústavy`` footnote into an institution inventory.
utils/lexikon_1910/lexikon_1910_ledger.py1.2.0The history of every mark of the 1910 register through the reading route (D-109).
utils/lexikon_1910/lexikon_1910_names.py0.6.0Anchor 1910 register blocks to known municipalities, and let the volume name itself.
utils/lexikon_1910/lexikon_1910_notes.py0.11.0The footnote apparatus printed below the 1910 register table.
utils/lexikon_1910/lexikon_1910_pages.py0.1.0Read a whole page of the 1910 register with a cloud model, and price the run first.
utils/lexikon_1910/lexikon_1910_register.py1.7.0lexikon_1910_register — the 1910 register's frozen-run output as analysis-ready municipal data
utils/lexikon_1910/lexikon_1910_vision.py0.5.0Read the below-table apparatus from the *image*, and hand the result to the
utils/lexikon_1910/lexikon_1910_volume.py0.2.0One profile per volume of the 1910 register — what differs from book to book.
utils/lexikon_1910/locate_glyphs.py1.1.0E3, first half: a box for every printed glyph of the apparatus (D-69, D-72, D-93).
utils/lexikon_1910/make_gold_worksheet.py—Cut the gold-sample pages into a worksheet laid out the way the page reads.
utils/lexikon_1910/make_gold_worksheet_de.py1.1.0Build the gold worksheets for the German edition (Spezialortsrepertorium Böhmen).
utils/lexikon_1910/read_transcription_md.py1.1.0Read the whole-page gold sheets, and grade the transcriber while doing it.
utils/lexikon_1910/reader_batches.py1.5.0The readers, batched: queued requests in, cached replies out (D-108's prices).
utils/lexikon_1910/report_unlisted_abbreviations.py1.0.0The offices items no code in ``institution_abbreviations.csv`` reaches, on one run's readings (D-156).
utils/lexikon_1910/rescore_passes.py1.4.0Re-score every cached symbol pass at the analysis unit. No model call, no spend.
utils/lexikon_1910/run_de_table.py0.2.0The German volumes' table read by a model, block by block, as the Czech table arm reads (D-160).
utils/lexikon_1910/run_e1_gemini_version.py1.2.1E1 — is the gap between the two cloud pipelines the model, or the view?
utils/lexikon_1910/run_e1b_second_reader.py1.3.0E1b — which cheap reader of another family errs somewhere else than Gemini?
utils/lexikon_1910/run_e3_pages.py0.1.0E3 on any page: the locator and DINOv3 on the readers' saved section crops (D-112).
utils/lexikon_1910/run_frozen.py0.21.0The frozen run: every stage of the reading route, for one edition and one page set (D-112, D-115).
utils/lexikon_1910/run_glyph_e3.py1.2.0E3, second half: name every located glyph by its nearest neighbours on DINOv3 (D-69, D-72, D-93).
utils/lexikon_1910/run_glyph_test_a.py—Test A — does an image encoder tell the fill pairs apart at all? (SYMBOL_ACCURACY_PLAN.md, D-69)
utils/lexikon_1910/run_lexikon_1910_pilot.py—Calibration run for the 1910 special place register — four channels, one grader.
utils/lexikon_1910/run_r5_german_notes.py1.4.0The German notes arm of ``r5``: the German edition's apparatus, read by the r5 readers (D-92).
utils/lexikon_1910/run_table_arm.py—The table arm of ``r5`` — the seventeen printed columns read by a model, not the text layer.
utils/lexikon_1910/run_verify_test_b.py1.12.0Test B — does one Opus verify call settle what Gemini and Haiku dispute? (D-72, D-89, D-98)
utils/lexikon_1910/score_certification.py1.6.0Score the sealed certification pages — once, against metrics written down first.
utils/lexikon_1910/score_r5_german_notes.py1.1.0Score the German notes arm of ``r5`` against the German gold, and set it beside the Czech (D-92).
utils/lexikon_1910/score_table_passes.py—Score the table readings against the gold tables, cell by cell.
utils/lexikon_1910/section_images.py1.1.0The readers' section crops, saved as images for every page (D-112, step 3 beyond the gold).
utils/linkage.py1.3.0Cross-dataset person linkage (legionnaires ↔ Habsburg fallen)
utils/parsing/affiliations.py2.3.0political affiliation, on the atlas's own genealogy
utils/parsing/amateur_theatre.py0.6.1is the amateur-theatre database usable as a pre-1914 covariate?
utils/parsing/amateur_theatre_model.py0.1.0The amateur-theatre model pass: the chronicle clauses the rules leave open, read by a model.
utils/parsing/census.py1.3.01910/1911 Cisleithanian census loader, joined to the district backbone
utils/parsing/census_hu.py1.1.01910 Transleithanian (Upper-Hungary) járás-level census panel
utils/parsing/census_labels.py1.3.0English column names + derived shares for the 1910 census panels
utils/parsing/clean.py1.0.1Generic raw-field cleaning
utils/parsing/dates.py1.2.0Date column cleaning
utils/parsing/districts.py1.2.01910 administrative-district backbone (okres + soudní okres), in Czech
utils/parsing/districts_hu.py1.0.01910 Transleithanian (Upper-Hungary) district backbone, Magyar + Slovak
utils/parsing/education.py2.1.0the Austrian school ladder, parsed rather than classified
utils/parsing/exit_codes.py1.2.0Exit code harmonisation for the 'Konec v legiích' field.
utils/parsing/geonames.py1.0.0named places from the ČÚZK Geonames database (Databáze geografických jmen ČR)
utils/parsing/habsburg.py1.2.0VÚA fallen-soldier field parsing
utils/parsing/lexikon.py1.7.0settlement crosswalk from the Historický lexikon obcí ČR 1869–2011
utils/parsing/lexikon_sk.py1.1.0Slovak settlement crosswalk + 1910 ethnicity from the Majo (2012) lexikón
utils/parsing/locations.py1.1.0Place-name decomposition (municipality / okres / land)
utils/parsing/locative.py0.4.0Czech place names printed in the locative (*v Brně*, *ve Vodňanech*, *v Hradci Králové*)
utils/parsing/matice.py0.8.0Matice česká and Matice moravská member registers — OCR to one row per printed entry.
utils/parsing/occupations.py1.4.0HISCO / HISCLASS coding of the free-text ``Zaměstnání`` field
utils/parsing/prng.py1.0.0Polish localities from the PRNG (Państwowy Rejestr Nazw Geograficznych), Těšín Silesia
utils/parsing/provenance.py1.0.0what source csol.cz cites for a legionnaire record
utils/parsing/ranks.py1.4.3Austro-Hungarian (Habsburg) **and** Czechoslovak Legion rank standardisation
utils/parsing/records_geo.py1.9.0attach the 1910 political-okres id to person records
utils/parsing/records_geo_hu.py1.4.0attach the 1910 Transleithanian járás/county to Slovak-born records
utils/parsing/religion.py1.0.0English gloss for the ``religion`` value (legionnaire dataset)
utils/parsing/ruian.py1.2.0today's official geography (ČÚZK RÚIAN) and the 1910 settlement crosswalk onto it
utils/parsing/schema.py1.13.0Canonical English schema for the two final datasets
utils/parsing/spatial.py1.1.0point-in-polygon court-district assignment from the MPIDR 1910 polygons
utils/parsing/theater.py1.1.0Theatre-of-service classifier (legionnaire dataset)
utils/parsing/units.py1.3.0Regiment / unit name parsing
utils/parsing/vanished.py1.0.0vanished and abandoned settlements of the Czech lands, from Wikidata
utils/parsing/vua_courts.py0.1.0the VÚA inventories of three Habsburg military courts, as tables
utils/parsing/wikidata.py1.4.0coordinates + QIDs for Czech settlements (enriches the lexikon crosswalk)
utils/plotting/th_style_v113.py1.2.0th_style — Academic Matplotlib Style
utils/reference_tools/background/prep_slices.py—Prepare reading slices for the qualitative pass (scratch; not part of the pipeline).
utils/reference_tools/background/verify_locators.py—Check every ledger entry's locator against its slice (scratch tool for the reading pass).
utils/reference_tools/education/build_page_index.py1.0.0Build the page index for Österreichische Statistik XCIII/1 (Unterrichtsanstalten 1908/09).
utils/reference_tools/education/check_unterricht_transcription.py1.0.0Invariants on the transcription of Österreichische Statistik XCIII/1 (1908/09).
utils/reference_tools/education/draw_education_sample.py1.0.0Draw the `Vzdělání` strings a person will code by hand, reproducibly.
utils/reference_tools/education/run_unterricht_check.py1.0.0Is the education ladder the one the Austrian state actually operated?
utils/reference_tools/education/score_education_gold.py1.0.0Score a hand-coded education worksheet against `education.py`.
utils/reference_tools/occupations/build_conflict_workbook.py2.1.1every unresolved occupation coding, as one adjudicable table
utils/reference_tools/occupations/build_czisco_bridge.py1.0.0generate reference/occupations/czisco_authority.csv
utils/reference_tools/occupations/build_hisco_authority.py1.1.0generate reference/occupations/hisco_authority.csv
utils/reference_tools/occupations/build_occupation_curation.py1.0.0generate/refresh reference/occupations/occupation_hisco_curation.csv
utils/reference_tools/occupations/czech_glosses.py1.2.0Czech occupational key -> German / English gloss
utils/reference_tools/occupations/download_hisco_sources.py1.0.0fetch the HISCO authority tables into data/raw/hisco/
utils/reference_tools/occupations/run_census_berufsstellung_check.py1.0.0is a `rolník` a farm *owner* or a farm *worker*?
utils/reference_tools/occupations/run_czisco_check.py1.0.0CZ-ISCO as a third coder, on the Czech side
utils/reference_tools/occupations/run_link_bridge.py1.2.0resolve the Czech glosses to HISCO codes via the German link table
utils/reference_tools/occupations/run_mosaic_beruf_check.py1.0.0does the German word for a farmer mean an *owner*, one record at a time?
utils/reference_tools/occupations/run_occcanine_check.py1.1.0independent second coder for the Czech occupation dictionary
utils/reference_tools/parties/check_atlas_parties.py1.0.0invariants on the transcribed IV.37 party register
utils/reference_tools/places/audit_full_run_pages.py0.1.0Every German full-run page, cut offline before anything is read: does the route's cut hold?
utils/reference_tools/places/build_curation.py—generate reference/places/district_cs_curation.csv (FIRST PASS)
utils/reference_tools/places/build_german_click_queue.py0.1.0A Token Review queue for clicking every pictogram of the German gold pages.
utils/reference_tools/places/build_german_click_strips.py0.1.0Cut the German gold's office sections into strips of a few printed lines, for the click page.
utils/reference_tools/places/build_matice_read_sheets.py0.1.0Sheets of scanned lines for re-reading the Matice residences the OCR garbled.
utils/reference_tools/places/build_sk_curation.py—generate reference/places/district_sk_curation.csv
utils/reference_tools/places/build_theatre_gold_page.py—The ruling page for the theatre model pass's gold (D-157).
utils/reference_tools/places/german_candidate_diagnosis.py0.1.0Which candidate-step filter removes the German glyphs the author clicked?
utils/reference_tools/places/german_click_recall.py0.1.0How well does the locator's box land on the glyph the author clicked? (German gold, offices.)
utils/reference_tools/places/german_locator_trial.py0.1.0Does a locator trained on the German gold place German claims better than the Czech one?
utils/reference_tools/places/german_supervised_placement.py0.1.0Does a glyph classifier trained on the author's clicks place German claims on the right glyph?
utils/reference_tools/places/map_intensities.py0.2.0The intensities of ``run_intensities.py`` side by side, one figure per level.
utils/reference_tools/places/map_matice.py0.3.0Maps of the Matice measure at three levels: political okres, court district, municipality.
utils/reference_tools/places/merge_matice_readings.py—Merge the residence readings made from ``build_matice_read_sheets.py``'s sheets into the
utils/reference_tools/places/run_amateur_theatre_pilot.py—Feasibility pilot — the amateur-theatre database as a pre-1914 covariate.
utils/reference_tools/places/run_german_detect_by_naming.py0.1.0Detect by naming: let the bank find the German glyphs, instead of a locator guessing where they are.
utils/reference_tools/places/run_german_oracle.py0.1.0The oracle test: does the Czech-built bank name German glyphs when the box is certainly right?
utils/reference_tools/places/run_german_reader_boxes.py0.2.0The reader gives the box: can DINOv3 check German claims on the boxes Gemini itself returns?
utils/reference_tools/places/run_intensities.py0.6.0Four intensities side by side, per 1,000, at three levels: political okres, court
utils/reference_tools/places/run_matice_pilot.py—Pilot — the Matice česká and Matice moravská member registers, read into tables and
utils/reference_tools/places/score_sealed_abbreviations.py1.0.0Score the text abbreviations on the sealed certification pages — a second metric, fixed before it was run.
utils/reference_tools/places/statutory_institutions.py0.5.0The statutory cities' institutions, from their verified prose: stage 2 of D-178.
utils/reference_tools/places/statutory_prose.py0.3.0The statutory cities' prose, transcribed precisely: two vision readers and the text layer.
utils/reference_tools/units/build_unit_authority.py—generate reference/units/unit_authority.csv
utils/reference_tools/units/build_unit_timeline.py1.0.0generate reference/units/unit_timeline.csv
utils/reference_tools/units/check_unit_timeline.py1.0.0invariants on reference/units/unit_timeline.csv
utils/reference_tools/units/run_vua_courts_pilot.py—Pilot — the VÚA military-court inventories as a Habsburg-side record of disloyalty.
utils/scraping/fetch_vua_courts.py0.1.0the three VÚA military-court inventories, fetched once
utils/scraping/progress.py1.0.0Progress-bar helper shared by the Kramerius downloaders.
utils/scraping/run_lexicon_downloads.py1.0.0Download the two outstanding 1910 *Spezialortsrepertorium* volumes into
utils/scraping/run_matice_downloads.py1.2.0Download the digitised member lists of two Czech national societies, the *Matice
utils/scraping/run_oeulk_downloads.py1.0.0Download every volume of *Österreich-Ungarns letzter Krieg 1914–1918* from the
utils/scraping/scraper_amaterskedivadlo.py0.2.2amaterskedivadlo.cz scraper — importable module + CLI. **Feasibility pilot.**
utils/scraping/scraper_csol.py—csol.cz legionnaire scraper — importable module + CLI.
utils/scraping/scraper_kramerius.py1.4.0Kramerius v5 downloader — importable module + CLI.
utils/scraping/scraper_kramerius7.py1.3.0Kramerius v7 downloader — importable module + CLI.
utils/scraping/scraper_verlustlisten.py0.1.0Verlustlisten Österreich-Ungarns 1914–1919 (DES index) scraper — importable module + CLI.
utils/scraping/scraper_vuapraha.py—vuapraha.cz scraper — importable module + CLI.
utils/tables/latex_tables.py1.2.0LaTeX table generation utilities.
utils/validation/build_validation_workbook.py—human-validation sample for place matching + rank parsing
utils/validation/check_ostafin_silesia.py0.1.0Austrian Silesia 1910 against Ostafin et al. (2020): an independent transcription of our source.
utils/validation/data_checks.py1.16.1the invariants ``data/final`` must satisfy
utils/validation/lexikon_population.py0.1.0The 1910 population of the ČSÚ *Historický lexikon obcí ČR 1869–2005*, díl I, and its notes.
utils/validation/run_completeness.py0.1.0Whether a frozen run has every reply it asked for, before anything is built on it.
utils/validation/score_sealed_retest.py0.1.0The full run's readings of the sealed certification pages, scored — a test–retest (D-181).

DescriptivesOrientation

Live figures and tables computed from data/final.

Datasets

FileUnitRowsColumns
persons/legionnaires_cleanlegionnaire132,993218
persons/habsburg_cleanfallen Habsburg soldier186,192102
census/census_okres_19101910 political okres158509
census/census_okres_1910_pooled1910 okres, city/rural twins pooled148510
census/census_hu_jaras_19101910 járás / town19290
geo/districts_1910court district33911
geo/districts_hu_1910járás / town19212
geo/settlements_1910Czech settlement18,91618
geo/settlements_hu_1910Slovak obec3,51121
persons/overlap_matchesconfirmed overlap1,34015

Analysis sample — legionnaires

Desertion is the outcome, so non-deserters are the controls, not losses — the cuts below are about covariate coverage. Bars are cumulative; the deserter count on each confirms the signal survives.

All legionnaires
132,993 100.0%3,717 deserters · 2.8%
the full scraped population
Theatre resolved
128,327 96.5%3,691 deserters · 2.9%
theatre ≠ Unknown −4,666
Unit label present
119,491 89.8%3,596 deserters · 3.0%
an enrolment unit is recorded −8,836
Unit not contradicted by Fidler
119,225 89.6%3,596 deserters · 3.0%
drops the 266 records whose regiment never existed in their theatre −266
Birthplace linked (okres/járás)
103,601 77.9%2,306 deserters · 2.2%
a 1910 census district, so covariates can be merged −15,624
Rank reset resolved
83,943 63.1%1,848 deserters · 2.2%
both ranks parsed — the headline regressor is defined −19,658
Habsburg unit at capture recorded
80,180 60.3%1,779 deserters · 2.2%
the pre-capture Habsburg unit string is present −3,763
Habsburg unit matched to Tabelle I
66,441 50.0%1,605 deserters · 2.4%
that unit resolves to a real 1914 regiment → its home district & nationality −13,739

Borderline: requiring the unit positively verified in Fidler (not merely un-contradicted) drops the sample to 54,963 — but most of that loss is support/rear units that carry no regiment number to check, not wrong data.

Build your own sample ⇱ Interactive

Tick the requirements an estimation sample should impose. The count updates instantly and breaks down by theatre; each unticked box shows what it would cost to add. The link and pandas snippet reproduce the sample you build.

Reproduce in a notebook

Analysis sample — Habsburg comparison population

The ~186k fallen Habsburg soldiers are the control group: men who did not cross into the Legions. The 1,340 who are legion-linked (they appear in the csol.cz records too) revealed exactly the choice a control is meant to lack, so they are disqualified as controls — the valid comparison group is the unlinked majority. The highlighted count on each bar is the valid controls that survive that covariate; the first toggle enforces the exclusion.

All fallen soldiers
186,192 100.0%184,852 valid controls · 99.3%
the full scraped population — 1,340 are legion-linked and disqualified
Theatre resolved
158,838 85.3%157,897 valid controls · 99.4%
front ≠ Unknown −27,354
Birthplace linked (okres/járás)
124,501 66.9%123,745 valid controls · 99.4%
a 1910 census district, so covariates can be merged −34,337
Habsburg unit recorded
79,426 42.7%79,004 valid controls · 99.5%
the Austro-Hungarian unit string is present −45,075
Habsburg unit matched to Tabelle I
63,333 34.0%63,088 valid controls · 99.6%
that unit resolves to a real 1914 regiment → its Ergänzung home district & nationality −16,093
Cause of death known
60,638 32.6%60,398 valid controls · 99.6%
the competing-risk death category is defined −2,695

Build your own sample ⇱ Interactive

Tick the covariates a comparison sample should require. The first box drops the legion-linked (the disqualified crossovers); the rest are coverage requirements. The count and its theatre composition update instantly.

Reproduce in a notebook

Outcome — exit from the Legions

CodeMeaningn% of records
BKilled in action2,0461.5%
ČSAServed in post-1918 Czechoslovak army2,9872.2%
DDeserted3,7172.8%
DMDemobilised61,50546.2%
NMissing6450.5%
NSUnfit for service1,4481.1%
PExecuted930.1%
SDied2,0801.6%
VReturned home16,89612.7%
XSee note17,53013.2%
ZDied of wounds5240.4%
—No code recorded (censored)30,77023.1%

D (deserted) = 3,717 is the primary outcome. Records with no exit code carry no outcome information and must be treated as censored, not as non-desertion.

Desertion by theatre

TheatreLegionnairesDesertedDesertion rate
Russia87,7343,6074.11%
Italy28,620660.23%
France10,62750.05%
Unknown4,666260.56%
Serbia/Balkans886111.24%
Britain/Dominions43320.46%
Post-war (ČSA)2700.00%

Rank reset (headline regressor) ⇱ Interactive ↕ Hover for values

MeasureN resolvedDemotedUnchangedPromotedMean
Fine (ALT-1 common scale)91,56118.9%77.2%3.9%-0.45
Coarse (NATO OF/OR band)91,56118.7%77.4%3.9%-0.58
Rank-reset view:7,696 records carry a non-combat entry status (pracovník / občan); their reset differences a status against a rank and is shown only in the inclusive view.
reset -15: 3 (0.0%)-15reset -14: 3 (0.0%)-14reset -12: 13 (0.0%)-12reset -11: 133 (0.2%)reset -10: 467 (0.5%)-10reset -9: 15 (0.0%)reset -8: 339 (0.4%)-8reset -7: 33 (0.0%)reset -6: 536 (0.6%)-6reset -5: 89 (0.1%)reset -4: 529 (0.6%)-4reset -3: 2,419 (2.8%)-32kreset -2: 5,793 (6.8%)-26kreset -1: 4,927 (5.7%)-15kreset +0: 66,803 (78.0%)+067kreset +1: 1,373 (1.6%)+1reset +2: 1,197 (1.4%)+2reset +3: 463 (0.5%)+3reset +4: 225 (0.3%)+4reset +5: 46 (0.1%)reset +6: 68 (0.1%)+6reset +7: 63 (0.1%)reset +8: 63 (0.1%)+8reset +9: 59 (0.1%)reset +10: 29 (0.0%)+10reset +11: 8 (0.0%)reset +12: 1 (0.0%)+12reset +13: 1 (0.0%)+13← demoted on Legion entrypromoted →

85,698 records have both ranks resolved. 17.9% demoted · 78.0% unchanged · 4.2% promoted · mean -0.36. Bar height is √count so the tail stays visible; the flat majority at 0 is the reset the Legion did not impose. The asymmetry — a longer left tail — is the raw material for the relative-deprivation story (hover a bar for its exact count).

reset -16: 1 (0.0%)-16reset -15: 5 (0.0%)reset -14: 8 (0.0%)-14reset -12: 68 (0.1%)-12reset -11: 258 (0.3%)reset -10: 784 (0.9%)-10reset -9: 15 (0.0%)reset -8: 574 (0.6%)-8reset -7: 36 (0.0%)reset -6: 871 (1.0%)-6reset -5: 102 (0.1%)reset -4: 598 (0.7%)-4reset -3: 2,609 (2.8%)-33kreset -2: 6,184 (6.8%)-26kreset -1: 5,205 (5.7%)-15kreset +0: 70,647 (77.2%)+071kreset +1: 1,373 (1.5%)+1reset +2: 1,197 (1.3%)+2reset +3: 463 (0.5%)+3reset +4: 225 (0.2%)+4reset +5: 46 (0.1%)reset +6: 68 (0.1%)+6reset +7: 63 (0.1%)reset +8: 63 (0.1%)+8reset +9: 59 (0.1%)reset +10: 29 (0.0%)+10reset +11: 8 (0.0%)reset +12: 1 (0.0%)+12reset +13: 1 (0.0%)+13← demoted on Legion entrypromoted →

91,561 records have both ranks resolved. 18.9% demoted · 77.2% unchanged · 3.9% promoted · mean -0.45. Bar height is √count so the tail stays visible; the flat majority at 0 is the reset the Legion did not impose. The asymmetry — a longer left tail — is the raw material for the relative-deprivation story (hover a bar for its exact count).

Rank reset by theatre ⇱ Interactive ↕ Hover for values

The same reset, split by theatre and drawn as overlapping areas (share of each theatre's resolved resets; height is √share so the tails read). The shapes are not alike: Russia carries a visibly heavier demotion shoulder than Italy, whose mass sits almost entirely at 0. Click a theatre to toggle it.

-15-14-12-10-8-6-4-2+0+2+4+6+8+10+12+13← demotedpromoted →

Russia −0.62 mean reset (23% demoted) vs Italy −0.03 (7%) vs France −0.23 (16%). The theatre that did the most rank-resetting is also the one that saw essentially all the desertion — which is exactly why the reset has to be read against theatre, not on its own.

The same reset, split by theatre and drawn as overlapping areas (share of each theatre's resolved resets; height is √share so the tails read). The shapes are not alike: Russia carries a visibly heavier demotion shoulder than Italy, whose mass sits almost entirely at 0. Click a theatre to toggle it.

-16-14-12-10-8-6-4-2+0+2+4+6+8+10+12+13← demotedpromoted →

Russia −0.62 mean reset (23% demoted) vs Italy −0.03 (7%) vs France −0.23 (16%). The theatre that did the most rank-resetting is also the one that saw essentially all the desertion — which is exactly why the reset has to be read against theatre, not on its own.

Nationalism, the Legion and desertion, side by side

The two nationalism measures, the Legion and desertion from it, each as a rate per 1,000 on the same 1910 units, side by side. Provisional (D-163): each definition is the simplest one that can be drawn, and moves when its measure is ruled on.

In plain terms

Four maps of the Czech lands, one per measure, at three levels: the political okres, the court district and the settlement. A switch sets the denominator of the first three: all 1910 inhabitants, or the Czech speakers among them. The question they put is whether the places that paid for Czech nationalism before 1914 are the places that sent men to the Legion, and whether those men stayed.

The four measures

Panel Numerator Denominator
Matice the land's own society: Matice česká joiners of 1895–1909 (Bohemia; D-184, D-188), Matice moravská members of 1 March 1909 (Moravia, Silesia; D-182); shrunk, taken as ln(1 + members per 100,000) and standardised within each society's territory, as Squicciarini & Voigtländer (2015) build Encyclopédie subscriber density (matice_own_z, per Czech speaker matice_own_z_czech; D-185). The tables carry prague, the dummy every regression on the measure includes, and the robustness forms matice_own_z_level and matice_own_any 1910 inhabitants, or Czech speakers
Amateur theatre Czech companies active in a year (certain), averaged over 1880–1913 1910 inhabitants, or Czech speakers
Legionnaires legionnaires born in the unit 1910 inhabitants, or Czech speakers
Desertion legionnaires leaving by desertion (exit code D); shrunk (Marshall 1991) legionnaires born in the unit; left out under 20

Czech speakers are the 1910 census's Umgangssprache; a rate per Czech speaker is left out where fewer than 1,000 speak Czech, so the districts with almost no Czech speakers are grey under that switch. The per-Czech Matice rate is shrunk and standardised over those remaining units only. The two denominators answer different questions: per inhabitant, how Czech-national a place was; per Czech speaker, how active its Czechs were, which a few Czech speakers in a German district can make high.

The Matice and theatre sheets say how their counts are built. The okres is the census-pooled okres (148 units, D-18); the court is the census court (330, the nine statutory-city courts pooled with their rural twin).

What the levels can and cannot carry

  • Court. Only about 57 % of the legionnaires born in the Czech lands name a settlement or court; the rest are known to their okres. A court's legionnaires are scaled up by its okres's ratio of all births to located ones, which assumes the unlocated births fall among the okres's courts as the located ones do. The desertion rate is a ratio of located births and is not scaled.
  • Settlement. The repo has no 1910 population per settlement yet (the 1910 register's full run will carry it), so the place view shows counts, not rates. Desertion could be a rate there, since its denominator is the legionnaires themselves, but shrinkage gives every settlement the same rate: Marshall's between-place variance comes out at zero, so across settlements desertion varies no more than chance would make it. The place view draws deserters as a count instead.
  • Scale. Each panel has its own colour scale, clipped at its 97th percentile. The maps show where each measure is high, not how the measures compare in size.

Still open

  • Rulings. The theatre window and bound, the Matice rulings (see the Matice sheet), and the per-Czech-speaker denominator for legionnaires born in German-majority districts, where a few Czech speakers make a high rate.
  • Settlement population. It is needed for rates at settlement level.
  • Slovakia. The Matice and theatre measures cover the Czech lands only, so the maps stop there; Slovak births are left out.

Run it (offline, under a minute each):

python utils/reference_tools/places/run_intensities.py
python utils/reference_tools/places/map_intensities.py

The maps ⇱ Interactive ↕ Hover for values

Unlike the rest of this sheet these come from data/interim/intensity/ (run_intensities.py), and every definition is provisional (D-163). One map per measure, the same units in each. Hover a district or a place and it is outlined in every panel, with every number in the tip. The second switch sets the denominator of the Matice, theatre and legionnaire maps: all 1910 inhabitants, or the Czech speakers among them (Umgangssprache); desertion is per legionnaire either way. Each panel has its own colour scale, from zero (or, for the Matice z and desertion, the 3rd percentile) to the 97th percentile; grey is a unit with no value — under 1,000 Czech speakers for a rate per Czech speaker, or under 20 legionnaires for desertion. 101,274 legionnaires were born in the Czech lands; 57,994 of them name a court or a settlement, so the court view scales each court up by its okres's share of births known only to the okres. The settlements have no 1910 population yet, so the place view shows counts as dots (area ∝ count, an outlined dot is capped), deserters among them: shrunk, every settlement gets the same desertion rate, since across settlements it varies no more than chance would make it.

Figure 1 — Matice own land's society per inhabitant, z within territory
Figure 2 — Amateur theatre 1880–1913 companies active per 1,000 inhabitants
Figure 3 — Legionnaires per 1,000 inhabitants
Figure 4 — Desertion per 1,000 legionnaires, shrunk

How the maps move together

Each cell is the Spearman rank correlation ρ between two of the mapped rates, taken across districts: rank the districts by the row's rate and by the column's, and ρ says how far the two orders agree — +1 the same order, 0 no relation, −1 the reverse. Blue is positive, red negative, darker is stronger. The matrix is symmetric, so each pair appears twice, and the diagonal is 1. A cell uses only the districts that have both rates — a rate per Czech speaker is missing where fewer than 1,000 speak Czech, desertion where fewer than 20 legionnaires were born — so cells can rest on different sets of districts; hover a cell for its count. Matice is the within-territory z, the rest are the rates as drawn on the maps.

Across the 148 political okresy. Units with a value per Czech speaker: matice 123, theatre 123, legionnaires 123; with a desertion rate: 146.

per inhabitantper Czech speakerper legionnaire
MaticeTheatreLegionnairesMaticeTheatreLegionnairesDesertion
Matice per inhabitant+1.00+0.60+0.65+0.73+0.02-0.28+0.06
Theatre per inhabitant+0.60+1.00+0.79+0.03+0.64-0.18-0.22
Legionnaires per inhabitant+0.65+0.79+1.00+0.03+0.10+0.14-0.25
Matice per Czech speaker+0.73+0.03+0.03+1.00+0.04-0.01+0.20
Theatre per Czech speaker+0.02+0.64+0.10+0.04+1.00+0.17-0.24
Legionnaires per Czech speaker-0.28-0.18+0.14-0.01+0.17+1.00-0.24
Desertion per legionnaire+0.06-0.22-0.25+0.20-0.24-0.24+1.00

Across the 330 court districts. Units with a value per Czech speaker: matice 241, theatre 241, legionnaires 241; with a desertion rate: 247.

per inhabitantper Czech speakerper legionnaire
MaticeTheatreLegionnairesMaticeTheatreLegionnairesDesertion
Matice per inhabitant+1.00+0.51+0.51+0.91+0.23+0.06-0.01
Theatre per inhabitant+0.51+1.00+0.79+0.14+0.88+0.14-0.12
Legionnaires per inhabitant+0.51+0.79+1.00+0.09+0.34+0.57-0.20
Matice per Czech speaker+0.91+0.14+0.09+1.00+0.16+0.12+0.06
Theatre per Czech speaker+0.23+0.88+0.34+0.16+1.00+0.25-0.03
Legionnaires per Czech speaker+0.06+0.14+0.57+0.12+0.25+1.00-0.07
Desertion per legionnaire-0.01-0.12-0.20+0.06-0.03-0.07+1.00

Place matching — birth

DatasetRowsCzech okres_idSlovak jaras_idCourt districtCoordinates
Legionnaires132,993101,274 (76.1%)11,236 (8.4%)58,00058,074
Habsburg fallen186,192122,552 (65.8%)21,539 (11.6%)68,38168,450
Czech tierLegionnairesHabsburg fallen
okres93287112360
court46181462
fuzzy24701919
alias3616
settlement6111363
muni2525432
Slovak tierLegionnairesHabsburg fallen
jaras918218131
town8621856
megye62443
fuzzy897528
settlement2951024

Tiers are ordered strongest → weakest; muni and fuzzy are the soft ones a strict specification can drop. A record is placed in at most one 1910 frame.

Cross-dataset linkage

ConfidenceEvidencePairs
highExact full date of birth + matching name1201
mediumVÚA legion note + birth okres + year + name139

1,340 confirmed overlaps over 1,340 distinct VÚA records; 319 satisfy both criteria. The rule is precision-first: weaker name/year coincidences are dropped, not down-tiered.

Coverage of key variables

VariableColumnLegionnairesHabsburg fallen
Date of birthbirth_date96.3%93.0%
Year of birthbirth_year96.3%93.0%
Birth district (raw)birth_okres91.1%75.0%
Educationeducation79.0%—
Occupationoccupation89.4%—
Religionreligion29.7%—
Habsburg rank at capturehabsburg_rank74.2%—
Legion rank at enrolmentlegion_rank85.9%—
Enrolment dateenroll_date90.9%—
Exit dateexit_date79.3%—
Date of deathdeath_date3.3%95.8%
Cause of deathcause—95.4%

Figures

Generated by notebooks/03_visualisation.ipynb; PDF and beamer variants of each live in output/figures/.

Legionnaires

Affiliation russia
Figure 5 — Affiliation russia
output/figures/descriptives/affiliation/affiliation_russia.png
Desertion by affiliation
Figure 6 — Desertion by affiliation
output/figures/descriptives/affiliation/desertion_by_affiliation.png
Educ by theatre
Figure 7 — Educ by theatre
output/figures/descriptives/composition/educ_by_theatre.png
Desertion by hisclass
Figure 8 — Desertion by hisclass
output/figures/descriptives/occupation/desertion_by_hisclass.png
Occupation hisclass composition
Figure 9 — Occupation hisclass composition
output/figures/descriptives/occupation/occupation_hisclass_composition.png
Rank reset hist
Figure 10 — Rank reset hist
output/figures/descriptives/rank/rank_reset_hist.png
Desertions by theatre
Figure 11 — Desertions by theatre
output/figures/descriptives/theater/desertions_by_theatre.png
Enrollment by theatre
Figure 12 — Enrollment by theatre
output/figures/descriptives/theater/enrollment_by_theatre.png
Russia enrol vs desert
Figure 13 — Russia enrol vs desert
output/figures/descriptives/theater/russia_enrol_vs_desert.png

Habsburg fallen

Cause breakdown
Figure 14 — Cause breakdown
output/figures/descriptives_habsburg/causes/cause_breakdown.png
Birth death year
Figure 15 — Birth death year
output/figures/descriptives_habsburg/composition/birth_death_year.png
Top birth okres
Figure 16 — Top birth okres
output/figures/descriptives_habsburg/composition/top_birth_okres.png
Deaths by theatre
Figure 17 — Deaths by theatre
output/figures/descriptives_habsburg/theater/deaths_by_theatre.png
Theatre composition
Figure 18 — Theatre composition
output/figures/descriptives_habsburg/theater/theatre_composition.png

figures

Intensity maps court
Figure 19 — Intensity maps court
output/figures/intensity/intensity_maps_court.png
Intensity maps municipal
Figure 20 — Intensity maps municipal
output/figures/intensity/intensity_maps_municipal.png
Intensity maps okres
Figure 21 — Intensity maps okres
output/figures/intensity/intensity_maps_okres.png
Matice map court
Figure 22 — Matice map court
output/figures/matice/matice_map_court.png
Matice map municipal
Figure 23 — Matice map municipal
output/figures/matice/matice_map_municipal.png
Matice map okres
Figure 24 — Matice map okres
output/figures/matice/matice_map_okres.png

Data QAOrientation

Invariant checks on the current build.

Invariants 126/127 passing

Defined once in utils/validation/data_checks.py and evaluated against the current data/final every time this page is built. Notebook 02 calls the same list as its final cell, so a run that produced broken data fails rather than reporting success. Each check corresponds to a bug that was real at some point in this project — see the audits of 2026-07-16 and 2026-09-22.
FAIL is a hard invariant that should never be violated; WARN is a soft one (it can drift for external reasons) that reports but does not block.

CheckIf it fails
PASSLegionnaires: data/final carries every column the current code emits0 column(s) missing — data/final predates the code; re-run notebook 02: []
PASSHabsburg fallen: data/final carries every column the current code emits0 column(s) missing — data/final predates the code; re-run notebook 02: []
PASSLegionnaires · birth: placed in at most one 1910 frame0 record(s) hold both a Czech okres_id and a Slovak jaras_id
PASSLegionnaires · birth: no Czech attributes on Slovak-placed records0 record(s) leak cross-frame attributes
PASSLegionnaires · home: placed in at most one 1910 frame0 record(s) hold both a Czech okres_id and a Slovak jaras_id
PASSLegionnaires · home: no Czech attributes on Slovak-placed records0 record(s) leak cross-frame attributes
PASSHabsburg fallen · birth: placed in at most one 1910 frame0 record(s) hold both a Czech okres_id and a Slovak jaras_id
PASSHabsburg fallen · birth: no Czech attributes on Slovak-placed records0 record(s) leak cross-frame attributes
PASSHabsburg fallen · home: placed in at most one 1910 frame0 record(s) hold both a Czech okres_id and a Slovak jaras_id
PASSHabsburg fallen · home: no Czech attributes on Slovak-placed records0 record(s) leak cross-frame attributes
PASSLegionnaire primary key uniqueduplicate legion_id values
PASSHabsburg primary key uniqueduplicate vua_id values
PASSMissing values do not fabricate an exit codea missing/placeholder cell parses to a non-empty code set
PASSexit_N plausible (= 645, not ~27k)exit_N looks fabricated from empty cells — see audit A3
PASSProvenance parser self-test0 provenance behaviour(s) regressed: []
PASSsource_spis is set exactly where a source is recorded0 row(s) where the flag and record_source disagree
PASSsource_karta is set exactly where a source is recorded0 row(s) where the flag and record_source disagree
PASSsource_extra is set exactly where a source is recorded0 row(s) where the flag and record_source disagree
PASSsource_extra_ref present exactly where source_extra is True0 row(s) disagree
PASSNo personnel-document name left in the extra citation0 extra citation(s) still contain a document name
PASSAffiliation parser self-test0 affiliation behaviour(s) regressed: []
FAILEvery canonical in PARTY_ORDER has a dummy columnmissing: ['cns']
PASSpol_* dummies are <NA> exactly where pol_recorded is False0 flag/row disagreement(s) across 23 dummies
PASSpol_atlas_codes is <NA> on unrecorded rows0 unrecorded row(s) carry atlas codes
PASSeducation_ord within 0..5an education_ord falls outside the six-rung ladder
PASSeducation label and education_ord agree0 record(s) where the label is not EDUC_ORDER[education_ord]
PASSApprentice flag never leaves a record below Elementary0 record(s) flagged apprentice sit below rung 1
PASSA missing education fabricates no companion valuecolumns filled where `education` is missing: {}
PASSNo completion claimed for a vocational course of unknown length0 vocational record(s) carry a completion verdict
PASSeducation_top's own rung equals education_ord0 record(s) disagree; 0 name an unknown institution
PASSTIE_BREAK covers every institution exactly onceTIE_BREAK and INSTITUTIONS disagree — the tie-break is not total and ties fall back on string order
PASSeducation_top is named in education_institutions unless it is imputed0 row(s) where educ_top is absent and not flagged
PASSEducation parser self-test0 education behaviour(s) regressed: []
PASSMissing occupation does not fabricate a HISCO codea missing/empty cell codes to a real occupation
PASSOccupation matcher self-test0 matcher behaviour(s) regressed: []
PASSNo HISCO output on empty occupation cells0 column(s) non-null where occupation is <NA>: []
PASSoccupation coding coverage (98.5% of non-empty)only 117,140/118,949 coded — the curation has regressed
PASSEvery occupation HISCO code exists in the authority0 code(s) absent from hisco_authority.csv: []
PASSoccupation HISCLASS within 1..13a HISCLASS value falls outside the documented range
PASSoccupation HISCLASS_5 within 1..5a HISCLASS_5 value falls outside 1..5
PASSEvery coded occupation carries a HISCO major group0 record(s) hold a HISCO code but no major group
PASSoccupation: no social class on a HISCO sentinela HISCO sentinel carries a stratification scale: {}
PASSEvery occupation HISCO status code is realstatus value(s) absent from the HISCO code list: []
PASSEvery occupation HISCO relation code is realrelation value(s) absent from the HISCO code list: []
PASSNo HISCO output on empty occupation_interwar cells0 column(s) non-null where occupation_interwar is <NA>: []
PASSoccupation_interwar coding coverage (84.6% of non-empty)only 4,299/5,080 coded — the curation has regressed
PASSEvery occupation_interwar HISCO code exists in the authority0 code(s) absent from hisco_authority.csv: []
PASSoccupation_interwar HISCLASS within 1..13a HISCLASS value falls outside the documented range
PASSoccupation_interwar HISCLASS_5 within 1..5a HISCLASS_5 value falls outside 1..5
PASSoccupation_interwar: no social class on a HISCO sentinela HISCO sentinel carries a stratification scale: {}
PASSoccupation_hisclass_high is the better of the two0 row(s) where the convenience column disagrees
PASSoccupation_career_stage_ord is the map of the label0 row(s) where the ordinal disagrees with the stage
PASSEvery career stage is a documented rungstage value(s) absent from CAREER_STAGE_ORD: []
PASSNo qualifier on a dual match0 dual row(s) carry a qualifier — the second occupation is being read as a modifier
PASSQualifier is never the whole occupation string0 row(s) where the qualifier swallowed the head noun
PASSillegitimacy_rate builtderived rate missing from the census panel
PASSpop_total has no zero rowsan all-missing okres summed to 0 instead of NA
PASSCensus pooled to the twin unit (148 rows, 10 pooled)148 pooled rows vs 158 okresy — pairs not collapsed
PASSPooled panel has no missing language share0 pooled unit(s) still <NA> — the pair total was lost, not merged
PASSPooled shares recomputed from pooled counts0 pooled share(s) do not equal counts/total — averaged, not rebuilt
PASSLegionnaires · birth: twin role set exactly on the twin population0 record(s) disagree between birth_okres_id and birth_okres_twin
PASSHabsburg fallen · birth: twin role set exactly on the twin population0 record(s) disagree between birth_okres_id and birth_okres_twin
PASSLegionnaires · birth: Prague tag only on Prague-area okresy0 record(s) tagged birth_okres_prague sit outside okres Praha and its suburb destinations
PASSLegionnaires · birth: every 'suburb' record actually left okres Praha (1,468 moved)0 record(s) tagged 'suburb' are still in okres Praha
PASSLegionnaires · birth: okres name matches okres id0 record(s) carry a district label that is not their id's
PASSLegionnaires · birth: court snap flag present exactly where a court is0 record(s) have a court without a flag, or the reverse
PASSHabsburg fallen · birth: Prague tag only on Prague-area okresy0 record(s) tagged birth_okres_prague sit outside okres Praha and its suburb destinations
PASSHabsburg fallen · birth: every 'suburb' record actually left okres Praha (69 moved)0 record(s) tagged 'suburb' are still in okres Praha
PASSHabsburg fallen · birth: okres name matches okres id0 record(s) carry a district label that is not their id's
PASSHabsburg fallen · birth: court snap flag present exactly where a court is0 record(s) have a court without a flag, or the reverse
PASSChartered towns sit in their own unit, not a járás0 obec row(s) named after a chartered town of their own county are filed on a járás — suspect an unmatched section header
PASSObec mother-tongue counts reconcile to the total (3,511 rows)0 obec row(s) do not sum to their population — suspect the x-coordinate column read (lexikon_sk._read_numbers)
PASSLegionnaires · birth: járás twin role set exactly on the twin population0 record(s) disagree between birth_jaras_id and birth_jaras_twin
PASSHabsburg fallen · birth: járás twin role set exactly on the twin population0 record(s) disagree between birth_jaras_id and birth_jaras_twin
PASSlegionnaires_census_1910: census join does not duplicate rows132,993 rows vs 132,993 persons — join blowup
PASShabsburg_census_1910: census join does not duplicate rows186,192 rows vs 186,192 persons — join blowup
PASSEvery rank label resolves to itself0 label(s) resolve elsewhere, e.g. []
PASSNo general scores below the OF-6 band0 record(s) name a general but score as a lower grade
PASSunit_key present wherever a regiment is0 record(s) have a regiment but no unit_key
PASSHabsburg note parser self-test0 behaviour(s) regressed: []
PASSTheatre classifier self-test0 behaviour(s) regressed: []
PASSLinkage name test self-test0 behaviour(s) regressed: []
PASSSlovak district matcher self-test0 behaviour(s) regressed: []
PASSLegionnaires · birth: every járás-placed record has land Slovakia0 record(s) sit in a Slovak járás with another land
PASSLegionnaires · home: every járás-placed record has land Slovakia0 record(s) sit in a Slovak járás with another land
PASSHabsburg fallen · birth: every járás-placed record has land Slovakia0 record(s) sit in a Slovak járás with another land
PASSHabsburg fallen · home: every járás-placed record has land Slovakia0 record(s) sit in a Slovak járás with another land
PASSHabsburg fallen · birth_cisleithanian agrees with birth_land0 record(s) where the flag contradicts the land
PASSHabsburg fallen · home_cisleithanian agrees with home_land0 record(s) where the flag contradicts the land
PASSLinkage is one-to-one (no id matched twice on either side)0 surplus link(s)
PASSNo linked pair has two full dates of birth that disagree0 linked pair(s) with conflicting full DOBs
PASSEvery linked pair clears the surname floor0 linked pair(s) with a surname below the floor
PASSLegion-side and VÚA-side links agree0 legion link(s) the VÚA side does not return
PASSexit_date within 1914–19390 date(s) outside the range
PASSexit_date_precision present exactly where exit_date is0 row(s) disagree
PASScapture_date within 1914–19180 date(s) outside the range
PASScapture_date_precision present exactly where capture_date is0 row(s) disagree
PASSenroll_date within 1914–19220 date(s) outside the range
PASSenroll_date_precision present exactly where enroll_date is0 row(s) disagree
PASSExit before enrolment, day-precise dates (246 — source contradictions) softexit dates before enrolment have grown — look at the parse
PASSEvery ladder rank has a common graderank(s) with no common grade: []
PASSLegionnaires: every column is in the data dictionary0 undocumented column(s): []
PASSHabsburg fallen: every column is in the data dictionary0 undocumented column(s): []
PASSCensus codebook (cis) documents every panel column0 undocumented column(s): []
PASSCensus codebook (hu) documents every panel column0 undocumented column(s): []
PASSSettlements 1910: key ['settlement_id'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · ruian_obce: key ['obec_kod'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · ruian_casti_obce: key ['cast_kod'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · ruian_ku: key ['ku_kod'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · ruian_zsj: key ['zsj_kod'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · ruian_obce_adjacency: key ['obec_kod', 'neighbour_kod'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · geonames_places: key ['geonames_id'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · prng_places: key ['prng_id'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · vanished_places: key ['qid'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCurrent geography · settlements_1910_geo: key ['settlement_id'] unique and never missing0 row(s) with a missing key, 0 duplicate key(s)
PASSCrosswalk: one row per settlements_1910 row, no more, no fewer18916 crosswalk rows vs 18916 settlements — rebuild §3e after §3c
PASSCrosswalk: every modern obec_kod is in ruian_obce0 row(s) point at no obec
PASSObec adjacency is symmetrica neighbour pair is recorded in one direction only
PASSCrosswalk: Wikidata points kept, or replaced only with wd_overridden0 Wikidata point(s) replaced without the flag
PASSCrosswalk: every point carries coord_source and coord_ref0 point(s) without a source or a citation
PASSCrosswalk: every vanished-place point is in vanished_places0 point(s) cite a QID the table lacks
PASSCrosswalk: neighbour matches within 5 km of their 1910 okres0 neighbour match(es) beyond the cap
PASSCrosswalk built after settlements_1910 softsettlements_1910 was rewritten after the crosswalk — re-run notebook 02 §3e
PASSCrosswalk points agree with Wikidata (median 8 m) softmedian distance above 50 m — suspect the S-JTSK transform
PASSCrosswalk points lie in the 1910 register's court (91.9% of 221) softbelow 85 % — the crosswalk or the register's anchoring has drifted; read the in_register_court == False rows
PASSTheatre agrees with Fidler (2025) unit authority (99.6% of 71,854) softagreement 99.6% — 266 record(s) place a regiment in a theatre where Fidler says it never existed
PASSSettlement coordinates land in their own okres, before snapping (90.2%) softcoordinate accuracy below 85% — suspect an unguarded name-match fallback

Roadmap / to-doOrientation

What's done and what remains. · source: reference/ROADMAP.md

In short

An honest map of where the project stands. Data construction is essentially complete and self-checking; the analysis has not yet begun. Status keys: ✅ done · 🔨 in progress · 📋 planned · ⏸ deferred · ⚠ known issue.

At a glance

Stage Status Note
Data collection (csol.cz, VÚA) ✅ done ~133k legionnaires + ~186k fallen, scraped
Cleaning & parsing ✅ done dates, exit codes, ranks, units, theatre, affiliations, places
Geography & census integration ✅ done CZ okres/court + SK járás backbones, settlements, coordinates, census panels
Today's geography (RÚIAN) + 1910 crosswalk ✅ done (2026-10-29, D-151, D-153, D-155) ČÚZK RÚIAN obce/parts/KÚ/ZSJ + adjacency, Geonames, Wikidata vanished places (zanikleobce.cz IDs), PRNG in data/final/geo/, every download with a SOURCE.txt; settlements_1910_geo gives 17,141 of 18,916 settlements a point (5,842 before; 95.8 % of rows with a 1910 entry matched; 1910 obce 98 %, osady 88–93 %), each cited in coord_ref. The 1910 register's court breaks ties and checks points (91.9 % of 221 on the test register); its gain arrives with the register's full run, on re-running §3e. §3c → §3e re-run 2026-10-29 evening (D-142 rows cleared). Next: move record geocoding and the court layer onto the crosswalk; re-run notebook 15 §18 so the register keys to the new settlements_1910
Cross-dataset linkage ✅ done precision-first, 1,340 confirmed overlaps (1,201 by date of birth + 139 by a Legion note). The rule was tightened on 2026-09-22 (D-59) — the old ~2,240 let a shared given name carry two different surnames and read a register note as Legion service
QA gate ✅ done 107 checks, all passing on the build of 2026-09-23; fails the notebook if data breaks — and, since 2026-09-22, if data/final is older than the code, which had let eight checks skip silently for two weeks
External validation (Fidler 2025) ✅ done theatre 99.6%, exit codes, ranks reconciled. It covers CS-Legion records with a numbered regiment only; the imputed theatres it could not reach were wrong in two places and are fixed (D-60)
External validation (education) ✅ done Österreichische Statistik XCIII/1 (1908/09), the state's own school census: taxonomy and pok. confirmed; education_complete fails (D-53, unfixed); pok. shown under-recorded. Cannot reach Hungary, so D-08 stays open. See VALIDATION_EDUCATION.md
English value translations ✅ done units, religion, rank tiers, occupation (HISCO labels)
Occupation → HISCO / HISCLASS ✅ done 98.5% coverage; authored Czech dictionary (495 keys) + German bridge + OccCANINE + CZ-ISCO + 1910 census Berufsstellung
Documentation site ✅ done this page
Variable construction 🔨 mostly done education's ladder is now externally checked, but education_complete has an open defect (D-53) and the Hungarian half is still unvalidated (D-08); a few unit fields open (below)
Person-period spell file 📋 planned the longitudinal input the analysis needs — the analytical blocker
Unit panel 📋 planned aggregate to unit_key for unit fixed effects / clustering
Habsburg recruiting-district join 🔨 built, not joined reference/units/ehnl_tabelle1_*.csv (443 units) is verified but has never been merged onto the person panel. Joining it on habsburg_unit_peacetime / habsburg_unit_capture gives every soldier a home okres and a unit ethnic-homogeneity value — the Costa–Kahn mechanism on the first desertion. See reference/units/ERGAENZUNG.md
1910 register digitisation 🔨 certified, full run under way (2026-11-01: Czech F1 0.996, German 0.957 blind; route frozen and verified; see Before the full run under the data structures. 2026-11-02: Schlesien's full run finished complete, and on its two sealed pages it scores F1 0.980 in sample against round 2's 0.990 (test–retest, D-181); the other three editions running) The Podrobný seznam míst / Spezialortsrepertorium, the only source in the project that reaches settlement level and the only one that records institutional presence rather than demography. 20-page pilot runs and grades itself: the table comes out at 83–97 % on the register's own arithmetic. The symbol reader is no longer the blocker (2026-09-17, settled 2026-09-22): Gemini Flash on one apparatus section per call, thinking off, reads F1 0.962 / entry_exact 0.802 (3.8, mean of three repeats; 3.7 the same at 0.965 / 0.787) on the 280-claim development gold, against 0.872 / 0.553 for the best local model, at $0.33 a 13-page pass — thinking bought 3.7 nothing and cost 3.8 accuracy, at ≈ $6 an edition on batch with thinking off (D-54; E1 ran with thinking on, which the ledger never counted — ≈ $36 on 3.8). E1 done 2026-09-17: the gap to the whole-page pipeline is the view, not the model version (+0.117 F1 from the view, +0.017 from the version), and two confusions survive both versions — an outline-roofed [castle] read as the solid-roofed [poorhouse], and the cooperative glyph split into [savings-bank] + [cooperative]. 2026-09-22 re-assessment: scored at settlement level the reader fell to 0.856 F1, and the cause was a decoder defect — the — n) part marker was never split, so 12.8 % of correctly read claims landed on the wrong part (D-56). Fixed 2026-09-23: settlement F1 rose to 0.915 for the default reader, and a second defect found on the way (strips of a tall section joined in the order they finished) is fixed too, so decoding is byte-for-byte reproducible. 2026-10-05 — the residue was the decoder and the gold (D-63 to D-68). The main scorer now keys on (entry, part, symbol) with counts, per-glyph rates and a page-clustered interval (D-63). The undiagnosed ~0.05 was the decoder again: a part that opens its own printed line carries no dash and was folded into the part above (D-64). The scored gold was still the August first reading; it is now the whole-page sheets — 372 claims over 61 entries on the same four pages, scan 0449 held out, seven claims settled against the legend plate — and the two readings of those pages agree at 0.977 (D-65). Re-decoded from cache, the default reader stands at settlement F1 0.958 (0.961 / 0.955 / 0.958; municipality 0.959) with no institution misfiled in any thinking-off pass; 3.7 with thinking off reads 0.954, 3.7 with thinking on 0.962, 3.8 with thinking on 0.900. That is not comparable with 0.915, because the gold changed too; on the gold of 2026-09-23 the decoder change alone gives 0.961. About 13 of the default reader's 18–22 false claims a pass are one segmentation defect on scan 0451 — a heading whose okresu the text layer damaged, so a parts section was read as offices. 34 of the volume's 1,238 headings are damaged that way and now open their section (D-66; no pass read since). Every glyph a regression will use is found each time it is printed; what the reader still gets wrong is rare — over three repeats [castle] 4 of 15, [ruin] 2 of 12, six false [poorhouse]. Nothing is certified. The gold is four purposive pages; calibration sheets 0019, 0076 and 0136 are partly read, the eight sealed pages are untouched, and no minutes are logged. The German edition gets its own gold (D-67): seven blank sheets are cut, none is read. 2026-10-06 — the r4 baseline, the first pass read with every fix in (Heavyhorse6, notebooks/07_lexikon_1910_baseline.ipynb, ≈ $1.96; D-68): the default reader at 0.972 (0.972 / 0.974 / 0.970), 3.7 with thinking off at 0.970, no institution misfiled, false claims a pass halved (20 → 10), scan 0451 from 0.905 to 0.955–0.964. What is left is the same few substitutions in every repeat — a castle as a poorhouse, a ruin as a solitary house, an evangelical preaching station as a Catholic vicariate, a railway station as a stop, a savings bank claimed beside a cooperative — checked against the legend plate the same day, and the gold stands in every one. Also 2026-10-06 — what stands beside the glyph (D-70): a pass now writes out the school's count, classes and language, the cooperative's kinds and language, and the named parts of Části osad, each parsed by the code the gold sheets go through. Replayed from cache on the six r4 passes: every school right in every column (56 of 56), cooperative kinds 55 of 55 with language right in five passes (the sixth skipped a printed line), named parts 175 of 175 in five passes (172 in the sixth) with houses and population exact, place-type symbols 0.92–0.93 for the default reader and 0.96–0.97 for 3.7 — myslivna read as hájovna by both versions in every pass (the solid block is printed with gaps), and the default reader leaving the parenthesised cottage-group unnamed. Two slips in the gold sheets turned up and were corrected on the author's ruling (Česká Skalice 3 2 Sch; Trutnov 30 part 2, the &). The language figures rest on few cases — no cooperative on the scored pages is marked German. The bar for the second reader is measured (D-71): a second Gemini pass catches only 26–30 % of the default reader's ≈ 21 errors a pass — seven in ten are shared inside the family — so a candidate is chosen on the errors it brings to review, not on its own F1. 2026-10-08 — the second reader screened, and the route for a disputed claim (D-72). E1b was run on Heavyhorse6 with Claude Haiku 5.5 (twelve passes, ≈ $1.0–1.4) — not with Haiku 4.5 and local qwen3.8 as planned, and ahead of the calibration transcription, so on the four gold pages. With thinking at high effort and the prompt's note, Haiku catches 87 % of the default reader's ≈ 21 errors a pass and shares 2.7 of them, against 30 % and 14 for a second Gemini; F1 after a perfect review would be 0.996. It raises 24 alarms a pass where the bar allowed eight, so about one claim in nine is disputed (11.1 % on all 13 pages; roughly 5,100 of 46,000 over the Czech apparatus), and in a dispute Gemini is right 57 % of the time. With thinking off Haiku 5.5 reads at 0.56. The route laid out for the volume — designed, not built: both readers read everything; a DINOv3 nearest-neighbour encoder names every boxed glyph, because it alone can reach an error both readers make; what the three do not agree on goes to one Opus 5.5 verify call (≈ $30–90 on batch) and then to the author; routing is fixed code, and local models are not the arbiter. 2026-10-13 — the printed order of the marks is a check (D-73). The marks of an offices entry stand in a fixed order, stated by example in the volume's Návod and exact on the ten transcribed sheets once the gendarme block is set aside (0 of 2,902 pair observations against it). Learned from the gold sheets and scored on the stored passes with nothing sent: it flags 1 of a Gemini pass's 10–12 false claims and 1–5 of haiku55nh's 9–14, at 0–1 of about 360 true claims; every disputed claim it flags is false (262 of 262 over 105 pairs of passes); and it reaches one of the errors two Gemini passes share. It cannot see a same-slot substitution, which is most of the residue. Built as functions and into the scorer only. Its place in the route is decided (D-74), designed and not built: both readers read blind — the order is not put in the prompt — then the order check, where a flagged mark gets one re-read of that glyph and is never rewritten by rule, then the readers' disagreement, then the author; roughly 630–780 flagged marks a reader over the volume, scaled by crops from 13 pages. 2026-10-13 — a section crop runs to the foot of its column (D-75). The last crop of an apparatus column ended at the last line the text layer recognised, so a printed line the engine dropped was on neither the reader's image nor the gold image. Fixed in lexikon_1910_notes 0.7.0 and counted over table scans 15–494 with no model called (measure_segment_foot.py): 23 of 908 columns have a gap that can hold a line, and read by eye 13 of them, on 13 pages, had print below the old crop — 12 whole entries, each a key and the same single mark, and 2 wrapped tails. The four scored pages lost nothing; calibration scans 0076 and 0316 lost three entries between them, their gold crops are re-cut; the two on 0316 were already in its sheet, and 0076's 22, 3) is still to be transcribed by the author. Not done: 6 of the 81 crops of a cached gold pass (scans 0076, 0316, 0376, 0118, 0449) match no cache entry and have to be re-read on Heavyhorse6 before that pass is replayed. 2026-10-14 — every printed line is inside a crop, and the table reads its own first and last rows (D-76 to D-82). lexikon_1910_notes 0.8.0 recovers 22 pages whose apparatus had never been read (470 keyed lines), opens the apparatus at the table's own foot and tiles each column, so 25 more printed lines on 18 pages are inside a crop; lexikon_1910 0.4.0 ends the table where the apparatus opens and opens it under the header's closing rule (137 + 233 rows that were never read, the gold tables unchanged); the okres comes from the running head through a folded matcher (24 pages change, 19 checked right). Profiles for four volumes and an image-only geometry with an ink account are built and opt-in (D-80). The seven headings the text layer missed (scans 0090 0141 0190 0241 0304 0384 0456) were ruled on 2026-10-19, all Části osad (heading_rulings.csv); the bake-off moves to r5, and the gold passes are read again on Heavyhorse6 (≈ $3) once the transcription is finished (D-81). 2026-10-15 — the crops come from the image (D-83 to D-87). The pilot cuts the apparatus on the page image by default (476 scans: 1,640 sections, labels identical to the text layer's on 464 pages, 38 headings only the image sees and a pass reads), reads the postal legend from the image, ends the table at the rule foot on the two scans where the body band overshot, writes a crop audit (printed lines against returned, headings in the reply), grades a block split by a page break as one (90 blocks), and stops guessing an okres the head names but the authority lacks; Loket and Kralupy nad Vltavou, which the register prints as okresy and the district backbone does not, now resolve to the backbone okres that holds them (D-88) — both were created in November–December 1913, and the register draws districts as at end-1914, so the 1910 backbone is right. The r5 re-read on Heavyhorse6 is nine Gemini passes, three on the old geometry as the comparison arm. 2026-10-24 — the certification plan (author, 2026-10-24): (1) the certification run (EDITION = "bohemia", PAGES = "certification": the eight sealed Czech pages and their four German twins) reads right after the gold re-run, on the frozen route — its freeze the same as the gold re-run's, which section 5 checks and names any difference of (D-125) — and before the full run; it is scored once, after the sealed sheets are transcribed blind to its output (D-55), and the full run's own readings of the same pages can be scored as a second, independent measurement of the same route. What the sealed pages certify is the headline only — ≈ 590 Czech claims in ≈ 115 municipalities (with 3 wrong, a 95 % interval of 0.17–1.49 % before page clustering) and ≈ 210 German claims, with 33 of the 46 Czech symbols under ten instances (estimated from the gold's own mix, 2026-10-24). (2) Later, once the full run exists: draw a random sample of about 150 Czech municipalities from its output, oversampling the rare symbols (≈ 8 h of the author's time), for the analysis variables' own error rates (audit §D; Mähren and Schlesien 60–80 municipalities each); the sampling rule and the metrics are written down before the draw. 2026-10-24 — the court key and an unattended notebook (D-122, D-123): every section's 1910 court beside its label (courts.csv; 1,247 of the 1,248 Czech headings resolve, and three exact matches the old rule got wrong — Teplice, Benešov, Planá — are now the head's own okres's courts); notebook 15 waits at each collect cell, will not finish a pass while its batch is processing, resends a failed Opus call once, and writes its queues outside Dropbox (≈ 150 GB for the full run). Gemini through its native batch API is built (D-124): switched on in notebook 15 only after its equivalence check (7e, ≈ $0.66) prints PASS on Heavyhorse6; until then Gemini is read live. 2026-10-24 — before the gold re-run (D-121): counts by the readers' majority (875 of 879 right on the Czech gold), D-58's independence test in section 14, and a sealed German Böhmen certification half (scans 0165, 0189, 0356, 0441 — the twins of four sealed Czech pages), blank sheets cut. 2026-10-24 — third audit (D-119, D-120; AUDIT_2026-10-24.md): the code moved under utils/ (utils/lexikon_1910/, utils/reference_tools/); the route's defects fixed — Czech gold replayed 0.995 → 0.999 (0 false, 2 missed), German token-only ceiling 0.975; final inventory one row per claim with its count; sealed pages never reach the author early. 2026-10-25 — the Czech half is certified (D-126, D-127): the gold re-run read both editions in one pass (Czech F1 0.999, 0 false and 2 missed of 881; German 0.945 on the shared entries and 0.905 over every entry its sheets print), and the eight sealed Czech pages were then read on the same frozen route and scored once, by a scorer whose metrics were written down first: F1 0.996, a 95 % page-clustered interval of 0.991–1.000, 1 false claim and 3 missed of 505, entries exact 104 of 107, count right on 501 of 502, every token with ten or more instances at sensitivity 1.000. That is the error rate the analysis may quote for the Czech symbols. Two defects were found on the way and fixed: D-125's freeze check digested the contrast sheet's file rather than its pixels, so a re-encode of an identical sheet read as a different route (it now digests both, and a non-gold run copies the gold's own file); and scoring keyed an entry on the printed district slug, which the text layer damages (mechanice for Nechanice, novaraka for Nová Paka), so the first scoring lost 170 of 505 claims that had all been read — the key is the 1910 court now (D-122). 2026-10-25, later — the German route rebuilt around the certified Czech one (D-129 to D-132), built and replayed offline, nothing sent: the 61 claims the German route misses on its gold are 28 from one lost heading (0380), 19 from Opus being shown only the list of parts for a numbered part, and 14 never asked. (1) The Czech anchor (D-129): the editions pair on every name a municipality prints (22 → 49 on the gold, 12 → 30 read in both), and each German claim the certified Czech inventory disputes is put to Opus on the German print alone; replayed, all 19 claims only the Czech side holds are on the German gold — every German miss on those municipalities. The Czech inventory never changes; D-58's two-measurement design is dropped for Bohemia's final inventories. (2) Opus sees the offices for every part (D-130), and a call whose images changed is asked again. (3) Place types are routed (D-131): a type one reading alone names now reaches Opus, and the route writes final_types.csv. (4) A German heading that fills its line is found (D-132): 0380's, and on 30 random German pages one more real one (0157, Kamenitz an der Linde), no false one; off for Czech. Also 2026-10-25 — Mähren and Schlesien drawn (D-133): 4 calibration and 4 sealed certification pages each, 2 sealed kept each by D-136 (Mähren certification 37, 105; Schlesien 18, 38), and notebook 15 reads them with PAGES = "certification" (run_frozen 0.13.0); the four sealed sheets of each must be transcribed blind before that run is scored. 2026-10-26 (D-135, D-136): the German half is certified pooled over eight sealed pages — Böhmen's four, Mähren 37 and 105, Schlesien 18 and 38 — with Böhmen also scored without the Czech anchor; their tables, ledger keys and names are drafted by Claude from crops cut above the notes divider, every row passing the register's arithmetic; the notes are the author's, blind, then double-coded. The calibration pages are not transcribed (ruled 2026-10-26): they are looked at in the full run's output, against the page image, before the sealed run. 2026-10-28 (D-146): Mähren and Schlesien are read with their own plates — 88 and 86 tokens, their own keys under symbols/de_maehren/ and de_schlesien/, prompts that name them and describe the registered-cooperative mark they print before the letters (Böhmen alone prints the letters bare, so D-139's heading claim is Böhmen's only); a language letter may be p, and a bare letter stays a cooperative of no stated language. Böhmen's prompts and key are unchanged byte for byte. 2026-10-28 (D-147): the religious marks are also scored on their function (religion_categories.csv: resident priest / church without a priest), so the pooled German score is one set of claims across three plates; still to do for it: check Böhmen's parish mark against the 1910 diocesan catalogues, and the two tokens on sealed sheets 0356 and 0441 the author must correct before Böhmen can be scored. 2026-10-29 (D-149): the introductions read — no default language for a cooperative letter or a school (an unmarked one is not stated); a mark read twice is now asked about the mark it may hide (43 repeats on the gold readings, the missed mark among the candidates on 27), and the worked examples join the order evidence. Applies from the German sealed run on. What the German route still cannot see: after D-129–D-132, the German gold's remaining misses are unanimous substitutions no reading disputes (castle read as poorhouse seven times; ruin as solitary house; sanatorium as baths); the anchor reaches half, and only DINOv3 reaches the rest — off on German pages because the Czech-trained locator misplaces German boxes (D-118). In Mähren and Schlesien there is no anchor, so a German locator (clicked boxes on the German gold pages) and a German glyph bank are the main lever left. Next: the German gold re-run on Heavyhorse6 (notebook 15, EDITION = "bohemia", PAGES = "gold", then 14b), which measures all four; then the four German sealed sheets transcribed whole and the German certification read and scored; the German table's municipality names, without which a municipality that prints no list of parts cannot pair (30 of 88 on the gold); then the entry-level sample (audit §D; the sample-size check on the certification page puts the design target at about 36 instances of each rare symbol); then the place-variable gaps (audit §C). 2026-10-26 — the text abbreviations routed and counted (D-134), built and replayed offline, nothing sent: the first of the audit's §C gaps closed — o. s., notář, 7 Adv., the cooperative letters now go the route's way (four readings, disputed ones to Opus in b- calls of their own) into final_abbreviations.csv with count, expansion, English and court key; on the Czech gold the agreed claims are 0 false and the 12 disputed hold the 9 they miss, counts 195 of 195; German 0 missed. Next for it: extend the abbreviation table (roughly half the text items on the gold are not decoded — Kreisgericht, BezKrankenk., Oberrealsch., Fortbk. as kind course) and let the decoder read a suffixed cooperative letter (Rč: 16 of 110 Czech letters on the gold); both need every pass re-decoded on Heavyhorse6 and the gold re-frozen. 2026-10-23 — the first frozen gold run (D-118): Czech route F1 0.995 (Gemini alone 0.955; 2 false, 7 missed of 881) for ≈ $0.77 of Opus; German fell to 0.899 because the Czech-trained locator misplaces German boxes — fixed (no ruling on an empty box, a coverage gate, token questions where it fails; 0.953 replayed) and the German half re-runs. 2026-10-22 — the German editions wired into the frozen run (D-115): notebook 15 reads Böhmen, Mähren and Schlesien (apparatus only; Mähren and Schlesien full only), batches the Czech table too, lets DINOv3 route German marks only after it passes a gate on the German gold, compares the two editions of Bohemia unreconciled, and sends the marks Opus cannot tell and the blind validation sample to the Token Review page. Next: EDITION = "bohemia" gold on Heavyhorse6 — both editions in one run, cross-checked (D-116). 2026-10-22 — the frozen run finished for Czech (D-114): notebook 15 reads gold, certification or the full volume with the same cells; the readers are batched through the crop cache, the section crops are saved and E3 runs on any page. Next: the gold run on Heavyhorse6; then certification and the volume; German still to wire. 2026-10-22 — the frozen run built (D-112): notebooks/15_frozen_run.ipynb runs every stage for the Czech gold (≈ $2 reading + ≈ $1–2 Opus); next: the readers' section crops saved as images (so E3 and Opus can run on any page), batch mode for the readers, and the German wiring — then certification and the volume. Notebooks 04–14 archived. 2026-10-22 — no local third reader (D-113): all six local vision models fail D-105 — the ones that reach the residue raise alarms on half the claims, and the accurate one (Qwen 3.8, F1 0.90) shares the pair's blind spots. 2026-10-22 — the frozen pipeline in one notebook (D-112): freeze → read → check → compare → Opus → rulings → ledger and final inventory → score and validation sample; edition and page set are switches, so the gold run and the volume run are the same cells. 2026-10-22 — every mark's reading history kept (D-109): an append-only ledger per edition, one row per step's reading of a mark, from which the final inventory is computed and each step's changes counted. 2026-10-22 — frozen run reads Gemini ×1, Haiku ×3 (D-108): ≈ $215 for all four volumes, batched. 2026-10-22 — the verify call shows the whole entry (D-107): every line of the municipality, both sections, across a break; on test B's 65 parts no entry is cut any more (the old crops cut 12 of 71 images). 2026-10-22 — sheet v2 measured (D-106): Gemini's calibration castles 7 → 15 of 57 and its purposive errors −28 %, but the look-alike swaps the bar counts did not halve, so Gemini reads without a sheet; Haiku keeps v1. Open: a pre-registered total-error test at the freeze. 2026-10-22 — local third-reader test set up (D-105): every pulled Ollama vision model reads the 12 gold pages once at r5 settings; it joins the route only if it reaches half of the errors the pair and DINOv3 leave (5 on the gold) with alarms ≤ 10 %. Notebook 14 on Heavyhorse6, GPU time only. 2026-10-22 — Mähren and Schlesien read on the Böhmen gold (D-104): the German Böhmen gold serves all three German volumes, with a one-to-two-page spot check each; after the frozen run the author back-checks a sample against independent sources, weighted to the look-alike tokens. 2026-10-22 — sheet v2 for Gemini set up (D-103): v1 misled Gemini because its castles were all lightly printed (heavy castles looked like its poorhouses); v2 spans print weight and pairs look-alikes from the same page. Gemini re-read ×3 in notebook 13 (≈ $1.40), kept if it clears D-99 and finds ≥ 7 of 57 calibration castles. 2026-10-21 — contrast arm scored (D-102): Haiku gets the sheet (look-alike swaps 7.3 -> 3.0 a pass on the purposive pages, errors flat); Gemini does not (swaps 5.3 -> 4.0; errors fell 28 -> 19 on purposive but rose 51 -> 62 on calibration, castle read as poorhouse more often). The pair shares 2.7 errors a repeat instead of 6.7. Open: why the sheet worsens Gemini on castle/poorhouse. 2026-10-21 — test B part arm set up (D-101): each outlined mark also asks whose it is — the municipality, a numbered part, or a part's type mark (no institution); 36 of the 65 parts get the question; kept only if it does no harm. Notebook 12 with ARM = "part", ≈ $1.5–4. 2026-10-21 — test B passes (D-100): Opus 5.5 settles 99 % of the gold disputes and is right on 98 % of them (always-Gemini: 63 %), the same in all three repeats, for $1.58; it enters the route as the verify call, ≈ $30 for the Czech volume's disputes. Open: a mark question cannot see a mark filed under the wrong part, and ♁A/♁H and ruin/solitary house still slip. 2026-10-21 — contrast-sheet arm set up (D-99): the 24 look-alike pairs the r5 readers swap, cut from calibration pages, sent between the key and every crop; Gemini and Haiku nh ×3, judged on the purposive pages — the sheet stays if swaps halve, errors rise ≤ 10 % and F1 holds. notebooks/13_contrast_arm.ipynb on Heavyhorse6, ≈ $2–3. At the freeze every learning component is rebuilt from all non-certification gold. 2026-10-21 — test B set up (D-98): the bar is written first — Opus 5.5 at effort low, batched and cached, three repeats on the 106 claims the reader-of-record pair disputes (always-Gemini 63.2 %); it enters the route only at ≥ 90 % right on what it settles, ≥ 70 % settled, ≥ +15 points over always-Gemini, no lean to Haiku and ≥ 85 % stable. Amended before sending: each outlined mark is a choice among the readers', DINOv3's, same-base and look-alike tokens, and the 38 agreed claims DINOv3 doubts ride along beside the bar. notebooks/12_verify_test_b.ipynb on Heavyhorse6, ≈ $1.5–4. 2026-10-21 — r5 read and scored (D-97): on 12 Czech pages Gemini 3.8 reads at F1 0.955, Haiku catches 58 % of its errors, the two dispute 13 % of claims (Gemini right in 59 %), ruling them by hand would take ≈ 17–24 h against ≈ $35–105 for Opus — test B is next; every reader reads the castle as a poorhouse, a blind spot only DINOv3 sees; the model reads the table at 99.7 % of number cells against OCR's 91.4 %. 2026-10-19 — r5 also reads the table (D-90): Gemini and Haiku on the 13 gold table pages, three repeats each (≈ $4), scored cell by cell against the gold beside the text layer, which on those pages gets 91.4 % of number cells and 60.6 % of names right (run_table_arm.py, score_table_passes.py). 2026-10-19 — and the German edition (D-92): the German apparatus is cut on the image alone and read by the same two readers, three repeats each, on the seven German gold pages (≈ $1.2, run_r5_german_notes.py), scored against the German gold and paired with the Czech r5 passes by court and municipality name for the D-58 independence test (score_r5_german_notes.py); built, not run. 2026-10-19 — E3 is built (D-93): a locator learned from the gold's counts and order alone puts 137 of the 141 clicked glyphs (97.2 %) in the box placed for their own claim, DINOv3 ViT-B/16 names each box from a calibration-only bank, leave-page-out, with a not in the bank answer below a threshold (95.2 % of in-bank marks on Heavyhorse6, pixels 93.8 %), and since its first run missed the bracketed and lettered marks a mark is named in parts — base, brackets found beside it without the claim, letter — and composed from the plate's table (glyph_composites.csv), which on pixels lifts the composite tokens from 71.7 % to 73.1 % and waits on a DINOv3 re-run on Heavyhorse6 (notebooks/10_glyph_e3.ipynb). 2026-10-21 — and an independent arm (D-94): the apparatus read from the image alone, with no reader claim and no text layer — token-blind detection (88 % of marks at 96 % precision), the same naming, and the keys read by shape and decoded under their order — gives on pixels a third reader at settlement F1 0.697 that catches most of the errors Gemini and Haiku share (16–21 of 21–28 a pair) and finds 9–10 of the 14–17 marks both miss, but raises ≈ 400 alarms a pass against the bar of 24.3 (≈ 107 if only what it sees routes a claim), its keys the bottleneck. The same day the author chose the reader-based design for the route (D-95) and parked the independent arm: the readers' claims are placed and named by DINOv3, every mark gets a verdict (agree, disagree, unnamed, unplaced), and the marks no reader claimed are flagged under the reader's own entry — on pixels 29–68 real omissions a Gemini 3.8 pass, mostly the bracketed cottage-group it leaves unnamed, at ≈ 50 false flags a pass; the DINOv3 run waits on Heavyhorse6 (notebook 10, sections 3–5). Next, in order (reordered 2026-10-06, transcription first; amended 2026-10-08): the author transcribes the notes on the eight calibration pages, which every pass already reads, so the existing passes are re-scored on twelve pages instead of four; then re-score the second-reader screen on those twelve pages (E1b, notebooks/08_lexikon_1910_second_reader.ipynb — run on Haiku 5.5; the haiku55h arm, Haiku 4.5 and local qwen3.8 are unrun); two tests that cost next to nothing — whether DINOv3 separates the fill pairs at all on hand-cut glyphs (done 2026-10-19, passes: ViT-B/16 on a rough window box, castle / poorhouse 95.0 % and station / stop 100 % against pixels' 67.5 % and 85.4 %; on a clean box plain pixels already reach 100 %, so the box is the hard part; D-91), and — only once r5 has measured how often Haiku and Gemini disagree, and only if its cost over the volume beats the author ruling those claims by hand — whether an Opus verify call settles them better than always siding with Gemini (D-89); if they hold, contrast pairs for the rare glyphs (E2), a box per glyph, the encoder over every glyph and the verify call on what the three disagree on (E3, D-69, D-72); freeze and read the Czech volume once; the German reader alongside (D-58); then certify on an entry-level sample and estimate through DSL / PPI rather than chase 99 % (D-55). A full run on Claude Opus 5.5 (batch) is estimated at ≈ $160–340 against ≈ $6 for the Czech apparatus on Gemini with thinking off, and was not pursued; Qwen3-VL and MiniMax M3 would read it for ≈ $5–7 — none of the three has been measured on the crops. Also the supply for the 106 blank Prague-suburb rows (D-36) and for D-37's institutional covariate. See SYMBOL_ACCURACY_PLAN.md (revisions of 2026-09-17, 2026-09-22, 2026-10-05, 2026-10-06, 2026-10-08, 2026-10-13 and 2026-10-14) and LEXIKON_1910.md
Analysis 📋 not started the modelling stage — approach still being decided
Notebook 03 (figures/tables) ✅ done re-run 2026-09-23 against the audit build: 28 figures + 14 LaTeX tables, with the Bolshevik-coup line moved to 7 Nov 1917, monthly series on day- and month-precise dates, and the rank-reset table on combat ranks. At the 2026-08-27 re-run: The class gradient survives D-41 intact — 41 keys recoded and 1,383 records moved between HISCLASS values, and no rate in the robustness table shifts by more than 0.18 pp (Farmers identical, rolník having been answered keep); the denominators move as expected. VALIDATION_OCCUPATIONS.md Check 4 carries the new numbers and the old ones for comparison.
Three orphaned sample/ tables ⚠ open output/tables/sample/{sample_waterfall,sample_desertion_cut,desertion_by_rankreset}.tex date to 2026-07-14 (the role-based reorg) and are built by nothing in the repo — no notebook, no script — and cited by no .tex. They cover sample construction and the rank-reset cut, i.e. the headline RHS, so they are the ones that most need a live builder. Either re-create them in notebook 03 or delete them; leaving stale orphans beside regenerated tables is how a superseded number gets cited. (descriptives_habsburg/data_quality/missing_rates.tex is not an orphan — notebook 01 builds it, and tex/25-05-26-EDA cites it.)

✅ What is already built

The table above is the whole of it. This sheet looks forward and does not narrate finished work: for what changed and why it mattered see What's new, for the commit record Commits & versions, and for the calls taken along the way Decisions.

🔨 Open — variable construction

Item State What's needed
Occupation ✅ done (2026-08-01) HISCO-coded: 98.5% of non-empty Zaměstnání → HISCO + HISCLASS / HISCLASS_5 / SOCPO (HISCAM dropped, D-30), plus occupation_qualifier (D-29) and occupation_career_stage (D-31). Czech→HISCO dictionary authored for this thesis (no Czech exists in the History of Work table or OccCANINE's 13 languages); German-gloss bridge + hand assignment. See OCCUPATION_CODING.md. Validated against OccCANINE as an independent second coder: 88.2% record-weighted HISCLASS agreement, 96.7% on the HISCO major group (VALIDATION_OCCUPATIONS.md). Third check added 2026-08-02: CZ-ISCO on the Czech reading; fourth the same day: the 1910 census Berufsstellung columns, which settle rolník — the one load-bearing key no other check could reach (D-33, Spearman +0.402 within-agriculture over 99 okresy). All 172 review flags adjudicated 2026-08-26 (D-41): 41 keys recoded — on the rebuilt frame 4,731 records take a different HISCO code and 1,383 move HISCLASS — 130 kept, 10 left open (163 records, seven of them one question: an employer written where a job was asked for). Leaving those ten is itself a decision, D-43: 156 of the 163 sit in the interwar column, 3.6 % of it, so a guess would bias the smallest occupational variable rather than complete it. zámečník was answered keep.
Record provenance ✅ done (2026-08-27) Zdroj informací — scraped since the beginning and unused until now — is parsed into two families that must not be pooled (D-44): source_spis / source_karta, which VÚA-VHA personnel document backs the record (95.7 % / 76.8 %, both 97,075), and source_extra (+ source_extra_ref verbatim, 489 distinct), a source cited beyond the two = modern editorial attention, not a different wartime document. Anchored prefix strip, never a substring test; missing stays <NA>. Six QA invariants + a parser self-test. The published-vs-archival split of source_extra_ref is left uncoded — the available heuristics mislabel in both directions, and 489 values is a hand-coding task, not a regex. See PIPELINE.md and VARIABLES.md §7.
Education (B3) ✅ built (D-08 open on Hungary only) Confirmed and sized, 2026-09-02 against Moore (2020) — see EDUCATION_LADDER.md. pok. (pokračovací = gewerbliche Fortbildungsschule, an apprentices' part-time continuation school) is mis-scored as Upper secondary on 8,805 records, 8.3 % of the non-empty field; uč.ústav sits level with gym. though it is entered from the Bürgerschule at 15; dot-less variants (pokr., 3mě) and hosp./odb./kadetka unmatched. The field is an itinerary with year counts, not a level — parse it, don't classify it. The vocational branch (~17 %) is now sourced as well (Lassnigg 2015: the Fortbildungsschule sat inside compulsory schooling and selected on nothing). education.py 2.0.0 implements the six-rung ladder (coverage 96.7 → 99.1 %; 8,117 records leave the gym. bucket); schema.py 1.10.0 carries the eleven columns. Notebook 02 re-run 2026-09-02, QA 66/66, data/final current. Residual blockers: the duration of a Fortbildungsschule/Fachschule year, and the Hungarian ladder for Slovak births. 2026-11-03 — the Hungarian half has its sources (EDUCATION_LADDER.md §5): the 1868 school law (Act XXXVIII), Kornis 1932 Education in Hungary (downloaded to library/education/), the 1910 Hungarian census and Puttkamer 2003. The records already follow the law: of Slovak births with elementary school only, 56 % state 6 years, the length of the Hungarian everyday school (§ 48), against 5 or 8 in the Czech lands; and a Slovak burgher school runs to 4 years where a Czech one runs to 3. So the rungs carry over, but the completion standard does not: 789 Slovak records with the full six-year course are scored education_complete = False against the Austrian standard. To rule together with D-53.
ah_regiment_no ⚠ known issue currently emits archival file numbers, not regiment numbers — do not use as a regiment id.
Habsburg unit English not done ah_unit_primary is a different abbreviation system; only ah_unit_type is English so far.
Religion tail ~0.5% <NA> the rare unmapped confessions could be curated further if needed.

⚠ Known issues — logged in the 2026-08-02 review of the occupation work

Found by reviewing the occupation change before it was committed, and deliberately committed unfixed so the record and the code stay in step. Every figure below was recomputed from data/final/persons/legionnaires_clean.parquet and occupation_hisco_curation.csv.

Item State What's needed
~~occupation_socpo contradicts the student policy~~ ✅ fixed 2026-08-26 (D-42) Wider than first logged. Every HISCO sentinel now carries <NA> in HISCLASS, HISCLASS_5 and SOCPO, asserted by a new invariant on both occupation fields (occupations 1.3.0, data_checks 1.8.0). The original finding — 3,098 students at SOCPO 42, "middle class, supervisors of skilled workers" — was the smaller half: absolvent / abiturient (−1, status 42) carried HISCLASS 2 Higher professionals and SOCPO 5 Elite on 160 records, 3.3 % of HISCLASS 2 and 5.3 % of the elite, i.e. the defect was in the headline variable and not only in the spare. Root cause: for a sentinel the status modifier alone drives the scales, and HSN reads a modifier as a social position. Requires re-running 02.
~~Published occupation figures disagree with the data~~ ✅ fixed 2026-08-02 Every figure re-derived from the build and corrected in both documents: hand-assigned keys 429 of 470 (was "216 of 459"); the bridge assigns 39; interwar coverage 84.0 %; the <NA> invariant now names both counts (14,044 enrolment / 127,913 interwar); the modifier fallback is 3 pairings / 9 keys / 204 records; zámečník's 0.12 is now given as one of the four lowest, with brašnář 0.022 the actual minimum; unmatched 1,840 (1.5 %). The match-kind shares were recomputed against the post-fix coder rather than carried over.
~~Stale figures elsewhere in OCCUPATION_CODING.md~~ ✅ fixed 2026-08-02 §1 now 118,949 / 8,001, §8 14,044 empty, §6 12,883 at 99900, dual 6,228 records, hand assignment 429 keys, header date current. The "~128 keys resolved outright" claim went with the §9 rewrite, which now carries a table of all three external checks with their blind spots.
Percentage convention still split 📋 open The docs floor (98.4 %, 95.8 %) where the notebook and the QA gate round (98.5 %) — the same 98.4674 %, two conventions. Harmless, but it makes two correct numbers look like a discrepancy. Pick one and state it.
~~No module version was bumped~~ ✅ fixed 2026-08-02 occupations.py 1.1.0, data_checks.py 1.4.0, schema.py 1.6.0, czech_glosses.py 1.1.0, run_link_bridge.py 1.1.0, build_hisco_authority.py 1.1.0. run_occcanine_check.py is still 1.0.0 — it has not changed since.
~~Duplicate dictionary key~~ ✅ fixed 2026-08-02 The earlier "finanční stráž" → 58230 (Detective) entry is deleted; 58220 stands. czech_glosses.duplicate_keys() now parses the module's own source — a duplicate literal key cannot be seen at runtime, because Python has already discarded the loser by the time the dict exists — and run_link_bridge.py refuses to run if it finds one.
Four HISCO_OVERRIDE entries never reach the curation minor zahradník na statku (added by this change), welder, učeš, rentiér — all have 0 occurrences in the raw field, so build_occupation_curation.py has no skeleton row to fill. Dead weight, but the new one suggests it was expected to land.
run_occcanine_check.py hardening ⚠ known issue The script is correctly outside the pipeline — torch/transformers/histocc are imported nowhere else, and data/final never depends on a model being installed. But: TLS verification is disabled unconditionally (load_model(relax_tls=True) is never plumbed through check() and no CLI flag exposes it), so weights are fetched unauthenticated on a machine already known to be TLS-intercepted — the default should be opt-in. It imports set_client_factory from the private huggingface_hub.utils._http where the symbol is public in the installed 1.7.2. The checkpoint is not pinned in code: OccCANINE_s2s_mix is the package default and the doc's claim is true, but unenforced (local checkout 9037b61, 2026-04-15). And todo.loc[~same, "review"] = "1" means the review column now mixes provenances — 158 flags = 102 model-driven (5 with no human note) + 27 human-only + 29 unjudged.
~~Two OccCANINE conflicts undiscussed~~ ✅ answered 2026-08-26 (D-41) Both went through the conflict workbook and both were kept: drogista (136) stays 41030 against the model's 06710 Pharmacist at p=1.00, účetní (615) stays 33110 against 11090 Other Accountants at p=0.86. knihvedoucí (19) — the identical row, same current code, same rival, same confidence — came back moved to 11090 and was reconciled onto 33110 with účetní the same day, so the two words for a bookkeeper stay together.
The 52 bridge overrides are not enumerated 📋 open OCCUPATION_CODING.md §5 says hand assignment beats the German link bridge so a documented judgement is not silently overwritten — right policy, but the 52 keys where link_hisco disagrees with the chosen hisco appear nowhere. They are recoverable from the curation CSV (link_hisco != hisco) and include zámečník, právník, soustružník, mechanik, montér, štukatér. Four of those six were re-decided on 2026-08-26 (D-41) and three moved to the bridge's own reading — právník → 12110, mechanik → 84300, štukatér → 95530 — so the set is smaller and its remaining members are now each backed by a recorded verdict.
Live adjudication calls 📋 partly settled ~~hajný (139)~~ → 64960 Trapper and Hunter, HISCLASS 10 (settled 2026-08-02, D-26): the rung between Forest Supervisor and Forestry Worker, and the code OccCANINE returned for Waldheger independently; myslivec moves with it. zámečník: 83930 stands (D-27), but the question shrank on 2026-08-02 — strojní zámečník (1,204) is now 84100 Machinery Fitter in its own right (D-29) and HISCAM, the only scale the choice moved, is gone (D-30). HISCLASS is 7 either way; what is open is the 5-digit code on the ~1,900 bare records. ~~obchodní příručí (1,740)~~ kept 2026-08-26 (D-41) — the second opinion this row asked for was the conflict workbook, and it came back for the current code. ~~skladník (140) and mlékař (14)~~ split the same day: skladník kept at 39140 Storeroom Clerk as modern drift, mlékař conceded to CZ-ISCO — 41030 → 77510 Dairy Product Processor, HISCLASS 4 → 9.

📋 Open — data structures for the analysis

  • Person-period spell file. The analysis will need a longitudinal structure — one row per person × period — that tracks who is still at risk in each period and how each spell ends (desertion, death, discharge, demobilisation). This is the main blocker between the current cross-sectional data and any modelling. Note ~30.8k records carry no exit code and are censored, not "missing". And 3,865 exit dates are known only to the month or the year (exit_date_precision, D-61): at monthly resolution they are interval-censored, not dated to the 1st.
  • Unit panel. Aggregate to unit_key for unit fixed effects, clustering and size denominators. Mind the lineage caveat: units were renamed and the same name denotes different units at different dates (see the External validation sheet), so a unit panel needs the record date, not just the name.
  • 1910 register → municipal tables — built 2026-10-29 (D-148), waiting for the full run. settlements_1910 has a key (settlement_id); notebook 15 §18 anchors every register municipality and part to it and writes data/final/register/ (places, institutions, cooperatives, one row per municipality), keys asserted in the QA gate. Built today from cz_certification as a test. Next: re-run §18 on the full run of each edition; German Böhmen goes through the Czech volume since D-150 (municipalities 66.7 % → 84.3 % on the gold), German parts and Mähren/Schlesien still rest on their own names; final_abbreviations.csv for the text and cooperative counts; the D-55 correction before any regression. Columns and caveats: REGISTER_VARIABLES.md.
  • 1910 register text abbreviations — combinations decoded 2026-10-29 (D-156). The print builds institutions out of its own abbreviation parts (hosp. pokr. k., Waisenh.); both printed lists are in abbreviation_components.csv, the code table grew by 148 rows, and notebook 15 §8b lists what no code reaches. Next: read that census on the full run and add what it prints; replay the frozen gold passes from their crop caches (Heavyhorse6) before scoring abbreviations; rule on the German GB. (the list prints Gerichtsbezirk, the table reads a court).
  • German table reader — built 2026-10-29 (D-160), not yet read by a model. Böhmen, Mähren and Schlesien's table is cut into blocks off the image (no OCR) and read by the Czech table's two readers; the frozen route reads it after each German edition's notes, and the register names German places off it. Next: the first paid read on Heavyhorse6 (Böhmen gold, then Mähren/Schlesien calibration); score Böhmen against the German gold (rf.score_table) and look at the M/S rows' sums.
  • The route's pre-freeze review — fixed 2026-10-30 (D-164 to D-167), not yet run. Six reviews of notebook 15, 47 findings ruled by the author: the route, the ledger, the Opus calls, the German table, the readers' caches, the register build and the sealed scorer, each fix with its test. Next, on Heavyhorse6: the Gemini batch check (7e) once, then "bohemia" / "gold" with REREAD — every pass from the cache where its prompt is unchanged; the Czech gold's Opus calls whose example cards changed, and its place-type calls, are asked again — then "de-bohemia" / "certification". The scorer refuses a run not read on its gold's frozen route (D-165), so nothing may change between the gold re-run and the last certification run.
  • German half certified 2026-10-31 (D-164's route): F1 0.957 [0.927, 0.982], pooled over the 8 sealed pages (Böhmen 0.976, Mähren 0.984, Schlesien 0.909); 0.966 against the sheet the author's rulings corrected. 40 of its 54 errors were two look-alikes, now settled by where the print sets them (D-169: the cooperative mark before a colon; a poorhouse in the castle's slot), replayed at 10 fixes and 0 breaks. Next, on Heavyhorse6: the gold re-read with the rules (REREAD), then the full run; the fixed route's own measure on the sealed pages is in-sample (D-170). Open: one ruling, Schlesien 0038 Kuttelberg 11,0.
  • German half certified a second time 2026-10-31 (D-170), in-sample: F1 0.995 [0.991, 0.999] (618 right, 1 false, 5 missed; entries exact 128/133; Böhmen 0.998, Mähren 0.998, Schlesien 0.990). The rules were found on these pages, so this is never quoted in place of round 1 or the Czech 0.996. Covariate symbols, blind round 1 / round 2: school 103/104 → 104/104, fire brigade 92/93 → 95/95, cooperative 69/71 → 71/71, midwife 60/62 → 62/62, post 34/35 → 35/35; false claims 1, 3, 0, 1, 1 → 0 each. Castle 6/14 → 13/14, poorhouse 7 false → 0. Text abbreviations on the sealed German pages (score_sealed_abbreviations.py): round 1 144/144 hit, 1 false, F1 0.997; round 2 139/144, 1 false, F1 0.979 (five cooperative letters lost on the re-read); Czech waits for the full run. The full tables are in LEXIKON_1910_ROUTE.md ("Everything certified, in one place"). DINOv3 still routes nothing for German (judges 0.115 of claims against 0.863 Czech); the rescale test (run_scale_e3_test.py, line height 66) has not completed. Next: the full run on this route.

  • Before the full run — 2026-11-01 (D-173 to D-179). The gold re-read and re-frozen on Heavyhorse6 (14:31); the tree on disk checked identical to that freeze, every route module and input, and 754 tests pass. Four reserve pages (Mähren 71 and 139, Schlesien 28 and 48) are sealed for the next certification (D-173); the entry-level validation sample is not drawn (D-174). German DINOv3 dropped (D-175 to D-177): with a box certainly on the glyph and German exemplars in its bank it names German pictograms well, but nothing finds them on the German print — the lighter ink breaks the strokes and the glyphs touch their neighbours, so the candidate step loses 9–29 % of the author's 203 clicked glyphs and the placement puts the box on the right glyph for at most 27 % of claims; given the reader's own boxes, cut tight, it still kept only 0.856 of true claims against a bar of 0.936 written first, filing one German pictogram in seven with the letters around it. In the route a DINOv3 doubt sends a claim every reader agreed on to Opus, so that is thousands of extra Opus questions on true claims to catch agreed false claims running at about 1 in 620 — at most about 0.1–0.15 pp of precision, and nothing for misses, 5 of the 6 German round-2 errors. The German route stays as certified, DINOv3 recorded and routing nothing. Every page was then cut offline before anything was spent: the 17 statutory-city pages are skipped and their institutions read from the prose (D-178; both stages read, 5 groups ruled by the author, 1 open), and German pages with faint rules are cut by position and the volume's template (D-179; page audit 0 cut fails, 57 through a fallback). Register 1.6.0 types every school on the legionnaires' education ladder (n_school_<type>, school_top_rung); 1.7.0 keeps every claim's way back to its glyphs (entry, marks → the run's ledger). The run is held to completeness by outcome (D-180): a reader's request with no reply or an Opus call with no answer — an exhausted balance included — stops the ledger until it is filled or named in ACCEPT_FAILED. Still open on the person side: legionnaires reach the register at court or okres level only (no birth settlement_id), to be fixed after the run. Next: the Schlesien full run on Heavyhorse6 as a smoke test (37 pages, none through a fallback), then Mähren (its 9 fallback pages), then Czech and German Böhmen; notebook 15 §18 then builds the register.

📋 Open — analysis

The modelling stage has not started and the approach is still being worked out; this page deliberately does not commit to a specification. What is already framed:

  • Question: what drives a second desertion — the revealed-preference core of the design.
  • Covariate of interest: relative rank deprivation on Legion entry (rank_reset), read on combat ranks (~legion_noncombat) and examined by theatre (Russia carries essentially all the desertion).
  • Identification threat (Ottinger): enrollment officers may have demoted soldiers showing prior disloyalty — to be addressed with late-enrollment restrictions and balance tests.
  • Occupation is missing in a way that tracks the outcome (found 2026-08-27, open). In Russia, occupation is absent on 8.87 % of Code-D deserters against 0.31 % of the demobilised, and the gap holds inside every enrolment cohort (1914: 27.3 % vs 1.0 %; 1916: 23.6 % vs 0.3 %; 1918: 7.2 % vs 0.2 %). It is not archival coverage — the two groups hold the personal card at an identical 87.11 %, and conditioning on the card the gap is 7.96 % vs 0.16 %, so the document is present and the occupation line inside it is blank. Consequences: (a) complete-case analysis on occupation silently selects on the dependent variable — carry missing as its own category; (b) report results with and without the occupation control; (c) the archival question — whether the field was entered at enrolment or completed over the course of service — is now worth putting to VÚA directly, because the answer decides whether occupation can be treated as pre-determined at all. The provenance flags (D-44) describe this differential; they do not control it away.

⚠ Left open by the 2026-09-22 audit

Everything the audit found is fixed, except what it chose not to decide:

  • education_complete (D-53) — still conflates years attended with classes completed; the recommended fix (<NA> on a bare ob. N) changes an emitted column and waits for a decision.
  • Probable duplicate persons — 625 csol and 281 VÚA rows share surname, given name, full date of birth and birth municipality with another row of the same file. dup_group flags them; whether to collapse any is an analysis choice, not a cleaning one.
  • Five Wikidata points shared inside one 1910 okres (11 settlements) — harmless for every okres-level variable, wrong for anything finer than the okres; kept rather than guessed.
  • Four settlement timelines still carry another entry's text: three where the register itself omits the comma between two clauses, one narrative. Below the level at which a rule would not also break a correct entry.
  • Anything computed on the old linkage — the overlap is 1,340 men, not 2,239, and the pairs that left are mostly the note-confirmed ones (463 → 139). Every comparison-design number from before 2026-09-22 has to be redone.

⏸ Deferred / out of scope

  • Occupation / education refinements — as above, when the analysis needs them.
  • The Slovak half of the party scheme. affiliations.py 2.0.0 rests on Akademický atlas plate IV.37, which is v českých zemích and so contains no Slovak party. SL (108 records, the largest unmatched token), ludovák/ludová, and the distinction between Slovenská liga and Hlinka's Slovenská ľudová strana are unresolved and recorded as refusals (D-50). Needs a Slovak party register — the natural counterpart to IV.37. 2026-11-03: the records settle SL without one. It is the Slovenská liga v Amerike (Slovak League of America), an émigré organisation: 188 of the 214 records naming SL or a liga mention the United States, and 139 enrolled in the Czechoslovak army in France, where the American volunteers went. The same check found a defect: ČNS, coded nar_soc (Č8), is on 220 records, 189 of which mention the United States, with applications filed in Cleveland, Chicago, New York and Omaha. That is the České národní sdružení (Bohemian National Alliance), not the National Social party. Fixed the same day (D-187, affiliations 2.3.0): SL joins slov_liga (63 → 211) and ČNS is its own dummy pol_cns (222), out of pol_nar_soc (6,541 → 6,319). Notebooks 02 and 03 must be re-run for data/final and the affiliation figures to carry it. A Slovak register is now needed only for ľudovák (15 records).
  • Habsburg military-court inventories (VÚA) — examined 2026-10-12; closed for good 2026-11-03 (the author: no way of doing this). Three name-level inventories were parsed (utils/parsing/vua_courts.py: 2,330 case files, 3,034 accused, 899 soldiers) and the soldiers linked to the legionnaire file on name + birth year + Habsburg unit (utils/reference_tools/units/run_vua_courts_pilot.py). The link is barely above coincidence — 96 soldiers with a candidate against 83 and 77 under a shifted-birth-year placebo, 8 picks corroborated by unit or birthplace — so it gives no person-level measure of prior disloyalty; and at district level the 1,896 civilians with a birthplace spread over 1,241 places, too thin for a covariate. Not pursued; the code and the DATA_SOURCES.md #23 entry stay as the record.
  • Amateur-theatre database (DbČAD) — scraped in full 2026-10-12; being built into a Czech-nationalism treatment since 2026-10-28. The Databáze českého amatérského divadla (NIK) was tried as a settlement-level, dated measure of pre-1914 Czech associational life in a legionnaire's birthplace. Every one of its 7,368 place entries is cached and parsed (utils/scraping/scraper_amaterskedivadlo.py → data/raw/theatre/places.csv and ensembles.csv): 34,569 ensemble rows with start and end years, a fuzzy 191x kept as a decade and never coerced to a year; and per place the první zpráva (first surviving report — not a founding date) together with the three free-text blocks kept whole for later reading — the chronicle (7,340 places), the bibliography (6,391) and the archive citations (5,359), one entry per line. 93.6 % of the places tie one-to-one to a row of settlements_1910. What the coverage tabulation shows (utils/reference_tools/places/run_amateur_theatre_pilot.py --all; every figure here is its printed output of 2026-10-13): an ensemble certainly starting before 1914 in 14.5 % of Bohemian and 21.0 % of Moravian 1910 settlements, but the gap is composition — inside okresy at least 90 % Czech-speaking it is 30.0 % against 28.2 % of ordinary communes. Three things stand between this and a covariate. The database records Czech theatre only (17 of 34,569 ensemble names look German), so a German-speaking village is uncovered, not zero, and language is known only at okres level until the 1910 register is merged. Coverage rises steeply with size — 69 % of judicial-district seats, 21 % of other communes, under 3 % of hamlets — and the repo has no settlement-level 1910 population, so size is a three-class proxy. And each okres was compiled from its own sources: first-report years heap on one year per okres (Jihlava 1929 × 21, Hradec Králové 1903 × 17, Kutná Hora 1949 × 21, all citing one district report), which argues for comparisons within an okres. Nothing is joined to the person files and nothing feeds data/final. When resumed: read the chronicle and archive lines for dates the ensemble list lacks, find a size measure, and settle language at settlement level. The chronicle is read (2026-10-27, amateur_theatre 0.2.0, D-137): chronicle_lines() turns every place's chronicle into one row per line, keyed (geo_id, line_no) — a dated event, an undated note, a periodical citation, the database's admin note on the present okres, a heading (a Loutkové divadlo heading opens a puppet section) or a separator — with the leading date as start and end intervals, the associations the line names (15 labels: the pilot's five plus the free-standing dramatic society, reading, singing and educational societies, Omladina, workers', Catholic, the national-defence unions, school and church) and a puppet flag. Figures from chronicle_report() on the full cache, 2026-10-27: 38,940 dated events in 7,115 places, 8,409 of them certainly before 1914; 2,966 places have a pre-1914 non-puppet event, and 256 of those have no ensemble certainly before 1914 — the chronicle adds organisations and dates more than places. The pilot writes the table to data/interim/theatre/chronicle_lines.csv; nothing goes to data/final. The database's own abbreviation key is read in as well (2026-10-27, amateur_theatre 0.3.0, D-138): saved as data/raw/theatre/abbreviations.csv (309 entries + 43 source sigla), it raises the pre-1914 events naming a free-standing dramatic society from 1,111 to 1,434 and leaves 3,130 of 8,214 naming none; 33 rest on the two-way ČOS and are flagged, 92 name ÚMDOČ. Still open: the archive and bibliography blocks, and the settlement key the person files lack (birth_settlement_id), without which no person reaches a theatre place except through birth_qid. Source, citation and rights: DATA_SOURCES.md #22. The treatment is being built (2026-10-28, amateur_theatre 0.4.0, D-140): the end product is a place × year activity panel, 1800–1913, with certain and possible bounds: companies active, performances, distinct plays, companies performing, attendance, foundings, national-cause proceeds; German, bilingual and puppet theatre in columns of their own. Settlement, court and okres tables are built from it, and nothing is joined to persons yet. Step 1 is done: chronicle_clauses() cuts each note where a new date opens (data/interim/theatre/chronicle_clauses.csv). That makes 8,656 pre-1914 non-puppet clauses in 2,974 places, up from 8,219 lines, and each one is tagged with the event types its words point to (EVENT_CUES, drafts and not codes). Step 2 is done (D-141): the author ruled each type and twelve design calls on the codebook page. Step 3, first pass, is done with rules: code_clauses(), then evidence_table() (the ensemble list, the clauses and the first reports in one interval form, with one company_id per company), then place_year_panel() (7,368 places x 1780-1913, each measure certain and possible), then area_year_tables() (summed to 1910 settlement, court and okres). All of it goes to data/interim/theatre/. Czech activity is certain in 3,063 places. Next is a model pass on what rules cannot settle (Haiku 5.5 against Sonnet 5.5 on a ~300-clause gold sample; prepared with spending off and run on Heavyhorse6), and the evidence table and panel. Step C is in (2026-10-28, amateur_theatre 0.6.0, D-145): the 682 ensembles that start before 1914 and print no end were scraped on Heavyhorse6 (data/raw/theatre/ensemble_pages.csv). The pages almost never give an end (1 year), but 588 list the decades active and 284 carry a chronicle of their own, 97 % of it absent from the place chronicle. By the author's rulings, a listed decade is possible activity, and a page that lists decades replaces the open end. A printed end closes the span, certain. The page's chronicle is coded like a place's and tied to its ensemble. Certain places 3,063 → 3,068; certain place-years 52,700 → 53,403; possible place-years 93,113 → 91,651. data/interim/theatre/ is built with it (2026-10-28, --all --reuse-tables). The rest of step C is in (2026-10-29): the pages of all 7,103 pre-1914 non-puppet companies are cached (the other 6,421 fetched from the laptop, 0 failed), and the runner has rebuilt data/interim/theatre/ on them. Every one of the new companies already had an end in the list, so they add what the companies did more than when: certain places 3,068 → 3,077 and certain place-years 53,403 → 53,696, but certain performances 2,917 → 11,322 (in 915 → 1,680 places), distinct plays 769 → 1,625, foundings 3,574 → 5,981. The ensemble chronicles now give 59,864 clauses, 13,193 of them left to the model pass. The full method is on the Theatre measure sheet.
  • Matice česká founder register — downloaded and read into tables 2026-10-29. The society's yearly list of its founders (zakladatelé), issues 1873 and 1890, in library/associations/ (utils/scraping/run_matice_downloads.py; source, rights and caveats in DATA_SOURCES.md #28). Each entry gives name, occupation, residence, entry year and membership number; the 1890 issue adds the deceased and the corporate founders, so it reaches back to 1831. A candidate okres-level measure of committed Czech nationalism beside the theatre measure; the design (unit, denominator, local vs posted founders, Moravia) is still to be discussed.
  • Matice moravská member lists — located, downloaded and read into tables 2026-10-29. The Moravian counterpart, printed inside Časopis Matice moravské: full rosters in roč. 15 (1891) and roč. 33 (1909, with a tabulation by occupation and place), partial lists for 1876–1878, the 1891 additions and the local trustees (důvěrníci). 1878 is held as an Internet Archive scan; the rest are jobs mm1876 … mm1909 of run_matice_downloads.py v1.1.0, run 2026-10-29 (7 items, 56 pages, every one with scan, OCR and ALTO). A small, educated membership, so an élite measure; source and caveats in DATA_SOURCES.md #29.
  • Both registers are read by rule (2026-10-29, matice 0.1.0, D-158). utils/reference_tools/places/run_matice_pilot.py writes data/interim/matice/matice_entries.csv, one row per printed entry with its own text beside name, occupation, residence (as printed), entry year, membership number, year of death and amount paid, and matice_totals.csv, each list against the total its society printed: Matice česká persons 99.2 % (1873) and 99.7 % (1890), deceased 97.8 / 98.9 %, corporate 106 / 96 %; Matice moravská 1909 97–100 %, the 1891 run-on roster 92–94 %. The membership number links 3,125 Matice česká founders across the two issues, and gives 2,350 of 2,396 deceased founders an entry year. The 1876 and 1877 lists' (Pokračování budoucně) turned out to continue in what is held: the 1876 contributors in the 1877 list ("a) Za r. 1876"), the 1877 ones in the 1878 Internet Archive list ("Za rok 1877"), which is now read from its PDF text layer (310 entries, each dated by paid_for_year).
  • The registers are an okres measure now (2026-10-29, matice 0.2.0 + locative 0.1.0, D-161, provisional). Residences are matched to 1910 settlements by declining every 1910 name into its locative and genitive forms (utils/parsing/locative.py): of Matice česká entries 49 % reach a settlement and 17 % Prague, 10 % are refused as ambiguous, 7 % lie abroad, 11 % are unmatched; of Matice moravská entries 73 % and 5 %. data/interim/matice/matice_okres.csv holds, per census okres, the founders and members placed there, rates per 1,000 inhabitants and Czech speakers, shrunk rates, posted and local counts apart, and matice_own_z — each land's own society, standardised within the land. Against the theatre measure (Czech-majority okresy, 1880–1890) it correlates 0.25 in Bohemia and 0.47 in Moravia. To rule: denominator, whether posted founders count. Ruled 2026-11-02: Moravia and Silesia are measured by the Matice moravská roster of 1 March 1909 (D-182; Moravian theatre correlation 0.49 → 0.37 against the 1890 list, the two lists rank okresy at Spearman 0.84). Matice česká has no founder list after 1891, so Bohemia stays on its 1831–1890 founders. Nothing reaches data/final or the persons yet. Name variants (locative 0.2.0): a settlement is found by its present Czech name, its 1910 name, its older Czech names (settlement_aliases_1910), a short list of historic names (Holomouc, Král. Vinohrady), old spellings (Oustí), abbreviations (Jindř. Hradci), and its German name both as written and as a Czech list spells it (Švarcbach, Šildperk); an OCR misspelling is matched only at rapidfuzz ≥ 96 with a clear margin, a cutoff set by reading every match at 92 (13 entries). Places abroad, Galicia and Upper Hungary included, are caught before matching. Since locative 0.3.0 the commonest OCR misreadings are repaired before matching (cli→ch, x→h, initial Y→V, a word cut in two, stray marks), and a qualifier is itself located: a settlement within 30 km of the place it names is preferred among homonyms, and a misspelling scoring 85–96 is accepted only when exactly one such nearby settlement fits (Pia šech u Plzně → Plasy). The parser also cuts entries the OCR ran together (… v Hranicích Štolba Josef, …). The 954 entries still unmatched after that (all but the 1878 PDF's) were re-read from line crops of the scans by a vision model (D-162, reference/places/matice_residence_readings.csv), and nineteenth-century spellings are tried as variants (Žehouni, Hořejní, Mezeřiči). Unmatched entries now: Matice česká 7.0 %, Matice moravská 4.3 % (694 in all, from 1,106). What remains is mostly small places the 1910 table does not hold and places abroad with no marker. Court districts and maps: data/interim/matice/matice_court.csv carries the measure per court district (court census 1910 as denominator, the nine statutory-city courts pooled as the census pools them), and utils/reference_tools/places/map_matice.py draws it by okres, by court and by municipality to output/figures/matice/.
  • Intensity maps, side by side (2026-10-30, D-163, provisional). utils/reference_tools/places/run_intensities.py writes data/interim/intensity/intensity_{okres,court,settlement}.csv: Matice (matice_own_z), theatre companies active per 1,000 (1880–1913, certain), legionnaires per 1,000 inhabitants and per 1,000 Czech speakers, and desertions per 1,000 legionnaires (shrunk). map_intensities.py draws them to output/figures/intensity/, and the docs site's Descriptives sheet carries them as linked panels, with the definitions from INTENSITY_MAPS.md above them. Limits: 57 % of Czech-lands births name a court (court counts scaled to the okres), and settlements have no 1910 population, so the place view shows counts. Next: settlement populations from the 1910 register; the rulings on each definition.
  • Matice česká joiners, 1886–1914 — found, downloaded and read 2026-11-03 (matice 0.4.0, D-184, provisional). Every issue of the Časopis Musea království Českého (Národní muzeum, Data sources #31) closes with the month's payments into the Matice, so the founders who joined after the register stops (1891) are named after all. run_matice_downloads.py 1.2.0 finds the lists by full-text search (--discover-payments → reference/places/matice_payment_pages.csv, 94 slices, 186 pages) and fetches them (--set payments); matice.parse_payments reads them month by month against each month's printed total, payment_persons gathers a payer's instalments, and the joiners of 1900–1909 are now Bohemia's measure, both lands standing at about 1909. The pilot writes matice_payments.csv, matice_payment_months.csv and matice_payers.csv beside the other tables and prints the checks. A joiner is dated by the first payment (ruled 2026-11-03). Placing improved the same day (locative 0.4.0: abbreviations that lost their last full stop, one-letter rivers, Prague streets, neuter plurals; matice.borrow_places): payments placed 82.7 % → 84.4 % (Prague included), the measure itself almost unchanged (Spearman 0.995). The combined measure is a log density since D-185, following Squicciarini & Voigtländer (2015): ln(1 + members per 100,000), z within territory, Prague (z 3.10, 8.26 in levels) a dummy in every regression, the level z and an any-member dummy as robustness. A small audit of the Matice and theatre code the same day fixed seven things (matice 0.6.0, amateur_theatre 0.6.1): founders keyed by number and surname (the OCR gave two people one number: 3,037 → 3,296 founders placed in Bohemia), institutions named anywhere kept out of the persons (Rokycany's gymnasium had been three joiners), a hyphen after an amount, OCR'd titles and totals, a stray bar read as a digit, each payer's place and occupation taken from whole payments, the name order read per month (1890 register check 50 % → 68 %), the theatre check set against each measure's own years, and the theatre tables rebuilt and keyed by settlement_id. The measure moved little (Spearman 0.996). Open: the window; that the joiners do not track the theatre measure inside Czech-majority Bohemia; and a look by eye at the months furthest from their printed total. Both denominators are carried (D-186, ruled 2026-11-03): per inhabitant (matice_own_z) and per Czech speaker (matice_own_z_czech, units with at least 1,000 Czech speakers), now built in matice 0.7.0; the intensity tables' matice_per_1000, which had been per million, is per 1,000. The window is 1895–1909 (D-188, ruled 2026-11-03): a decade was too few people (median two joiners per okres), and the wider window ranks okresy closer to their joining outside it (Spearman 0.83 against 0.79); 973 joiners placed in Bohemia, 25 okresy without one (35 before), matice_own_z 0.93 with the 1900–1909 version. Still open: whether posted members count (329 of the 973 Bohemian joiners are priests, teachers and officials, and 323 students).
  • Side study: Habsburg censorship and the disruption of Czech amateur theatre, 1780–1914 — found 2026-10-28; dropped 2026-11-03 (the author: no longer relevant). The DbČAD chronicle (data/interim/theatre/chronicle_lines.csv) records refusals and bans of named performances with place and year, and nearly all of them hit local players — students, burghers, clergy, from the 1860s the amateur societies — not travelling companies. Three regimes show: church and state bans on passion and religious plays in the 18th century; the Bohemian governor's office (gubernium) refusing Czech student and burgher performances in the 1810s–1840s, sometimes over a regional office's recommendation; and district offices (hejtmanství) refusing permits for single named plays, or punishing an unpermitted one, in the 1860s–1880s — censorship at the level of the play, with the stated reason sometimes given. A keyword pass found 46 pre-1914 ban notes in 41 places, about a third of them WWII bans or noise, so nearer 30 real ones; beside them about 50 fires of the venue (inn, town hall, Sokol hall) that stopped theatre, a shock from outside the society, and about 40 suspensions (Bach's absolutism, WWI). Small, but dated and placed, and it links to Purš's repression of the tábory (D-37) and to the repression-and-loyalty mechanism of Rozenas, Talibova & Zhukov (2024). The event codebook of the theatre work codes these types; the study would need the archive block's governor's-office files and the cited SÚA Podání register to go further. Counts from the exploratory scripts of 2026-10-28, not from a build.
  • PhD-pipeline paper — a separate interwar-electoral project sits outside this thesis.

Edit this file (reference/ROADMAP.md) as work progresses and rebuild the docs; it is the canonical source for this page.

What's newOrientation

Site updates + the live-page link. · source: reference/SITE_CHANGELOG.md

In short

Changes to this documentation site (features and sheets), newest first — the curated half of the record: what changed and why it mattered. The machine half is Commits & versions; the calls taken are in Decisions; what remains is on the Roadmap.

Live page: tomashaufer3.github.io/legions-docs — unlisted (a noindex tag keeps it out of search); share by link.

2026-11-03

  • The Overview's map has the denominator switch. As on Descriptives, the Matice, theatre and legionnaire maps can be read per inhabitant (the default) or per Czech speaker; desertion stays per legionnaire. The map now draws the Descriptives panels themselves (_IX_PANELS), so its columns and labels cannot drift from theirs.

  • The Overview opens with five interactive panels. Under the summary card, one tabbed At a glance block: the pipeline, every source, build step, check and the planned analysis as one diagram with its live count and status (hover for what it is, click for its sheet); desertion over time, by month and theatre, as desertions, enrolments, men in service or the rate per 1,000 men in service — the spell sample the hazard model will use, enrolment and exit month both known — with the February revolution, Zborov, the coup and the armistice marked; the map, Matice, theatre, legionnaires and desertion per inhabitant by 1910 okres (provisional, from data/interim/intensity); one page of the 1910 register, each zone glossed on hover; and one man, two decisions, a complete record drawn from a fixed-seed sample of 300, half of them deserters, without name or municipality and dated to the month.

  • The Overview makes the case for the project. It was the repository README: data table, publishing notes and repository tree, with its numbers typed. It now runs question → the double-desertion design → the headline fact (the Russian monthly series around the Bolshevik coup, and the desertion rate by theatre drawn from the build) → the data → how it is checked → where it stands, then the reading routes, every sheet, how to run it and the module index. Its prose is reference/OVERVIEW.md, whose {{token}} slots overview_facts() fills at build time from data/final, the curation tables, the Ostafin check's output and the QA gate; a token the builder does not compute stops the build. Typing the numbers had already gone wrong once: the README's "under 0.5 % in every other theatre" is contradicted by the Serbian/Balkan formation, which the theatre bars now show with its count. The OccCANINE figure is computed by agreement() in run_occcanine_check.py 1.1.0, shared by the check and the page, on the current curation.

  • Bohemia is measured by the Matice česká joiners of 1900–1909, on a log scale. The founder register stops in 1891, but every issue of the Časopis Musea lists the month's payments into the society; Matice, Data sources #31 and the intensity maps on Descriptives now take Bohemia from those lists: a joiner is a person whose first payment falls in 1900–1909 (D-184). The combined measure is ln(1 + members per 100,000), standardised within each society's territory, as Squicciarini and Voigtländer (2015) build Encyclopédie subscriber density, with a Prague dummy for every regression (D-185); Prague drops from +8 to about +3 on the map. The theatre check sets each Matice measure against theatre of its own years. A small audit of the Matice and theatre code the same day fixed founders the OCR gave one membership number, schools and offices counted as joiners, and the theatre tables' settlement key (Roadmap).

  • Matice moravská is its 1909 roster. On Matice and the intensity maps on Descriptives, Moravia and Silesia are now measured by the members of 1 March 1909, the list nearest the 1910 census, not those of 1890 (D-182); the map circles, hover labels and the combined matice_own_z follow. The theatre check keeps the 1890 rate as a row for comparison. Bohemia stays on the Matice česká founders of 1831–1890, since the society printed no founder list after 1891.

2026-11-03

  • The register's German table has an outside check (D-183). Ostafin et al. (2020) typed every Silesian commune's 1910 census from the same volume the full run read. The Validation register and the reading route now carry the comparison: the census we use is identical in all 12 political districts, and the full run's table equals their transcription on 98.6 % of 441 communes. The same comparison shows why the ČSÚ lexicon 1869–2005 does not reconcile: its 1910 figures sit on 2005 parts. The source is on Data sources and in the references.

2026-11-02

  • The correlation tables are full matrices that say what they are. How the maps move together on Descriptives printed a lower triangle with numbered columns; it is now the full symmetric matrix, its columns named and grouped by denominator (per inhabitant, per Czech speaker, per legionnaire), shaded by sign and size, with each cell's number of districts on hover — a cell uses only the districts that have both rates.
  • Ten more requirements in the legionnaire sample builder. Build your own sample adds the hazard model's spell (enrolment date known, exit date known, no exit before enrolment) and what it conditions on: Czechoslovak Legion only (no foreign armies), combat rank at entry, born in the Czech lands, birth year known, occupation class coded, education rung coded, and no probable duplicates. The link and the pandas snippet carry them.
  • Four intensity maps, and a denominator switch. On Descriptives the maps are now Matice, theatre, legionnaires and desertion; the second legionnaire panel (per Czech speaker) became a switch that sets the denominator of the first three, all 1910 inhabitants or the Czech speakers among them. run_intensities.py 0.2.0 adds the Matice and theatre rates per Czech speaker (the Matice one shrunk and standardised as per inhabitant); desertion stays per legionnaire, and the settlement view, which has no population, keeps counts. The correlation tables carry both denominators.
  • The settlement maps can be read. On Descriptives the place view of the intensity maps drew every settlement as a dot of radius 3.5–22 on a panel about 490 units across, so nearly 5,000 overlapping dots covered the map; they are now scaled as on the Matice map (0.9–7) and translucent. The desertion panel was black: shrunk, every settlement gets the same desertion rate (between-place variance comes out at zero), so its colour scale had no width and every dot was given an invalid colour. It now draws deserters as a count.
  • Every script sits in its stage. Nine parsing modules — among them occupations, religion, provenance, ruian and the theatre model pass — had been filed under Output & support because the role table missed them; they now sit under Person parsing or Geography & census.
  • Pipeline names every module again. The guide still described three notebooks and the state of 16 July, and named about a third of the modules in utils/. It now covers notebooks 15 and 16 and notebook 02's §3e, and adds three sections: the downloaders beside the two scrapers, the 1910 place register (the runner, the page modules, the readers and checks, the ledger and register build, the gold and the score), and the reference tools and pilots. Each module gets at least one line; its own sheet still holds the detail.

2026-11-01 (the register before its full run)

  • The run cannot finish with replies missing (D-180). The reading route's Notebook 15 — how it runs table gains ACCEPT_FAILED and says what 7d, 12 and 14b now check: every queued request has its reply and every Opus call its answer, whatever stopped one, or the ledger is not built. 1910 register — the municipal data documents the two new columns that lead from any register claim back to its glyphs' history in the run's ledger, entry and marks.
  • The statutory cities: 26 open groups down to 1. The reading route and the municipal-data sheet say how: the judge's rows are now accepted with a count printed after se/mit/( and a code given as its expansion (20 groups, from the cached verdicts), and the author's ruling settles five (a council with language sections is the council and a row for its sections). Prague now agrees across the two editions on transport and professions too.

  • 1910 register — the reading route: the route chart (The route a mark travels) now shows all four editions — German Moravia and Silesia join Czech and German Bohemia — and says of Mähren and Schlesien that they have no gold of their own and are held to the Böhmen gold's freeze. The step texts carry the decisions since 30 October: the statutory-city pages skipped and read from their prose (D-178), the faint-rule cut on German pages (D-179), the look-alike rules from the print's syntax (D-169) and the German part-0 question (D-171), and the entry-level sample not drawn (D-174). Step 3 now states why DINOv3 routes nothing on the German volumes (D-175–D-177): it names a correctly boxed German glyph well, but nothing finds the glyphs on the lighter German print, and with the reader's own boxes it still flagged one true claim in seven, each of which would have gone to Opus.

  • The chart counts Opus again. Since the claim-level rulings, a claim's rule joins its marks' rules (claim ruled: opus + opus, Czech anchor, claim ruled: opus), and the chart knew only the bare opus: most of Opus's settlements fell under "no band" (on German Silesia, Opus showed 0). A claim is now put under the strongest step that settled it, and the D-169 position rule is a fifth exit, off step 3: settled by where the print sets it. Every rule string on disk lands in a band.
  • The German certification by round. The chart and the Is the sample large enough? tables label round 1 blind, the record and round 2 in sample (D-172); the site had been showing round 2 as the certification. Those tables had also been missing since the paragraph they hang from was renamed; the build now warns if that happens again.
  • The sheet's certification section is corrected where the pre-run review found it overstated: eight German sealed pages, not four; the full run's readings of the sealed pages are a test–retest, not an independent measure; exact bounds on the error counts lead, and errors that cluster by geography are named.
  • Roadmap, Decisions and 1910 register — the municipal data carry the state before the full run: D-172 to D-179, the statutory cities' institutions (register_statutory, printed totals flagged) and the school types on the education ladder (register 1.6.0).

2026-10-30 (intensity maps, side by side)

  • Descriptives gets a section, Nationalism, the Legion and desertion, side by side. It shows Matice, amateur theatre, legionnaires per 1,000 inhabitants and per 1,000 Czech speakers, and desertion per 1,000 legionnaires as five linked maps, which can be switched between political okres, court district and settlement. Hovering a unit outlines it in every panel and lists all five numbers. The definitions and what each level can carry (reference/INTENSITY_MAPS.md) sit above the maps, and a rank-correlation table follows. Built from data/interim/intensity/, provisional (D-163).
  • Matice map dots: the settlement circles now stop growing at the 99th percentile. Prague, Brno and the other largest places get an outlined dot instead of a disc covering half a land. The white halo is gone, and the dots are opaque enough to read over the shading.

2026-10-30 (the Matice measure gets a sheet)

  • Matice measure, a new How it works sheet: the two member registers, how the entries are read from the scans, how a residence finds its 1910 settlement, the measure, and what is open. Its tables are built from data/interim/matice/ at each build: entries found against every printed total, where the residences landed, and the check against the theatre measure.
  • An interactive map on it: every 1910 court district shaded by its political okres or by its own value — the combined measure or either society's shrunk rate per 1,000 — with the settlements' members as circles that can be switched off, and the numbers on hover.

2026-10-29 (the Matice česká and Matice moravská member registers)

  • Data sources #28: the Matice česká founder register, issues 1873 and 1890, downloaded into library/associations/ as a candidate measure of committed Czech nationalism. The row says what one entry holds, the three lists of the 1890 issue and the three caveats (residence at the list date, appointed clergy and officials, two issues only). Not parsed yet.
  • Data sources #29: the Matice moravská member lists printed in Časopis Matice moravské — the full rosters of 1891 and 1909 (the latter tabulated by occupation and place), the partial lists of 1876–1878, the 1891 additions and the local trustees — located from every public volume's contents and downloaded beside #28.
  • Both registers are now read into one row per printed entry (utils/parsing/matice.py, D-158), each list held to the total its society printed; the module and its runner have script sheets.
  • Their residences are matched to 1910 settlements (utils/parsing/locative.py, a new script sheet) and summed into a provisional okres measure (D-161), with its check against the theatre measure on the Roadmap. The matcher also reads German names and guarded OCR misspellings, the measure is carried down to court districts, and a new script sheet, map_matice.py, draws it by okres, court and municipality. Residences the OCR garbled are re-read from the scans and kept as a curation file (D-162).
  • The downloaders in utils/scraping/ are now all filed under Scraping on the script sheets; the Kramerius modules and the run_*_downloads runners had been under Output & support.

2026-10-29 (the register becomes municipal data)

  • 1910 register — the municipal data, a new How it works sheet: the four tables in data/final/register/ (places, institutions, cooperatives, one row per municipality), every column, how a claim becomes a variable, and the caveats. Its numbers are a block the build rewrites (D-148).
  • Brzice carried past the frozen run on Digitising the 1910 register, in a new section under What one village had: the municipality and its four osady anchored to settlements_1910 (with the runner-up and why Grund is found through its 1910 name), the four claims, Brzice's row in the municipal table, and Brzitz found through the Czech volume. The last step shows the German alphabetical order the Czech table predicts, the gap the gold page cannot close (Aujezdetz is Újezdec, on a later Czech page) and the leave-one-out test of the rule (D-150). Built by utils/lexikon_1910/build_register_walkthrough.py from the gold runs.
  • The figure's after it box no longer says the editions are matched by name only.

2026-10-28 (the theatre measure gets a sheet)

  • Amateur-theatre measure, a new How it works sheet. The measure had grown across five decisions (D-137 → D-145) with no page of its own. The sheet sets out what the database gives, the six steps from chronicle text to the 1910 area tables, what certain and possible mean record by record, every ruling, the panel's columns, the before/after counts of D-145 (the ensembles' own pages) and what is still open.
  • Three interactive figures on it, read from what the theatre runner writes (data/interim/theatre/) and absent until it has run:
  • Czech theatre over time: places, companies or performances per year, 1780–1913, the certain line inside the possible band, with 1848 and 1867 marked.
  • Where Czech theatre was: every 1910 court district shaded by the share of its settlements with Czech theatre, with a year slider and play, certain or possible.
  • From records to the panel: for a few places picked by rule, each record's certain and possible years and what the place gets in the panel. Facts only; the chronicle's prose stays in data/.
  • The sheet opens in plain terms: what the database tells us about each company (when, what, where) and what we make of it, before the technical account. Where it stands has the build on all 7,103 ensemble pages (2026-10-29) beside the 682-page one, and Open flags how one-off events take an open record's possible years.

2026-10-25, late night (the page loads in a quarter of its weight)

  • The 117 script sheets load when opened. They were 32 of the page's 41 MB — mostly the line-numbered source — and every visit parsed all of them to show one. Each is now its own file under scripts/, fetched the first time it is opened: the page is 8.7 MB. A link to a function's anchor still lands (the page knows which sheet holds it), and the search box still searches the source: their text is fetched once, on the first search. Opened straight from disk rather than through the site or a local server, a script sheet says why it cannot load.
  • Mähren and Schlesien have their own sealed pages (D-133) — the decision and the draw are on the Decisions sheet; the roadmap says what the German route still cannot see.

2026-10-25, night (is the sample large enough; the German route anchored on the Czech)

  • Is the certification sample large enough? The certification section now answers it, with tables built from the score file rather than typed: for the headline, yes — even counting claims as independent, the error per printed claim is bounded at about 2 % (4 of 505); for each symbol, no — only the thirteen symbols with ten or more instances are bounded at all, and a symbol needs about 36 instances read without a miss for its sensitivity's lower bound to reach 0.90. That is the design target for the entry-level sample.
  • The route chart reads every run on disk. Each edition × page set now shows its own run, so the Certification pill shows the sealed Czech run's own counts instead of going blank.
  • Step 7 of the route changed (D-129). The German edition of Bohemia is no longer kept apart: every German claim the certified Czech inventory disputes goes to Opus, on the German print alone. Replayed on the gold, all 19 claims only the Czech side held were on the German gold. With D-130 to D-132 — the offices section shown for every part, the place types routed, and a German heading that fills its line found — this is what the German gold re-run will measure.

2026-10-25, last (two guards against a key that is only a label)

  • A run says when a section's heading was missed (D-128). An offices section whose heading the page image does not find runs on under the label of the section before it, and a claim reaches the inventory only from a crop named offices — so those entries are not misfiled, they are dropped. Every court with osady on a page and no offices section beside them is now flagged next to the claim count. Grouped by the printed slug the check cried wolf twice; grouped by the 1910 court it flags one thing over the three runs on disk, and it is the real one.
  • The inventory carries a key, not just a label. entry is page/court/municipality/settlement, with the printed district kept beside it as what it is. The slug the text layer gives a heading is damaged often enough to be useless as a join — mechanice for Nechanice, novaraka for Nová Paka — and keyed on it the first scoring of the sealed half lost 170 of 505 claims that had all been read.

2026-10-25, later (the Czech half is certified)

  • The sealed Czech pages are read and scored — once (D-127). Eight pages nothing was tuned on, read on the gold re-run's frozen route: F1 0.996, a 95 % page-clustered interval of 0.991 to 1.000 — 502 claims right, 1 false, 3 missed of 505; 104 of 107 entries reproduced exactly; the printed count right on 501 of 502; every token with ten or more instances read at sensitivity 1.000. Eight pages are eight clusters, so the interval is the result. The scorer's metrics were written down before any sealed page was scored, and it refuses to score an edition twice.
  • Two defects found on the way, both fixed. The freeze check digested the contrast sheet's file rather than its pixels, so a re-encode of a sheet that was pixel for pixel identical reported the certification run as a different route (D-126); and the scorer keyed an entry on the printed district slug, which the text layer damages — mechanice for Nechanice, novaraka for Nová Paka — so its first run lost 170 of 505 claims that had all been read. The key is the 1910 court now.
  • The German number depends on which entries you count. The same reading scores 0.945 over the entries the gold and every reader reached and 0.905 over every entry the sheets print. The gap is one page: on scan 0380 the page image misses the heading Ämter, Anstalten … Reichenau an der Kněžna, so its section runs on as parts and its 28 institutions are dropped rather than misfiled.

2026-10-25 (the route, drawn)

  • The reading route opens with a flow chart of itself. The eight steps down the spine, in the runner's own order, and beside them the four ways a mark can be settled — accepted because all four readings agree, the reader of record's alone, settled by Opus, or ruled by the author — each with the share that actually took it. Click a step for what it does, why it is there and the decisions that settled it; the pills switch the edition and the page set. On the Czech gold the author's queue is empty, which the chart shows rather than asserts.
  • Its numbers are the run's own. The stages come from run_frozen.py and every count from that run's final_inventory.csv, whose rule column says how each mark was settled; a rule the figure has no band for is counted and named rather than dropped. Switching to the sealed half or the whole volume blanks every number instead of carrying the gold's under another label — which is the page's own argument about what certification measures, made visible.

2026-10-24, later (the court of every section, and a notebook that runs itself)

Decisions D-122 to D-125 and the Roadmap carry the record.

  • Every section of the register now carries its 1910 court (D-122). The name a heading prints is matched against the court list of its own land, the register's own names for courts, the courts the page's running head prints, the courts of the okres it names, and last what the readers wrote when they read the heading. Of the Czech volume's 1,248 headings 1,247 now resolve, and three names the old rule matched to the wrong court are now right: on the Broumov pages Teplice is Teplice nad Metují, on the Děčín pages Benešov is Benešov nad Ploučnicí, on the Krumlov pages Planá is Horní Planá. Two courts the register prints were created after the census and are filed under the 1910 court each was cut from. The two editions are paired by court.
  • Notebook 15 runs unattended (D-123). It waits for its batches at each collect cell, will not finish a pass while a batch is still processing, sends a failed Opus call once more, and writes its batch queues outside Dropbox — about 150 GB for the four volumes otherwise.
  • Gemini can be batched at half price (D-124), through Google's native batch interface with exactly the live request, once a check on the machine of record shows its readings are the live reader's. Identical replies cannot be the test: the model ignores its temperature, so the batch is compared with a fresh live read of the same crops made at the same time.
  • A certification or full run says whether it used the gold's frozen route (D-125), comparing the code module by module, the frozen components, the packages and the machine.
  • The register's pages are reorganised. Digitising the 1910 register keeps the document itself. The reading route replaces the accuracy-and-plan sheet and the gold-transcription sheet with one page: the route step by step and why, what was tried and dropped, the gold's results, the certification to come, how notebook 15 runs, and the gold — with the dated plan folded at its foot. Links to the two old sheets land on it.
  • The certification plan: the sealed pages are read right after the gold re-run, on the frozen route; once the full run exists, a random sample of about 150 Czech municipalities, rare symbols oversampled, measures the analysis variables' own error rates, with its rule written down before the draw.

2026-10-24 (one notebook reads the register, and a third audit)

Decisions D-89 to D-121 and the Roadmap carry the record; the third audit is new on the Audits page. The accuracy-and-plan sheet still opens with its revision of 15 October, so the numbers below are not yet written up there.

  • A second reader, and a judge (D-89, D-97 to D-102, D-110). Every page is now read by two model families, Gemini and Claude Haiku. They disagree on about one claim in eight, and Claude Opus settles those disputes: on the gold it settled 99 % of them and was right on 98 %, against 63 % for always taking Gemini (D-100). What it cannot tell goes to the author.
  • A picture check of every mark (D-91, D-93 to D-95). A locator puts each claimed symbol on a box of the page image, and an image encoder (DINOv3) names the box from examples cut from other pages. Where the encoder and a reader disagree, the claim goes to Opus. It is never rewritten by rule.
  • A contrast sheet for the look-alikes (D-99, D-102, D-103, D-106). The pairs of symbols the readers swap are shown to them side by side. It more than halved Haiku's swaps. For Gemini it did not, so Gemini reads without it.
  • No local model joins (D-105, D-113). Six vision models that run on the project's own machine were tried as a third reader. The ones that reached the pair's errors raised alarms on half the claims, and the accurate one shares the pair's blind spots.
  • The table and the German edition are read too (D-90, D-92, D-96, D-104). On the gold tables the model reads 99.7 % of the number cells, against 91.4 % for the scan's text layer. The German edition of Bohemia has its own gold and reader, and that gold also serves the Moravian and Silesian volumes, with a spot check in each.
  • One notebook runs it all (D-107 to D-117). Notebook 15 freezes every setting, reads each page once with Gemini and three times with Haiku, checks every mark, compares the readers and the two editions, asks Opus each question once, and writes the final inventory. Every mark keeps a history of how each step read it (D-109). The gold, the sealed certification pages and the whole volume run through the same cells.
  • The first gold run (D-118). The Czech edition comes out at F1 0.995, for about $0.77 of Opus. The German edition fell to 0.899, because the box-finder learned on Czech pages misplaced German boxes. Three fixes followed, and the German half is read again.
  • A third audit (D-119 to D-121). All code now lives under utils/, the register's in utils/lexikon_1910/, and reference/ holds data only. The audit fixed the route's defects (the Czech gold replayed at 0.999: no false claim, two missed), made the printed count the readers' majority (875 of 879 right), runs the Czech–German independence test on the gold, and adds four sealed German certification pages, drawn by rule. Its last two sections say what the output still lacks for place-level variables, and why the certification as planned is not yet enough.

2026-10-15 (the page image decides where the crops go)

Decisions D-83 to D-88, the Roadmap, Validation, Place matching, the 1910 volume note and the accuracy-and-plan sheet follow.

  • The notes are cut on the page image (D-83). The reader's images now come from the printed page itself, not from where the scan's text layer thinks the lines are. Each image is still labelled with its section heading; where only the image sees a heading, that one line is read by the model before the section is labelled.
  • The postal and railway legend is read from the image too, so the table's post and station numbers can be turned into place names with the reader's spelling.
  • Two pages ended the table in the wrong place (D-84), and read a heading of the notes as table rows. Fixed.
  • Every reading now checks itself (D-85): how many lines the image shows against how many the reader returned, and whether the reader saw a different heading.
  • A municipality split across two pages is checked as one (D-86).
  • The district from the page head (D-87): a district name that cannot be matched is now reported as unknown rather than guessed from the page before; Prague and Liberec are read from their heads.
  • Loket and Kralupy nad Vltavou (D-88). The register prints both as districts of their own; the district map the project is built on has them inside Falknov and Slaný. Both are right. The register counts people as at the end of 1910 but draws districts as at the end of 1914, and its German edition dates both new districts to November and December 1913 (the Kralupy court to March 1912). So the register's pages now resolve to the 1910 district that held them, and the 1910 district map stays as it is — it is the map of the census, and of every legionnaire's birth. The two districts belong to a later map, for the interwar elections.

2026-10-14 (everything printed is now inside some image)

The accuracy-and-plan sheet opens with a new revision, and Decisions (D-76 to D-82), the Roadmap and Validation follow it.

  • 22 pages had never been read (D-76). On those pages the scan's text layer put the end of the table inside the notes, so the notes were never found and no image of them was cut. They are now: 470 entries that no reader had seen.
  • Every printed line of the notes is now inside an image. The images of one column now meet edge to edge, so a line the text layer dropped between two sections can no longer fall into a gap. Table rows that used to be sent as notes are not any more.
  • The table reads its first and last rows (D-77, D-78). Its top and bottom are taken from the printed rules: 233 rows at the top and 137 at the bottom that had never been read, and none of the gold tables changes.
  • The district is read from the page head despite garbled letters (D-79). It changes on 24 pages, and on 19 of them the old answer was wrong.
  • The pipeline can describe any volume (D-80). One profile per volume, and the page layout read from the image alone; tried on the German Bohemian, Moravian and Silesian volumes. It is not switched on yet.
  • Prague and Liberec (D-82). Their pages are written as running text; the text is now extracted, and a vision read is prepared.
  • What is open. Seven section headings the text layer missed altogether wait for the author's ruling, and the stored readings are read again (r5, about $3) once the transcription is done (D-81).

2026-10-13 (a crop that stopped one line short)

The accuracy-and-plan sheet opens with a second revision of the day, and Decisions, the Roadmap and Validation follow it.

  • What was wrong (D-75). The notes under each table are cut into small images, one per section, for the reader and for the person transcribing. The last image of a column ended at the last line the scan's text layer had recognised. Where that layer had dropped the final line, the line was on neither image, so the reader and the transcriber both missed it and agreed.
  • How often. Counted over the whole Czech volume without asking a model: 13 of 454 pages had print below the old edge — twelve entries and the last line of two more. Every one of the twelve is a number and the same single symbol, the shortest line the register prints. The four pages the reader is scored on lost nothing.
  • The fix. The last image of a column now runs to the foot of the column. The two affected transcription images were cut again; no transcription was changed, and three entries on them are still to be read by the author.
  • What it costs. Stored readings are matched by the exact image, so six of the 81 images of a stored pass have to be read again before it can be replayed. Not done yet.

2026-10-13 (the order the register prints its symbols in, as a check)

The accuracy-and-plan sheet opens with a new revision, and Decisions, the Roadmap and Validation follow it.

  • The symbols of an entry stand in a fixed order, and that is now a check (D-73). The volume's own preface reads one entry out in order — offices, churches, schools, doctor, midwife, fire brigade, savings bank, cooperative, post, railway, and a castle last. On the ten transcribed pages the order has no exception once the gendarme, the canteen and the transport station are set aside, which the register prints in two positions. The check learns the order from those pages and asks no model.
  • What it finds. A symbol read as another that belongs elsewhere on the line: above all a castle read as a poorhouse, the reader's commonest confusion, where the castle stands last. It flags about one of the ten or so wrong symbols in a pass and almost never a right one. A wrong symbol standing where the right one would — a railway stop for a station — it cannot see.
  • It found slips in the gold. Two entries of the transcription broke the order, and both were slips of the sheet, corrected by the author against the print.
  • Where it goes in the reading (D-74), designed and not built. The readers are not told the order, so that it stays evidence about them. It is applied after the read and before the two readers are compared; a symbol it flags is looked at again, on the image, and none is changed by rule.

2026-10-13 (the amateur-theatre database — scraped, tabulated, parked)

A settlement-level source for pre-1914 associational life is on file, and the Roadmap says where it stands.

  • What it is. The Databáze českého amatérského divadla lists, place by place, the amateur ensembles of the Czech lands with their years. All 7,368 place entries were fetched on 2026-10-12 and parsed into two tables; 93.6 % of the places tie to a 1910 settlement. Its entry under Data sources (#22) carries the citation and the rights, and it is in the References.
  • What it showed. Bohemia and Moravia are covered alike once language and size are held fixed — 30.0 % against 28.2 % of ordinary communes in Czech-speaking okresy have an ensemble starting before 1914. But it records Czech theatre only, coverage rises steeply with the size of the place, and each okres was compiled from its own sources.
  • Where it stands. Parked, to be picked up later; nothing is joined to the person files. The chronicle, bibliography and archive citations of every place are kept on disk for that later reading.
  • Two new script sheets: the scraper (scraper_amaterskedivadlo) and the coverage tabulation (amateur_theatre).

2026-10-08 (typeface — Libertinus)

  • Libertinus throughout: running text, tables and sidebar in Libertinus Sans, headings in Libertinus Serif Bold, matching the paper. The faces are embedded (subset to Latin/Czech/Cyrillic, utils/docs/fonts/, build_fonts.py), so the page no longer depends on which fonts the reader has installed.

2026-10-08 (a second reader that catches nine errors in ten, and the route for a disputed claim)

The accuracy-and-plan sheet opens with a new revision, and Decisions and the Roadmap follow it.

  • The second reader has been screened (D-72). Claude Haiku 5.5, read beside Gemini on the same crops, disagrees with it on 87 % of the errors Gemini makes — a second Gemini pass finds 30 %. It also disagrees where Gemini was right, three times as often as the bar allowed, so about one claim in nine ends up disputed. The figures rest on four pages and are to be re-scored on twelve.
  • What happens to a disputed claim is laid out, and none of it is built. Both readers read the whole volume; an image encoder names every symbol as a third, different kind of check; what the three do not agree on goes to one call on a costly model and then to the author. Which model is called on what is fixed in code. Re-reading the disputed sections with local models was weighed and set aside, and the sheet says why.
  • Two cheap tests come first, and either can stop its half of the design: whether the encoder can tell an outlined symbol from a filled one at all, and whether the costly model settles disputes better than always taking Gemini's word, which is right 57 % of the time.

2026-10-06 (the register's reader at 0.972, and what a second reader would have to add)

The accuracy-and-plan sheet now carries a day's worth of measurements, and its plan says what is done, what is next and in what order.

  • The baseline with every fix in: 0.972. Six fresh passes over the gold-sweep pages. Nothing is filed under the wrong part of a municipality any more; what is left is a handful of look-alike symbols the reader gets wrong in every repeat — a castle for a poorhouse, a railway station for a stop — each checked against the print by the author.
  • What stands beside a symbol is scored (D-70). A school's count, classes and language and a cooperative's kinds and language are read right on all 116 cases in five of the six passes (the sixth skipped one printed line); the named parts of Části osad are all found with their houses and population exact, and their place-type symbols are the weak column, at 0.92–0.93. Four parsing defects that had passed for reader errors are fixed, and two slips in the hand-read sheets — found by this scoring — are corrected.
  • The bar for a second reader (D-71). Reading the same pages again with Gemini finds only 26–30 % of its errors: seven in ten are made by both versions, every time. So a second reader will be chosen on the errors it brings to review, not on its own score. The screen is written and starts with two readers that need no new account.
  • The order of work puts transcription first. Every pass already reads eight calibration pages that have no gold yet; with it, each comparison is made on twelve pages instead of four. The stage table's last column is kept current from now on.

2026-10-05 (the register's reader at 0.958, and the plan it had been missing)

The work on the 1910 register now has its own sheets. Until today the page carried the description of the volume and nothing about how well it is being read, though the Decisions register has pointed at that argument since August — and none of those links could be followed.

  • 1910 register — accuracy and plan opens with where the reading stands and how the pipeline changes next, stage by stage; the dated record of how it got here follows. The default reader is at settlement F1 0.958 on the development gold, every symbol a regression will use is found each time it is printed, and what is still wrong is rare ([castle], [ruin]).
  • 1910 register — gold transcription says where every hand-read sheet lives and what is queued, the German edition included.
  • Seven decisions, D-63 to D-69. The scorer now works at the unit the analysis uses; a part of a municipality that opens its own printed line is filed where it belongs; the scored gold is the whole-page sheets; a damaged section heading no longer files a whole section under its neighbour; the German edition gets its own gold; every paid pass runs on one machine; and an image encoder becomes the second method for the rare symbols.
  • A price is no longer set as a formula. Two dollar amounts on one line were read as the two ends of an inline formula, and the words between them came out as a run of italic letters. It affected the register's row on the Roadmap, three places on the Decisions sheet and one entry below; it would have affected 26 places on the new accuracy sheet. A closing $ followed by a digit is now left alone, and all 86 real formulas on the Variables sheet still render.

2026-09-23 (the second audit, and a smaller, truer overlap)

The whole cleaning pipeline was read line by line a second time, two months after the first audit, and every finding was measured on the data before it was fixed. The Audits sheet — renamed from Audit — 2026-07-16, because it now holds both — records 8 defects that changed an analysis variable, 25 narrower ones and the documentation drift, each with its before-and-after number. Notebooks 02 and 03 were re-run on the fixed code, so every figure, table and number on this site is from the new build.

  • The cross-dataset overlap is 1,340 men, not 2,239. The name test scored surname and given name as one string, so a shared František could carry two different surnames over the line, and 111 fallen soldiers were each matched to two to four legionnaires. The second criterion read a note field that mostly says which interwar regiment keeps the death register, not that the man served in the Legion. Each part of the name must now pass on its own, the match is one-to-one, and the note must name the Legion (D-59). This is the change a reader of the comparison design needs to know about.
  • 214 legionnaires born in Galicia had been placed in a Slovak district. Halič is Galicia in Czech and a Novohrad town in Slovak; the town's census covariates went to men born in Lvov and Tarnopol. The seat is no longer resolved by name.
  • The exit date — the hazard model's event time — is range-checked now. It ran from 1818 to 2192. Dates known only to the month or year are kept and say so in a precision column instead of being dropped (D-61); 3,827 more records have an exit date.
  • US applicants are imputed to France, not Britain (1,659 of 1,685 with a known unit served there), and the Polish Legion no longer lands in the Balkans (D-60).
  • Missing stays missing, in four more places. A blank cause of death is no longer "not killed in action" for 8,297 fallen soldiers (D-62).
  • The QA gate fails a stale build. data/final had been four days older than the code, and eight checks written for the newer columns skipped themselves silently. It now runs 107 checks, all passing, and the first one asks whether the build is current.
  • The Bolshevik coup is on 7 November 1917 on the three timing figures, not 7 September.
  • On the reference sheets: Pipeline, Variables, Place matching, Occupations, the Validation register, External validation, Decisions (D-59–D-62) and the Roadmap, which gains a list of what the audit deliberately left open.

2026-09-17 / 09-22 (the reader got good, and it was the view, not the model)

The 1910 register's symbols now have a reader worth keeping. Until September the apparatus below the table — the pictograms that record which municipality had a school, a post office, a gendarmerie post — was read by local models at F1 ≈ 0.70, with 30 % of municipalities exactly right. A cloud reader and one controlled experiment later, the Digitising the 1910 register sheet, the Roadmap, the Decisions (D-54–D-58) and the Validation register say something different:

  • Gemini Flash, one apparatus section per call, thinking off: F1 0.962, entry_exact 0.80, $0.33 for the 13-page gold sweep — ≈ $6 for the whole Czech apparatus on batch. Experiment E1 held the crops, the prompt and every flag fixed and changed one thing at a time over 17 passes. With thinking off on both sides, section crops beat the whole page by +0.132 F1 and +0.295 entry_exact; the model version makes no difference; and Gemini's default thinking bought 3.7 nothing and cost 3.8 accuracy.
  • The cost ledger had been counting a sixth of the bill. Through the OpenAI-compatible endpoint Gemini reports its answer as completion_tokens and bills its thinking as output without reporting it — ~5,000–5,700 tokens a crop on 3.8. A pass logged at $0.32 really cost ≈ $1.95. The reader now books the hidden share, and thinking is off.
  • Claude Opus 5.5 was priced and not pursued: ≈ $160–340 on batch for the full pipeline, against ≈ $6 for the Czech apparatus on Gemini, for a reader no one has measured on these crops. The second reader the German edition needs (D-58) will come from a cheap screen of Qwen3-VL and MiniMax M3 on the same crops instead.
  • Two silent defects, found by scoring at the right unit, and fixed (2026-09-23). Scored at the settlement level the analysis will use, the reader had fallen to ~0.85 — not a reading error but a decoder defect: the — 2) marker that opens a municipality's second part was never split, so 12.8 % of correctly read claims landed on part 1 (D-56). Fixed, settlement F1 is 0.915. Checking the fix exposed a second one: the reader joined a tall section's strips in the order they finished rather than the order they were cut, so the same cached answers scored differently live and on replay. Fixed too, and two replays of every E1 pass are now byte-identical.
  • What still fails, named. About 0.05 of part attribution is still wrong and not yet diagnosed, and an outline-roofed [castle] is still read as the solid-roofed [poorhouse] by every model version.
  • Nothing is certified. Every figure is on four purposive pages read once. The gold is now the binding constraint, and D-57 splits it by purpose so that the symbol validation sample can be read in hours rather than the 48–74 h the whole-page protocol costs.

2026-09-07 (the education ladder finally has a source that could contradict it)

Education was the one major variable with no external check at all. It appeared in the validation register only under "What none of them reach". The ladder was built on two modern histories — Moore (2020) and Lassnigg (2015) — neither of which counts schools, and one of whose figures begin in 1924 in a different country. §5 of the education page had named the missing source by class: "the Austrian statistical administration's own school taxonomy … which classifies schools rather than describing them."

That volume is now held and read: Österreichische Statistik XCIII/1, *Statistik der Unterrichtsanstalten … für das Jahr 1908/1909* (Wien 1912), the k.k. Statistische Zentralkommission's own annual census of every school in Cisleithania — 279 pages, merged from 15 scan parts, with a new External validation — education sheet carrying five tests.

  • It confirms the hardest call in the variable. The pokračovací continuation school is kept off the ordinal, and the administration files it exactly so: its own category in both the industrial (p. XX d) and commercial (p. XVI) branches. It was also the mass institution — Bohemia had 504 of them teaching 50,836 pupils against nine state trade schools teaching 3,518, i.e. 84.0 % of all vocational pupils. The pre-rewrite reading, which scored it level with the Gymnasium, would have put the largest and least selective school in the empire at the top of the ladder.
  • It breaks education_complete (D-53, deliberately not patched). The column scores a bare elementary schooling complete at eight years, on the law's eight-year obligation. Table 34 counts elementary schools by how many year-groups they actually ran: an eight-class Volksschule existed 10 times in all Cisleithania and twice in the Czech lands, against 1,114 five-class schools in Bohemia alone. So 3,894 records are marked complete against a form that barely existed and 3,137 reading the ordinary full ob. 5 are marked incomplete. The law's years and the school's classes are different units and the column conflates them. Fixing it changes an emitted column and the choice is a judgement, so it is logged with two candidate resolutions rather than quietly patched.
  • It shows pok. is badly under-recorded. The Czech lands had 5.3 continuation pupils per full-time vocational pupil; the records name them at 0.86 to one. Men did not write down the evening classes they were obliged to attend, so education_apprentice is a lower bound, not a rate — a caveat the paper needs and nothing internal could have found.
  • It cannot touch Hungary, being Cisleithanian by definition, so D-08 stays open for exactly the reason it already was, and the ~10,500 Slovakia-born records sit outside every test.

Two errors in the printed volume turned up on the way, each decided by a second arithmetic path rather than assumed: a roll-up in Table 20 that is one short of its own parts, and a prose figure on p. XXXII (22.341 schools) that contradicts both Table 34 and the volume's own summary table (22.241). The transcription follows the page and declares the errata.

And the QA gate went from 66 to 72. Education had three invariants, all of them behind a guard on a column the current build does not have — so in practice it had none. Six now run unconditionally, including the one that would catch the old defect coming back: a continuation school may act as a floor on the ladder, never as a promotion.

2026-09-07 (two variables that answered questions nobody asked them)

Both variables rewritten in the last fortnight were re-run against the raw field, and both had a defect the rewrite had not touched. Neither was visible in the finished data: the QA gate passed 66/66 with both present, because both are about what the parser did with a string, not about what the frame ended up containing.

  • An unrecorded affiliation was False, and now it is <NA> (D-52). Politická příslušnost is blank on 93,377 of 132,993 records — 70.2 % — and every pol_* dummy was writing False there. That is not "not a Sokol"; it is a source that said nothing, and the difference is the same one that fabricated exit_N on 26.4k empty cells in the 2026-07-16 audit and that provenance.py already refuses for its own flags. pol_recorded (39,616 True) marks the base; a record that is filled in but matches no token keeps False, because there the register genuinely failed to recognise something. It matters now rather than later: occupation missingness was found on 2026-08-27 to track the outcome, so a silently-false covariate on 70 % of the sample is not a cosmetic problem.
  • A tie between two schools was being broken by word order (D-51). 1,512 education records name two institutions on the same rung — reálka + obch.akademie, gym. + uč.ústav, odb. + hosp. — and the parser took whichever the clerk wrote first. The same pair came out both ways: obch. + odb. gave commercial on 20 records and trade on 164. It is not a labelling question. It sets education_track (academic vs teacher vs military) and education_complete, whose standard course lengths differ across the tied schools — a Realschule is seven years, a Lehrerbildungsanstalt four, so 152 records were being graded against the wrong yardstick. The order is now written down, on two principles: a generic word (odb. = "specialised", střední = "secondary") yields to the school the record actually names, and the terminal professional school outranks the general school that feeds it. The second rests on reading the cell as a chronological itinerary — which the data supports, on 92.4 % of multi-rung records the ordinals never fall along the string.
  • Semesters were being counted as years. technika 4 sem. is four semesters at the Technische Hochschule; the bare digit attached backwards to the institution and landed in education_years_secondary on 722 records. Universities counted in semesters and the Mittelschulen in years, and the two were sharing a column. They now have their own.
  • The scan is word-anchored, and that found three abbreviations. technik had been matching inside hydrotechnika and ob inside pob. — the unanchored-substring shape that ranks 1.4.0 had to remove after Fidler caught generálmajor scoring as major. Anchoring it exposed spellings the patterns had never covered: univ. (55 records, where only univer… was matched), the lék.fak. / fil.fak. faculty form, and vyš.prům. (37 records, which had been falling to the 3-year Fachschule instead of the 4-year Staatsgewerbeschule). Coverage 99.07 % → 99.10 %.
  • One thing that was wrong in the plan, and is recorded as wrong. The review that opened this work claimed the technick… pattern was over-promoting secondary technical schools to Tertiary. It was not: technik and technick diverge at the sixth letter, so the pattern never matched technická at all. The real defect there was the opposite — eight records that parsed to nothing. Checked before it was fixed, and the fix is the one the data called for.
  • Three modules now carry a self-test the QA gate runs, alongside the data invariants: provenance_selftest(), education_selftest() and affiliation_selftest(). They hold the rules a finished frame cannot express — that reálka obch.akademie and obch.akademie reálka give the same answer, that the refusal token sl does not match inside slov.líga. A first attempt at an invariant here failed honestly: "semesters never equal secondary years" looked right and would have broken the gate on 13 records that legitimately state the same number twice (ob. 5 gym. 8 univ. 8 sem.). A rule about the parser belongs in a test of the parser.

⚠ data/final is one build behind this entry. The modules, the schema and the gate are at 2.1.0 / 1.12.0 / 1.10.0; the four new invariants are dormant until notebook 02 is re-run, and the affiliation figure in notebook 03 will change base — it must report on the 39,616 recorded records, not on 132,993.

2026-09-03 (the party family that a source dissolved)

The political-party genealogy cited Wikipedia, and it carried an ideological axis in every figure. affiliations.py folded mladočeši, realisté, stát.pokr. and lid.pokr. into nar_dem on the strength of four Wikipedia articles, and kept a separate pokr bin labelled Liberal. A photograph of one atlas plate settled it from the print — and overturned the bin.

  • New sheet: Party register. Akademický atlas českých dějin, plate IV.37: 55 parties of the Czech lands 1861–1918 in five blocs, each with its founding year, area of operation, and a notes column recording every merger, renaming and dissolution. Transcribed to reference/parties/atlas_iv37_parties.csv.
  • pokr is not a party family. The register has Č13, Č15 and Č17 each as "1918 spoluzakladatelka Č 23", Č23 as "1919 přejmenována na Národní demokracii", and Č7 merged into Č15 in 1908. Masaryk's realists are the same line — Č12, "realisté, 1905 transformace v Č 13". So every pokroková party in this data ends in the National Democracy, and a Liberal group standing apart from it was an artefact of the reading. The canonical is abolished. pol_pokr (869) disappears: 663 already carried nar_dem and only lose a spurious second flag, 78 newly gain it, 42 become volna_myslenka — Free Thought being a freethinker association, not a party, and absent from the register.
  • It confirmed two calls, on better evidence than they had. lid.pokr. → nar_dem (Č17 is a co-founder of Č23) and real. → nar_dem (Č12 → Č13 → Č23) were both taken from Wikipedia; the plate says the same thing from the source.
  • Three refusals, printed rather than buried. A bare lidová — eight register parties carry it, Catholic and liberal-national and Masarykite alike, so the module having no such token was right all along and is now a documented refusal. A bare demokrat. And SL, 108 records and the largest unmatched token, together with every Slovak formation: plate IV.37 is v českých zemích and contains no Slovak party at all. A source gap, on the roadmap.
  • A transcription that defends itself. The plate was read from a hand photograph of a curved page, where the notes column drifts against its rows — the compensating-error shape the validation register already warns about for the 1910 register. But the notes cross-reference by code, so check_atlas_parties.py can require that Č7's "merged with Č10 into Č15" faces Č10's "merged with Č7 into Č15". 12/12 invariants hold; a one-row offset would not have survived them.
  • Also dropped: rolník → agr. It mapped an occupation into a party dummy on the theory that a self-described farmer is an Agrarian, while dělník in the same field mapped to nothing — an asymmetry with no source behind it.

2026-09-02 / 09-03 (the school that was never a school)

Vzdělání was being read as a level, and it is an itinerary. ob. 5 mě. 3 pok. 3 names three schools and the years spent at each; the parser took the first token it recognised and threw the rest away. The worst consequence was one token. pok. — pokračovací, the gewerbliche Fortbildungsschule — is the apprentices' part-time continuation school, attended a few hours a week alongside a trade. It scored Upper secondary, which put 8,117 records — the classic apprentice itinerary — in the same bucket as the Gymnasium. The variable was flagged as deferred (D-08, audit B3) on the suspicion; two sources arrived this week and turned the suspicion into a number.

  • The ladder now has a source. Moore (2020), Teaching the Empire, gives the general half: compulsory schooling ages 6–14 under the 1869 Reichsvolksschulgesetz, reached as Volksschule 5 + Bürgerschule 3, with Gymnasium (8 years) and Realschule (7) entered at age ten in parallel with the Bürgerschule rather than after it, and the Lehrerbildungsanstalt entered from the Bürgerschule at fifteen. So ob. 5 mě. 3 is the legal minimum, not an achievement; ob. 8 is the same eight years taken in one building; and uč.ústav was sitting a rung too high.
  • The vocational branch has one too. Lassnigg (2015, IHS WP 108) reaches the ~17 % of the field Moore does not: the Fortbildungsschule was legally part of compulsory mass schooling, governed communally alongside the Volksschule while the full-time schools ran from the centre, and it selected on nothing — an apprenticeship required no educational credential at all, and the school obligation followed the contract, where obch., prům. and odb. required Bürgerschule grades plus an entrance exam. That is what settles it: pok. is not a rung.
  • New sheet: Education ladder. What each school was, who entered it from where, and the two things neither source can give us — the duration of a Fortbildungsschule year, and anything at all about Hungary, whose schools the Ausgleich handed to Budapest. The second is why D-08 stays open: the ordinal is not established for Slovak and Rusyn births.
  • education.py 2.0.0 parses instead of classifying. Six rungs — No schooling < Elementary < Burgher school < Vocational secondary < Upper secondary < Tertiary — read off a tokenised itinerary, with year numbers attaching backwards to the school they follow (ob. 5) and forwards when the cell opens with one (8 ob., 3mě), which is how the dot-less variants that used to fall through now parse. Eleven columns replace two. Coverage of non-empty strings 96.7 % → 99.1 %.
  • Two refusals worth naming. The ordinal is attendance, not attainment — a bare reálka does not say whether the man finished, so completion lives in its own column that is allowed to be missing, and is missing for the whole vocational branch because no source establishes those course lengths. And the year columns are not summed into a total, for the same reason: continuation-school hours varied by Land and trade under regional laws passed between 1907 and 1931.
  • What moved. The old Upper secondary bucket of 18,756 redistributes — 7,579 stay, 3,973 to Vocational secondary, 2,606 to Burgher school, 4,598 to Elementary. 8,815 records (6.6 %) now carry education_apprentice, which never raises the ordinal. Notebook 02 re-run, QA gate 66/66.
  • One label changed after the first run. Rung 0 was called None, which in any value_counts is indistinguishable from a missing value — the exact shape of the 2026-07-16 fabricated-exit_N bug. It is No schooling now.
  • The chart's colours now carry the order. Notebook 03 was re-run on the new ladder, and at six levels the categorical palette misread: running dark-navy → steel-blue → bright red → dark red → light grey → dark grey, visual weight peaked in the middle of an ordered variable and the top rung whispered. Education now takes CU_SEQUENTIAL, the ramp the style module already documents for one-directional variables — light at the bottom rung, navy at the top, sampled from 0.22 rather than 0.0 so the lowest rung is not white on white. 28 figures and 14 tables rebuilt; the occupation figures, which had predated D-41 since 2026-08-02, are current again as a side effect.
  • The ladder sheet shows the ladder. Education ladder now carries the chart itself, not just the argument. It is globbed rather than named, so a second education figure appears there without a code change — the contract the Descriptives sheet already offers.

2026-08-26 (the review flag becomes a question that can be answered)

172 keys of the occupation dictionary carried a review flag, and nothing in the project could clear one. A key looked identical whether nobody had examined it or somebody had examined it and concluded the code was right, so every rebuild of the conflict workbook asked the same 172 questions and a day of adjudication left no trace in the repository. The 2026-08-26 pass (D-41) answered 171 of 181, the answers are now in the code rather than in a spreadsheet, and the flag falls to 10 keys / 163 records. It means unsettled now, not unread.

  • The ballot came back as a register. czech_glosses.ADJUDICATED records one verdict per word — keep (130), recoded (41), open (10) — and run_link_bridge.py 1.2.0 applies it last and is the only writer of review, so the German bridge can go on flagging genuinely new ambiguity without re-raising anything answered. The new code always lives in HISCO_OVERRIDE and never in the register, so a reader looking a word up finds one answer; the bridge refuses to run if a recoded key has no override entry, or if an override still claims the flag of an adjudicated key. Both would leave the curation looking settled at the old code. The run rewrote 41 codes, wrote a verdict column on all 181 keys, and took the flag from 172 keys to 10.
  • What it moves in the data. 4,731 legionnaire records take a different HISCO code (203 more in the interwar column), and 1,383 move HISCLASS. Nearly all of the class movement is one word: slévač and its spellings, 1,028 records from 7 to 9, recoded 72500 Metal Moulder → 72420 Metal Pourer on OccCANINE at p=0.99. Behind it, 172 estate and forest officials go 3 → 5 (Office Clerk, which is what an estate official actually did), 57 továrník go 3 → 1, 51 ošetřovatel 9 → 4, 28 farmaceut 4 → 2, 16 mlékař 4 → 9. Coverage is untouched: 98.46 % → 98.48 % of non-empty occupations coded.
  • What it costs, priced rather than avoided. 219 records trade the class for the code — štukatér (106) → 95530 Stucco Plasterer, cementář (59) → 95240 Cement Finisher, rukavičkář (47) → 79475 Glove Cutter, živnostník (7) → the −1 sentinel. Each is a sharper five-digit code that HSN gives no HISCLASS at all. The sheet says so where the numbers are, because a class variable that quietly shrinks is worse than one that shrinks in writing.
  • D-42: a stratification scale stopped ranking men whose occupation was never recorded. The exclusion was believed to be in force and was not. For a HISCO sentinel there is no occupation, so the status modifier alone carried the scales, and HSN reads a modifier as a social position where this project reads it as a refusal to code. 3,098 students — 19 % of that cell — were SOCPO 42, middle class, supervisors of skilled workers, and 160 men holding a school-leaving certificate were HISCLASS 2, higher professionals, and SOCPO 5, Elite. Nothing was fabricated: HSN observed those combinations 378 and 9 times, and they answer a different question. Withdrawing them takes HISCLASS 2 from 4,907 to 4,750 and the SOCPO elite from 3,013 to 2,730; HISCLASS coverage falls 95.8 % → 95.5 %. label_en and the major group stay, because they describe the code and do not rank a man. The defect survived because no invariant covered SOCPO at all; two now do, one per occupation field.
  • Ten questions survived, and seven of them are the same question. čsd, zaměstnanec čsd, čsa, pošta and the three spellings of the ČSD workshops at Nymburk are an employer or a workplace written where a job was asked for — and the project has no reason to believe ČSD names a railwayman rather than a clerk, a fitter or a stoker in those workshops. They keep the -2 sentinel and the flag. The other three are real questions: vlakvedoucí (the guard, currently sharing a code with the engine driver), politik, and the bare správce. The workbook now opens on those ten instead of repeating itself — and on rebuilding it, all ten land on Nothing to choose from: no coder ever proposed a rival for any of them, so what is left is not a choice between two codes but a question about what the word means. The Decide and CZ-ISCO says otherwise sheets are empty for the first time, which took a fix of its own (build_conflict_workbook 2.1.1): a sheet with no rows was being written with no header either, and the workbook's own verifier caught it.
  • Leaving those ten is itself a decision, and it is on the sheet as one (D-43). Not a backlog item: 156 of the 163 records are in the interwar occupation column, which codes 4,295 records in all, so the ten are 3.6 % of that variable and the ČSD/post-office family alone 3.1 %. Guessing "railwayman" would not round a 133,000-row sample, it would plant a fabricated occupational profile in the smallest occupational measure the project has — the one the interwar-electoral work runs on — and it would read as a finding rather than as missing data. Since D-42 the -2 sentinel is a place for unknown to live: a code, a label and a major group, and no class. The flag stays up until a source answers it — a ČSD staff schematism, the Nymburk workshop rolls — and then the whole family moves together or none of it does.
  • A code with nothing behind it, caught on the way in. cementář → 95240 was about to be delivered with no English label and no major group: 32 of the authority's 1,696 codes were only ever observed under a modifier and have no base row, so the lookup missed its own key and the base fallback alike, and the QA gate would have read 59 records as a hole. occupations.py 1.3.1 takes the label and the major group from whichever row the code appears on — they never vary within a code — while every scale stays absent, which is the price D-41 accepted and not a rung more. Same principle as D-42: a description is not a ranking.
  • Where to read it on the site. Occupations carries the adjudication and the ten open keys; Decisions gains D-41 and D-42; the Validation register's Open section is now an answered list with its reversals written out — mlékař conceded to CZ-ISCO, skladník kept against it, obchodní příručí kept after asking for the second opinion this workbook was.

2026-08-11 (a page of the register, with the boxes drawn on it)

New sheet: Digitising the 1910 register. Eighteen modules, a 20-page pilot and a hand-keyed transcription worksheet existed for the Podrobný seznam míst and none of it was written down anywhere the project could see — no row in Data sources, no number in Decisions, no entry in the Validation register. All four now exist.

  • The figure is a photograph of page 256, in both editions, with the boxes over it. Twenty-two clickable regions, and not one of them placed by eye: every box is derived from the same ALTO geometry the parser uses — the printed column rules, the fitted grid, the block segmenter, the divider, the per-line bounds — so the overlay cannot drift from what the code actually reads. The panel quotes, verbatim, what the text layer made of whatever is under the box. utils/lexikon_1910/build_lexikon_figure.py renders it out of two PDFs (one of them 3.34 GB and gitignored) and writes a 1.5 MB tracked artifact; the docs build only reads that, so it still runs on a machine with nothing from library/.
  • Brzice is the worked example, and it earns it. 541 hectares, 88 houses, 420 people over four osady — small enough to check the block sum by eye, and it holds a real defect: a stray inverted comma makes Žďár's male count ‘ 11, so the row comes back unparseable rather than failed, and the grader refuses to guess what the press meant. Under the table, in one printed line, is what the village had: a two-class school, a midwife, a fire brigade and one Raiffeisen credit union. That line is set out three ways — as printed, as the text layer read it (1, 1) g II, $, f, R., four marks and one slot), and as it actually is, drawn with the plate's own glyphs.
  • The German volume is on the same figure, and it is not decoration. The identical figures under a different running head, on a different printed page, in a different position in the district — Brzice is municipality 1 in Czech and 3 in German, because each edition sorts an okres alphabetically in its own language. That is the whole argument for matching the editions by name and never by the printed number, and it is now something you can see rather than a sentence in a docstring.
  • Both Výklad značek plates, and the 82 designs cut out of them, searchable. Which is also where the difficulty lives: the plate sets its symbols at display size and an entry's whole printed line is 44 px tall, so template matching from the plate peaks at 0.644 and the detections cluster with no separation at all. Across five reader passes, all 109 compared marks are contested and none is unanimous.
  • Two decisions recorded that were load-bearing and unwritten — D-38, that a reader is never shown the invariants it will be graded on and a failing block is re-read rather than repaired; and D-39, reversing the assumption that the text layer knows where every marker is, on a measured threefold undercount.

2026-08-05 (the census place register is complete, and a long download is watchable)

All four 1910 Spezialortsrepertorium volumes are now on the shelf. Böhmen, Mähren and Schlesien in German, plus the Czech-language Podrobný seznam míst edition of Böhmen — the census's own register of every Ortschaft and Katastralgemeinde, which is the printed authority behind the settlement crosswalks. Two of them were half-downloaded and finished here: 122 pages from MSVK and 756 from the NDK.

  • A second Kramerius generation, and a host that lies about which one it is. ndk.cz presents a modern viewer but answers on the v5 API, so it belongs to scraper_kramerius.py, not the v7 module written for MSVK. Both are now driven from one place, run_lexicon_downloads.py, which runs the two hosts concurrently and stacks a progress bar per volume — 878 pages is long enough that "is it still going?" is a real question. progress.py keeps the bar optional: without tqdm the downloaders fall back to their old log line and nothing else changes.
  • --name had been renaming the volume, not the file. It overwrote the title as well as the filename stem, so the OCR text's own header announced itself as NDK_Specialni-lexikon-obci-… instead of Podrobný seznam míst zemí rakouských … IX. Čechy. The stem and the title are separate again.

2026-08-05 (the district map draws the unit you asked for)

The borders were being erased rather than drawn. Every polygon was stroked as if it were a district, and a second layer then over-printed the court-only lines in pale grey to take them back out. On a pale district that reads as a hairline; on a dark one it reads as white scratches, and at political okres — where a court line is not a boundary at all — the map was covered in them.

  • Three border layers, one per class, drawn instead of undrawn. Each edge is classified exactly (adjacent MPIDR polygons share their border vertex for vertex, so this is arithmetic rather than tolerance) into court — both sides inside one okres, twin — the city and ring of a pooled pair, and okres — everything else. The polygons now carry fill only. At political okres the map draws only district boundaries; at soudní okres the court lines come in underneath them as pale hairlines.
  • Boundaries are chained, not scattered. The okres boundary is ~20,000 individual segments; emitted raw, each gets its own line caps and a stroked border reads as a dotted scribble at any width above a hairline. _stitch walks them into 1,021 polylines that join cleanly — and the court layer got smaller in the process (28.4 kB → 16.8 kB).
  • City twins are now a control, not a fixed choice. Ten Czech seats are a Statutarstadt plus the rural okres around it. They are two okresy and the records are split between them, but the court-level census cannot separate nine of the ten, so the analytical unit is the pooled pair (Decisions D-18). Separate draws 158 units; pooled draws 148 — the city border comes off and the arithmetic follows, in the shading, the quantile bins, the district panel and the rank. A merged region showing two shades with no line between them would have been worse than either answer.
  • Pooling a mean needed its denominator. rank_reset is stored per okres as a mean, and averaging two means would have given Brno město — a fifth of the records — the same weight as the ring around it (−0.99 and −0.37 average to −0.68; the correct pooled value is −0.50). The mean now travels with its own n, and the rank is recomputed against the pooled totals rather than inherited from a member.
  • What the toggle removes is real. In all ten pairs the city is the paler patch — a small light core inside a darker rural ring — so legionnaires were rural-born even relative to their own district town. Desertion runs the other way in the larger pairs (Brno město 2.98 % against the ring's 1.56 %, Liberec 4.21 against 2.44) and vanishes in the smaller ones. Pooling averages every one of those contrasts away, which is the price the census charges.

2026-08-05 (the rolník decision gets an individual-level witness)

Check 6 settled 18 % of the sample and named its own weakness in the same breath. The 1910 Berufsstellung test is ecological: it correlates okres compositions, so it can say that where the census counts more own-account farmers the legionnaires' farm occupations are more owner-coded, and it can never say anything about one man. It also could not tell a holder from a farmer's son. Both limits were written into D-33 rather than glossed over, and both are now partly answered.

  • Check 7, from a source that records people one at a time. The Vienna Database household listings pair an occupational title — drawn from a closed list of 521 German terms — with a household position assigned separately, for 163,400 people. BAUER is 0.3 % Gesindeperson; LANDARBEITER/KNECHT is 94.9 %. Among men of Legion age BAUER is 97.5 % household head and 0.3 % son, which is the farmer's-son half: on these listings a farmer's son is written down as SOHN, and the farm title goes to the holder.
  • It corroborates, and the sheet says so in the status line. Title and household position are not independent — one clerk assigned both, under a convention that hands the farm title to the head — so this shows what the word meant to the people recording it, not an independent measurement of who farmed what. D-34 records the ecological limit as narrowed, not closed, and D-33 keeps its row and its date.
  • The two checks fail in opposite directions, which is why both are kept. The census is the right territory and the right decade at the wrong level of aggregation; the listings are the right level in the wrong territory (44 Austrian localities, no Czech lands) and mostly the wrong period. Neither alone would carry 18 % of the sample.
  • The weakest link is named on the sheet, not buried. rolník is glossed Landwirt; the term on the closed list is Bauer, and a bare LANDWIRT does not appear on it at all. The bridge runs through a synonymy the check assumes rather than demonstrates — worth stating plainly, since this project's gloss for sedlák, the Czech word that literally means Bauer, covers six records.
  • The source is old enough to be genuinely independent. The Vienna Database on European Family History was built 1974–1984 at the University of Vienna with the MPI for History in Göttingen and the University of Salzburg. Its occupational coding was done by historical demographers reading the listings, decades before this project existed and with no contact with it — which is what "external" is supposed to mean and often does not. Cited in the form MOSAIC prescribes; free on IPUMS registration, with no redistribution grant, so the file stays in gitignored data/ and only derived counts reach reference/.

2026-08-04 (a plain-English doorway on every method sheet)

The register these sheets are written in is correct, and it is also a wall. A method sheet has to be precise, hedged, and free to assume the reader knows what a járás or a HISCLASS is. That is right for the appendix it grounds and wrong for the first thirty seconds of a supervisor, a referee or a historian who has never opened the project.

  • "Explain it simply" on 14 sheets. A folded card sits directly under the In short summary on every How it works sheet and on the validation and decision sheets. Open it and the same mechanism is explained the way you would explain it over coffee: an analogy, then what the code actually does, then the honest limit. Posting a letter addressed only to Nová Ves is the place matcher; a job title becoming a rung on a ladder is HISCO; the 26,000 blanks that became 26,000 confident answers is the audit.
  • Deliberately a <details>, not a drawer or a modal. It costs no JavaScript, the browser's own Ctrl+F reaches inside it, the sidebar search indexes it, and the print stylesheet already expands every fold — so the plain-language layer travels into the PDF instead of vanishing from it. The one print rule added keeps its title, which the unlabelled source folds do not need.
  • It carries no interactive chip, on purpose. The ⇱ badge means this figure responds to input, and the sidebar's interactive dot is derived from those badges rather than declared. A disclosure triangle on a paragraph of prose is not an interactive figure, and labelling it as one would have made the dot claim fourteen sheets had figures they do not.
  • The text lives in build_docs.py, not in reference/*.md. Those files are the record; this is the doorway. An analogy about post offices does not belong in the middle of a document the data-construction appendix is built from. A build-time check warns if a sheet in scope loses its entry.

2026-08-02 (the validation evidence is published, and indexed)

The evidence outgrew the page it was appended to. The entry below folded VALIDATION_OCCUPATIONS.md into the bottom of the Occupation → HISCO sheet — the right call while that was the document's only home, but it left the occupation checks with no place in the validation section, where a reader looking for "what has this project been checked against" actually goes. Fidler sat there alone, and the two documents never met. With a third external check added, that no longer holds: this supersedes the "one page, not two" call below.

  • Three validation sheets, not one. Validation register (new, reference/VALIDATION.md) lists every check — Fidler, the German link bridge, OccCANINE, CZ-ISCO — with the column that makes it honest: what each one is blind to. External evidence and self-checks are kept in separate tables, because a QA invariant catching a build that disagrees with its own rules is not evidence the coding is right. The two detail pages sit beside it: External validation — units & ranks (the former "External validation") and External validation — occupations.
  • A summary on the method page, not a second copy. OCCUPATION_CODING.md §9 was three lines; it is now a table of the three external checks with their headline numbers and blind spots, linking through. The full append at the bottom of the sheet is gone — with the document rendered as its own sheet, keeping the append would have printed every word twice.
  • Seven published figures were wrong and are now right. The roadmap had been carrying them as a known issue: hand-assigned keys read "216 of 459" against an actual 429 of 470, unmatched "1,920 (1.6 %)" against 1,840 (1.5 %), the empty-cell invariant quoted 14,044 for both fields when the interwar side is 127,913, and zámečník's 0.12 was called the lowest OccCANINE confidence when brašnář is 0.022. All re-derived from the build rather than edited by hand. Publishing a page is a reason to check its numbers, not to assume someone already did.
  • CZ-ISCO as a third coder. The one check that tests the Czech reading itself — the other two both start from a German gloss this project wrote. 99.1 % agreement on the records it can score, and 15 substantive disagreements worth reading.

2026-08-02 (earlier — the occupation method finally has a page)

The most-authored thing in the repo had no sheet. OCCUPATION_CODING.md and VALIDATION_OCCUPATIONS.md were never in the site registry, so every one of the four cross-references to them rendered as a dead local path — the reader was pointed at a file they could not open. Both are now one sheet, Occupation → HISCO, sitting beside Place matching where it belongs: the two documents are siblings, one resolving a birthplace to a district and the other an occupation to a social class.

  • One page, not two. The method and the evidence are one argument. Splitting them would have put the OccCANINE agreement table a click away from the coding decision it vouches for, so the validation runs on below the method under its own rule.
  • A funnel, and where the code lands. The same grammar as Place matching, because it is the same shape of problem: four rules tried in order, an earlier one wins, and the ribbon down the left is what is still uncoded. The step at exact swallowing five sixths of the lane is the finding, not a caption about it. On the right, the HISCLASS ladder — which is where an occupation sheet has to end, since the class is what the analysis consumes. Click a rule for what it refuses and the strings it actually matched; click a rung for the trades on it and how often those men deserted.
  • The robustness check, as something you can do. This sheet has always claimed that the three compound rules are not manufacturing the class gradient. Now you can untick them. The gradient below redraws on whatever is ticked, with 95 % Wilson intervals — the interval that behaves when the sample shrinks and the event is rare — so the bars widening rather than moving is the answer. The readout hands back the exact pandas mask.
  • Nothing is transcribed. Every count, ribbon width, example string and rate is recomputed from data/final at build time. The live figure and the Check 4 table in the prose are the same five numbers, which is the only way they can be trusted to stay that way.

2026-08-02 (a second coder for the occupations, and a gate that could not see half the data)

The occupation dictionary was already the most-authored thing in the repo — there is no Czech HISCO source anywhere, so every code is a decision taken here. Two things were missing: an outside opinion, and a gate that watched the whole of what the dictionary produces.

  • OccCANINE as an independent second coder. The transformer of Dahl, Johansen & Vedel (2024) was fed the German glosses, not the Czech — Czech is not among its 13 training languages, so feeding it Czech would measure the model going out of distribution rather than measure our coding. Both coders therefore see identical input and every disagreement is about the code, not the translation. 221 glossed-and-coded keys, 96,079 records.
  • Read the class row, not the first one. Exact 5-digit agreement is 53.8 % of keys, and that number is close to meaningless here: most disagreements are neighbouring codes inside one trade — kovář 83110 vs 83190 are both blacksmiths, holič 57030 vs 57025 both hairdressers, knihař 92620 vs 92625 bookbinder by hand or by hand-or-machine. What the analysis consumes is HISCLASS and the major group, and there the two coders agree on 88.2 % and 96.7 % of records.
  • The model is unsure about the words we had already flagged. Mean confidence is 0.83 where the coders agree and 0.75 where they do not — but the single largest disagreement, zámečník / Schlosser (2,162 records), comes back at 0.12. That is the key this project had marked review for the same reason: Schlosser is genuinely ambiguous between locksmith, machinery fitter and plumber. Two independent coders hesitating over the same word is the check working, not failing.
  • A gendarme with no code, hiding behind a half-blind gate. četnictvo was coded 58210, which does not exist in the HISCO authority — so those records resolved to no label, no class, no HISCAM. The reason it survived is the more interesting half: every occupation invariant read occupation_hisco alone, and četnictvo appears 0× at enrolment and 58× in the interwar field. An enrolment-only check was structurally incapable of seeing it. Both halves had to move together — widening the check while the code was still wrong just fails the gate. Now every prefix-scoped invariant iterates both families, and the gate went 37 → 41.
  • Four modifier pairings the Dutch data never saw. The authority is keyed on (code, status, relation, product) and built only from combinations HSN observed, so hospodářský adjunkt — 21230 with status 33, Subordinate — matched nothing and returned no scales at all. Those now fall back to the base code, modifier preserved in its own column. The fallback is suppressed wherever relation is set, because HISCO withholds HISCLASS there on purpose: a miner's widow is not a miner.
  • A cottager is not a farmer with ten hectares. domkář, chalupník and malorolník move off 61110 General Farmer (HISCLASS 8) to 61115 Small Subsistence Farmer (HISCLASS 10) — the rung HISCO provides between the holder and the farm labourer. rolník stays at 61110, and the split makes that position easier to defend, not harder.
  • Coverage 97.3 % → 98.5 %. The curated skeleton doubled to the top 2,000 keys and 459 now carry a code. New entries are explicit, including the typos: an edit-distance rule that turns rolnlík into rolník would just as happily turn one real trade into another.
  • And the coding does not manufacture the result. Switching off the compound-matching rules and keeping only whole-string dictionary hits leaves the desertion gradient across social classes unmoved in shape — levels drift up by a fifth of a point, the ordering does not budge. Those rules are the least externally-anchored part of the method, so this was the thing worth testing. See the Occupation and External validation sheets; the calls still open are on the Roadmap.

2026-08-01 (the other half of the map — Slovakia's twins, and a lexicón read too narrowly)

The Czech twins were settled in the morning; the Slovak side had the same structure and none of the fix. Working through it turned up two defects in the lexicón parse that had nothing to do with twins and everything to do with the same habit — writing a rule for the commonest case and never checking what else the page prints.

  • Twenty-six járás/town twins, and the town units were empty. A chartered town sat administratively outside the district it gave its name to, so Bratislava, Košice, Nitra, Prešov, Žilina and 21 more name two units apiece. 12,760 records sat in a pair and only 387 on a town. Now: a bare name means the járás, and the municipality promotes to the town — 1,888 records move, 71 with no municipality go to the ring flagged undecided. Košice vonkov resolves by rule instead of through the fuzzy tier, where 589 records had been reaching the right answer by luck. Decisions D-19.
  • And here the census can split them — all 52 units have their own row, so unlike the Czech pairs there is nothing to pool and both members keep their covariates. Which is the point: the ring around Žilina was 0.964 Slovak, the town 0.540; Bratislava's ring 0.633 Slovak against the city's 0.149. The asymmetry with D-18 is the source talking, not a second opinion.
  • A plural the parser could not read. The lexicón closes each county with its chartered towns under one of four headers; the matcher knew three. The fourth — mestá so zriadeným magistrátom — is the only one seven counties use, so their 27 town rows silently kept the county's last district: obec Nitra, 16,419 people, filed under okres Nové Mesto nad Váhom. Right county, wholly plausible, wrong. Bratislava's four towns escaped only because a singular header sat above them on the same page. Decisions D-20.
  • Nitra's 913 Serbs. The numeric columns are right-aligned; the header anchors mark their left edges. Bucketing each number by its centre drifted the narrow ones a column to the left, and dropped two into the same bucket, where they concatenated — Nitra's 9 Croats and 13 Serbs became 913 Serbs. Reading the right edge repairs 27 broken row sums and, more to the point, 135 rows that reconciled perfectly while sitting under the wrong heading. No share-bearing column moved on any row: the damage was confined to Romanian, Croat, Serb and Other.
  • Nineteen villages in the wrong valley. Three okres blocks are headed adjectivally (Hradný okres) rather than okres X, which the regex did not match at all — so the Orava villages under Hradný inherited the heading above them and were filed under Trstená instead of Tvrdošín, ~98 records. Two more blocks lost their district outright. The three district names are curated onto the backbone; unmapped obce fall from 93 to 57, and the remaining 57 are the four districts that genuinely sit outside the Felvidék frame. Decisions D-21.
  • A coin flip, retired. Ilava nebo Zlaté Moravce scores 100 against both, and the winner was whichever key the lookup happened to hold first. 38 such records across 32 strings, every one a cell naming two districts or truncated past recognition. A tie is now a refusal — the same rule the settlement pass has always applied. Decisions D-22.
  • The same coin flip on the Czech side — where the record can be asked instead. Checking whether the defect had a Cisleithanian twin found 5,100 records resolved by dict order. But refusing them, as Slovakia does, would have been the wrong answer: the record's own municipality knows which okres it sits in, and it disagrees with the arbitrary winner on 59 of the 224 births it can decide. 22 men recorded as born in Ostrava were born in okres Frýdek — the Silesian side, not the Moravian city; 14 Nové Město are na Moravě, not nad Metují. Curating the frequent cases by population, which was the obvious alternative, would have got every one of those backwards. What the municipality cannot decide now refuses. Decisions D-23.
  • Praha's suburbs were not Praha. Smíchov, Žižkov, Karlín and Královské Vinohrady were political okresy in their own right in 1910, so Praha - Smíchov names Smíchov — and the matcher had been reading it two ways at once, sending Smíchov to the capital while sending Žižkov and Karlín to their own units. 68 births move out of Praha, and the court districts come with them (Praha - Říčany → okres Žižkov). It never overrides the municipality: where both can speak, all 217 agree.

2026-08-01 (the twins, settled — and a census that cannot split nine of them)

The morning's fix asked a question the afternoon had to answer: a Statutarstadt was its own district, so what does a record naming one actually mean? Two answers, and the second was not the one anyone expected.

  • One naming convention, so a bare name means one thing. Nine pairs were X / X (město), Brno alone Brno (city) / Brno-venkov (ring) — so a bare district resolved to opposite members, and nine city units held 0 records while Brno city held 2,768. Brno's two rows are renamed to match: city X (město), rural bare X, everywhere. That is also what the ČSÚ register means by a bare name — all 118 ring villages around Brno record okr. Brno — and under the old naming 117 of them sat in the city okres, which is why okres Brno-venkov had not one birth coordinate. Decisions D-17.
  • The municipality picks the member, by definition rather than by inference. A statutory city was a single municipality — one locality apiece in the census size bands — so naming the city puts a record in the city unit and naming anywhere else puts it in the ring. 3,827 records move. The 66 with no municipality at all go to the ring, flagged undecided. A trailing venkov now resolves by rule instead of through the fuzzy tier, where it had been reaching the right answer by luck and was afterwards indistinguishable from a bare name.
  • The census cannot separate nine of the ten pairs at all. A statutory city had no Gerichtsbezirk of its own, so the court-level workbooks carry 330 of the 339 Czech-lands courts and the nine missing ones are exactly the shared seat courts. Opava (město)'s 0.463 Czech share is not the city — it is Gerichtsbezirk Troppau, city and ring; Opava's 0.069 is only Odry and Vítkov. Not coarse: mislabelled. Jihlava, Olomouc and Frýdek had the whole pair on one row and nothing on the other. Only Brno genuinely splits.
  • So the baseline analytical unit is the pooled pair — 148 units, counts summed and every share re-derived from them, never averaged across the two rows. The 158-row member panel keeps the okres-level covariates (literacy, illegitimacy, occupation, population) split for all twenty and blanks the court-derived ones on the eighteen the source merges. Decisions D-18.
  • Coordinates, finally. Brno-born legionnaires go from 0 to 773 of 775, and Praha from 0 to 2,691 of 2,693 — the register's okres field had been swallowing the "se člení na osady" narrative, so the key that matches Wikidata was a whole sentence. Praha, Frýdek, Těšín and Bílsko are absent from Wikidata's reach entirely and now carry curated points.

2026-08-01 (the map says what it means; a city-shaped hole closed)

The evidence panel added this morning earned its place by lunchtime: it surfaced Brno → okres Jilemnice, and pulling that thread found a defect with a single cause and a wider reach than the misplacements suggested.

  • The ČSÚ register leaves a statutory city's okres empty — the city was its own political district — and the settlement lookup is built only from settlements that have one. So the city vanished and a like-named hamlet won by default: 456 records into okres Jilemnice, plus Těšín → Jičín and Lvov → Falknov. Twelve headwords are now curated in, and a bare municipality may no longer resolve onto an osada — a named part of another village — which closes the class rather than the instance. Decisions D-16.
  • The coordinates did not come back with them. A city-born record still has none: 775 for Brno, 154 Kroměříž, 132 Olomouc, 119 Jihlava, 94 Liberec, 86 Opava — and 2,693 for Praha. The curated row carries an okres_id for the city district, while a bare recorded Olomouc resolves to the rural one, so the (municipality, okres_id) join that attaches coordinates misses by exactly one unit. That is the same inconsistency the twins entry below describes, seen from the other end, and it is fixed with them.
  • The map draws court districts but measures political okresy. Every internal court line used to read as a district boundary. They are now told apart exactly: adjacent polygons in the MPIDR file share their border vertex for vertex, so an edge whose two owners sit in the same okres is a court line and everything else is a district boundary. Court lines are pale hairlines, district boundaries solid. A unit toggle shades the courts themselves from birth_court_id — a coordinate-derived layer covering 53,746 records, so a cross-check rather than a finer version of the same count.
  • Click a district and the map runs backwards, from the place to the men: how many were born there and where it ranks, the desertion rate with a 95% interval against the national one, the theatre mix, and the mean rank reset on combat ranks. Prague, for instance: 7,250 legionnaires, 2.87% ± 0.38 against 1.98% overall. Rates are suppressed under 30 births.
  • The town twins are not an error — but the matcher reads them two different ways. Ten districts come in city/rural pairs — Brno / Brno-venkov, Jihlava, Olomouc, Opava, Znojmo, Liberec, Kroměříž, Uherské Hradiště, Frýdek, Bílsko — because a Statutarstadt was its own district. Nine of them are named X (rural) and X (město) (city); Brno alone is named the other way round, Brno for the city and Brno-venkov for the ring. So a bare recorded district name lands rural for nine cities and city for Brno: 9,355 legionnaires and 8,983 VÚA records sit in a twin pair, nine of the ten city units hold 0, and Brno city holds 2,768. Measured, not asserted — and being corrected next, along with the census question underneath it. Decisions D-17.
  • How good are the boundaries? Asked directly, answered on the sheet: about 95%, decomposed into unit identity (~99–100%), topology (every interior edge shared by exactly two polygons, none by more than two), a point well inside a district (~98–99%) and a point near a line (lower — the file is generalised to ~41 vertices per polygon).

2026-08-01 (place matching: a funnel, a filter and a map)

The place-matching figure was a picture of the pipeline. It is now an instrument for interrogating it — three additions, all recomputed from data/final at build time.

  • The cascade is a funnel. A ribbon runs down each lane showing what is still unplaced, narrowing by exactly what each tier placed, and each box carries a bar for its tier against the largest in its lane. The dominance of the exact-okres pass — two thirds of the ribbon gone in one step — is now visible before a number is read. The residual box at the foot of the Czech lane and the "in" caption at the head of the Slovak lane are the same count, which is what "the second frame runs on the first frame's residual" actually means.
  • The tiers can be ticked off. This sheet has always said an analysis can drop the weak tiers and see what moves; now it can be done here. Untick a tier and the readout gives the surviving N, its share, and the three-line pandas mask that reproduces it — copyable. The headline: 113,571 of 132,993 legionnaires (85.4 %) carry a birth district on all tiers; dropping the exact-okres pass alone leaves 20,336.
  • A map, with both frames on one plane. Czech okresy (MPIDR, EPSG:3035) and Upper-Hungary járás (GISta Hungarorum, EPSG:3857) drawn together — the Hungarian coordinates inverted to lon/lat and re-projected through the same LAEA forward as the Czech side. Validated against okres_centroids_1910.csv (median 3.1 km) and the known positions of Bratislava, Košice, Prešov and Žilina (1–8 km). Shading is quantile bins, not a linear ramp: birth counts are skewed enough (Prague at 7,250 against a median in the hundreds) that scaling to the maximum washes every other district to the same near-white.
  • Each stage now shows the strings it actually matched. The panel lists the commonest recorded string → resolved district pairs for the selected tier — aggregates over place names, no individual records. This is the part that earns or loses trust: fuzzy turns out to be doing honest work (Ostrava → Moravská Ostrava, Dvůr Králové → Dvůr Králové nad Labem), while muni immediately shows Brno → okres Jilemnice (3 records) — the Praha problem again, a big city missing from the ČSÚ register so a like-named hamlet captures the births. Surfaced, not silently fixed; the whole tier is 241 records and is ticked off in one click.

2026-08-01 (affordance chips)

  • The "this responds to input" label is now one rule, applied by the builder. It had drifted: Place matching and Rank ladders showed an ⇱ chip but carried no red dot in the sidebar, one chip sat inside a toolbar where nobody scanning headings would see it, and two sheets announced themselves with the word "Interactive:" in the blurb while the rest did not.
  • A chip now sits at the end of the heading that introduces the block, and nowhere else — emitted only by ix_head(), one chip per affordance, so a figure that both toggles and carries tooltips shows both. A build-time check warns if a chip ever escapes a heading again.
  • The sidebar dot is derived from the page, not declared. A sheet is marked interactive iff it actually rendered an ⇱ chip, so the registry can no longer disagree with what a reader sees. Six sheets carry the dot; Place matching and Rank ladders are the two it gained.

2026-07-30 (rank bridge, second pass)

  • Both ladders are now clickable, in both directions. A Habsburg rank still answers "where did these men end up"; a Legion rank now answers "where did they come from", which is the direction the endogeneity question is actually asked in. Same joint distribution, conditioned the other way.
  • At OF-3 and above the panel lists the individual records — 94 of them, 74 Legion and 20 Habsburg — because at those counts an aggregate is not checkable: a bar reading "31 majors" hides whether those are 31 real majors or a parser artefact. Each row gives the legion_id as a link to its csol.cz entry, the birth year, the rank on the other ladder and the theatre. Ids only, no names, so the published page still carries no named individuals.
  • The fine common grade is back, as a bare muted numeral on each ladder's outer edge — the rung rank_reset is actually measured on, next to the coarse NATO band it collapses into.
  • An open question logged, not silently fixed. svobodník is coded OR-3, but §G2 reports the APersP-01 CZE note as vojín, svobodník = OR-1, and Bednařík's 1916 POW-camp pay scale pays "pěšák a svobodník 15" — private and svobodník identically, the first real step at desátník. So the enlisted band rests on the weakest evidence in the figure. It is now Decisions D-15 (Open) with the full argument, flagged under the figure, and nothing in the data was changed: the band is only the robustness axis, and repairing one rung would leave the rungs above it inconsistent.

2026-07-30 (later)

  • References is now the master bibliography, and complete. Every work cited anywhere on the site is listed — which it was not: the three Legion chronicles (Vaněk, Bednařík, Boháč), the k.u.k. rank tables (Dolejší et al., Rest et al.), the NATO APersP-01 / STANAG 2116 standard the whole common scale is expressed in, the Vašátko insignia plates, the four interwar statutes and the superseded 1910 Ortsverzeichnis were all cited but absent. 41 entries, in three sections (literature · statutes and standards · data sources).
  • reference/references.bib is generated from that list on every build, with the Zotero citekeys pinned so a re-import updates records instead of forking them. A build-time assertion refuses to emit one without the other, so the sheet and the .bib cannot disagree. The hand-maintained rank_sources.bib is retired, its entries folded in.
  • Citation linking fixed. A work cited in two textual forms on one sheet — Vaněk et al. (1925–1929) in one paragraph, Vaněk et al., 1925–1929 in the next — had only the first form linked, because a match on one pattern stopped the others being tried. Inline citations went from 200 to 263.
  • Rank bridge corrected. The right-hand connectors were mirrored: they left the grade rung at the rank's height and arrived at the rank at the rung's height. Each rung is now labelled with its NATO band alone; where consecutive rungs repeat a band, that repetition is the point — the three subaltern rungs all reading OF-1 is why the fine scale exists.
  • Figures regenerated by re-running 03_visualisation against the current data/final — all twelve, including the exit-code table that the audit flagged as stale.

2026-07-30

  • Structure given rules. Every sheet now has the same bones — one title, a group chip, a TL;DR card, a contents strip, a "see also" footer — instead of per-sheet improvisation. Links follow one rule too: prose links underline and firm up on hover, navigation never underlines, and outbound links carry a ↗.
  • The record of change split three ways, each answering one question and nothing twice: Roadmap (what is open) · What's new (this sheet — what changed and why) · Commits & versions (the git record). A strip at the top of each says which is which.
  • Decisions — a register of every judgement call, with a one-line reason and a link to the argument. The audit sheet is now titled by its date, because that is what it is: one dated review, not the decision log.
  • Place matching gained a flow diagram — the Czech and Slovak passes side by side, each stage carrying its live record count and its refusal rules.
  • Rank ladders gained a cross-army bridge: Habsburg ladder, the common grade scale, and the Legion ladder in one picture, which is what rank_reset actually subtracts.
  • Overview gained a reading-path map for first-time readers.
  • Cross-sheet section links now work (a link to another sheet's heading used to change the URL and nothing else), and the page has a favicon.

2026-07-23

  • 1914 Habsburg order of battle — the complete peacetime army digitised from Ehnl (1934) Tabelle I (443 units: common-army, Landwehr and Honvéd infantry, Feldjäger, cavalry, artillery and technical troops). Each regiment carries its Ergänzung home district (recruiting Bezirk, standardised to modern place names — Trutnov, not Trautenau) and its national composition (mother-tongue shares, dominant language, Slav share, Herfindahl).
  • Habsburg army & recruitment sheet — how the territorial Ergänzung system tied a regiment to a home district and, through it, to a nationality; the natural-experiment angles this opens; qualitative context from the reference works.
  • Regiment explorer — a filterable, sortable table of all 443 units (filter chips by branch and by language, live search, click-to-sort).
  • Descriptives waterfalls now separate "Habsburg unit recorded (present)" from "Habsburg unit matched to Tabelle I" — a recorded unit string is not the same as one that resolves to a real 1914 regiment (and hence to a home district and nationality). Legionnaires: 100,531 present (75.6%) → 81,911 matched (61.6%); fallen: 116,138 present (62.4%) → 89,378 matched (48.0%). A short decomposition explains what the unmatched remainder is (mostly no-unit records, plus wartime Landsturm absent from a peacetime order of battle).
  • Source provenance gained the new Habsburg-army sources (Ehnl, the 1914 Schematismus, the secondary literature) and mechanism nodes for the recruiting district, unit nationality and deployment; the side panel now shows each source's full reference and a one-line note on how it enters the pipeline.
  • Terminology unified: the "A-H" abbreviation is now spelled out as "Habsburg" throughout.

2026-07-22

  • Habsburg comparison-population waterfall added next to the legionnaire one on the Descriptives sheet — same interactive machinery (toggle covariates, live theatre composition). The control group is the fallen soldiers who did not cross into the Legions; a legion link disqualifies a control, so the first toggle excludes the legion-linked and the highlighted count tracks the valid controls.
  • Overview made less committal about the headline regressor and estimation method, which are still in development.
  • This What's new sheet added, with the live-page link.

2026-07-21

  • English value translations surfaced (units, religion, rank tiers) in the data dictionary and variable notes.
  • References sheet (APA): literature and data sources (census, lexicons, GIS, the two scraped databases). Inline author-year citations across every sheet are now clickable and preview the full reference on hover.
  • Roadmap / to-do sheet (done vs. remaining).
  • Interactive vs static affordances: an "⇱ Interactive" chip on figures that respond to input, a red dot on those sheets in the sidebar, and a "↕ Hover for values" chip on the static-with-tooltip histogram.
  • Source texts consolidated under library/ (lexicons, reference works, books).
  • --noindex build option for link-only publishing.

2026-07-20

  • Interactive sample-size waterfall (legionnaires): cumulative coverage bars, a live filter panel with per-toggle marginal cost, desertion rate by theatre, a shareable link, and a runnable pandas snippet.
  • rank_reset: diverging histogram, by-theatre overlapping areas, and a combat-ranks-only / inclusive toggle (the legion_noncombat decision).
  • Units & armies sheet (regiment vs unit_key, Fidler cross-check).
  • Source provenance — interactive map from each source to the mechanisms it grounds.
  • Architecture map — click a module for an info panel (role, version, imports).
  • External validation sheet (vs Fidler 2025) and the Data QA gate panel.

Earlier

  • First generated site: pipeline, variables, place-matching and rank-ladder references; live descriptives recomputed from data/final; per-script sheets; symbol-usage index; data dictionary and census codebook.

PipelineHow it works

What every script does. · source: reference/PIPELINE.md

Explain it simply

Somewhere on a Czech government server there are 133,000 web pages, one per legionnaire, each a little table of half-filled fields typed up from a paper card: born Kolín, trade zámečník, joined 12.3.1917. A person can read one of those in a few seconds. Nobody can read 133,000, and no statistical model can read even one — a model wants a rectangle of numbers, and this is a heap of Czech sentences.

The pipeline is the machine that turns the heap into the rectangle, and it is built in three stages so that each can be checked. Collect: fetch the pages and store them exactly as they came, never touched again. Clean: read every field with a small specialist — one for dates, one for ranks, one for jobs, one for places — and write out a table where a column means the same thing in every row. Visualise: draw the pictures, and read only from that final table, so a figure can never quietly disagree with the data it claims to show.

The rule that makes the whole thing survivable is that the raw copy is sacred. Every judgement — every guess, every repair, every dropped record — happens in the middle stage, in code, where it can be read, argued with and undone. If a decision made in 2026 turns out to be wrong, the fix is one function and a re-run, not a dataset nobody can reconstruct.

Script index

One sheet per module — its docstring, public API, version, callers and git history, all read from the source. Pick one in the sidebar under Pipeline scripts. The narrative below explains how they fit together.

155 rows

ScriptRoleDoes
fetch_vua_courts.pyScrapingDownloads the name-level archival inventories (*inventáře*) of three H
progress.pyScrapingProgress-bar helper shared by the Kramerius downloaders.
run_lexicon_downloads.pyScrapingDownload the two outstanding 1910 *Spezialortsrepertorium* volumes int
run_matice_downloads.pyScrapingDownload the digitised member lists of two Czech national societies, t
run_oeulk_downloads.pyScrapingDownload every volume of *Österreich-Ungarns letzter Krieg 1914–1918*
scraper_amaterskedivadlo.pyScrapingamaterskedivadlo.cz scraper — importable module + CLI. **Feasibility
scraper_csol.pyScrapingcsol.cz legionnaire scraper — importable module + CLI.
scraper_kramerius.pyScrapingKramerius v5 downloader — importable module + CLI.
scraper_kramerius7.pyScrapingKramerius v7 downloader — importable module + CLI.
scraper_verlustlisten.pyScrapingVerlustlisten Österreich-Ungarns 1914–1919 (DES index) scraper — impor
scraper_vuapraha.pyScrapingvuapraha.cz scraper — importable module + CLI.
affiliations.pyPerson parsingParses the free-text ``Politická příslušnost`` field into per-party bo
clean.pyPerson parsingFirst-pass normalisation of scraped archival records, applied **before
dates.pyPerson parsingParses Czech date strings across four standalone date columns spanning
education.pyPerson parsing``Vzdělání`` is **not a level**. It is an *itinerary*: a sequence of
exit_codes.pyPerson parsingExit code harmonisation for the 'Konec v legiích' field.
habsburg.pyPerson parsingField-specific harmonisation for the VÚA *padlí v 1. sv. válce* dump
locations.pyPerson parsingDecomposes a free-text Czech place string into structured components.
occupations.pyPerson parsingThe raw field is Czech free text: 119,046 non-empty values over 8,004
provenance.pyPerson parsingParses the free-text ``Zdroj informací`` field into two separate famil
ranks.pyPerson parsingCollapses the free-text rank fields onto **within-system ordinal** sca
religion.pyPerson parsingThe raw ``Vyznání`` field is a small, abbreviated Czech vocabulary: de
schema.pyPerson parsingSingle source of truth for column naming. The cleaning notebook
theater.pyPerson parsingFour-pass imputation of the Legion theatre from the raw csol.cz unit/p
units.pyPerson parsingExtracts a clean regiment label and unit type from
vua_courts.pyPerson parsingThe Vojenský historický archiv publishes name-level inventories of thr
amateur_theatre.pyGeography & census**Feasibility pilot.** Nothing here feeds ``data/final``. The module
amateur_theatre_model.pyGeography & censusThe amateur-theatre model pass: the chronicle clauses the rules leave
census.pyGeography & censusHarmonises the Rumpler-Seger / MPIDR census workbooks in ``data/raw/ce
census_hu.pyGeography & censusCisleithanian analogue: :mod:`utils.parsing.census` (okres-level Rumpl
census_labels.pyGeography & censusThe Cisleithanian census (:mod:`utils.parsing.census`) carries **Germa
districts.pyGeography & censusBuilds the **authoritative** 1910 district crosswalk for the Czech cro
districts_hu.pyGeography & censusThe Cisleithanian analogue is :mod:`utils.parsing.districts` (Czech la
geonames.pyGeography & censusThe RÚIAN tables (:mod:`utils.parsing.ruian`) stop at the official čás
lexikon.pyGeography & censusParses the ČSÚ *Historický lexikon obcí* alphabetical register (the so
lexikon_sk.pyGeography & censusTransleithanian analogue of :mod:`utils.parsing.lexikon` (the Czech ČS
locative.pyGeography & censusCzech place names printed in the locative (*v Brně*, *ve Vodňanech*, *
matice.pyGeography & censusMatice česká and Matice moravská member registers — OCR to one row per
prng.pyGeography & census1910 Austrian Silesia did not end at today's Czech border: the eastern
records_geo.pyGeography & censusMaps a soldier's birth district string (``birth_okres``) to the 1910 p
records_geo_hu.pyGeography & censusCisleithanian analogue: :mod:`utils.parsing.records_geo` (birth_okres
ruian.pyGeography & censusThere are no 1910 municipal boundaries for Bohemia and Moravia, and th
spatial.pyGeography & censusAssigns a **court district (soudní okres / Gerichtsbezirk)** to any po
vanished.pyGeography & censusRÚIAN and Geonames list what exists today. A 1910 settlement that has
wikidata.pyGeography & censusFetches Czech settlements (obec Q5153359 + část obce Q15978299) with *
linkage.pyLinkageDetects individuals appearing in **both** final datasets so the
bakeoff.py1910 registerThe bake-off, as a module: the configuration and the run loop, in one
build_contrast_sheet.py1910 registerThe contrast sheet: the look-alike symbols the readers swap, side by s
build_de_page_map.py1910 registerPair every Czech page of the 1910 register with the German pages that
build_glyph_bank.py1910 registerCut the boxed glyphs out of the gold crops: the bank test A reads.
build_lexikon_figure.py1910 registerCut the docs-site figure out of the 1910 register — images plus derive
build_mark_ledger.py1910 registerFill the mark ledger (D-109) from what the r5 gold run left on disk, a
build_register_walkthrough.py1910 registerThe docs site's Brzice walkthrough: from the printed line to the place
build_symbol_atlas.py1910 registerCut the volume's own symbol legend into a labelled atlas of pictogram
build_symbol_atlas_de.py1910 registerCut the German edition's own symbol legend into a labelled atlas (D-92
build_token_review.py1910 registerQueue the suspect tokens of the gold sheets for a person to decide, on
check_transcription.py1910 registerValidate a human transcription worksheet, and turn it into a reader.
draw_gold_sample.py1910 registerDraw the pages a person will transcribe, reproducibly.
extract_prose_pages.py1910 registerThe prose of the statutory towns' pages, from the text layer.
lexikon_1910.py1910 registerPodrobný seznam míst / Spezialortsrepertorium 1910 — grid, blocks, inv
lexikon_1910_attributes.py1910 registerWhat the register prints *beside* two glyphs: a school's classes and l
lexikon_1910_census.py1910 registerReconcile transcribed 1910 register blocks against the okres-level cen
lexikon_1910_courts.py1910 registerThe court each section of the register belongs to: one 1910 backbone c
lexikon_1910_crosswalk.py1910 registerBoth volumes print the same 1910 municipalities of Bohemia, each under
lexikon_1910_de.py1910 registerSpezialortsrepertorium Böhmen 1910 — the German twin of the Czech volu
lexikon_1910_de_notes.py1910 registerThe German edition's apparatus, read from the image alone (D-92).
lexikon_1910_de_table.py1910 registerThe German volumes' table, cut into blocks from the image and read by
lexikon_1910_editions.py1910 registerThe two editions of the 1910 register as two measurements of one truth
lexikon_1910_image.py1910 registerPage geometry of the 1910 register from the rendered page alone — no t
lexikon_1910_institutions.py1910 registerDecode the ``Úřady, ústavy`` footnote into an institution inventory.
lexikon_1910_ledger.py1910 registerThe history of every mark of the 1910 register through the reading rou
lexikon_1910_names.py1910 registerAnchor 1910 register blocks to known municipalities, and let the volum
lexikon_1910_notes.py1910 registerThe footnote apparatus printed below the 1910 register table.
lexikon_1910_pages.py1910 registerRead a whole page of the 1910 register with a cloud model, and price t
lexikon_1910_register.py1910 registerA frozen run (``output/frozen/<edition>_<pages>/``, notebook 15) settl
lexikon_1910_vision.py1910 registerRead the below-table apparatus from the *image*, and hand the result t
lexikon_1910_volume.py1910 registerOne profile per volume of the 1910 register — what differs from book t
locate_glyphs.py1910 registerE3, first half: a box for every printed glyph of the apparatus (D-69,
make_gold_worksheet.py1910 registerCut the gold-sample pages into a worksheet laid out the way the page r
make_gold_worksheet_de.py1910 registerBuild the gold worksheets for the German edition (Spezialortsrepertori
read_transcription_md.py1910 registerRead the whole-page gold sheets, and grade the transcriber while doing
reader_batches.py1910 registerThe readers, batched: queued requests in, cached replies out (D-108's
report_unlisted_abbreviations.py1910 registerThe offices items no code in ``institution_abbreviations.csv`` reaches
rescore_passes.py1910 registerRe-score every cached symbol pass at the analysis unit. No model call,
run_de_table.py1910 registerThe German volumes' table read by a model, block by block, as the Czec
run_e1_gemini_version.py1910 registerE1 — is the gap between the two cloud pipelines the model, or the view
run_e1b_second_reader.py1910 registerE1b — which cheap reader of another family errs somewhere else than Ge
run_e3_pages.py1910 registerE3 on any page: the locator and DINOv3 on the readers' saved section c
run_frozen.py1910 registerThe frozen run: every stage of the reading route, for one edition and
run_glyph_e3.py1910 registerE3, second half: name every located glyph by its nearest neighbours on
run_glyph_test_a.py1910 registerTest A — does an image encoder tell the fill pairs apart at all? (SYMB
run_lexikon_1910_pilot.py1910 registerCalibration run for the 1910 special place register — four channels, o
run_r5_german_notes.py1910 registerThe German notes arm of ``r5``: the German edition's apparatus, read b
run_table_arm.py1910 registerThe table arm of ``r5`` — the seventeen printed columns read by a mode
run_verify_test_b.py1910 registerTest B — does one Opus verify call settle what Gemini and Haiku disput
score_certification.py1910 registerScore the sealed certification pages — once, against metrics written d
score_r5_german_notes.py1910 registerScore the German notes arm of ``r5`` against the German gold, and set
score_table_passes.py1910 registerScore the table readings against the gold tables, cell by cell.
section_images.py1910 registerThe readers' section crops, saved as images for every page (D-112, ste
audit_full_run_pages.pyReference toolsEvery German full-run page, cut offline before anything is read: does
build_conflict_workbook.pyReference toolsThe Czech→HISCO dictionary flags keys ``review`` where the coding is n
build_curation.pyReference toolsEncodes the accented-Czech names for the 1910 Czech-lands districts, k
build_czisco_bridge.pyReference toolsCZ-ISCO (ČSÚ classification 80146, 2025) as a **Czech→English occupati
build_german_click_queue.pyReference toolsA Token Review queue for clicking every pictogram of the German gold p
build_german_click_strips.pyReference toolsCut the German gold's office sections into strips of a few printed lin
build_hisco_authority.pyReference toolsOne row per **(hisco, status, relation, product)**, carrying the Engli
build_matice_read_sheets.pyReference toolsSheets of scanned lines for re-reading the Matice residences the OCR g
build_occupation_curation.pyReference toolsEmits the **skeleton** of the Czech occupational dictionary: every nor
build_page_index.pyReference toolsBuild the page index for Österreichische Statistik XCIII/1 (Unterricht
build_sk_curation.pyReference toolsMagyar → Slovak/Rusyn name curation for the 1910 Upper-Hungary (Felvid
build_theatre_gold_page.pyReference toolsThe ruling page for the theatre model pass's gold (D-157).
build_unit_authority.pyReference toolsDerives a **factual authority table of Czechoslovak Legion units** fro
build_unit_timeline.pyReference toolsWhere every Czechoslovak Legion unit was, whom it answered to and who
check_atlas_parties.pyReference tools``atlas_iv37_parties.csv`` is a **hand transcription of a photograph**
check_unit_timeline.pyReference tools``unit_timeline.csv`` is parsed by ``build_unit_timeline.py`` from the
check_unterricht_transcription.pyReference toolsInvariants on the transcription of Österreichische Statistik XCIII/1 (
czech_glosses.pyReference toolsThe authored half of the occupation pipeline, and the piece no externa
download_hisco_sources.pyReference toolsThe occupation pipeline needs two external ingredients, and only one o
draw_education_sample.pyReference toolsDraw the `Vzdělání` strings a person will code by hand, reproducibly.
german_candidate_diagnosis.pyReference toolsWhich candidate-step filter removes the German glyphs the author click
german_click_recall.pyReference toolsHow well does the locator's box land on the glyph the author clicked?
german_locator_trial.pyReference toolsDoes a locator trained on the German gold place German claims better t
german_supervised_placement.pyReference toolsDoes a glyph classifier trained on the author's clicks place German cl
map_intensities.pyReference toolsThe intensities of ``run_intensities.py`` side by side, one figure per
map_matice.pyReference toolsMaps of the Matice measure at three levels: political okres, court dis
merge_matice_readings.pyReference toolsMerge the residence readings made from ``build_matice_read_sheets.py``
prep_slices.pyReference toolsPrepare reading slices for the qualitative pass (scratch; not part of
run_amateur_theatre_pilot.pyReference toolsFeasibility pilot — the amateur-theatre database as a pre-1914 covaria
run_census_berufsstellung_check.pyReference toolsis a `rolník` a farm *owner* or a farm *worker*?
run_czisco_check.pyReference toolsMatches each curated Czech key against the **head noun** of a CZ-ISCO
run_german_detect_by_naming.pyReference toolsDetect by naming: let the bank find the German glyphs, instead of a lo
run_german_oracle.pyReference toolsThe oracle test: does the Czech-built bank name German glyphs when the
run_german_reader_boxes.pyReference toolsThe reader gives the box: can DINOv3 check German claims on the boxes
run_intensities.pyReference toolsFour intensities side by side, per 1,000, at three levels: political o
run_link_bridge.pyReference toolsApplies ``czech_glosses.py`` to the curation skeleton and fills in the
run_matice_pilot.pyReference toolsPilot — the Matice česká and Matice moravská member registers, read in
run_mosaic_beruf_check.pyReference toolsdoes the German word for a farmer mean an *owner*, one record at a tim
run_occcanine_check.pyReference toolsCodes the **German glosses** with OccCANINE (Dahl, Johansen & Vedel, "
run_unterricht_check.pyReference toolsIs the education ladder the one the Austrian state actually operated?
run_vua_courts_pilot.pyReference toolsPilot — the VÚA military-court inventories as a Habsburg-side record o
score_education_gold.pyReference toolsScore a hand-coded education worksheet against `education.py`.
score_sealed_abbreviations.pyReference toolsScore the text abbreviations on the sealed certification pages — a sec
statutory_institutions.pyReference toolsThe statutory cities' institutions, from their verified prose: stage 2
statutory_prose.pyReference toolsThe statutory cities' prose, transcribed precisely: two vision readers
verify_locators.pyReference toolsCheck every ledger entry's locator against its slice (scratch tool for
build_docs.pyOutput & supportRenders every authored reference document, the tabular crosswalks, and
build_fonts.pyOutput & supportSubset the Libertinus faces the docs site embeds -> utils/docs/fonts/*
build_validation_workbook.pyOutput & supportDraws a **tier-stratified** sample of resolved records and writes an E
check_ostafin_silesia.pyOutput & supportAustrian Silesia 1910 against Ostafin et al. (2020): an independent tr
data_checks.pyOutput & supportOne definition of "is the built data sound?", used in two places:
latex_tables.pyOutput & supportLaTeX table generation utilities.
lexikon_population.pyOutput & supportThe 1910 population of the ČSÚ *Historický lexikon obcí ČR 1869–2005*,
run_completeness.pyOutput & supportWhether a frozen run has every reply it asked for, before anything is
score_sealed_retest.pyOutput & supportThe full run's readings of the sealed certification pages, scored — a
th_style_v113.pyOutput & supportA personal, reusable Matplotlib configuration module built around the

In short

One section per module: its job, its inputs/outputs, and the core piece of code that does the real work. Written as the ground for the paper's data-construction appendix and as a re-entry map for future-me. Companion: VARIABLES.md (every constructed variable, with formulas), PLACE_MATCHING.md (the place-matching logic in plain language), DATA_SOURCES.md (provenance of every input).

Audit status: audited and fixed three times — 2026-07-16, 2026-09-22 and 2026-10-24, each with its findings and measured before/after numbers. Brought back in line with the code on 2026-11-02, when this guide named about a third of the modules in utils/: every module is now named here, at least in one line. The docs site gives each module its own sheet, built from its docstring and source, so that is where the detail lives; this guide says what each one is for and where it sits in the run.


The pipeline at a glance

Five notebooks orchestrate everything; all logic lives in utils/, the notebooks only call it in order. Three are the person pipeline (collect → clean and link → draw); two drive the slow, paid reading passes, kept apart from the fast code as the code standard asks: the 1910 place register (15) and the amateur-theatre model pass (16). A few measures are still pilots run from the command line (utils/reference_tools/, §7) and write only to data/interim/. data/ holds machine inputs/outputs (gitignored); reference/ holds small curated inputs (tracked).

01_data_collection      scrapers → data/raw/{legionnaires,habsburg}/*.csv  + post-scrape QA
02_data_cleaning        §1 legion parse      raw csv → interim parquet   (clean → parse → to_english)
                        §2 habsburg parse    raw csv → interim parquet
                        §3 places+census     §3a districts_1910  §3b census_okres_1910
                                             §3c settlements_1910 (+coords, courts)
                                             §3d districts_hu_1910 + census_hu_jaras_1910
                                                  + settlements_hu_1910
                                             §3e today's geography (RÚIAN, Geonames, PRNG,
                                                  vanished places) + settlements_1910_geo
                        §4 linkage + place standardisation → data/final/persons/*
                        §4b wide analysis files (persons × census) → data/final/analysis/*
                        §5 data_dictionary.csv
03_visualisation        reads data/final/ ONLY → output/ figures + LaTeX tables
15_frozen_run           the 1910 register: freeze → read (batched) → check (E3) → Opus →
                        ledger → output/frozen/<edition>_<pages>/ → §18 data/final/register/
16_theatre_model_pass   amateur-theatre chronicle clauses the rules leave open, read by a
                        model → data/interim/theatre/  (gold → full; SPEND off, D-157)

Notebook 02 §3e reads the register as last built by notebook 15 (a register court breaks settlement name ties, D-155), so a register rebuild is followed by a rerun of 02 from §3e.

Dependency chain inside §3/§4 (each arrow = "consumed by"):

MPIDR .dbf/.shp ─→ districts.py ─→ districts_1910 ─→ census.py ─→ census_okres_1910
                                        │                              (census_labels.py renames → English + shares)
   ČSÚ lexikon PDF ─→ lexikon.py ───────┤
   Wikidata SPARQL ─→ wikidata.py ──────┼─→ settlements_1910 (names + okres timeline + coords + court)
   MPIDR polygons ─→ spatial.py ────────┘
GISta .dbf ─→ districts_hu.py ─→ districts_hu_1910 ─→ census_hu.py ─→ census_hu_jaras_1910
   Majo PDF ─→ lexikon_sk.py ─→ settlements_hu_1910
persons (interim) + all of the above ─→ linkage.py + records_geo.py + records_geo_hu.py
                                     ─→ data/final/persons/{legionnaires,habsburg}_clean

Module conventions: every parser exposes add_*_columns(df) -> df.copy() (never in-place), a printing *_report(df) diagnostic, and __version__.


1 · Scraping (utils/scraping/)

scraper_csol.py

Scrapes the csol.cz Databáze legionářů (/legionar/{id}/, ~133k sequential ids) into data/raw/legionnaires/legionnaires.csv. Importable module + CLI; supports resume, rate-limit delay, and per-record test_id() inspection. csol.cz is a community-enriched fork of the VÚA database — the enumerable record pages are why it is the scrape target.

scraper_vuapraha.py

Same pattern for vuapraha.cz; used for the fallen-soldier database (/padli-1-svetova/{id}/, 186,191 records → data/raw/habsburg/habsburg_fallen.csv). The parser reads generic div.inventory__item label–value pairs, so new fields on the site are captured without code changes. Empirical id-ceiling probing (autodetect_max_id).

The other downloaders

They follow the project's scraping rule — sequential, paused, cached and resumable, with a User-Agent that names the script, never the author. None of them feeds the person pipeline.

  • scraper_verlustlisten.py — the index of the k.u.k. casualty lists (CompGen's DES project ou1wk); a feasibility pilot so far, run from notebook 01 into data/raw/verlustlisten/pilot/.
  • scraper_amaterskedivadlo.py — the Databáze českého amatérského divadla (places, ensembles, ensemble pages) behind the amateur-theatre measure (§3).
  • scraper_kramerius.py / scraper_kramerius7.py — whole-document downloaders for Kramerius 5 and 7 digital libraries: page scans as one PDF, OCR as one TXT, and on v7 the ALTO word coordinates. Driven by three runners: run_lexicon_downloads.py (the outstanding 1910 Spezialortsrepertorium volumes → library/lexicons/), run_matice_downloads.py (the Matice česká and Matice moravská member lists, and the Matice česká monthly payment lists it first finds in the Časopis Musea → library/associations/) and run_oeulk_downloads.py (Österreich-Ungarns letzter Krieg, all sixteen parts → library/ah-army/). progress.py is their shared progress bar.
  • fetch_vua_courts.py — the VÚA inventories of three Habsburg military courts, three PDFs, fetched once into data/raw/vua_courts/ (parsed by utils/parsing/vua_courts.py).

2 · Person-record parsing (utils/parsing/)

Applied in notebook 02 §1 (legion) and §2 (habsburg, via the habsburg.py umbrella).

clean.py — generic first-pass cleaning

?/-/empty/punctuation-only cells → pd.NA; surnames unwrapped from parentheses/quotes with a trailing-? uncertainty flag; place fields lose the + verification marker (okres text and German exonyms are deliberately preserved — they are the census-merge key).

# _clean_name_value: peel wrappers, flag uncertainty
#   ("Adamek-ova")  -> Adamek-ova       (Adet?) -> Adet, uncertain=True
while prev != s and s:
    if s[0] in "(" and s[-1] in ")": s = s[1:-1].strip()
    if s[0] in _WRAP_QUOTES and s[-1] in _WRAP_QUOTES: s = s[1:-1].strip()
    if _TRAILING_Q_RE.search(s): uncertain = True; s = _TRAILING_Q_RE.sub("", s)

Outputs: surname, given, name_uncertain; cleaned place strings in place.

dates.py — the four date columns

Parses Datum narození / zajetí / zařazení do legií / úmrtí → birth_date / capture_date / enroll_date / death_date, each clipped to a plausible range (birth 1860–1905, capture 1914–18, enrol 1914–22, death 1914–23). Pipeline per cell (parse_date, 1.2.0): strict DD.MM.YYYY → DD.MM.YY + century fix (a year after 1930 moves back a century if that lands in range: 6.12.19 → 1919; for capture, enrolment and exit a year before the range also moves forward: 1.4.1818 → 1918) → regex-extract the first full date (rescues double-date cells) → for capture and enrolment, a month-year (4.1918) or an impossible day (29.2.1918) keeps the month → year-only fallback → first in-range 4-digit year anywhere. Year guards refuse anything outside 1800–2100 before a timestamp is built.

A partial hit dates to the 1st of the month or Jan 1, and says so. Birth and death keep their birth_year_only / death_year_only flags; capture and enrolment carry capture_date_precision / enroll_date_precision (day / month / year), so a monthly timing figure can filter to the dates that support it (D-61). Before 1.2.0 a month- or year-only capture or enrolment string was dropped without a flag — 2,440 and 2,463 of them. The legion death clip keeps 4,410 of 12,480 recorded deaths; 8,034 of the rest are veterans who died after 1923, so the raw string is kept as death_date_raw.

def _century_fix(ts, lo, hi, upward=False):
    if ts.year > 1930 and lo.year <= ts.year - 100 <= hi.year:
        return ts.replace(year=ts.year - 100)              # 2019 -> 1919
    if upward and ts.year < lo.year and lo.year <= ts.year + 100 <= hi.year:
        return ts.replace(year=ts.year + 100)              # 1818 -> 1918 (partial columns)
    return ts

exit_codes.py — the outcome variable

Parses Konec v legiích ("DD.MM.YYYY ( CODE )") into a frozenset of canonical exit codes (B, ČSA, D, DM, N, NS, P, S, V, X, Z), one boolean dummy per code, the shorthand deserted = exit_D, and exit_date from the text before the parenthesis, through the same dates.parse_date as every other date — range 1914-01-01 … 1939-12-31, month and year fallbacks, exit_date_precision (1.2.0, D-61). It is the hazard model's event time and until 1.2.0 was the one date never range-checked: it ran from 1818 to 2192, and 3,930 strings that state a year got no date at all. Full Czech words are substituted to codes first ("Demobilizován" → DM, longest first); then each whitespace token of the parenthetical is checked against a drop list of archival markers and must parse completely as a sequence of codes (greedy 3→2→1) — a token with unparseable residue is skipped whole, so unknown strings can never fabricate codes (the two ambiguous MD/DMD rows are refused, not guessed).

m = re.search(r"\(([^)]+)\)", token)          # take the parenthetical if present
token = m.group(1).strip() if m else token
for word, code in _WORD_TO_CODE.items():      # "Zemřel na následky zranění" -> Z before "Zemřel" -> S
    token = token.replace(word, code)
...
for tok in token.split():                     # whole tokens; archival markers struck first
    if tok in _DROP_TOKENS or not tok.isalpha():
        continue
    # greedy ČSA/DM/NS/letter parse — the WHOLE token must be consumed, else skipped

affiliations.py — political affiliation dummies

Politická příslušnost → one boolean pol_* per canonical token: parties (soc_dem, nar_soc, agr, lid, nar_dem, klerical, …), civic organisations kept separate (sokol, dtj, orel, slov_liga), nationality markers (german, russian, …), bezpart. Matching is on the normalised string with every token anchored at a word start (2.2.0; karpatorus no longer contains rus), so one cell can set several dummies ("soc.dem., Sokol" → both), with shadowed tokens dropped — a shorter token contained in a longer matched one does not fire, so stát.pokr. is Č15 and not also Č13/Č17 via the bare pokr. PARTY_IDEOLOGY provides the plot grouping.

An unrecorded affiliation is <NA>, not False (2.1.0, D-52). The field is blank or a placeholder on 93,377 of 132,993 records — 70.2% — and writing False there asserts not a Sokol about a source that said nothing. That is the shape that fabricated exit_N on 26.4k empty cells (audit A3) and the rule provenance.py already states for its own flags. pol_recorded (39,616 True) marks the base and every dummy is <NA> outside it; a record that is filled in but matches no token keeps False, because there the source spoke and the register did not recognise what it said. Any specification reading pol_* as party affiliation must condition on pol_recorded — and must still exclude the civic organisations, which PARTY_IDEOLOGY marks but nothing structurally prevents. The dummy columns are built from PARTY_ORDER rather than from whatever happened to match, so pol_orel (8 records) cannot disappear from a filtered frame, and a QA invariant asserts the column set.

The genealogy is a source, not Wikipedia (2.0.0, D-50). It comes from Akademický atlas českých dějin plate IV.37 — the register of 55 parties of the Czech lands 1861–1918, with each party's founding year, area of operation and a notes column recording every merger and renaming — transcribed to reference/parties/atlas_iv37_parties.csv and held by check_atlas_parties.py (12 invariants; the notes cross-reference by code, so a merger must be reciprocal). Every token in TOKEN_MAP carries the atlas code(s) that justify it, and pol_atlas_codes takes a coded record back to a row of the printed table.

What the register changed: pokr is not a party family. Č13, Č15 and Č17 are each "1918 spoluzakladatelka Č 23", Č23 is "1919 přejmenována na Národní demokracii", Č7 merged into Č15 in 1908, and Masaryk's realists are Č12 → Č13. So every pokroková party in this data ends in the National Democracy; the canonical is abolished and its tokens go to nar_dem. It confirmed lid.pokr. → nar_dem (Č17) and real. → nar_dem (Č12→Č13), both previously taken on weaker evidence. volnomyšlenkář — a freethinker association, absent from the register — becomes its own civic org instead of a party.

Three refusals, in REFUSED and printed by the report so they stay visible: a bare lidová (8 register parties carry it, across Catholic, liberal-national and Masarykite), a bare demokrat, and SL plus every Slovak formation — plate IV.37 is v českých zemích and contains no Slovak party at all. Each refusal now carries how to match it — stem (matches from a word start and lets the ending run on: lidov → lidová) or word (must also end at a word boundary). The report used to count them as bare substrings, which made sl a hit inside every slov… string and printed 193 for a token that occurs 108 times; anchored, it prints 111. The unmatched tail is counted against the recorded base, so the 93k blank cells no longer sit in it. Also dropped: rolník → agr, which mapped an occupation into a party dummy while dělník in the same field mapped to nothing.

2.2.0 (audit 2026-09-22 B2). A bare socialista is a fourth refusal: it had been coded soc_dem while a bare demokrat was refused, though Č24 Česká strana socialistická is the national socialists' own name — and národní socialista had been coded a social democrat by the same rule. The written national-socialist forms (národní socialista, nár. socialista) now go to nar_soc. 18 records change.

education.py — the 1869 school ladder, parsed rather than classified

Vzdělání is not a level: it is an itinerary — a sequence of school types, each optionally followed by the years completed (ob. 5 mě. 3 pok. 3). v2.0.0 tokenises it and reports the highest institution attended (educ_ord 0–5: No schooling < Elementary < Burgher school < Vocational secondary < Upper secondary < Tertiary), the years at each rung, the track, and whether the top rung was finished. Thirteen columns; educ_clean / educ_ord keep their names. Coverage 99.1% of non-empty (was 96.7%).

Year numbers attach backwards to the institution they follow (ob. 5) and forwards when the string opens with one (8 ob., 3mě), so the dot-less variants v1.0.0 missed now parse. Institution patterns are ordered most-specific-first, so pok.prům. beats pok., obch.ak. beats obch., and vyšší prům. beats prům. — one rung apart, the qualifier doing the work exactly as in occupations.py.

The rung that is not a rung. pok. — pokračovací, the gewerbliche Fortbildungsschule — was the apprentices' part-time continuation school. v1.0.0 scored it Upper secondary, which put 8,117 records in the same bucket as gym. It was legally part of compulsory schooling, governed communally alongside the Volksschule, and it selected on nothing: apprenticeship required no educational credential at all. So it is carried by the boolean educ_apprentice and never raises the ordinal — only a floor, from unknown to Elementary, since a continuation school followed the compulsory eight years. Grounded in Moore 2020 and Lassnigg 2015; the argument and page citations are in EDUCATION_LADDER.md, the call is D-08.

What 2.1.0 fixed, all three found by running the parser back over the raw field. (i) A tie on the top rung was being broken by word order: 1,512 records name two schools on one rung, and obch. + odb. came out commercial on 20 records and trade on 164. TIE_BREAK now states the order — a generic word (odb., střední) yields to the school the record names, and the terminal professional school (uč.ústav, kadetka, obch.ak.) outranks the general school that feeds it. It moves educ_track and educ_complete, whose standard course lengths differ across the tied schools (Realschule 7 vs uč.ústav 4). The rule is D-51; the reasoning is EDUCATION_LADDER.md §6a. (ii) Semesters were being stored as years — technika 4 sem. is four semesters, and the digit attached backwards into educ_years_secondary on 722 records; the count is now lifted into educ_semesters before the itinerary is scanned. (iii) The scan is word-anchored, so technik no longer matches inside hydrotechnika — the unanchored-substring shape ranks 1.4.0 had to remove. Anchoring exposed three abbreviations the patterns missed, now covered: univ. (55 records), the lék.fak. / fil.fak. faculty form, and vyš.prům. (37, which had been falling to the 3-year Fachschule instead of the 4-year Staatsgewerbeschule). Coverage 99.07% → 99.10%.

Two smaller consequences. The apprentice floor is now flagged: on 1,112 records the only school named is the continuation school, so educ_top is imputed and is the one case where it is absent from educ_institutions — educ_top_imputed marks them, their track reads continuation rather than compulsory, and a QA invariant asserts that no other row disagrees. And a dead branch is gone: educ_complete computed the Volksschule standard as "5 if a Bürgerschule follows, else 8", but the Bürgerschule outranks the Volksschule, so a record naming both is scored under burgher against its standard of 3 and the five was unreachable (measured: 0 records).

Two deliberate refusals. educ_ord is attendance, not attainment — a bare reálka does not say whether the man finished, so completion lives in educ_complete, which is allowed to be <NA>; and it is <NA> for the entire vocational branch, because no source establishes those course lengths. No educ_years_total is emitted for the same reason: the year columns are not commensurable and must not be summed.

religion.py — English gloss of the confession

Vyznání → religion_en, a curated normalised-key map (~40 entries covering 99.5% of records: řím.kat.→Roman Catholic, pravosl.→Orthodox, bez vyz.→No confession, plus change-of-confession phrases like vystoupil z řím.kat., bez vyz.→"Left Roman Catholic (no confession)"). Conservative: the rare unmapped tail is <NA>, not guessed. A value translation — religion is untouched. (Called on the raw Vyznání column, since the religion rename happens only at to_english.)

occupations.py — HISCO / HISCLASS coding of the occupation

Zaměstnání (and Povolání v letech 1918 - 1938) → occupation_hisco, occupation_en, occupation_hisclass(_5, _label), occupation_socpo, occupation_status/_relation, occupation_match, occupation_qualifier, occupation_career_stage(_ord), plus *_2 columns for the second occupation where two were recorded. Coverage 98.5% of non-empty values; HISCLASS on 95.5%. HISCAM is no longer emitted (D-30).

No Czech HISCO dictionary exists — not in the History of Work title table (FR/SW/NL/UK/PT/ NO/GE/DK/SP/CA/GR, no CS) and not in OccCANINE's 13 training languages — so the Czech→HISCO map is authored (utils/reference_tools/occupations/czech_glosses.py → occupation_hisco_curation.csv, 495 curated keys), built from a German-gloss bridge against the HISCO project's own German coding plus hand assignment read off hisco_authority.csv.

Matching is exact on the normalised key, on token boundaries only — never a substring test. (Until 2026-09-02 this sentence read "deliberately unlike education.py"; that module now tokenises too.) Compounds resolve as dual (rolník zámečník → two codes, both kept), head (mlynářský dělník → dělník) or prefix (soustružník železa → soustružník), and which rule fired is recorded in occupation_match so an analysis can restrict to exact matches. Abbreviations expand only when marked by a trailing period inside a longer phrase, because several stems are themselves occupations (cihlář, stavitel); ABBREV_CONTEXT lets the word that follows decide an ambiguous one (zem. before dělník is zemědělský, not zemský). A list — rolník, tovární dělník — is coded segment by segment (1.4.0): the first coded segment is the primary occupation and the next the secondary, as dual. Until 1.4.0 the last segment's head noun won and the first job was filed as a "qualifier" (464 records). Missing and unmatched alike stay <NA>.

Two guards stop the compound rules inventing a class (both added 2026-08-02 after they were caught doing so). Where the qualifier changes the trade and not just its object, the whole string is curated instead of decomposed — akademický malíř is a fine artist, not the house painter the head noun gives. And ADJECTIVE_KEYS (lesní, mlynářský, sladovnický) are eligible only as an exact whole-string match: because dual is tried before head, the adjective lesní was becoming the primary occupation and putting ~150 forest labourers and clerks on 63220 Forest Supervisor, HISCLASS 3. matcher_selftest() holds 17 regression cases for both, and the QA gate calls it.

Full method, coverage table and the judgement calls that move HISCLASS for a large share of the sample (rolník → 61110 / HISCLASS 8; dělník → 99900 / HISCLASS 13; students → HISCO −1 with status 41 and no class): OCCUPATION_CODING.md.

units.py — Legion regiment and unit identity

Jednotka v době zařazení → regiment (parenthetical stripped), legion_army (read off the last parenthetical, trailing ? stripped — 1.3.0; the first one is a British regimental nickname as often as an army: "Royal Fusiliers (R.F.)" had been the French Army), unit_key, unit_type (via abbreviation map: stř.pl. rifle, děl.pl. artillery, …), and regiment_plot (regiments under 500 records collapsed to "Other (theatre)"). v1.2.0 also emits English value renderings regiment_en / unit_key_en (translate_unit: number + English unit_type, e.g. 6.stř.pl.→"6th Rifle Regiment"; foreign/Other units pass through verbatim), and the notebook applies translate_unit to the last-held unit (last_legion_unit_en).

The parenthetical is an army, not just a theatre. It names the formation served in — Čs. legie v Rusku, but also Cizinecké legie (French Foreign Legion), Francouská armáda, Srbská legie, Ruská armáda, Britská armáda. Each numbers its own regiments, so regiment alone is not a unit identifier: 4,347 records (3.3 %) carry a foreign army's number, and 1.pl. is majority Foreign Legion — the FFL's first regiment, and rota Nazdar with it, not the Czechoslovak 1st Rifle. Until v1.1.0 that distinction was discarded at parse time and unrecoverable downstream.

ARMY_MAP = {"čs. legie v rusku": "CS Legion (Russia)",
            "cizinecké legie":   "French Foreign Legion",
            "rota nazdar":       "French Foreign Legion",   # the Czech company inside it
            "francouská armáda": "French Army", ...}        # unmapped values pass through

unit_key = f"{regiment} @ {army or 'unknown'}"      # 6,058 labels -> 6,609 identities

Use unit_key, not regiment, wherever a unit is an entity — unit fixed effects, clustering, size denominators. regiment is the display label. Discovered by checking against Fidler (2025): reference/VALIDATION_FIDLER.md §2.4.

ranks.py — rank standardisation + the rank reset (headline RHS)

Two within-system ordinal ladders — k.u.k. Habsburg (1–20) and Legion/early-ČSA (1–14) — encoded exactly as captured in ranks/rank_ladders.md (§A anchored on Dolejší et al. 2003 p. 408; §B on Purdek 2013 + the Rozkaz č. 15/117 plates). add_rank_columns(df, source_col, system, prefix) parses one raw rank column into nine columns: _clean (canonical Czech label), _en, _ord (highest listed rank), _ord_low (lowest — progressions like "vojín, desátník" keep both ends), _jd (one-year-volunteer status flag, stripped before matching), _branch (arm of service from the rank word), _tier, and the two cross-system columns _common and _band.

Matching machinery: de-accent + lowercase, split on ,;/, then first-match-wins over specificity-ordered token rules (any token that is a substring of another rank's token appears first: podplukovnik before plukovnik, nadporucik before porucik, …). System-specific drop lists send non-ranks to <NA> — Habsburg podporučík/rotmistr (not k.u.k. ranks), Legion zajatec/repatriant/žák/ sestra/adjutant.

The two ordinals are not subtractable across systems (a Habsburg poručík is ord 10, a Legion poručík ord 9 — same OF-1 grade). The bridge is the name-anchored ALT-1 common scale (RANK_COMMON, rank_ladders.md §G): the same Czech rank word gets the same common grade in either system, with the three subaltern grades sub-graded inside NATO OF-1 (podporučík 10 < poručík 11 < nadporučík 12) so the reset keeps lieutenant-level resolution the raw NATO code lacks.

# add_rank_reset — the thesis's headline RHS
hab_c = df["habsburg_rank_common"].astype("Int64")     # rank at capture  (k.u.k.)
leg_c = df["legion_rank_common"].astype("Int64")       # rank at Legion ENROLMENT
df["rank_reset"] = leg_c - hab_c                       # negative = demotion; <NA> unless both resolved

hab_b = df["habsburg_rank_band"].map(_BAND_ORDER)      # coarse NATO OF/OR index
leg_b = df["legion_rank_band"].map(_BAND_ORDER)        # (OR-1..9 -> 1..9, OF-1..10 -> 10..19)
df["rank_reset_band"] = leg_b - hab_b                  # robustness axis: subaltern<->subaltern = 0

Notebook 02 §1 runs it three times: habsburg_rank_* (from Hodnost v době zajetí), legion_rank_* (enrolment, Hodnost v době zařazení — this side drives the reset), last_legion_rank_* (Poslední hodnost v legiích).

theater.py — Legion theatre (4-pass imputation)

theater ∈ {Russia, Italy, France, Serbia/Balkans, Britain/Dominions, Post-war (ČSA), Unknown}. Pass 1: regex on the enrolment-unit string ("Čs. legie v Rusku" …). Pass 2: regiment-mate majority vote for residual unknowns (the regiment key is the unit text before the parenthetical). Pass 3: application-city lookup (Omsk → Russia, Padula → Italy, Chicago → France…). Pass 4: same regex on the transfer unit. Achieved coverage ~96.5% (stopped — diminishing returns).

1.1.0 (D-60). US application cities imputed Britain/Dominions until 2026-09-22, although US applicants whose own unit names a theatre served in France 1,659 times and in a British or Dominion unit 26 times; and Polská sent all 41 Polish-Legion records to Serbia/Balkans, though their units are Haller's Armée polonaise (now France) and Russian formations. Neither rule was reachable by the Fidler check, which covers CS-Legion records only; theater_selftest() now holds both. The Habsburg theater column shares the name but not the label set — only Russia, Italy and France compare across the two files.

provenance.py — what source csol.cz cites for the record

Zdroj informací → two separate families, because the field concatenates two things that mean different things. (i) Which VÚA-VHA personnel document backs the entry — source_spis (legionářský poslužný spis, 95.7%) and source_karta (osobní karta legionáře, 76.8%), 97,075 records citing both. (ii) Whether csol's editors added a source on top — source_extra (4.4%) with the citation verbatim in source_extra_ref (489 distinct: published okres volumes, regimental memorial books, family archives, museum fonds). The second is a marker of modern editorial attention, not of a different wartime document — see D-44. Matching is anchored at the front of the string and is never a substring test: the two document names are stripped in sequence and whatever survives is the extra citation (zero document-name text leaks into the residual across all 132,562 non-null values). Missing stays missing — where the field is <NA> all four columns are <NA>, never False, since an unrecorded source is not evidence a document is absent. source_extra_ref is deliberately left uncoded rather than classified into published-vs-archival: the obvious heuristics mislabel SOkA Olomouc and OTÁHAL, FIŠER. Nevydané paměti obce Luká in opposite directions. Six QA invariants + provenance_selftest() (6 groups, incl. the mid-string non-match case).

habsburg.py — VÚA field parsing (umbrella)

add_habsburg_columns = dates → cause → theatre → Habsburg units → notes → locations.

  • Cause of death Příčina úmrtí → cause (primary = earliest stem in the string), cause_codes (all), one dummy per code {KIA, DIS, MIS, EXE, DRO, SUI, CAP}, cause_uncertain (? present). Stems fold gendered/declined forms (padl ⊂ padla). A blank cause leaves every dummy <NA> (1.2.0, D-62) — 8,297 records, which False had called "not killed in action".
  • Theatre Bojiště → theatre (earliest stem of makedon/rumun/francouz/ital/ srb/rus/zápol) + in_captivity ("v zajetí"; <NA> on a blank field).
  • Habsburg unit → ah_unit_primary (first ;-separated unit), ah_unit_type (arm, keyword list with specific arms before the dominant pěší), ah_unit_org (Common army / Landwehr / Honvéd / Landsturm), ah_regiment_no, ah_n_units.
  • Note enrichment Poznámka → note_legion (+ matched phrase note_legion_ref) — an explicit Legion mention in the note of a fallen Habsburg soldier, the archival signal used by linkage criterion (B); note_register_ref — the Czechoslovak regiment that keeps the death register (provenance, not service); note_declared_dead (+ file ref), note_rank (word-start anchored, so dělostřelec is not střelec), note_disease.

The note mostly says where the register is kept (1.2.0, D-59). Until 2026-09-22 any "čs. … pluk/prapor N" counted as Legion service. The interwar Czechoslovak army's regiments kept the death registers of their recruiting districts, so "náhradní prapor čs. pěšího pluku 33" is written against men who fell in Serbia in 1914: 10,509 of the 11,042 flags were that phrase (median death 1915), and a flagged record matched a legionnaire less often than an unflagged one (2.9 % vs 4.8 %). Meanwhile 372 KLEG (Kancelář legií) notes were missed. note_legion now takes explicit Legion vocabulary only — 966 records — and the register unit moves to its own column, so nothing is lost:

_WS = r"(?<![^\W\d_])"                                   # a word start
_LEGION_RE = re.compile(
    _WS + r"(legion\w*|kancelář\s+legií|legi[eií](?![^\W\d_])|kleg(?![^\W\d_])"
    r"|čs\.?\s*dobrovol\w*|rot\w*\s+nazdar|česk\w*\s+družin\w*"
    r"|čs\.?\s*střel\w*\.?\s*(?:pluk\w*|pl\.)"         # the twelve Russian rifle regiments,
    r"(?:\s*(?:č\.?\s*)?(?:1[0-2]|[1-9]|\?)(?!\d)(?!\s*,?\s*str\.)"   # never a register page
    r"|(?!\s*(?:č\.?\s*)?\d)))", re.IGNORECASE)

locations.py — VÚA place decomposition

Free-text Místo narození / Domovem → {prefix}_municipality, {prefix}_okres (anchored on the first ", okres X"), {prefix}_german (first non-okres parenthetical), {prefix}_land (text-marker classification on whole words — 1.1.0; the bare substring uhersk had sent 1,747 births in okres Uherské Hradiště and Uherský Brod to Hungary — later superseded by records_geo.add_land's positive classification), {prefix}_cisleithanian (re-derived from the corrected land by records_geo.sync_cisleithanian after every land update), {prefix}_loc_uncertain, {prefix}_n_candidates. The legionnaire dataset does not need this — csol.cz has separate Obec/Okres narození columns.

vua_courts.py — Habsburg military-court inventories (pilot)

Reads the three VÚA court inventories into tables of the accused — name, rank, unit, birth year — the only Habsburg-side record of disloyalty in the project. A pilot (utils/reference_tools/units/run_vua_courts_pilot.py) pairs them with the legionnaires and writes to data/interim/vua_courts/; nothing reaches data/final yet.

schema.py — canonical English schema

The single source of truth for final column naming. to_english(df, dataset) renames (raw Czech or parser output → final snake_case) and prunes to the ordered *_FINAL list (+ dynamic pol_* for legion). write_data_dictionary() emits data/final/data_dictionary.csv from the rename maps + curated descriptions, including the §4 geo-derived columns. schema_report flags FINAL columns missing from a frame (= a parser did not run). Habsburg theatre is renamed → theater here so the two datasets share the comparison axis.


3 · Geography & census (utils/parsing/)

districts.py — Cisleithanian district backbone (§3a)

Reads the MPIDR/Rumpler-Seger 1910 shapefile attribute table with a minimal stdlib dBASE reader (no geopandas), filtered to Böhmen/Mähren/Schlesien → one row per court district (soudní okres/Gerichtsbezirk) with its parent political okres: okres_code ("301-77"), court_id (ID_1), German names, and curated Czech names from reference/places/district_cs_curation.csv (accent restoration; *_cs = 1910 name, *_cs_modern = today's; un-curated rows fall back to MPIDR's de-accented Czech and are flagged needs_review). The MPIDR id encodes the hierarchy — that is what makes all later joins code-exact:

# ID_1 = REGION·10^6 + DISTRICT·10^3 + LOCAL_  (land → political okres → court)
"okres_code": f"{r['REGION']}-{r['DISTRICT']}",
"court_id":   r["ID_1"],

validate_against_code() audits the German unit names against the census CODE-file (same Rumpler-Seger project, IDCourtdistr == court_id) — an independent authority. GERMAN_NAME_FIX (1.2.0) restores six German names the shapefile's encoding had mangled (Sziszkow = Žižkow, Neubydszow, Rosznau am Radhost, Kneszna, Lusznitz ×2). Output: data/final/geo/districts_1910 (339 courts / 158 okresy).

census.py — Cisleithanian census panel (§3b)

Loads every census workbook in data/raw/census/ (18 files: language, religion, literacy, occupation, population by settlement size, births/deaths/causes, livestock, savings banks, court caseload), joins to the backbone by exact unit code — never by name — and aggregates court-level (934) files up to the okres:

if level == "court":
    tidy["okres_id"] = tidy["unit_id"] // 1000    # court_id -> parent IDPoldistrict
    tidy = tidy.drop(columns="unit_id").groupby("okres_id").sum(min_count=1)

Column naming is the delicate part: each workbook's Variable Names sheet is a multi-row merged-cell German header, read with openpyxl so every merged range is expanded exactly; within each topic family the shared leading labels are trimmed, leaving the discriminating tail (rel_romisch_katholisch, …). As the last step the whole panel is renamed to English by census_labels.py. Output: data/final/census/census_okres_1910 (158 okresy × 504 covariates, 0 unmatched).

census_labels.py — English names + derived shares

Two-stage translation: curated clean names for the analytically central families (lang_*, rel_*, lit_*, pop_*), compositional token translation (German→English / Magyar→English, stopwords dropped) for the long tail, with numeric suffixes guaranteeing uniqueness. Then the derived ratios that go on the RHS — lang_*_share, rel_*_share, literacy_rate, pop_total, illegitimacy_rate (cis) and religion shares (hu). Full crosswalk: reference/census/census_column_labels.csv; families: reference/census/census_codebook.{csv,md}.

lexikon.py — Czech settlement crosswalk (§3c)

Parses the ČSÚ Historický lexikon obcí alphabetical register PDF (narrative entries like "BLATEC v r. 1869-1910 obec v okr. Olomouc, v r. 1921-1930 …") into one row per settlement: modern Czech headword, the 1910-era name ("pod názvem X" — the German exonym for Sudeten places), the 1910 okres (→ okres_id via the records_geo matcher), status (obec/osada/část + parent), and — the key trick — the full okres timeline okres_all across all census years, which later lets records written with post-1910 district labels (Praha-venkov, Kralupy) snap back to their 1910 okres. Cross-reference stubs ("X viz Y") come out as an alias table. A short curated-extras list (_CURATED_EXTRA) appends headwords the register lacks — notably Praha (the capital is not an "obec" entry there), without which every bare-"Praha" birth had no Czech candidate at all.

Headwords (1.5.0, audit 2026-09-22 B23). An entry starts where an upper-case headword meets its separator (v r., od r., viz). The pattern had required the headword to be bare, so 116 entries whose headword carries a mixed-case parenthetical, a t. ALT alternate or a separator the PDF broke into v r . never started — they vanished, and their text ran on into the previous settlement's okres timeline. The pattern now accepts all three, and the parenthetical or alternate becomes an alias row. Four polluted timelines remain (three where the register itself omits a comma between clauses, one narrative). Output: data/final/geo/settlements_1910 (18,916 settlements; 88.6% resolve to a 1910 okres_id, 30.9% geocoded).

wikidata.py — coordinates + German labels

SPARQL-fetches Czech settlements (obec + část obce classes) with coordinates, Czech and German labels, and modern okres (P131) from WDQS, cached to data/raw/wikidata/. add_settlement_coords attaches wd_qid/lat/lon to the lexikon crosswalk (homonyms resolved only when the modern okres uniquely matches; a point claimed by settlements in more than one 1910 okres is refused — 1.4.0, since one of the two places was getting the other's coordinates, 95 settlements on 46 points); add_settlement_de_name picks a genuine German exonym (Wikidata de label where it differs from the Czech; else the lexikon 1910 name when it looks German); okres_centroids gives okres-mean coordinates for maps / Kelly-2019 spatial SEs. Coverage ~31% of settlements ⇒ ~44% of legion births (births cluster in towns).

spatial.py — court district by point-in-polygon

Pure-numpy: minimal ESRI .shp polygon reader (record order aligns 1:1 with the .dbf), a closed-form ellipsoidal LAEA forward projection (EPSG:3035, the shapefile CRS), and ray-casting point-in-polygon with bbox prefilter. Locates every geocoded settlement inside a 1910 court polygon → court_id + names. The court is a sub-okres unit that name matching cannot resolve — only geometry can. Containment is even-odd over all rings (odd count = inside; correctness hardening — on the MPIDR polygons the fix changes no live assignment, only 7 of 339 polygons are multi-ring). Validation: the court's okres agrees with the name-based okres on 90.0 % of settlements on containment alone (build of 2026-09-23), ~96 % after the D-35 near-miss repair. The QA gate counts the rows the repair snapped as the disagreements they were, since they agree by construction. The 68 % measured on 2026-07-16 and once explained here as "structural" was a coordinate bug — an unguarded unique-name fallback (first audit §D2) — and the claim that central-Prague points land in Královské Vinohrady was wrong too (D-35). Treat court_id as a complementary juridical layer, not a validation of okres_id.

records_geo.py — Czech-lands place standardiser (§4)

The heart of the census key. add_place_standardization(df, districts, settlements, prefix) runs, per role (birth/home):

  1. add_birth_okres_id — resolve the raw district string against 1910 political-okres names and court-district names (both → parent okres_id; the raw field mixes the two levels), alias overrides first, a rural-suffix strip and retry next, then a space-free exact lookup (1.8.0: the register PDF breaks words — Prachat ice — and 81 settlements had gone unmapped; a key that two districts share without spaces is dropped), token-set fuzzy (≥90) last. Tiers: okres > court > fuzzy > alias. A fuzzy tie — the score is 100 against every district the cell names — is not a match: it is decided per record by the municipality, else by the Prague-suburb rule, else refused (D-23). The settlements-less call from lexikon.py keeps first-match, which is correct there because the register writes the 1910 period name for the Bohemian twin.
  2. add_land — positive land classification: resolved okres_id ⇒ "Czech lands"; curated Slovak district list ⇒ "Slovakia"; country nouns as whole words for the rest (1.8.0 — the substring markers had put Polská Ostrava in Poland, Nemecká Ľupča in Germany and Kapušany overseas, via "usa"). {prefix}_cisleithanian is re-derived from the land at the end of every call (sync_cisleithanian): 2,270 Czech-lands VÚA births had been flagged not Cisleithanian and 8,997 Slovak births flagged as it.
  3. add_birth_settlement_okres — second pass via the municipality: if the raw okres exists, accept a settlement whose okres timeline contains it (tier settlement — the post-1910-label recovery); if no okres at all, accept only an unambiguous settlement name, vetoed for positively-foreign lands and for cross-frame homonyms (names that also exist in the Slovak obec frame — exclude_names; tier muni, the weakest).
  4. add_land again (pass-2 resolved more), then add_settlement_attrs — standardised Czech/German municipality names, court_id/cs/de, lat/lon/qid from the matched settlement (keyed (name, okres_id), unique-name fallback land-vetoed), and add_okres_names (okres cs/de from the backbone). resolve_prague_suburbs(…, settlements=…) moves a Praha birth to its 1910 suburb okres and re-keys the settlement attributes to the new okres; until 1.8.0 all 1,537 moved births lost their standardised municipality, which was keyed on the old one.
# the timeline gate that sees through fifty years of renamings
hit_ids = {oid for tl, oid in cands if ok in tl}      # raw okres ∈ settlement's okres_all
if len(hit_ids) == 1:
    ids[pos], tiers[pos] = next(iter(hit_ids)), "settlement"

districts_hu.py — Upper-Hungary backbone (§3d)

GISta Hungarorum shapefiles (UTF-8) → one row per 1910 census unit of the Felvidék (+ Komárom/Esztergom north bank): 192 units (144 járás + 48 chartered towns), each with unit_id (= census kod), county (megye_*), the Magyar seat name, and curated Slovak/Rusyn names from reference/places/district_sk_curation.csv. The spine is MO_JarasVaros because that is what the járás-level census keys on 1:1 (chartered towns are standalone census units).

census_hu.py — járás-level census panel (§3d)

Joins the ETN workbook (mother tongue incl. tot_anyanyelv = Slovak + religion) and the Agrar workbook to the backbone by exact kod = unit_id; derives the mother-tongue shares (slovak_share, …) and hands off to census_labels for English names + religion shares. Output: data/final/census/census_hu_jaras_1910 (192 Felvidék units).

lexikon_sk.py — Slovak settlement crosswalk (§3d ii)

Parses the 1910.1 section (printed pp. 758–875) of Majo (2012), Historicko- demografický lexikón obcí Slovenska, with pdfplumber word x-coordinates: nine numeric columns follow each obec name, but zero cells are simply not printed, so a number can only be assigned to its category by its position under the column-anchor row (a b 1 2 … 9) — text order alone mis-assigns. The columns are right-aligned while the anchors mark their left edges, so the key is each token's right edge and the boundaries shift by half a column (D-20):

def _read_numbers(nums, anchors):
    bnds = _bounds(anchors)                       # anchor midpoints + half a column width
    for w in nums:
        x1 = w["x1"]                              # right-aligned -> the RIGHT edge decides
        col = 0
        while col < 8 and x1 >= bnds[col]: col += 1
        buckets[col].append(w["text"])            # split thousands re-join per column

Each obec/časť row gets its 1910 okres/župa, mother-tongue counts (n_slovak…) + shares, and a sum_ok reconciliation flag (100%, a hard QA invariant). The okres (a district named in Slovak) is mapped to the GISta unit_id by keying every járás on: its curated Slovak seat names, its Magyar seat, and the district name recovered from the Magyar adjectival jaras_hu via the backbone's own Magyar↔Slovak dictionary (the chartered-town exonym pairs — Érsekújvár↔Nové Zámky); subset-guarded fuzzy last, for the towns as well as the districts (Brezno → Brezno nad Hronom).

Both section dividers are printed in more than one wording and the regexes cover all of them: okres X and adjectival X okres (D-21), and four chartered-town headers, singular and plural (D-20). A county's towns trail its districts and carry no okres of their own, so a missed header does not fail — the rows silently keep the county's last járás. Output: data/final/geo/settlements_hu_1910 (3,511 obce of which 39 chartered towns, 98.4% mapped).

records_geo_hu.py — Transleithanian matcher (§4)

Runs only on records the Cisleithanian matcher left unplaced — both passes gate on {prefix}_okres_id being <NA>, so a Czech-placed record can never also acquire a Slovak district. Three passes: add_jaras_id — the district string against the curated Slovak seat names (tiers jaras/town), else county-only (megye; the lookup covers the Slovak noun, the Magyar county name, and the adjectival župa forms — Nitrianska, Hontianská stol. …), else a rural-suffix strip and retry (Košice vonkov → the járás, by rule rather than by fuzzy accident), else fuzzy — and fuzzy refuses a top-score tie across two units, because a cell naming two districts scores 100 against both (D-22). One seat is never resolved from the district string at all: Halič is Galicia in Czech and a Novohrad town in Slovak, and 214 Galicia-born legionnaires (Lvov, Přemyšl, Tarnopol…) had been placed in it and given its census covariates (_AMBIGUOUS_SEATS, 1.4.0; a Slovak Halič birth still resolves through the corroborated settlement pass); add_settlement_jaras — still-unplaced records resolve their birth_municipality through the Majo obec crosswalk under refuse-over-guess rules: unambiguous obec only; a district string, when present, must corroborate (match a candidate's okres_sk/župa) or the match is refused; bare names are vetoed for positively-foreign lands and for cross-frame homonyms (names also in the Czech lexikon — exclude_names) unless the land is positively "Slovakia"; resolve_jaras_twins — 26 seats name both a járás and the chartered town at its centre, and since both earlier passes send every bare district string to the járás, the record's municipality is what promotes it to the town (a chartered town was one municipality, so this is a definition, not an inference). Runs after the settlement pass, which can itself place a record on a town unit. Unlike the Czech twins there is no pooling: the GISta census carries a row for all 52 units, so both members keep their own covariates (D-19). Adds {prefix}_jaras_id/_match/_sk/_hu, {prefix}_jaras_twin (city/ring/undecided), {prefix}_megye_id/_sk/_hu, and {prefix}_obec_sk (the key to obec-level mother-tongue shares).

ruian.py — today's geography and the 1910 settlements placed on it (§3e)

There are no 1910 municipal boundaries for Bohemia and Moravia, and a 1910 settlement needs a place, which survives boundary changes. Reads the ČÚZK RÚIAN state file into current obce, cadastral areas, parts and basic settlement units (data/final/geo/ruian_*), then finds every row of settlements_1910 among them by name: in its 1910 okres, then in the adjacent obce within 5 km, then in the modern okres its own timeline names. Ties are refused, never broken by order (D-22); where the 1910 register files a settlement under a court, that court breaks the tie and is checked against the point (D-155). Writes settlements_1910_geo with every coordinate's source cited (D-151, D-153). Three smaller sources fill what RÚIAN cannot reach, each with its own module:

  • geonames.py — ČÚZK Geonames: the mills, lodges and solitary houses below the část obce, many of which survive only as a name on the map (geonames_places);
  • prng.py — the Polish state gazetteer, for the half of Těšín Silesia that went to Poland in 1920 (prng_places);
  • vanished.py — Wikidata's vanished and abandoned settlements, for places gone since 1910 (vanished_places).

Associational life: the Matice and amateur-theatre measures

Both are built outside notebook 02, by pilots in utils/reference_tools/places/ (§7), and write only to data/interim/; their rulings are open (MATICE_MEASURE.md, THEATRE_MEASURE.md).

  • matice.py — the Matice česká founders and Matice moravská member lists, OCR to one row per printed entry with the entry's own text kept beside every field; the Matice česká monthly payment lists of 1885–1914, one row per payment checked month by month against the printed total, gathered to persons, the joiners of 1895–1909 being Bohemia's measure (D-184, D-188); residences placed on settlements_1910; the okres and court measures; eb_rate, the Marshall (1991) empirical-Bayes shrinkage every intensity uses.
  • locative.py — the member lists print a residence in the locative (ve Vodňanech). Declines every 1910 name forward into the locatives Czech allows and indexes them, so a printed form finds its nominative; a form claimed by two settlements is settled only by what the entry carries (a court seat, u Příbrami), and refused otherwise.
  • amateur_theatre.py — reads the theatre database's chronicles clause by clause into dated company activity per settlement, with a certain/possible split (D-137…D-145). Not to be confused with theater.py, the war theatre.
  • amateur_theatre_model.py — the clauses those rules leave open, put to a model one clause a request through the Message Batches API (notebook 16); replies cached under data/interim/theatre/model_pass/, so a replay costs nothing.

4 · Linkage (utils/linkage.py, §4)

Detects individuals present in both final datasets (a legionnaire who also appears among the fallen), so the comparison design does not double-count. Candidates are blocked on birth year and the phonetic surname initial, so Wesely meets Veselý. Surname and given name are then scored separately (1.3.0, D-59) on a phonetic key that folds the orthography the two archives mix (tsch/cz/tz/z → c, sch → s, w → v, y/ie → i, umlaut digraphs, doubled letters): the surname must reach 90 — or 85 when the birth okres agrees as well — and the given name 80. A pair then survives only if it clears one of two gold standards (precision-first; everything weaker is dropped, not down-tiered):

def _tier(date_exact, okres_match, year_exact, note_legion):
    if date_exact:                                   # (A) identical full day+month+year of birth
        return "high"
    if note_legion and okres_match and year_exact:   # (B) the VÚA note names the Legion
        return "medium"                              #     + birth okres + birth year pin WHICH one
    return None                                      # everything else -> drop

Two refusals follow. A pair whose full dates of birth both exist and disagree is dropped, whatever the note says. And the match is one-to-one on both sides: each legionnaire keeps its best VÚA record and each VÚA record its best legionnaire, and an exact tie is refused rather than broken by row order (matches.attrs["refused"] counts both).

Why it changed (audit 2026-09-22 A1–A3). Until 1.3.0 one token-sort ratio over "surname given" had to reach 80, and a long shared given name carried it: STANĚK František ↔ Soja František scored exactly 80. 389 of the 2,239 old pairs had a phonetic surname similarity below 60, 111 VÚA records were claimed by two to four legionnaires, and criterion (B) read a note that mostly names the regiment keeping the death register (see habsburg.py). The thresholds were set against pairs that are wrong by construction — exact date of birth, same given name, surnames with different initials.

Result (build of 2026-09-23): 1,340 confirmed overlaps = 1,201 DOB-confirmed (high) + 139 note-confirmed (medium); every VÚA record is linked at most once, and the two frames' flags agree pair for pair. 1,176 of the 2,239 old pairs survive. Flags written to both final frames: in_other_dataset, match_id, match_confidence.

add_duplicate_groups also flags probable duplicate persons inside one file — same surname, given name, full date of birth and birth municipality — in dup_group (625 csol and 281 VÚA rows). They are flagged, never merged: a pair of csol records can be one man entered twice or two men the clerk could not tell apart, and the data cannot say which.


5 · Outputs (§4b–§5)

  • data/final/persons/{legionnaires,habsburg}_clean.{parquet,csv} — canonical person files: English schema + rank/theatre/exit parses + linkage flags + the full place standardisation for birth and home.
  • data/final/analysis/{legionnaires,habsburg}_census_1910.parquet — wide person × Cisleithanian-census files (join birth_okres_id = okres_id); kept separate so the canonical files stay lean. *_census_hu_1910.parquet — the Transleithanian analogue (join birth_jaras_id = unit_id).
  • data/final/data_dictionary.csv — per-column source + description for both person datasets, the geo-derived columns, and the geo/census reference tables.

6 · The 1910 place register (utils/lexikon_1910/, notebook 15)

The Podrobný seznam míst / Spezialortsrepertorium of 1910 prints, for every municipality and every part of it, a 17-column census table and an apparatus of offices, schools, parishes and cooperatives — mostly as pictograms. Four editions are read: Czech Bohemia and German Bohemia, Moravia and Silesia. The route and its certification are written up in LEXIKON_1910_ROUTE.md and LEXIKON_1910.md; this is the map of the code. Every *.py in this folder is digested into a run's freeze (run_frozen._code_digests), so a change to any of them takes a run off its gold's route. Code that checks a run from outside lives in utils/validation/ for that reason.

The runner. run_frozen.py is the one runner behind notebook 15: freeze → reading plan and estimate → batched reads → completeness → E3 check → Opus route → ledger → final inventory → the two Bohemian editions reconciled → the author's queue → the register build. reader_batches.py queues the reader requests and collects the batched replies into the crop caches; section_images.py saves each section crop the readers saw, so the checks cut from the same pixels.

The page. One module per thing the page prints, shared by every stage:

  • lexikon_1910.py — the table grid from the Czech NDK ALTO, graded against the arithmetic the table must satisfy; lexikon_1910_image.py — the same geometry from the pixels alone, for the German scans that have no usable text layer;
  • lexikon_1910_notes.py (Czech) and lexikon_1910_de_notes.py (German) — the apparatus below the table, cut into sections under their court headings; lexikon_1910_vision.py — reads it from the image and hands the result to the parser unchanged;
  • lexikon_1910_institutions.py — the apparatus decoded into an institution inventory, with the volume's abbreviations (institution_abbreviations.csv); lexikon_1910_attributes.py — what is printed beside two glyphs: a school's classes and language, a cooperative's kind and language;
  • lexikon_1910_pages.py — the whole-page cloud reader and its price, before anything is sent; lexikon_1910_de_table.py — the German table cut into blocks from the image; lexikon_1910_volume.py — one profile per volume, for what differs from book to book; lexikon_1910_de.py — the German Bohemia volume as the Czech one's twin;
  • lexikon_1910_names.py — anchors each block to a known 1910 obec; lexikon_1910_courts.py — the 1910 court of each section heading (D-122); lexikon_1910_census.py — reconciles the table against the okres census, which catches a missing or invented block the page's own identities cannot.

The readers and the checks. run_lexikon_1910_pilot.py reads the Czech pages (the apparatus and, channel A, the table); run_r5_german_notes.py the German apparatus; run_table_arm.py and run_de_table.py the tables read by a model. E3 is the check that shares nothing with the language-model readers: locate_glyphs.py boxes every printed glyph, run_glyph_e3.py names each box by its nearest DINOv3 neighbours in a bank of labelled glyphs, run_e3_pages.py runs both on pages without a gold sheet. A claim the readers dispute goes to Opus (run_verify_test_b.py, D-72, D-98); E3 and Opus route and never overwrite (D-38, D-55).

The record. lexikon_1910_ledger.py keeps every mark's history through the route (append-only, D-109) and computes its final value; build_mark_ledger.py rebuilds it for the gold run. lexikon_1910_editions.py sets the Czech and German Bohemia editions side by side as two measurements of one truth (D-58, D-92). lexikon_1910_register.py turns a run's final inventory into data/final/register/ — places, institutions, cooperatives and one row per anchored municipality (D-148) — and lexikon_1910_crosswalk.py pairs the German Bohemia volume's places with the Czech one's (D-150).

The gold and the score. draw_gold_sample.py draws the pages a person transcribes, as a probability sample, before anything is read; make_gold_worksheet.py / make_gold_worksheet_de.py cut them into worksheets; read_transcription_md.py reads the filled sheets (and check_transcription.py the four early free-form ones); build_token_review.py queues tokens off the atlas for the author to decide; build_glyph_bank.py cuts the author's clicked glyphs into E3's bank; build_symbol_atlas.py / build_symbol_atlas_de.py cut each volume's own symbol legend into a labelled atlas, and build_contrast_sheet.py shows the look-alike pairs at printed size (D-99). score_certification.py scores the sealed pages, once, on metrics fixed before any sealed page was scored (D-127); score_r5_german_notes.py, score_table_passes.py and rescore_passes.py score the passes on the gold; report_unlisted_abbreviations.py lists the offices items no code reaches (D-156).

Kept for the record. bakeoff.py (the August reader bake-off, as a module), run_e1_gemini_version.py and run_e1b_second_reader.py (the model and second-reader screens), run_glyph_test_a.py (does an image encoder tell the fill pairs apart at all) — finished experiments whose code the route still imports. build_de_page_map.py pairs the Czech pages with the German pages that print them; extract_prose_pages.py takes the statutory cities' running prose from the text layer; build_lexikon_figure.py and build_register_walkthrough.py build the docs site's figure and its Brzice walkthrough.


7 · Reference tools and pilots (utils/reference_tools/)

Scripts that build or check the curated tables in reference/<topic>/, and the pilots of measures not yet ruled on. They are run from the command line, never from notebook 02, and write to reference/ or data/interim/ — never to data/final. The occupation and education scripts are described under §8 below; the rest:

  • Intensity maps (D-163, provisional): run_intensities.py puts the Matice, theatre, legionnaire and desertion intensities side by side at okres, court and settlement level (data/interim/intensity/, INTENSITY_MAPS.md); map_intensities.py and map_matice.py draw them.
  • Matice: run_matice_pilot.py reads the member lists into data/interim/matice/ and sums them per okres; build_matice_read_sheets.py cuts the lines whose residence the OCR garbled into sheets for reading by eye, and merge_matice_readings.py merges the readings into reference/places/matice_residence_readings.csv, never overwriting one made by eye.
  • Amateur theatre: run_amateur_theatre_pilot.py parses the scraped database and builds the measure in data/interim/theatre/; build_theatre_gold_page.py writes the ruling page for the model pass's gold (D-157).
  • The 1910 register, from outside the freeze: audit_full_run_pages.py cuts every German full-run page offline before anything is read; statutory_prose.py and statutory_institutions.py transcribe the eleven statutory cities' prose and turn it into one row per institution (D-178); score_sealed_abbreviations.py scores the text abbreviations on the sealed pages.
  • German E3 trials (D-175, all negative on placement): build_german_click_queue.py and build_german_click_strips.py set up the author's clicks on the German gold; german_click_recall.py measures any placement against them; german_candidate_diagnosis.py, german_locator_trial.py, german_supervised_placement.py, run_german_oracle.py, run_german_detect_by_naming.py and run_german_reader_boxes.py are the attempts.
  • Units: build_unit_timeline.py reads where every Legion unit was, month by month, from Fidler (2025) into unit_timeline.csv, held by check_unit_timeline.py; run_vua_courts_pilot.py (§2).
  • Occupations: run_mosaic_beruf_check.py, the Vienna Database check on rolník (D-34).
  • Background reading: prep_slices.py and verify_locators.py, scratch tools of the qualitative reading pass.

8 · Presentation & support

utils/tables/latex_tables.py

Canonical LaTeX export (booktabs). summary_table, regression_table, series_table, exit_code_table, missing_table; every user-facing string is escaped unless wrapped in RawLaTeX. All tables in output/tables/ come from here — never hand-written.

utils/plotting/th_style_v113.py (import as ths)

Charles University palette + academic rcParams. set_academic_style() once; get_palette(n) (n-aware colour selection); style_legend(ax, n=n) (legends move outside for n ≥ 4); save_for_latex(fig, name) writes serif PDF+PNG plus _beamer sans variants.

utils/validation/data_checks.py

The invariants data/final must satisfy — one definition, two consumers: notebook 02 calls assert_data_ok() as its final cell (§6), and the docs site renders the same list on its Data QA sheet, so the page and the pipeline cannot disagree.

The point is that it is a gate, not a report. Every bug in the 2026-07-16 audit was silent — the fabricated exit_N produced a plausible-looking 20% "Missing" category, and 6,320 double-placed records still carried valid okres names. A displayed check is found whenever someone next opens the page, possibly after figures and regressions have been built on it; a check that fails the run is found at the moment it breaks.

Several modules carry a behaviour self-test the gate runs rather than a data invariant — provenance_selftest(), education_selftest(), affiliation_selftest(), matcher_selftest() (occupations) and, since 1.12.0, habsburg_selftest(), theater_selftest(), linkage_selftest() and records_geo_hu_selftest(). They exist for rules a finished frame cannot express: that technika 4 sem. puts 4 in educ_semesters and nothing in educ_years_secondary, that reálka obch.akademie and obch.akademie reálka give the same top rung, that the refusal token sl does not match inside slov.líga. Checking those against the output would only prove the output is self-consistent; checking them against a known string proves the rule still holds.

# notebooks/02_data_cleaning.ipynb §6 — the last cell
from utils.validation.data_checks import assert_data_ok
assert_data_ok()          # raises DataCheckError listing every broken invariant

A stale build fails (1.12.0). The first check asserts that data/final carries every column the current schema emits. Until 2026-09-22 a check written for a new column was guarded on that column existing — so a data/final older than the code skipped the new checks silently, and eight of them had been skipped since D-51/D-52 (audit 2026-09-22 A6). The same release added hard invariants for every defect class that audit found: a járás-placed birth has land Slovakia, *_cisleithanian agrees with the land, the linkage is one-to-one with no date-of-birth conflict and no surname below the floor, every date lies in its range and carries its precision, every ladder rank has a common grade, and the data dictionary and the census codebook document every column. Build of 2026-09-23: 107 / 107.

Hard vs soft. A gate that cries wolf gets switched off, so only invariants that should never be violated are hard: frame exclusivity, cross-frame attribute leakage, key uniqueness, no fabricated exit codes, derived columns present, no census-join blow-up, plus the three added in v1.1.0 — every rank label resolves to itself, no general scores below the OF-6 band, unit_key present wherever a regiment is. Coordinate accuracy (counted before the D-35 snap), exits before enrolment on day-precise dates, and theatre agreement with the Fidler (2025) authority are soft — they rest on external sources that can drift or genuinely disagree, so they warn. Scope: the checks run after the notebook writes data/final, so they cannot prevent bad data being written — they prevent you unknowingly building on it.

The v1.1.0 additions each encode a bug that actually happened. "Every rank label resolves to itself" catches a re-ordered or over-broad matching token silently re-pointing a whole rung; "no general scores below OF-6" is the built-data form of the defect that recorded Legion generals as majors. Both were found by validating against Fidler (2025) — reference/VALIDATION_FIDLER.md.

utils/validation/run_completeness.py and score_sealed_retest.py

Checks on a frozen run from outside the register's folder, so they do not move its freeze. run_completeness.py (D-180) asks whether a run has every reply it asked for: a failed call whose error the route did not recognise would otherwise leave its marks out of the inventory, which reads as not printed. Notebook 15 calls it at 7d, 12 and 14b, and a gap stops the ledger. score_sealed_retest.py (D-181) scores a full run's readings of the sealed certification pages beside the certification run's — a test–retest, reported beside the certification score and never in its place.

utils/validation/check_ostafin_silesia.py and lexikon_population.py

The register's German table against an outside transcription (D-183). Ostafin et al. (2020) typed every Silesian commune's 1910 census from the same volume the full run read; check_ostafin_silesia.py compares it with our census (political districts), with the full run's table readings commune by commune, and with the ČSÚ lexicon 1869–2005, whose 1910 column lexikon_population.py reads from díl I (its font is decoded by a fixed shift) together with the notes that say where the lexicon's 2005 parts join or move 1910 places. Run from the command line after a Silesian full run; writes output/validation/ostafin_silesia.{json,txt}.

utils/docs/build_docs.py and build_fonts.py

build_docs.py renders every document in reference/, the curated tables and a live Descriptives and QA sheet into one self-contained output/docs/index.html, with a sheet per module of utils/ read from its source; every number on the live sheets is computed from data/final at build time. build_fonts.py subsets the Libertinus faces the page embeds.

utils/validation/build_validation_workbook.py

Tier-stratified human-validation sample (25 place rows per match tier, 20 rank rows per stratum, seed 42) → output/validation_sample.xlsx with dropdown verdict columns — measures the pipeline's precision, which coverage stats cannot. The easy tiers dominate the data, so plain random sampling would barely touch the risky tiers (fuzzy/settlement/muni); an unmatched stratum probes recall. Re-runs the Slovak passes in-process so the sample reflects current code.

utils/reference_tools/occupations/build_conflict_workbook.py

Every unresolved occupation coding, as one adjudicable table → output/occupation_conflicts.xlsx plus a tracked CSV twin. The CSV holds one row per (review-flagged key × candidate HISCO code), each candidate carrying its own English job title and its own HISCLASS, so "code A or code B?" can be answered by reading rather than by reconstruction. Candidates come from the German link bridge (link_hisco, semicolon-separated), OccCANINE, and five-digit codes named in the curation note — the last of which turns arguments that existed only as sentences into rows. The workbook asks one question per word, not one per candidate (v2.0.0): the sheet that opens is Decide — the 76 of 172 flagged keys where a rival moves the HISCLASS, 12,022 records, one row each, biggest first. The 44 arguments that leave the class where it is sit on Same class either way, the 52 keys no coder proposed an alternative for on Nothing to choose from. Nine columns, not twenty-five: the four candidate columns collapse into one wrapped cell (59 of the 76 are a plain A-or-B, one key reaches G), the German gloss + note + CZ-ISCO's verdict merge into one evidence cell, and hisclass_basis — four values needing a paragraph of instructions to serve eleven rows — becomes a parenthesis in the cell it qualifies. CZ-ISCO is never a candidate (ISCO-08 has no path to a HISCO code) — it appears as evidence, and the nine keys where it dissents and no check flagged them get their own sheet, since run_czisco_check.py never writes the review column. verify_xlsx() reads the saved file back and asserts every per-key dropdown offers exactly the letters printed in its own alternatives cell — the row index it is placed by was silently one short once. Writes nothing back: an accepted decision is applied by hand to czech_glosses.HISCO_OVERRIDE, as with czisco_tail_candidates.csv.

The answers come back as a register, not as a spreadsheet (D-41). The filled workbook is transcribed into czech_glosses.ADJUDICATED — keep / recoded / open, one line per word — which run_link_bridge applies last and which is the only writer of review. The 2026-08-26 pass answered 171 of 181 questions: 41 recodes (4,359 records, 23 of them moving HISCLASS on 1,434), 130 keeps, 10 left open with the reason each survived. The flag count falls from 172 keys / 25.3k records to 10 keys / 163 records, and because the workbook builds from the live flag it now opens on those ten. A recoded key must have its new code in HISCO_OVERRIDE and an override may not claim the flag of an adjudicated key; both are asserted, because a verdict recorded while the code it implies never lands would leave the curation looking settled at the old code.

utils/reference_tools/places/build_curation.py / build_sk_curation.py

One-off generators of the two curation CSVs (Czech accent restoration keyed on the MPIDR German names; Magyar→Slovak district/county names keyed on the GISta ids + Magyar names). The CSVs — not the scripts — are the authoritative artefacts; the scripts document how they were seeded and are re-runnable.

reference/occupations/ — the HISCO toolchain

Four scripts, run in order, all re-runnable and non-destructive:

  • download_hisco_sources.py — fetches the five History of Work assets (hierarchy, title→HISCO link table, status/relation/product code lists) into data/raw/hisco/ and writes MANIFEST.TXT with byte size, SHA-256, source URL, retrieval date and licence for every file in the folder. Idempotent; skips files whose checksum already matches. The HSN release sits behind a click-through and is checked for, never fetched. TLS verification is attempted first and relaxed only if the local trust store rejects the chain (it does, on this project's machine) — integrity rests on the recorded checksums instead.
  • build_hisco_authority.py → hisco_authority.csv (6,112 rows, 1,696 codes), keyed on (hisco, status, relation, product) because the code alone does not determine HISCLASS — 806 of 1,346 codes take more than one. Asserts that functional dependency rather than assuming it, and labels the 18 codes HSN placed above micro level from their ancestor group.
  • build_occupation_curation.py → the frequency-ordered skeleton of the Czech dictionary, read from data/raw/legionnaires/ (not data/final/, which would be circular). Merges on key and never overwrites a hand-entered code.
  • run_link_bridge.py — applies czech_glosses.py: German gloss → exact match in the HISCO project's German labels → code; ambiguous glosses are flagged, not assigned; HISCO_OVERRIDE beats the bridge, and ADJUDICATED runs last and owns review (D-41), writing each key's verdict to the curation's verdict column.
  • run_occcanine_check.py — validation only, like build_unit_authority.py. Codes the same German glosses with OccCANINE as an independent second coder. torch is deliberately not in requirements.txt.
  • build_czisco_bridge.py / run_czisco_check.py — validation only. CZ-ISCO as a third coder on the Czech reading itself; never writes hisco (D-28).
  • run_census_berufsstellung_check.py — validation only, writes no file at all. Tests rolník — 18 % of the sample, and the one key no coder can reach — against the 1910 Volkszählung's own agricultural Berufsstellung split (Selbständige / Pächter / Arbeiter / Taglöhner / mithelfende Familienangehörige), which was already parsed into census_okres_1910.parquet and previously unused. Two tests: share-of-births with nádeník / dělník placebos, and a within-agriculture composition test that cancels okres size. See D-33.

reference/education/ — the school statistics (validation only)

Added 2026-09-07. Nothing here is a pipeline input; none of it writes into data/.

  • build_page_index.py — emits unterricht_page_index.csv, the folio → PDF-page map for the 279-page merged scan of Österreichische Statistik XCIII/1. The volume has two independent numbering sequences (roman for the Einleitung, arabic for the tables) and two gaps, so the offset is not constant; the script asserts that every PDF page is claimed exactly once.
  • unterricht_t{34,20,19}_*.csv — three tables transcribed by hand from the image-only scan, shaped like the printed pages: Volksschulen by ascending classes (Table 34), the four-way Gewerbeschule split (p. XX), and the Handelslehranstalten (p. XVI).
  • check_unterricht_transcription.py — the transcription gate, 27/27. Same role as check_atlas_parties.py: a row-offset in a table read off a scan leaves no grammatical trace, so it is caught arithmetically. Works because the volume over-determines itself — it prints the parts and the roll-ups and the column totals, and Table 34's class columns must also sum across each row. Two genuine errors in the printed volume are declared in KNOWN_SOURCE_ERRATA, each decided by a second arithmetic path rather than assumed.
  • draw_education_sample.py / score_education_gold.py — validation only. The other half of the evidence: run_unterricht_check.py tests the ladder against an outside source, these test the tokeniser against a person. 300 records drawn disproportionately by risk (seed 1910), split calibration / sealed certification, into a blind worksheet — raw string only, no parser output and no stratum label, because showing the machine's answer measures assent. The coder names institutions from a closed list, never a rung; the scorer derives the rung from their institutions with the parser's own table, so a disagreement is unambiguously about reading. Headline figures are reweighted to the true stratum sizes. Coder instructions, in Czech: education_gold/NAVOD.md.
  • run_unterricht_check.py — validation only, writes no file at all. Five tests of the education ladder against the administration's own school census: taxonomy coverage, the pok. decision, Table 34 against education_complete (fails — D-53), the continuation : full-time ratio, and the Czech-language benchmark. Restricted throughout to Czech-lands births, because the volume is Cisleithanian by definition and cannot speak to the Slovak half — which is why D-08 stays open. See VALIDATION_EDUCATION.md.

utils/parsing/_archive/gazetteer.py

Superseded parser of the OCR Ortsverzeichnis 1910 gazetteer — replaced by lexikon.py (the OCR was too corrupted for district names). Kept for the record.


9 · Notebook orchestration map

Notebook cell Calls Output
01 §setup–full-run scraper_csol.scrape_range data/raw/legionnaires/legionnaires.csv
01 §vua-fallen scraper_vuapraha data/raw/habsburg/habsburg_fallen.csv
01 §QA integrity checks, missingness tables appendix tables
02 §1 normalize_missing → clean_names → clean_places → add_exit_columns → add_date_columns → add_affiliation_columns → add_education_column → add_unit_columns → add_rank_columns ×3 → add_rank_reset → add_theater_column → to_english("legion") data/interim/legionnaires_clean.parquet
02 §2 clean_dataframe → add_habsburg_columns → to_english("habsburg") data/interim/habsburg_clean.parquet
02 §3a districts.build_district_backbone (+ validate_against_code) data/final/geo/districts_1910
02 §3b census.build_census_panel (→ census_labels.to_english_census) data/final/census/census_okres_1910
02 §3c lexikon.build_settlement_crosswalk → wikidata.add_settlement_coords/_de_name → spatial.add_settlement_courts data/final/geo/settlements_1910 (+ aliases, okres centroids)
02 §3d districts_hu.build_jaras_backbone → census_hu.build_census_hu; lexikon_sk.build_crosswalk_sk districts_hu_1910, census_hu_jaras_1910, settlements_hu_1910
02 §3e ruian.build_current_geography (+ Geonames, PRNG, vanished places; register courts from data/final/register/) data/final/geo/ruian_*, geonames_places, prng_places, vanished_places, settlements_1910_geo
02 §4 linkage.add_duplicate_groups (both frames) → find_overlap → add_overlap_flags; cross-frame guard sets via rg.settlement_name_set; per role: records_geo.add_place_standardization(…, exclude_names=sk_names) → resolve_prague_suburbs(…, settlements=); then records_geo_hu.add_jaras_id → add_settlement_jaras(…, exclude_names=cz_names) → resolve_jaras_twins → set_land_from_jaras; frame-exclusivity, twin-role and járás ⇒ Slovakia assertions data/final/persons/*_clean + overlap_matches.csv
02 §4b merges on birth_okres_id / birth_jaras_id data/final/analysis/*_census*_1910.parquet
02 §5 schema.write_data_dictionary + curated geo/census rows data/final/data_dictionary.csv
02 §6 data_checks.assert_data_ok — gate: fails the run if a hard invariant broke (raises, or passes silently)
03 reads data/final/ only; ths figures + latex_tables exports output/figures/, output/tables/
15 §1–17 run_frozen stages, one edition and page set at a time; run_completeness at 7d, 12, 14b output/frozen/<edition>_<pages>/
15 §18 lexikon_1910_register.build_register data/final/register/
16 amateur_theatre_model (gold → full) data/interim/theatre/model_pass/
command line utils/reference_tools/ pilots and builders (§7) reference/<topic>/, data/interim/
command line utils/validation/check_ostafin_silesia.py, score_sealed_retest.py (after a full run) output/validation/, output/frozen/sealed_retest/

Place matchingHow it works

Birthplace → 1910 census district. · source: reference/PLACE_MATCHING.md

In short

How the pipeline turns a soldier's free-text birthplace into a 1910 census unit (okres in the Czech lands, járás in Upper Hungary), so census covariates can be attached. Plain-language walkthrough + the core logic + the sources it leans on.

Explain it simply

Imagine posting a letter addressed only to Nová Ves, Bohemia. There are dozens of Nová Ves — New Village is the Czech Springfield — and the one you want stopped existing under that name in 1918, was called something else in German, and has since been swallowed by a town. That is the problem on every one of these birth records, and it has to be solved 300,000 times, because a soldier's birthplace is how he gets attached to the 1910 census: how literate his district was, how many Czechs and Germans lived in it, how much of it farmed.

So the matcher works like a careful postal clerk rather than a clever one. It tries the strongest evidence first — does this string name a district outright? — then weaker and weaker readings: a court district, a village in the gazetteer, a near-miss spelling. Crucially it writes down which rung it used. Every record carries a label saying how confident the match is, so an analysis can throw the weak ones away and see whether the result survives.

Two things it refuses to do. It will not break a tie by picking whichever candidate the lookup table happened to list first — a coin-flip dressed as an answer. And it will not place a village in Slovakia just because a Slovak village has the same name, when the record says the man was born in Galicia. Where the evidence genuinely runs out, the field stays empty, and empty is a finding.

How a birthplace becomes a district ⇱ Interactive ↕ Hover for values

Two legal frames, two lanes, one rule down both: a pass accepts only what it can corroborate and refuses the rest to the next pass. The ribbon down each lane is what is still unplaced — every step in it is a tier doing its work, and the bar inside a box is that tier against the largest in its lane. Colour is the confidence the match will carry in the data; ⊘ marks a stage with explicit refusal rules. Click a stage for the strings it actually matched and the map of where they landed; untick a tier to drop it from the sample below.

okres highestcourt highjaras hightown highsettlement highalias curatedmegye mediumfuzzy mediummuni weakest
CZECH LANDS → political okresruns on every birth record132,993 inTick to keep this tier in the sample belowCurated alias override36 · 0.0%aliashand-written exceptionsthis tier against the largest in its lane⊘1 refusal rule(s) — click the stageTick to keep this tier in the sample belowExact — political okres93,287 · 70.1%okresthe district names itselfthis tier against the largest in its lane⊘2 refusal rule(s) — click the stageTick to keep this tier in the sample belowExact — court district4,618 · 3.5%courtsoudní okres → parent okresthis tier against the largest in its lane⊘1 refusal rule(s) — click the stageTick to keep this tier in the sample belowFuzzy match ≥ 902,470 · 1.9%fuzzytoken-set ratiothis tier against the largest in its lane⊘3 refusal rule(s) — click the stageTick to keep this tier in the sample belowVillage → okres, timeline-gated611 · 0.5%settlementdistrict recorded but unmatchedthis tier against the largest in its lane⊘1 refusal rule(s) — click the stageTick to keep this tier in the sample belowVillage alone, land-gated252 · 0.2%munino district recorded at allthis tier against the largest in its lane⊘4 refusal rule(s) — click the stageCoordinates → court polygon58,000 · 43.6%coorda layer over the lane, not a step in itthis tier against the largest in its lane⊘1 refusal rule(s) — click the stageHanded to the second frame31,719 · 23.9%= what the second lane runs onUPPER HUNGARY → járásruns only on what the Czech lane left unplaced31,719 in — the Czech lane's residualTick to keep this tier in the sample belowDistrict seat, Slovak name9,182 · 6.9%jarasthe járás itselfthis tier against the largest in its lane⊘2 refusal rule(s) — click the stageTick to keep this tier in the sample belowChartered town862 · 0.6%towntörvényhatósági / rendezett tanácsú városthis tier against the largest in its lane⊘1 refusal rule(s) — click the stageTick to keep this tier in the sample belowCounty only624 · 0.5%megyejárás left <NA>this tier against the largest in its lane⊘1 refusal rule(s) — click the stageTick to keep this tier in the sample belowFuzzy match ≥ 90897 · 0.7%fuzzytoken-set ratiothis tier against the largest in its lane⊘2 refusal rule(s) — click the stageTick to keep this tier in the sample belowObec → járás295 · 0.2%settlementMajo 2012 lexiconthis tier against the largest in its lane⊘4 refusal rule(s) — click the stageLeft unplaced, on purpose19,859 · 14.9%no honest candidate

Where the matches land ⇱ Interactive ↕ Hover for values

The same cascade, on the ground: Czech okresy from the MPIDR 1910 boundaries and Upper-Hungary járás from GISta Hungarorum, the second re-projected onto the first so the two frames sit on one plane. Shading is records per district for the tiers ticked above; click a stage to isolate that tier, or click a district to see who was born there. The strong tiers should reproduce the population, so read those as a sanity check — it is fuzzy, settlement and muni that are worth looking at, where a tier piled onto one district would mean a rule firing on a name rather than on a place.

The mesh is finer than the measurement: the polygons are court districts (soudní okresy), the unit the boundary file draws, while the matching resolves to the political okres above them. Every edge is classified exactly — adjacent polygons share their border vertex for vertex — so drawn at political okres only district boundaries are drawn at all, and the court sub-divisions appear as pale hairlines only when you ask for them. Switch the unit to shade the courts themselves, from birth_court_id; that layer is coordinate-derived, so it covers fewer records and is a cross-check rather than a finer version of the same count.

City twins. Ten Czech seats name both a Statutarstadt and the rural okres around it — Brno and Brno (město), Olomouc and Olomouc (město). They are two okresy and the records are split between them, but the court-level census cannot separate nine of the ten, so the analytical unit is the pooled pair (census_okres_id, 148 units rather than 158; Decisions D-18). Switch City twins to see the map the census can actually support: the city border comes off and the two shares merge into one. Worth doing once on Brno or Olomouc, because the thing it removes is real — in all ten pairs the city is the paler patch, a small light core inside a darker rural ring (Brno 773 births against the ring's 2,118), so legionnaires were rural-born even relative to their own district town. Desertion runs the other way in the larger pairs and not at all in the smaller ones — Brno město 2.98 % against the ring's 1.56 %, Liberec 4.21 against 2.44, but Uherské Hradiště město none at all on 51 births. Pooling averages every one of those contrasts away, which is the price the census charges.

Draw atCity twins

The tiers are a single column in the data (birth_okres_match / birth_jaras_match), which is what makes the filter above reproducible in three lines. Why the weak tiers are kept rather than dropped at source is Decisions D-11.


Companion to DATA_SOURCES.md (where each dataset comes from). Code lives in utils/parsing/; this file explains the why.


The problem, in one breath

Every soldier is a row with a birthplace written as free text by a clerk a century ago — a village, sometimes a district, in whatever spelling and language he used. The census is organised into districts, each with a fixed ID number. To hang a census fact on a soldier ("his birth district was 40 % German-speaking"), you first have to work out which district his scribbled birthplace belongs to. That translation — messy old text → one district ID — is the whole matching job.

Picture sorting old letters into pigeonholes, one pigeonhole per 1910 district. Every letter has a smudged return address; you must drop each into exactly the right hole.


The three moves (both countries do this)

You never try to be clever all at once. You try the cheap, safe thing first, then fall back:

  1. Read the district straight off the address. The record often names the district — just look it up.
  2. If that fails, read the village and look up its district. You keep a big dictionary, "village → its 1910 district", so a record that gives only a village still finds its pigeonhole.
  3. When unsure, refuse to guess. A wrong district never announces itself — it quietly poisons every downstream statistic. So the rules leave a record unmatched rather than force a bad match.

And one thing makes it all cheap: the moment a birthplace becomes a district ID number, attaching the census is just matching numbers to numbers (a code join) — you never fight with names again.

# once birth_okres_id / birth_jaras_id exists, the census attaches purely by code:
wide = persons.merge(census_okres, left_on="birth_okres_id", right_on="okres_id", how="left")
wide = persons.merge(census_hu,    left_on="birth_jaras_id", right_on="unit_id",  how="left")

The two sides share this skeleton but differ in what makes the addresses hard to read — that is the interesting part.


Czech lands — the enemy is time

Districts come from a 1910 boundary map (MPIDR / Rumpler-Seger shapefile, DATA_SOURCES.md #3, districts.py), and a scholarly settlement register (ČSÚ Historický lexikon obcí, lexikon.py) lists every village. Names are mostly one language, so translation is not the problem. The problem is that districts were redrawn over the decades, and a record may write the birthplace using a newer district name than the one that existed in 1910.

Move 1 — read the district (exact okres, then court, then fuzzy)

The record's district string is looked up against 1910 district names; failing that, against the finer court-district names (a court sits inside one district, so a hit still fixes the parent); failing that, a close spelling match.

# utils/parsing/records_geo.py · add_birth_okres_id  (core logic)
for raw in distinct:                    # work on the ~few-thousand DISTINCT strings, then broadcast
    k = _norm(raw)
    if k in aliases:                    # 0. curated override
        ...
    hit = lut.get(k)                    # 1. exact: okres name, else court-district name
    if hit is not None:
        id_map[raw], tier_map[raw] = hit[0], hit[1]        # tier "okres" or "court"
        continue
    if fuzzy:                           # 2. close spelling (token-set ratio ≥ 90)
        m = process.extractOne(k, keys, scorer=fuzz.token_set_ratio)
        if m and m[1] >= threshold:
            id_map[raw], tier_map[raw] = lut[m[0]][0], "fuzzy"
            best = {lut[x][0] for x in keys if fuzz.token_set_ratio(k, x) == m[1]}
            if len(best) > 1:           # names two districts -> decided per RECORD, below
                tied[raw] = best

A broken word is not a spelling error (records_geo 1.8.0). The register PDF splits words at line ends — Prachat ice — and those strings met the fuzzy tier, which either failed them (81 settlements unmapped) or matched them on a guess (169). Between the exact and the fuzzy steps there is now a space-free exact lookup: prachatice finds Prachatice with nothing to weigh. A key that two districts share once the spaces are gone is dropped from that index rather than given to either. On the 169 rows both steps reached, the two answers agree on every one.

When the district names two places, ask the municipality

A token-set ratio scores 100 against every district a cell names, so Tábor Praha, Ostrava and Nové Město all tie at the top — and for years the winner was whichever key the lookup happened to hold first. Dict insertion order, wearing a match's clothes.

The fix is the move the rest of this pipeline already runs on: corroborate. The record's own birth_municipality sits in exactly one of the tied okresy, or it does not, and that settles it — the same logic that picks the member of a city/rural twin, and the same standard the settlement pass holds itself to. Only if nothing corroborates does the record refuse.

# records_geo.py · _resolve_fuzzy_ties  (per record, not per string)
hit   = muni_ids.get(_norm(mu))                  # every okres a settlement of this name sits in
inter = (cands & hit) if hit else set()
if len(inter) == 1:                              # 1. the municipality decides
    ids[i], tiers[i] = next(iter(inter)), "fuzzy"
elif praha in cands and len(cands) == 2 and cands <= ring:
    ids[i], tiers[i] = next(iter(cands - {praha})), "fuzzy"   # 2. the SUBURB, not the city
# 3. else: nothing decides it -> stays <NA>, on purpose

This is worth stating precisely, because the obvious alternative — curating the frequent cases by base rate — would have been wrong. The municipality does not merely tidy the answer, it overrules it on 59 of the 224 birth records it can decide:

recorded district first-match winner what the municipality says records
Ostrava Moravská Ostrava okres Frýdek — the Silesian side 22
Nové Město nad Metují na Moravě 14
Frýdek Místek Místek Frýdek 5
Krumlov Český Moravský 3

The Prague fallback covers the 79 records with no usable municipality: the capital's suburbs (Smíchov, Žižkov, Karlín, Královské Vinohrady) were separate okresy in 1910, so Praha - Smíchov names Smíchov — 68 births move out of the capital — and the court districts resolve too (Praha - Říčany → okres Žižkov, Praha - Zbraslav → Smíchov). It never contradicts the municipality: on all 217 rows where both can speak, they agree.

One deliberate exemption. lexikon.py resolves the register's own okres_1910 strings through the same function, without a municipality to corroborate — and there first-match is already right, so nothing changed for those 481 rows. The ČSÚ register writes the 1910 period name for the Bohemian member of a Bohemia/Moravia twin — bare Krumlov and Budějovice, whose timelines read Krumlov|Český Krumlov — and always qualifies the Moravian one (Moravský Krumlov, Moravské Budějovice). Refusing them would have cost the settlement layer 481 okresy to fix nothing. See Decisions D-23.

(The Slovak lane refuses a tie outright (Decisions D-22) rather than asking the obec crosswalk. That is measured, not assumed: the municipality could rescue 6 of the 38 Slovak ties, against 224 on the Czech side, so the second mechanism would not pay for itself.)

Move 2 — drop to the village, and see through time

Here is the clever bit. The register does not merely store each village's 1910 district — it stores its entire district history, every renaming across census years (okres_all). So when a record says "Kralupy" or "Praha-venkov" (names the 1910 district backbone does not carry — the 1910 register prints Kralupy nad Vltavou and Loket as political okresy only because it draws districts as at end-1914, and both date from 1913, D-88 — names that exist only after 1910), you look up the village and check: does the record's district appear anywhere in that village's timeline? If yes, you confidently snap it back to the 1910 district. This one gate undoes half a century of administrative reshuffling.

# lexikon.py · _parse_entry  — the register entry is comma-separated DATED clauses;
# keep every okres it ever sat in, and single out the segment covering 1910
for clause in body.split(","):
    mo = re.search(r"okr\.\s*(.+)$", clause)
    if mo: okres_all.append(mo.group(1))          # okres from EVERY year-segment
    cm = re.match(r"v r\.\s*(\d{4})(?:-(\d{4}))?", clause)
    y0, y1 = ...                                   # the segment's year range
    if y0 <= 1910 <= y1:
        okres = mo.group(1)                        # -> okres_1910
# records_geo.py · add_birth_settlement_okres  (core logic)
#   name_index:  norm(village name, in modern / 1910 / German) -> [(okres timeline set, okres_id)]
for pos in out.index[unmatched & has_muni]:
    cands = name_index.get(_norm(out.at[pos, muni_col]))     # settlements named like this village
    if not cands:
        continue
    if okres_present.iat[pos]:
        ok = _norm(out.at[pos, okres_col])
        hit_ids = {oid for tl, oid, _st in cands if ok in tl}  # raw okres must lie in the TIMELINE
        if len(hit_ids) == 1:
            ids[pos], tiers[pos] = next(iter(hit_ids)), "settlement"
    else:                                                    # no okres to corroborate ->
        if land is not None and land.iat[pos] != "Czech lands":
            continue                                         # veto clearly-foreign births
        if exclude_names and k in exclude_names:
            continue                                         # name exists in the SLOVAK frame too
        all_ids = {oid for _, oid, st in cands if st not in _NOT_A_PLACE}   # obce only
        if len(all_ids) == 1:                                # accept only if the name is UNIQUE
            ids[pos], tiers[pos] = next(iter(all_ids)), "muni"

(The register lacks a handful of headwords — notably Praha, the capital not being an "obec" in its sense — so lexikon.py appends curated extra rows; without that, ~1.8k bare-"Praha" births had no Czech candidate at all.)

The city-shaped hole in the register (fixed 2026-08-01)

Praha was not the only one, and the rest failed more quietly. A Statutarstadt was its own political district, so the register leaves its okres column empty — and the lookup above is built only from settlements that have an okres. The city therefore vanished, and wherever a like-named hamlet existed it was left as the "unique" candidate and won:

recorded resolved to records
Brno okres Jilemnice — an osada of Bratrouchov 453 VÚA + 3 legion
Těšín okres Jičín — an osada of Železnice 7
Lvov okres Falknov — an osada of Lomnice 5

The cost in coordinates was larger than the cost in districts: 775 Brno-born legionnaires had no birth coordinates at all, and so did every birth recorded in Opava, Znojmo, Liberec, Kroměříž, Uherské Hradiště or Frýdek. Two changes, together:

  1. lexikon._CURATED_OKRES fills the okres on the twelve headwords the register leaves without one — for a statutory city, the city district and not the rural ring (okres Brno 302-5, not Brno-venkov 302-4). Těšín, which a modern Czech register has no reason to carry at all, is appended outright.
  2. records_geo._NOT_A_PLACE stops the bare-name tier from resolving onto an osada or a část. With an okres to corroborate it, a hamlet is a fine answer; with nothing but a name, it is the weakest inference in the pipeline — and it is where this entire bug family lived.

Result: Brno resolves to okres Brno (772 of 775 legion, 505 of 506 VÚA), Lvov resolves to nothing, Těšín reaches okres Těšín — and the Czech frame places ~36 more legion and ~330 more VÚA records than before. See Decisions D-16.

City and rural twins — one convention, and a unit the census can actually carry

Ten Czech-lands districts come in pairs, a city and the ring around it: Brno, Jihlava, Olomouc, Opava, Znojmo, Liberec, Kroměříž, Uherské Hradiště, Frýdek and Bílsko each have a (město) unit distinct from the rural okres of the same name — plus Praha, whose suburbs (Vinohrady, Žižkov, Nusle …) were separate okresy of their own in 1910. A Statutarstadt was its own political district. This is the administration as it was, not a defect in the backbone.

It was, however, read two different ways. Nine pairs were named X (rural) and X (město) (city) while Brno alone was named Brno (city) and Brno-venkov (rural), so a bare recorded district resolved to opposite members: nine city units held 0 legion records and Brno city held 2,768. Worse, the ČSÚ lexikon writes the rural district bare — all 118 ring villages around Brno record okr. Brno — so under the old naming 117 of them landed in the city okres, and okres Brno-venkov had not a single birth coordinate.

One convention now, no exceptions: city = X (město), rural = bare X. Brno's two backbone rows were renamed to match the other nine (Decisions D-17). Three rules follow:

  1. A bare district name means the rural district, for all ten pairs.
  2. A trailing rural qualifier is stripped by rule, not by luck. Olomouc venkov, Brno (venkov), Brünn Umgebung used to reach the right unit through the fuzzy tier, which happened to drop the extra token — and were afterwards indistinguishable from a bare Olomouc. They now resolve at the okres tier.
  3. The municipality picks the member. A statutory city was a single municipality — the census size bands confirm it, one locality in each of the ten city units — so this is a definition, not an inference: name the city and the record is in the city unit; name anywhere else and it is in the ring. 3,827 records move. The 66 with no municipality at all go to the ring and are flagged undecided in birth_okres_twin, so they can be dropped.

The census cannot split nine of the ten

The reason this matters less than it looks — and more, in a different place. A statutory city had no Gerichtsbezirk of its own: the city and the rural seat court around it were one court, and the census keeps one row for it (Iglau/Jihlava Stadt, Olmuetz/Olomouc Stadt). The court-level workbooks carry 330 of the backbone's 339 Czech-lands courts, and the nine missing ones are exactly those shared seat courts. Aggregating court_id // 1000 therefore puts the pair's total on whichever member the id encodes and leaves the other empty.

The result is not coarse, it is mislabelled:

unit what the number really covers Czech share
Opava (město) Gerichtsbezirk Troppau — city and its rural ring 0.463
Opava (rural) only the Odry and Vítkov courts 0.069
pooled pair the whole Opava district area 0.357

Only Brno genuinely splits (it has real Brno Stadt and Brno Land court rows), and its 0.339 city / 0.896 ring contrast is the one that can be believed. Jihlava, Olomouc and Frýdek had the whole pair on one row and <NA> on the other.

So the census panel is mixed-resolution at the twins, and the pipeline says so rather than averaging over it (Decisions D-18):

frame rows what it carries
census_okres_1910 158 court-derived columns (lang_*, rel_*, just_*, savings_*) blanked on the 18 merged twin members; Brno keeps its. Okres-level (406) covariates — literacy, illegitimacy, occupation, livestock, population — valid for all 20
census_okres_1910_pooled 148 each pair collapsed onto the rural member's id. The baseline merge unit, keyed census_okres_id

Pooling happens on the counts, and every share is re-derived from them afterwards — never averaged across the two rows, which would weight a 5,174-person city like a 107,015-person ring. okres_census_crosswalk.csv maps okres_id → census_okres_id; persons carry birth_census_okres_id. The pair total is always exact: rel_total sums to pop_total for every one of the ten.

One consequence worth stating plainly: because the baseline merges on the pooled unit, none of the 3,827 records that moved change their language or religion covariates. The member split matters for the map, for coordinates, and for the okres-level robustness axis.

(The register also stores each village's 1910-era German name, so a Sudeten village recorded as "Abertham" still matches its modern Czech "Abertamy".)

Three quiet errors in the settlement layer (2026-10-28)

All three gave an answer rather than failing, which is why none was caught by a coverage number (Decisions D-142–D-144).

  1. A broken year or okr. hid the 1910 okres. The text layer splits v r. 186 9-1910 and v okr . Hradec Králové, and the 1910 segment is found by exactly those tokens, so 182 entries came out with no 1910 okres — Kukleny, Lázně Bohdaneč, Rudolfov and the Prague suburb Michle among them. lexikon._heal_ocr rejoins a year only where the register prints one (after r. and after a range dash), so a day-dated clause is never touched, and on the register as read it changes no okres that was already found.
  2. The register's bare okr. Beroun is Moravian. In 1910 there was no Bohemian okres Beroun — the register files the town itself v okr. Hořovice — and all 43 rows printing bare Beroun carry Moravský Beroun later in their own timeline. The matcher read them as the Bohemian court Beroun, so 43 Moravian villages sat in okres Hořovice. lexikon._REGISTER_OKRES_1910 reads them as Moravský Beroun, only where the row's timeline says so.
  3. A shared key took whichever row came last. add_settlement_attrs keys a birth on (municipality, okres_id), and 265 keys are held by several register rows. The last one read won, silently: 1,341 legion and 1,592 VÚA births, 763 / 834 of them on candidates with different courts or coordinates. Now the one obec among them wins (the bare-name tier's rule, D-16) and two or more obce are refused — the register prints two obce named Podbořany in okres Podbořany in 1910, and nothing in the record can choose. The modern headword is no evidence: Boršice u Blatnice was plain Boršice in 1910. rg.settlement_homonyms(settlements) lists every shared key and its decision.
# records_geo.py · _pick_homonym — one (name, okres) key, several register rows
if len({_rec_key(rec) for rec in recs}) == 1:     # one place, reached by several names
    return recs[0]
obce = [rec for rec, status in found if status == "obec"]
return obce[0] if len(obce) == 1 else None        # two municipalities: refuse

Replayed on the final person files, the three together move the settlement match of births with a Czech okres from 84.54 % to 84.56 % (legion) and 84.48 % to 84.82 % (VÚA): the gains from the healed rows slightly outweigh the coin flips now refused (652 / 722), and 124 / 211 births move from a hamlet to the municipality of the same name.

Move 3 — coordinates for the finest unit

Villages that carry map coordinates (Wikidata, wikidata.py) are dropped physically inside a court-district polygon by ray-casting point-in-polygon (spatial.py) — a purely geometric "which shape is this dot in?" — pinning the soudní okres the name matcher cannot.

Mental image: an address written in "2020 language", read against a history book of address changes to find where the place sat on the 1910 map.

Today's geography — a place for every 1910 settlement (RÚIAN, D-151, D-153, D-155)

There are no 1910 municipal boundaries for Bohemia and Moravia, and the project does not need them. A 1910 settlement needs a place, and a place survives boundary changes: a village that was its own commune in 1910 and is a část obce of the next town today is still where it was. So the settlement layer is located against today's official geography, and the 1910 frame is kept where it already lives, in the MPIDR court polygons. Where the place itself is gone, a register of vanished places supplies the site.

Four sources, in a fixed order (DATA_SOURCES.md #24–#27; each download has a SOURCE.txt with URL or query, time, size and sha256):

Tier Source What it holds Licence
0 ČÚZK RÚIAN (#24), state file ST_UKSG, 2026-08-31 every obec, část obce and ZSJ that exists today, with an official definition point CC-BY 4.0
1 ČÚZK Geonames (#25), 2026-09-30 named places on the state map below the část obce — mills, lodges, solitary farms, castles, works CC-BY 4.0
2 Wikidata vanished places (#27), 2026-10-29 settlements gone since 1910, most with a zanikleobce.cz ID CC0
3 PRNG (#26), 2026-10-28 Polish localities of 1910 Těšín Silesia free, any use

Inside a scope a later tier is read only where every earlier one has nothing of that name: a castle never outbids the village it is named after, and a vanished hamlet never a living one.

What is built (utils/parsing/ruian.py, geonames.py, vanished.py, prng.py; notebook 02 §3e; all in data/final/geo/):

Table Key What
ruian_obce obec_kod 6,258 municipalities: name, modern okres, definition point, polygon (GeoParquet, OGC:CRS84)
ruian_casti_obce cast_kod 15,106 official parts with their obec and definition point
ruian_ku ku_kod 13,074 cadastral areas with obec and polygon
ruian_zsj zsj_kod 23,598 základní sídelní jednotky, where many former osady survive by name
ruian_obce_adjacency obec_kod, neighbour_kod obce sharing a boundary, both directions (RÚIAN's boundaries are topologically exact, so this is vertex sharing; shared_vertices = 1 is a corner touch)
geonames_places geonames_id 43,359 named points, incl. 17,534 místní části
vanished_places qid 6,884 Wikidata items (2,875 settlement-like, so matchable), each with the 1910 court it falls in and the modern obec whose territory holds the site
prng_places prng_id 1,241 Polish localities inside the 1910 courts of Těšín Silesia
settlements_1910_geo settlement_id the crosswalk — one row per settlements_1910 row

Coordinates are S-JTSK (EPSG:5514) in the Czech sources; the inverse Křovák projection and the EPSG:1623 Helmert to WGS 84 are written out in closed form (the repo carries no geo stack). The projection reproduces the EPSG worked example to under a millimetre (tests/test_ruian.py); the datum shift is checked against Wikidata below.

How a 1910 settlement finds its place. The key is the modern headword (cs_name), normalised as everywhere else (records_geo._norm) with spaces removed — the register's text layer breaks words (Kutná Hor a). Three scopes, stopping at the first that finds the name:

  1. okres_1910 — candidates whose point lies in the settlement's 1910 okres;
  2. neighbour — candidates in a modern obec that reaches into that okres or borders one, and within 5 km of the okres (the ring is one municipality wide, but a municipality can be Praha: before the cap a Prague Háje found the Háje in Roztoky);
  3. modern_okres — candidates in the modern okres the register's own okres_all timeline ends on; the only scope for the 1,983 rows with no 1910 entry.

Then three fallbacks, each only when everything before found nothing anywhere:

  1. fuzzy (ratio ≥ 90, as in records_geo) in the 1910 okres — 21 found, 9 kept;
  2. numbered half (Hlásek 1. díl, Království I) through scopes 1–3 under the bare name — 117 kept. A half is part of that village, so it shares the point with whoever holds it and is refused only when that holder is;
  3. positional prefix (Horní Kšely → Kšely, Moravský Leskovec → Leskovec) only inside the 1910 parent's modern obec — 25 found, but a prefixed name can be a different place from a 1910 row of the bare name, so 22 yield to one and 3 are kept.

Several places of one name in a scope are one place only if they sit in the same modern obec. Otherwise the settlement's own evidence may choose — its modern okres, then its 1910 parent (read through the parent's own row, so Mühlbach is recognised as Pomezí nad Ohří) — and what neither decides is refused (ambiguous), never broken by order (D-22); a refusal does not fall through to a wider scope. One modern place goes to one settlement: rival claimants are refused (shared) unless they are the register's own duplicate, and a row with no 1910 entry yields to one that has it. Wikidata's own point (wikidata.py) is kept wherever it exists.

Where every point comes from. coord_source names the source and coord_ref cites the record:

coord_source coord_ref, e.g. Points
wikidata Wikidata Q392624 (the point wikidata.py attached; kept) 5,830
ruian_part RÚIAN část obce 72541 9,244
ruian_zsj RÚIAN ZSJ 67067 903
ruian_obec RÚIAN obec 569089 5
geonames ČÚZK Geonames mistni_cast:5082 682
wikidata_vanished Wikidata Q27659648 (vanished; zanikleobce.cz 673) 477

The transfer is data too: obec_kod/obec_name/okres_name give the modern obec (for a vanished place, the obec whose territory holds the site); obec_absorbed marks a 1910 commune now part of another obec (5,137), parent_changed a 1910 part whose parent is neither its modern obec nor one of that obec's parts (1,911).

The 1910 register's court (D-155). The ČSÚ lexicon gives a settlement's okres but not its court; the 1910 register (Podrobný seznam míst, DATA_SOURCES.md #7a) is printed court by court, and the register-variables build anchors its places to settlement_id (D-148). Where it has anchored a settlement (its own ANCHORED statuses, never a tie) to a single court, ruian.register_courts passes that court to the crosswalk, which uses it twice:

  • ties — several places of the name in the 1910 okres are first narrowed to those inside that court's 1910 polygon (match_why = "court_1910");
  • rival claims — of two rows claiming one place, the one whose court holds the place keeps it.

It is also a check: court_1910_register and in_register_court say whether the matched point lies in the court the register files the settlement under. On the register's 9-court test build (2026-10-29, cz_certification) it reaches 226 settlements: 203 of 221 matched points (91.9 %) lie in the register's court; of the 18 that do not, 10 are neighbour matches (over a boundary by construction) and 8 sit in the next court of the same okres, 4 of them around Chabařovice — an outline question for the MPIDR courts more than a matching one. It settles no tie yet: the five refused rows those 9 courts reach are not ones a court decides. The gain — about 65 ambiguous rows and part of the 204 shared ones measured above — arrives with the register's full run; notebook 02 §3e reads data/final/register as notebook 15 last wrote it, so re-running §3e after the full run is all it takes.

The court-polygon test. Every crosswalk point is located in the MPIDR courts (point_court_id, point_okres_id) and compared with the settlement's 1910 okres (in_own_okres, dist_own_okres_m). An okres_1910 match is inside by construction; the test bites on the rest. On the build of 2026-10-29 (notebook 02 §3c → §3e re-run, evening):

Scope Matched Inside ≤ 1 km out 1–5 km out > 5 km out No 1910 okres
okres_1910 14,598 14,598 — — — —
neighbour 1,633 — 1,072 513 0 48 (point outside every Czech-lands court)
modern_okres 870 — 2 12 2 854

The ≤ 1 km band is the MPIDR generalisation the D-35 snap already allows for; 1–5 km is a hamlet on the other side of a boundary that moved, or the same edge noise further out. The test caught a real defect on its first run: the settlements_1910 then on disk still filed 42 Moravian villages of okres Moravský Beroun in Bohemian okres Hořovice — the fault D-142 fixed in lexikon 1.6.0 after that table was written — and the crosswalk found 29 of them ~240 km from that okres. With §3c re-run the 43 rows (one more, a text-layer Berou n) are in okres Moravský Beroun and the band beyond 5 km is down from 31 to 2; the same re-run gave 182 rows their 1910 okres (133 also their 1910 status) from the year ranges lexicon 1.6.0 heals.

Against Wikidata. Where both have a point (5,802 settlements) the median distance is 8 m, p90 12 m, p99 93 m — RÚIAN definition points are where Wikidata's Czech coordinates come from, so this is chiefly a test of the datum shift, and it passes. 16 disagree by more than 5 km; in 12 of them the Wikidata point lies outside the settlement's own 1910 okres and the crosswalk point inside it (Čečovice, okres Přeštice: Wikidata has the Čečovice 46 km away in okres Domažlice). Only there is Wikidata replaced, flagged wd_overridden, with wd_in_own_okres kept.

Coverage — share of settlements_1910 rows with a point, before (Wikidata only) and after:

Land 1910 status Rows Before After
Böhmen obec 7,703 44.1 % 98.1 %
Böhmen osada 5,157 5.4 % 92.7 %
Böhmen část 93 1.1 % 97.8 %
Mähren obec 2,895 60.0 % 97.9 %
Mähren osada 415 7.0 % 89.6 %
Mähren část 7 0.0 % 100 %
Schlesien obec 412 43.2 % 97.6 %
Schlesien osada 172 2.3 % 87.8 %
no land / no 1910 entry — 2,062 10.7 % 45.7 %

Of the 16,933 rows with a 1910 entry, 16,229 are matched (95.8 %) and 96.0 % have a point; 377 found nothing, 209 are shared, 118 ambiguous. Over all 18,916 rows: 17,101 matched, 1,371 none, 265 shared, 179 ambiguous; 17,141 have a point, against 5,842 before.

What is still without a place, and why (diagnosed 2026-10-29, before the fallbacks and the vanished places closed part of it): names on no modern or vanished list; places split in the register whose halves both claim one point under different villages; ties between two places of a name in different 1910 courts of one okres — the 1910 register knows each place's court, so wiring its court into the tie-break is the next step (about 65 ambiguous rows and part of the shared ones); and the rows with no 1910 entry at all.

Using it. Join on settlement_id; the final point is lat/lon with coord_source and coord_ref. settlements_1910 itself is not changed — its Wikidata lat/lon still drive the court layer and the record geocoding (spatial.py, wikidata.add_birth_coords). Moving those onto the crosswalk is a separate step.

How good are the boundaries themselves?

Everything above hangs on one file — the MPIDR / Rumpler-Seger digitisation of the 1910 Cisleithanian units (DATA_SOURCES.md #3). Asked for a single number: about 95% confidence that a birth is attributed to the district it historically belonged to. That number decomposes, and the parts are worth more than the headline:

what how sure evidence
Which units exist, their codes and their neighbours ~99–100% all 339 court districts join by unit code to the 1910 census's own CODE file; the 158 okresy include every city/rural twin the administration had
Topology — no gaps, no overlaps, no slivers effectively certain of 7,622 distinct Czech-lands edges every interior one is used by exactly two polygons and none by more than two; 306 edges match the Austrian and Hungarian neighbours vertex for vertex, and the 1,253 unshared ones are the imperial frontier
A point well inside a district ~98–99% 99.7% of geocoded settlements fall inside some court polygon; polygon means sit a median 3.1 km from independently derived settlement centroids (144/144 okresy)
A point near a boundary lower the file is generalised — 14,031 vertices across 339 polygons, about 41 apiece — so a line is good to a kilometre or so, not to a field boundary

The one number that looks alarming is the 89.3 % agreement between a settlement's declared okres and the okres its coordinates land in (89.5 % once the city/rural twins and the Prague ring are set aside — a ring village declared in okres Brno legitimately sits inside the city polygon's neighbour, and before the D-17 rename it was compared against the wrong member). That is not the boundary's error rate: it is the joint error of three things — the border, the Wikidata coordinate (a modern point standing in for a 1910 village), and the register's own okres string. Read it as a floor.

Why the courts do not add up to the okresy

The obvious thing to do with two nested units is to total the small ones and check they make the big one. They do not, and the reason is not an error — but the map used to leave you to find that out.

They are different measurements on different populations. On the current build, of 132,993 legionnaires:

records why
have a 1910 okres 101,272 read off the district string, which is usually written
have a court 58,182 needs a geocoded village; a district name alone cannot give one
okres but no court 43,098 ← the whole gap, and it is coverage, not disagreement

So the court layer sees 43 % of the sample where the okres layer sees 76 %. Summing courts inside a district recovers a little over half of it, and the shortfall is largest exactly where villages are least geocoded.

On top of that, of the 58,174 records carrying both, 1,355 (2.3 %) sit in a court belonging to a different okres than their name. That residual is the point of having two layers: the district string and the settlement's coordinates are independent evidence, and where they disagree one of them is wrong. The concentrations are worth reading as leads — 169 records naming okres Hořovice land in court Dvorce, 59 naming Vyškov land in Brno — each one either a district string the register got wrong or a municipality that matched the wrong homonym. (The Hořovice/Dvorce lead was both at once: the register's bare okr. Beroun is the Moravian okres, and the Moravian town shared a key with Bohemian Beroun. See Three quiet errors in the settlement layer.)

Note this is a different disagreement from the one D-35 repairs. D-35 reconciles a settlement's coordinate with that settlement's own okres. These 1,355 are a conflict between the record's okres string and the settlement it matched, which no amount of boundary correction can touch.

The near-miss repair, and the case that forced it (D-35)

A generalised line is good to about a kilometre — which is enough to put a coordinate on the wrong side of its own border. Prague is the case that made this visible, and it hid in plain sight for three weeks because the code comment said it was fine.

Okres Praha carries 7,148 legionnaire births. At court level it carried none. All 2,691 of its court-placed records had gone to neighbouring Královské Vinohrady, because every Praha birth shares one coordinate — the single crosswalk row, Q1085, Old Town Square at 50.0875 N 14.4214 E — and that point falls outside the polygon labelled Prag/Praha St. The polygon is the right size (20.8 km² against the city's ~21 km²); its eastern edge is simply drawn ~500 m too far west. Staré Město was inside the 1910 city, so this was generalisation error, not history. spatial.py's own docstring had recorded it as correct and "verified" — the most expensive kind of comment there is, because every later reader who checked stopped checking.

CourtLocator.locate_snapped now pulls a point back into the polygon of its own (name-derived) okres when it sits within SNAP_TOL_M = 1,000 m of it. The tolerance is read off the measured distribution rather than chosen: of 5,660 geocoded settlements 624 (11.0 %) land in a court belonging to a different okres, their distance to their own okres has median 765 m, and the tail runs to 318 km. 1,000 m captures 382 of them and stops well short of that tail — beyond it the disagreement is about which place this is, not about where a line runs, and must be left standing to say so.

before after
settlements with a court 5,892 5,899
court okres agrees with name okres 89.3 % 95.9 %
snapped — 382

The price is named: the court layer is no longer fully independent of the name layer, so it is weaker as a cross-check on the okres matcher. court_snapped marks every moved row for exactly that reason — drop them, or pass snap=False, whenever the layer is being used as evidence rather than as a finer unit.

Greater Prague — a 1922 boundary written onto 1910 records (D-36)

The same district exposed a second, larger problem, this one in the name layer.

Prague absorbed 37 municipalities on 1 January 1922. Both person databases were compiled afterwards, so a clerk copying the card of a man born in Žižkov in 1890 wrote the okres he knew — Praha — for a town that in 1910 was the seat of its own political okres. The okres tier matches that outright and never consults the municipality, so the record inherited inner-Prague census covariates:

municipality births raw okres on the card 1910 okres
Žižkov 553 Praha ×518 Žižkov (301-104)
Smíchov 419 Praha ×398 Smíchov (301-91)
Vinohrady + Královské Vinohrady 285 Praha mostly Královské Vinohrady (301-39)
Karlín 162 Praha mostly Karlín (301-34)

This is the twin rule's shape — the municipality decides — but not the twin rule: a twin pair coexisted in 1910, whereas here one unit swallowed the others twelve years later. So it has its own resolver and its own column, birth_okres_prague (city / suburb).

Only the five okres seats are settled, because those are certain by definition: a town that gives an okres its name sits in it. That moves 1,468 legion + 69 habsburg records. Moving them also moves what hangs off the okres: the standardised municipality, court and coordinates are keyed on (name, okres_id), so a record moved after they were attached kept the old key and lost all three. Until 2026-09-22 every one of the 1,537 moved births carried no standardised municipality; resolve_prague_suburbs(…, settlements=…) now re-keys them to the new okres, and 1,364 of the 1,468 legion births and all 69 VÚA births resolve. The other 106 curated rows — Nusle, Vršovice, Břevnov, Košíře, Michle and the rest, ~2,083 records — sit in reference/places/prague_suburb_curation.csv with a blank okres and do nothing until it is filled from the 1910 Spezialortsrepertorium. An unverified reassignment would be worse than the anachronism it replaces, because it would look settled. A cadastral number (Praha 2 = Nové Město, inside the 1910 city) matches nothing and correctly stays.


Slovakia — the enemy is two languages, and a naming trap

Here the difficulty has nothing to do with time. In 1910 the official language was Hungarian, but the soldiers and the modern census speak Slovak: the GIS map says Nyitra, the record says Nitra; the map says Érsekújvár, the record says Nové Zámky. Every address is potentially in the "wrong" language. This side (GISta Hungarorum backbone + Majo-2012 lexicon — DATA_SOURCES.md #8–#9; districts_hu.py + lexikon_sk.py) runs only on records the Czech frame left unplaced.

Move 1 — read the district, through a Magyar↔Slovak dictionary

Match the record's district string to a district seat (Slovak name, curated onto the Magyar backbone), else to just the county (megye), else a fuzzy spelling.

# records_geo_hu.py · add_jaras_id  (core logic)
hit = unit_lut.get(k)                    # 1. district SEAT (Slovak) -> unit; tier "jaras" / "town"
if hit is not None:
    jid[raw], tier[raw] = hit; ...
ck = _county_key(raw)                    # 2. county only -> megye (no district), tier "megye"
if ck in megye_lut:
    jid[raw], tier[raw], mid[raw] = None, "megye", megye_lut[ck]; continue
bare = _RURAL_SUFFIX.sub("", k).strip()  # 3. "kosice vonkov" -> "kosice"; by RULE, not by luck
if bare != k and unit_lut.get(bare):
    jid[raw], tier[raw] = unit_lut[bare]; continue
if fuzzy:                                # 4. close spelling
    m = process.extractOne(k, fkeys, scorer=fuzz.token_set_ratio)
    if m and m[1] >= threshold:
        best = {unit_lut[x][0] for x in fkeys if fuzz.token_set_ratio(k, x) == m[1]}
        if len(best) == 1:               # a tie means the cell names two districts -> refuse
            uid = unit_lut[m[0]][0]; tier[raw] = "fuzzy"; ...

The naming trap (and how the map disarms it)

Subtle, and worth understanding. The lexicon names a district in Slovak — "okres Nové Zámky". The GIS map stores that same district under its Hungarian adjective — jaras_hu = "Érsekújvári" — and labels it with its seat town, which is a different place (name_sk = "Šurany"). So "Nové Zámky the district" and "Šurany the seat" are one administrative unit wearing two unrelated names; a naive matcher never connects them.

The fix: the map already contains its own Magyar↔Slovak dictionary, for free. The chartered towns on the map carry both names (Érsekújvár = Nové Zámky). So to name a district in Slovak, strip the Hungarian adjective (Érsekújvári → Érsekújvár) and look that town up in the map's own two-language town list → "Nové Zámky". For the last handful whose base town is not on the map (Fülek→Fiľakovo, Párkány→Štúrovo, Rimaszécs→Rimavská Seč, Udvard→Dvory nad Žitavou) we asked Wikidata for the Slovak label and wrote those pairs into district_sk_curation.csv.

# lexikon_sk.py · build_crosswalk_sk  (core logic)
# the backbone's OWN Magyar->Slovak dictionary — chartered towns carry both names
hu2sk = {_norm(r["name_hu"]): r["name_sk"] for _, r in backbone_hu.iterrows()
         if r.get("name_hu") and r.get("name_sk")}

for _, r in jar.iterrows():
    keys = [r["name_sk"], r["name_sk_alt"], r["name_sk_modern"], r["name_hu"]]
    base = _hu_adj_base(r["jaras_hu"])            # "Érsekújvári" -> "Érsekújvár"
    if base:
        keys += [base, hu2sk.get(_norm(base))]    # + the district's SLOVAK name -> "Nové Zámky"
    for nm in keys:
        seat_lut.setdefault(_norm(nm), r["unit_id"])

Move 2 — drop to the village, unambiguous and corroborated only

Villages resolve through the Majo obec crosswalk (build_crosswalk_sk above), matched on birth_municipality. The pass runs only on rows the Czech frame left unplaced (okres_id empty — a Czech-placed record must never also acquire a Slovak district), and it refuses rather than guesses, three ways:

# records_geo_hu.py · add_settlement_jaras  (core logic)
gate = out[jid_col].isna() & out[muni_col].notna()
gate &= out[f"{prefix}_okres_id"].isna()     # NEVER touch Czech-placed rows

units = name_units.get(k)                    # járás unit_id(s) for villages of this name
if ok:                                       # record names a district -> it MUST corroborate
    units = {u for u in units if ok in name_okres.get((k, u), ())}   # okres_sk or župa
    if not units:
        continue                             # contradicts every candidate -> refuse
else:                                        # bare name -> positive-signal rules
    if land in _VETO_LANDS:
        continue                             # positively foreign (Galicia, Russia, ...) -> refuse
    if land != "Slovakia" and k in exclude_names:
        continue                             # name exists in the CZECH frame too -> refuse
if len(units) != 1:
    continue                                 # ambiguous -> leave <NA>, DO NOT guess

Cross-frame homonyms — the "Praha problem"

The same village name can exist in both countries (the hamlet Praha in okres Lučenec vs. the capital; Petrovice, Čechy, Teplá, …). A bare municipality with no district text gives no way to tell which country's place is meant, so neither side's bare-name tier is allowed to claim a name that appears in the other side's frame (exclude_names above — the Czech muni tier symmetrically refuses names present in the Slovak obec list). The only overrides are positive signals: a district string that corroborates one side, or a land already positively classified ("Slovakia" via the curated district list — so a bare Košice keeps its Slovak match). Everything else stays <NA>: an unknowable address is left unmatched, not guessed.

A seat that is also a country — Halič

Halič is a small Novohrad town (Hungarian Gács) and the seat of a járás — and it is also the Czech name for Galicia. Resolved by name, the district string Halič put 214 legionnaires born in Lvov, Přemyšl, Kraków, Kolomyja and Tarnopol into a Slovak járás (131 on the exact string, 83 on fuzzy strings like Brody - Halič), where they carried its census covariates although their own birth_land already said Galicia. Of the 218 records in the unit, 4 were Slovak. The seat is therefore never resolved from the district string (_AMBIGUOUS_SEATS in records_geo_hu 1.4.0 — exact, strip and fuzzy steps alike). A genuine Halič birth still resolves through the settlement pass, whose municipality must be corroborated; the unit now holds 7 legion births, none of them Galician.

Land and the Cisleithanian flag follow the placement

birth_land is decided twice on the Czech side (before and after the settlement pass) and was never revisited once the Slovak passes ran, so 1,041 legion and 2,707 VÚA births placed in a járás still had no land, and 237 and 17 more had whatever land their text markers guessed. set_land_from_jaras now sets Slovakia wherever a járás is set: a járás is a Slovak unit, so this is a definition, not an inference. The remaining text markers are matched as whole country nouns — as substrings they had put Polská Ostrava in Poland, Nemecká Ľupča in Germany, Kapušany overseas ("usa") and Ruskov in Russia.

The *_cisleithanian flag came from locations.py's text-only land and was never re-derived after the land was corrected. 2,270 Czech-lands VÚA births were flagged not Cisleithanian — 1,747 of them in okres Uherské Hradiště or Uherský Brod, because the substring uhersk meant Hungary — and 8,997 Slovak births were flagged Cisleithanian. sync_cisleithanian now derives it from the land after every land update, and the QA gate asserts both directions.

Járás and town twins — the same convention, and a census that can split them

Twenty-six seats in Upper Hungary name two units: the járás, and the chartered town (rendezett tanácsú / törvényhatósági város) at its centre, which sat administratively outside the district it gave its name to. Bratislava, Košice, Nitra, Prešov, Žilina, Trenčín, Zvolen, Banská Bystrica and eighteen more. This is the administration as it was — structurally the same problem as the Czech X / X (město) pairs, and it was read the same careless way: 12,760 records sat in a pair and only 387 of them on a town.

The rules are now the Czech ones, deliberately (Decisions D-19):

  1. A bare district name means the járás. It is the larger unit and it is what the string actually names. The lookup says so outright rather than inheriting the order sort_values("unit_type") happened to produce.
  2. A trailing rural qualifier is stripped by rule. Košice vonkov, Bratislava - venkov, Košice-okolí) all mean the ring, and all reached it by luck — token-set fuzzy dropped the extra token and scored 100. 589 records were resolved at the fuzzy tier and were afterwards indistinguishable from a bare Košice. They now resolve at jaras, and the same regex serves both countries (records_geo._RURAL_SUFFIX).
  3. The municipality picks the member. A chartered town was one municipality, so this is a definition and not an inference. 1,888 records move to a town unit (729 legion / 1,159 VÚA); the 71 with no municipality at all go to the járás, flagged undecided in birth_jaras_twin.

Why there is no pooled unit here

This is the one place the two countries genuinely diverge, and it is worth stating plainly so the asymmetry does not read as an oversight. The Czech twins had to be pooled (D-18) because a Statutarstadt had no Gerichtsbezirk of its own and the court-level workbooks keep a single row for the pair — the source cannot make the split. The Hungarian census can: the GISta járás panel carries a separate row for all 192 backbone units, both members of every one of the 26 pairs included. So there is no census_okres_id equivalent on the Slovak side, no counts to sum and no shares to re-derive. Assignment is the whole problem, and both members keep their own covariates — which is what makes it worth getting right:

pair Slovak share, ring Slovak share, town
Žilina 0.964 0.540
Prešov 0.846 0.398
Bratislava 0.633 (Magyar 0.136) 0.149 (Magyar 0.405)

The lexicón's own two traps

Both were found on 2026-08-01 and both were the same mistake — a regex narrower than the printed page (Decisions D-20, D-21):

what the page prints what was matched cost
town block headers, four wordings incl. plural mestá so zriadeným magistrátom ^mesto so only 27 town rows kept the county's last járás — obec Nitra filed under okres Nové Mesto nad Váhom; 79 records to the wrong járás
okres headers, okres X and adjectival X okres ^okres only 19 Orava obce silently inherited járás Trstená instead of Tvrdošín (~98 records); 36 more left unmapped

The town blocks trail each county, so a missed header does not fail loudly — the rows keep the last-seen okres, land in the right county, and look entirely plausible. Bratislava's four towns escaped only because a singular header sat above them on the same page.

(The Majo tables are read with word x-coordinates, because zero columns are unprinted and text order alone mis-assigns the mother-tongue counts. The columns are right-aligned while the header anchors are their left edges, so each number is bucketed by its right edge — reading the centre drifted narrow values one column left and could merge two of them: Nitra's 9 Croats and 13 Serbs became 913 Serbs. Fixing it repaired 27 broken row sums and 135 rows that reconciled while sitting under the wrong heading — no share-bearing column moved on any row. See lexikon_sk._read_numbers.)

Mental image: an address in Slovak, a map labelled in Hungarian, and a bilingual signpost (the chartered towns) to translate one to the other — plus a dictionary (Wikidata) for the few words the signpost is missing.


What differs, and why

Czech lands Slovakia (Upper Hungary)
Target unit political okres (okres_id) járás / chartered town (unit_id)
Core difficulty districts renamed over time two languages (Magyar map ↔ Slovak record)
Key trick village's full okres timeline (okres_all) sees through renamings self-built Magyar↔Slovak dictionary (chartered towns) sees through language
Village dictionary ČSÚ Historický lexikon (lexikon.py) Majo 2012 lexikón (lexikon_sk.py)
Runs on all births only births the Czech frame left unplaced
District↔seat okres is named by its seat district jaras_hu ≠ seat name_sk (the trap)
City/town twins 10 pairs; bare name = rural, municipality promotes to the city 26 pairs; bare name = járás, municipality promotes to the town
…and the census cannot split 9 of the 10 → pooled unit is the baseline (D-18) splits all 26 → no pooling, both members keep their covariates (D-19)

One-sentence version. Both sides turn a smudged birthplace into a district ID by trying the district name, then the village name, then refusing to guess — the Czech side using a history of renamings to see through time, the Slovak side using a self-built Magyar↔Slovak dictionary to see through language; once the ID is in hand, the census clicks on by number.


Reading the output — the match-tier column

Every resolved birth carries a tier in birth_okres_match (CZ) or birth_jaras_match (SK), strongest first, so a strict analysis can drop the weak tiers:

Tier Side Meaning Confidence
okres CZ district named directly highest
court CZ court-district name → parent okres high
jaras / town SK district seat / chartered town high
settlement both village → district via the lexicon high (unique / timeline-gated)
megye SK only the county resolved (járás <NA>) medium
fuzzy both close spelling match (≥ 90). A top-score tie means the cell names two districts: on the CZ side the municipality breaks it (else the Prague rule, else refuse), on the SK side it refuses outright medium
muni CZ unique village, no okres to corroborate (land-gated) weakest
<NA> both left unmatched on purpose —

A second column — birth_okres_twin (CZ) or birth_jaras_twin (SK) — says which member of a twin pair the record sits in: city, ring, or undecided where there was no municipality to decide (66 Czech and 71 Slovak records across both datasets, all assigned to the ring). It is <NA> for records outside a twin pair. The tier and the twin column answer different questions: the tier is how the district was found, the twin is how the member was chosen, and the second does not weaken the first.


Coverage (measured on the build of 2026-09-23)

Step Result
CZ birth_okres → okres_id legion 101,274 (76.1 % of all rows) / habsburg 122,552 (65.8 %) — the fuzzy tier refuses ambiguous cells (D-23); the space-free exact step adds 2 and 29
CZ settlement crosswalk (settlements_1910) 18,916 settlements (incl. the curated Praha, Těšín and Bílsko rows; 250 more than before the headword fix, audit 2026-09-22 B23), 16,755 with a resolved 1910 okres_id; 5,841 geocoded (points shared across two 1910 okresy refused)
City/rural twins 9,392 legion (7.1 %) / 8,991 VÚA (4.8 %) records in a pair; split 1,513 / 7,854 and 1,580 / 7,370 city/ring, 25 + 41 undecided
Census units member panel 158 okresy (court-derived columns blanked on 18 merged twins) · pooled panel 148 — the baseline merge unit
Settlement coordinates land inside their own name-based okres 90.0 % of the time on containment alone, counting the rows the D-35 repair snaps as the disagreements they were (was 68.3 % before the first audit — a third of geocodes pointed at a like-named village a median 75 km away, audit 2026-07-16 §D2)
SK obec crosswalk (settlements_hu_1910) 3,511 obce (39 of them chartered towns), 98.4 % mapped to a járás, 100 % ethnicity row-sums reconcile. The 57 unmapped are the four genuinely-foreign okresy the backbone does not carry (Cserhát, Rajka, Tósziget-Csilizköz, Žitný ostrov)
Járás/town twins 4,892 legion (3.7 %) / 7,878 VÚA (4.2 %) records in a pair; split 729 / 4,144 and 1,159 / 6,667 town/ring, 19 + 52 undecided
SK birth_jaras_id legion 11,236 / habsburg 21,539 (legion −211: the Galicia-born records Halič no longer takes)
SK county resolved, of Slovak-land births legion 99.7 % of 11,560 · habsburg 99.4 % of 21,684 (every járás-placed birth now has land Slovakia)
Frame exclusivity 0 records carry both an okres_id and a jaras_id, and 0 járás-placed records carry Czech attributes (coords/court/names) — both asserted/verified
bare-"Praha" all 1,915 VÚA Praha-born resolve: 1,909 → okres Praha (301072, tier muni), 6 → járás via their "Lučenec" hint
Residual (SK) mostly Subcarpathian Rus (Mukačevo, Iršava, Užhorod — outside the Slovak lexicon) + region labels

(Pre-fix: 6,320 legion + 4,287 habsburg births were double-placed into both frames, and 124 járás-placed records carried Czech coordinates. See AUDIT_2026-07-16.md A1 and §D.)


Resources & references

Modules (utils/parsing/)

Module Role
districts.py / districts_hu.py 1910 district backbones (CZ okres+court / SK járás+town)
census.py / census_hu.py okres- / járás-level census panels, joined by exact unit code
lexikon.py Czech settlement register → settlements_1910 (name + 1910 okres + okres_all timeline)
lexikon_sk.py Slovak Majo lexicon → settlements_hu_1910 (obec → járás + mother-tongue), x-coordinate table read
wikidata.py Czech settlement coordinates + labels (WDQS, CC0); district-exonym validation (SK)
spatial.py point-in-polygon court-district assignment from the MPIDR polygons
ruian.py today's geography (ČÚZK RÚIAN): obce, parts, KÚ, ZSJ, adjacency + the settlements_1910 → current crosswalk (D-151)
geonames.py / vanished.py / prng.py named small places (ČÚZK Geonames) / vanished settlements (Wikidata, zanikleobce.cz IDs) / Polish localities of Těšín Silesia (GUGiK PRNG) — crosswalk candidates after RÚIAN, in that order
records_geo.py CZ place standardiser — add_birth_okres_id (Move 1) + add_birth_settlement_okres (Move 2)
records_geo_hu.py SK matcher — add_jaras_id (Move 1) + add_settlement_jaras (Move 2) + resolve_jaras_twins (járás/town member)

Data sources — full provenance, licences and paths in DATA_SOURCES.md: MPIDR/Rumpler-Seger GIS + census (#3–#4); ČSÚ Historický lexikon obcí (#5, library/lexicons/); Wikidata (#6); GISta Hungarorum GIS + census (#8a/#8b); Majo 2012, Historicko-demografický lexikón obcí Slovenska 1880–1910, ŠÚSR (#9, library/lexicons/).

Curated crosswalks (auditable, tracked): district_cs_curation.csv (accented Czech district names), district_sk_curation.csv (Magyar→Slovak district names + the Wikidata-validated exonyms).

Orchestration: notebooks/02_data_cleaning.ipynb §3a–§3d (backbones, census, both settlement crosswalks) and §4 (the two-pass matching applied to legionnaires + Habsburg fallen).

Digitising the 1910 registerHow it works

One page of the printed census register, annotated — and why the pictures under the table are the hard part. · source: reference/LEXIKON_1910.md

In short

The 1910 census printed a register of every place in Bohemia: 480 pages of table, and under every table a second document made mostly of pictures. The table is the part a machine can read. The pictures are the part worth having — they record what a village had, not how many people lived in it — and they are the reason this is the hardest reading job in the project.

Explain it simply

In 1916 the imperial statistical office printed a book that lists every inhabited place in Bohemia — every village, every hamlet, every mill with a name. For each one it gives a row of numbers: how many hectares, how many houses, how many people, how many Catholics, how many spoke Czech. That part is a spreadsheet that happens to be made of paper, and a computer reads it about as well as you would expect: nine numbers in ten come out right.

The interesting part is underneath. In tiny type at the foot of every page, the book also records what each village had — a school, a midwife, a fire brigade, a savings bank, a post office, a Czech credit union rather than a German one. That is a completely different kind of fact from a headcount, and nothing else in this project comes close to it: you can measure whether a man grew up somewhere with institutions or somewhere with none. There is one problem. To save space, the 1916 typesetter printed those institutions not as words but as little pictures — a tiny open book for a school, a stylised helmet for a fire brigade — and defined them once, on a key page at the front.

A computer cannot read a picture it has never been shown. Text-recognition software sees the school symbol and confidently types g. Modern AI models, asked what the mark is, guess — and guess differently from each other: handed the identical symbol, two of them answered “doctor” and “curate”. Over one test page they agreed with each other about the words of an entry two times in three, and about the symbols about one time in ten.

So the work splits in two. The numbers upstairs are read by machine and then made to prove themselves: men plus women must equal the total, the villages must add up to their parish. Crucially the machine is never told what the answer should be — tell it the total and it will happily produce numbers that reach that total, and the test stops testing anything. Downstairs there is no such arithmetic, nothing adds up to anything, and the only defences are a fixed list of names a reader is allowed to use, and a human sitting down with the key page. A person reading those marks by hand got 286 of them and was unsure about none, which settles one thing at least: the difficulty is not the scan. It is that we are asking machines to recognise a picture nobody has drawn for them.

One page, three documents ⇱ Interactive ↕ Hover for values

The volume's own Návod divides every page into three: a table, a two-column apparatus below it, and a legend under a rule at the foot. The table is what the text layer reads well. The apparatus is what the project is actually after, and it is mostly pictures. Both editions of the same municipality are here — click any box.

Podrobný seznam míst — IX. Čechy scan 0270, printed page 256 · 18 of 18 rules found on this page
Podrobný seznam míst — IX. ČechyThe running headSeventeen columns, eighteen rulesColumn 1 — two sequencesColumn 3 — the block delimiterThe last two columnsA summary row is not a municipalityBrzice — one municipality blockThe apparatus, left columnThe apparatus, right columnA heading that survivedThe same heading, lostWhat Brzice hadThe same field for a district seatThe postal and railway legend
Spezialortsrepertorium — IX. Böhmen scan 0283, printed page 263 · no rules in the text layer at all
Spezialortsrepertorium — IX. BöhmenA different running headThe same columns in GermanBrzitz — the identical figuresThe municipality before itThe municipality after itThe apparatus, and a gutter found in the whitespaceThe Böhmisch Skalitz institutionsThe same four symbols, keyed 3 instead of 1

What one village had, and what three readers made of it

One printed line, read three ways. This is the whole difficulty of the source in four glyphs: the arithmetic upstairs has an identity to check itself against, and down here there is none.

What the page prints
1, 1) [school] II, [midwife], [fire-brigade], [cooperative]: R.
What the text layer read
1,	1) g II, $, f,	R.

Four printed marks; one unresolved slot. A pictogram the engine rendered as Щ, ф or $ becomes a slot; the same pictogram rendered as g, f, p or S — the commoner outcome — is indistinguishable from type and is never emitted at all.

What is actually printed
1, 1) [school]obecná škola II, [midwife]porodní bába, [fire-brigade]hasičský sbor, [cooperative]zapsané výdělkové a hospodářské společenstvo (družstvo): R.

Hand-keyed against the plate. Across the whole worksheet: 286 symbols over 55 entries, every one marked certain, none unidentifiable — so the distinctions are on the paper at 399 DPI and the limit is the readers, not the scan.

And the German volume
3, 1) [school] II, [midwife], [fire-brigade], [cooperative]: R.

The same four marks, set by a different compositor in a different city, printed against key 3 because the German edition sorts the district alphabetically in German. This is the closest thing the project has to an independent second keying of the apparatus — and the only reason it can be used is that the two are matched by name, or by position in the German alphabet, and never by the printed number.

So Brzice in 1910 had a two-class primary school, a midwife, a fire brigade and one Raiffeisen credit union — and no post office, no doctor, no parish and no railway station. None of that is in the census; all of it is in this line.

From the printed line to the place authority

The frozen run settles what Brzice's entry prints, but it keeps no place name and no link outside the book. The step after it (D-148) gives every municipality and part a key into settlements_1910, the table the legionnaires' birthplaces already resolve to, so the merge onto persons is a plain join. Everything below is computed by that code on the Czech gold run, which holds this page (lexikon_1910_register 1.1.0, built 2026-10-29). Columns and caveats: 1910 register — the municipal data.

1. The place — a key into settlements_1910

placeprinted name · sourcesettlement_idin the authoritystatusscore / runner-up
municipalityBrzice, Brzitz tablebrzice:1Brzicematched100 / 72
osada 1Brzice, Brzitz tablebrzice:1Brzicematched100 / 72
osada 2Grund tablezlibecek:1Žlíbeček (1910: Grund) osada of Brzicematched100 / 60
osada 3Harcov, Harcow tableharcov:2Harcov osada of Brzicematched100 / 66
osada 4Žďár, Žďar tablezdar:9Žďár osada of Brzicematched100 / 80

The candidates are the settlements of okres Náchod (301057), the okres of the section's 1910 court, Česká Skalice (courts.csv). For the municipality's cell the best three are Brzice 100, Zlíč 72, Doubravice u České Skalice 62: a match needs a similarity of at least 84 and a lead of 5 over the runner-up, a tie is refused and never broken on lookup order, and each settlement is taken once per okres. The names come from the table reader's rows; the run folder itself holds none. Grund is anchored through its 1910 name: the lexikon files it under its modern headword Žlíbeček.

2. What it had — register_institutions

placemarkcountrulesettled bycommon category
osada 1[cooperative]1agreedagreed[cooperative]
osada 1[fire-brigade]1agreedagreed[fire-brigade]
osada 1[midwife]1agreedagreed[midwife]
osada 1[school]1agreedagreed[school]

One row per place and mark, with the rule the frozen run settled it by and the mark's category across the volumes' plates (D-147): a Mähren Lokalie and a Bohemian parish meet on [rc-cure-seat]. The Raiffeisen credit union is the letter R after the cooperative mark, a text abbreviation; those reach the tables through final_abbreviations.csv, which no run has written yet.

3. One row per municipality — register_municipalities

columnvalue
settlement_idbrzice:1
editioncz
editions_seencz
runcz_gold
court_id301057121
court_csČeská Skalice
okres_id301057
muni_seq1
printed_nameBrzice, Brzitz
anchor_statusmatched
anchor_score100
n_parts4
n_parts_anchored4
abbreviations_readFalse
n_claims4
n_claims_disputed0
columnvalue
n_claims_author0
n_claims_opus0
n_claims_reader_of_record0
rc_cure_seatFalse
rc_parish_strictFalse
n_rc_cure_seat0
n_coop<NA>
n_cat_cooperative1
n_cat_fire_brigade1
n_cat_midwife1
n_cat_school1
n_mark_cooperative1
n_mark_fire_brigade1
n_mark_midwife1
n_mark_school1

Built from the long tables by code and keyed by settlement_id, so it joins onto a legionnaire's birthplace without a second match. Columns that are zero for Brzice are left out here. n_coop is <NA>, not 0: the letters were not read, which is not the same as none printed.

4. Brzitz in the German volume — through the Czech one

The German volume prints Brzitz as No. 3, because it sorts the court by German names. The Czech table prints every municipality's German name as the last form of its cell, so sorting the Czech cells by that name, accents folded, predicts the German numbers. The table is in that order; the German numbers are read off the German page in the figure above.

Czech No.Czech cellGerman name, foldedGerman No.German page printspaired on the gold by
later pageÚjezdec, Aujezdetz—1Aujezdetz, Oujezdecrefused — no candidate on the gold page
3Česká Skalice, Böhmisch Skalitzbohmischskalitz2Böhmisch Skalitz, Česká Skalicename
1Brzice, Brzitzbrzitz3Brzitz, Brzicename
2Červená Hora, také Červená Hůra, Čerwena Horacerwenahora4Cerwena Hora, Červená Horaname
4Doubravice, Doubrawitzdoubrawitzbeyond the figure
5Hořičky, Hořičkahorickabeyond the figure

On the gold, the German places Nos. 2–4 pair with the Czech ones by name. Suppose German No. 1 had no name on the reader's list, as 32 German municipalities on the gold do not. The order step looks at the gap before Böhmisch Skalitz: one German slot and no Czech candidate, because Aujezdetz is Újezdec, printed later in the Czech alphabet on a page the gold did not read. The counts differ, so it refuses (nothing to fill), rather than slide a neighbour into the hole. With that page read, one slot and one candidate: German No. 1 is Újezdec, Aujezdetz (via_czech_order). Because the German numbers between two pairs are a closed run, an equal count proves no municipality is missing, and a wrong fill cannot happen silently.

Tested by leave-one-out on the German gold: each of the 48 pairs the German volume anchored on its own was dropped and the order step asked to find it again. It filled 34, all 34 correctly, and left the rest open. The German shortfall was never the print, which carries both names, but the reader's list, which leaves many places out; the anchoring rates before and after are on the municipal data sheet.

Výklad značek — the plate, and the vocabulary taken from it ⇱ Interactive

The volume defines its own pictograms once, on a plate in the front matter, and both editions print it. It is the only place in 756 pages where the designs are drawn large enough to recognise: [school] is 112 × 48 px here, and an entry's whole printed line downstairs is 44 px tall. Click either plate to enlarge.

Výklad značek
Figure 1 — Czech edition, printed page XI (scan 0013)
Zeichenerklärung
Figure 2 — German edition, same plate (scan 0012)

That 0.45× is why matching the plate's own crops against the entries does not work, and it was measured rather than assumed: against its own source crop a template scores 1.000; against a real entry line at the right scale it peaks at 0.644, and over a whole section the detections cluster 0.62–0.83 with no separation at all — the strongest hits being settlement symbols that cannot occur in an offices section and are in fact correlating with ordinary letters. Nothing downstream consumes it as an identification.

Those 82 designs, cut off the plate and given names, are the closed list a reader is allowed to answer from — with [?] for a mark it cannot place, so that an unknown symbol has somewhere to go that is not a wrong answer. There is no arithmetic identity below the rule to catch a confabulated symbol, so this is the only defence there is.

[mountain-inn]alpský (horský) hostinec[mountain-inn]
[gauging]cejchovní úřad[gauging]
[customs]celní úřad, poboční celní úřad[customs]
[brickworks]cihelna[brickworks]
[hamlet]dědina, viska[hamlet]
[ev-parish-ab]ev. fara augšpurského vyznání[ev-parish-ab]
[ev-parish-hb]ev. fara helvetského vyznání[ev-parish-hb]
[ev-filial-ab]ev. filiální obec augšpurského vyznání[ev-filial-ab]
[ev-filial-hb]ev. filiální obec helvetského vyznání[ev-filial-hb]
[ev-preacher-ab]ev. kazatelský úřad augšpurského vyznání[ev-preacher-ab]
[ev-preacher-hb]ev. kazatelský úřad helvetského vyznání[ev-preacher-hb]
[cadastre-office]evidence katastru daně pozemkové[cadastre-office]
[fire-brigade]hasičský sbor[fire-brigade]
[transport-station]hnanecká stanice[transport-station]
[inn]hostinec (hotel)[inn]
[castle]hrad, zámek, lovecký zámek[castle]
[gamekeeper]hájovna[gamekeeper]
[single-house]jednotlivý dům (chalupa)[single-house]
[cadastral]katastrální (berní) obec[cadastral]
[monastery]klášter[monastery]
[colony]kolonie[colony]
[farrier]kovář koněznalecký[farrier]
[baths]lázně, lázeňské místo[baths]
[doctor]lékař[doctor]
[pharmacy]lékárna[pharmacy]
[sanatorium]léčební ústav[sanatorium]
[kindergarten]mateřská škola[kindergarten]
[besides]mimo to[besides]
[mill]mlýn, pila, brusírna[mill]
[huntsman]myslivna[huntsman]
[obec]místní obec[obec]
[mesto]město[mesto]
[mestys]městys[mestys]
[hospital]nemocnice, špitál[hospital]
[port]obchodní přístav[port]
[school]obecná škola[school]
[revenue-guard]oddělení finanční stráže[revenue-guard]
[creche]opatrovna, dětská útulna[creche]
[osada]osada[osada]
[knacker]pohodný[knacker]
[manor-farm]poplužní dvůr, dvůr[manor-farm]
[midwife]porodní bába[midwife]
[pilgrimage]poutnické místo[pilgrimage]
[post-agency]poštovna[post-agency]
[post]poštovní úřad[post]
[suburb]předměstí[suburb]
[scattered]roztroušené domy (chalupy)[scattered]
[shepherd-hut]salaš, koliba[shepherd-hut]
[solitary]samota[solitary]
[cottage-group]skupina chalup[cottage-group]
[house-group]skupina domů[house-group]
[savings-bank]spořitelna[savings-bank]
[revenue-admin]správa kontrolního okresu finanční stráže[revenue-admin]
[cask-gauging]stanice k cejchování sudů[cask-gauging]
[canteen]stravovna[canteen]
[revenue-technical]technická finanční kontrola[revenue-technical]
[telephone]telefonní hovorna[telephone]
[telegraph]telegrafní stanice[telegraph]
[factory]továrna[factory]
[tourist-hut]turistická bouda, chýše[tourist-hut]
[ves]ves[ves]
[poorhouse]zaopatřovací ústav[poorhouse]
[cooperative]zapsané výdělkové a hospodářské společenstvo (družstvo)[cooperative]
[vet]zvěrolékař[vet]
[ruin]zřícenina[ruin]
[gendarme]četník[gendarme]
[city-part]část města[city-part]
[mestys-part]část městyse[mestys-part]
[rc-parish]ř.-k. fara[rc-parish]
[rc-parish-exp]ř.-k. farní expositura[rc-parish-exp]
[rc-vicariate]ř.-k. farní vikářství[rc-vicariate]
[rc-filial]ř.-k. filiální kostel[rc-filial]
[rc-chapel]ř.-k. kaple[rc-chapel]
[rc-curate]ř.-k. kurátní beneficium[rc-curate]
[rc-pilgrim-chapel]ř.-k. poutnická kaple[rc-pilgrim-chapel]
[rc-pilgrim-parish]ř.-k. poutnický farní kostel[rc-pilgrim-parish]
[rc-pilgrim-church]ř.-k. poutnický kostel[rc-pilgrim-church]
[school-exp]školní expositura[school-exp]
[manor-house]šlechtické sídlo, usedlost[manor-house]
[rail-station]železniční stanice[rail-station]
[rail-stop]železniční zastávka[rail-stop]
[jewish]židovská náboženská obec[jewish]

How well it comes out

Over a 20-page pilot. Not applicable is never counted as a pass — a rate that silently absorbs the rows it could not read measures nothing — and a failing block is re-read, never repaired. The readers are never shown these identities: a model told what the numbers must add up to returns numbers that add up.

CheckPassedFailedUnreadableNot applicablePass rate
I1_sex632713696.9%
I2_confession5434957983.7%
I3_language56237491086.7%
I5_sequence56318383991.0%
I8_parts241700277.5%
I4_block (all columns)80369692,59985.3%

Below the rules there is nothing to add up, and it shows. Over the same pages, 109 printed marks were compared across 5 passes — two local vision models with and without the plate in the prompt, and one human. Every one of them is contested: none is claimed by all five, and 73 are claimed by exactly one. Two models given the identical mark returned [doctor] and [rc-curate]; over the pilot they agreed on symbol identity 9.5 % of the time while agreeing on the words of the same entries 68.7 % of the time. They are not failing to read — they are being asked to name a glyph they have never seen drawn. And agreement bounds accuracy without measuring it: two readers agreeing 21.5 % is equally consistent with both being 45 % right and independent, or both being 22 % right.

Which is why the division of labour the pipeline was designed around — the text layer knows where every marker is, the image read knows what it is — is false. Joining the two over the pilot leaves 125 marks the image found and the text layer never emitted, against 5 the other way: the text layer sees roughly a third of what is printed, and which third is decided by how the OCR happened to fail. For the apparatus the image read has to be the primary record and the text layer's slots a lower bound.


Companion to DATA_SOURCES.md (where the volumes come from), PLACE_MATCHING.md (what a birthplace resolves to today) and the reading route (how the register is read today, and why). Code lives in utils/lexikon_1910/: the library modules lexikon_1910*.py and the scripts that drive them, with finished experiments and one-off measurements in utils/lexikon_1910/_archive/ (D-119). The register's data — gold sheets, symbols, volume profiles, curation tables — stays in reference/places/.


The volume

Two editions of one book, composited and proofed separately, from the same census returns:

Czech edition German edition
Title Podrobný seznam míst zemí rakouských … IX. Čechy Spezialortsrepertorium der österreichischen Länder … IX. Böhmen
Imprint Vídeň / Wien, 1916, c. k. dvorní a státní tiskárna Wien, 1915, k.k. Hof- und Staatsdruckerei
Held as 756 scans, NDK Kramerius uuid:c2d4a3a0-6fb2-11ec-bc36-005056827e51 764 pp., Universitätsbibliothek Wien urn:nbn:at:at-ubw:g-80326
Text layer full ALTO — word boxes and the printed rules plain text only; page.rects and page.lines are empty

Mähren and Schlesien are on the shelf in German and have not been read.

480 pages, not 756 scans. The table runs scans 15–494; the rest is front matter, the two Přehled země tables, the appendix, the changes lists and the alphabetical index. Every cost and wall-clock estimate extrapolated over the scan count is therefore 57 % too high, which is the difference between a run that is worth starting and one that is not. The scan-to-print offset is +14: scan 0270 prints page 256.


What one page holds

The volume says it itself, in the Návod on p. VI:

Hlavní část tohoto díla rozděluje se na tři, na každé straně se nalézající, k sobě patřící skupiny: 1. Na tabelární část … 2. Na dvousloupcovou část … 3. Na jednosloupcovou část pod čarou …

So three stacked documents on every page, and the third is keyed to the first.

1 — the table, 17 columns and 18 printed rules. A bold municipality row carrying the area in hectares, then its osady beneath it carrying none. Area, houses, present population (total / male / female), four confessional columns, three language columns, foreigners, and then two columns of small raised figures.

2 — the two-column apparatus. Not a two-part layout but a flow: text runs down the left column and continues at the top of the right. Two kinds of section alternate, each under its own heading — Části osad v soudním okresu X:, which names the parts each settlement consists of with a house–population pair, and Úřady, ústavy a t. d. v soudním okresu X:, which lists what the place had.

3 — the legend below the rule, full width: the postal delivery districts and the railway stations the last two columns point at.

The last two columns

Column 16 is the doručovací poštovní obvod, the postal delivery district. Column 17 is the nearest station or halt and the walking distance to it. Neither prints a name; both print a key. The Návod is exact about how to read them:

Malé číslice v posledních dvou sloupcích poukazují na dole uvedená jména, velké číslice v posledním sloupci označují vzdálenost hlavní části osady od železniční stanice v pochodových hodinách. Vzdálenost menší jedné hodiny se neudává.

and about where the name it points at will be found:

Ježto jména … v jednom soudním okresu pod čarou jen jednou jsou uvedena … dlužno hledati příslušné jméno nejen na téže stránce, nýbrž mnohdy i na jiných stranách téhož soudního okresu.

That single sentence is why lexikon_1910_notes.py builds the postal legend across a whole soudní okres rather than per page, and why 85 of 535 keys in the pilot come back legend truncated — the page that would resolve them was not in the run.

One further rule from p. VII removes a whole class of OCR problem before it starts: in the legend a station is printed bare and only a zastávka carries a mark. The mere presence of a glyph answers the question, so the glyph never has to be identified.

Two things the preface settles

The confession columns are not what a modern reader would guess, and the book says so: katolické pools Roman, Greek and Armenian Catholic; evangelické pools Augsburg and Helvetic; only Jewish is shown separately; and everyone else, including the unaffiliated, goes into ostatní. The groupings the invariants add up are therefore the printed ones, not an assumption.


Why it is worth reading

It reaches the settlement. Everything else in the project stops at okres or court level — the Rumpler–Seger workbooks, the GISta census, the census codebook. The birthplace strings on both person datasets name villages. This is the only source that measures the unit the record actually names.

It records institutional presence, not demography. Per municipality: a district court, a tax office, a notary, schools with their number of classes in Roman numerals, a doctor, a midwife, a vet, a pharmacy, a hospital, a fire brigade, a gendarmerie post, a savings bank, cooperatives by type, post, telegraph, telephone, station, parish, castle, mill, brickworks, factory. Nothing else in the project carries any of it.

It carries an ethnic-institutional variable the census cannot. A cooperative's language suffix — Hč against Hn — marks it Czech or German. That is exactly the material DECISIONS.md D-37 says a Czech national-mobilisation covariate should be built from: resident institutions, by founding wave, not crowd events. It is also the thing the OCR is worst at: the suffix survives on 2 occurrences in 188.

It closes D-36's open half. 106 curated Prague-suburb rows — Nusle, Vršovice, Břevnov, Košíře, Michle, roughly 2,083 person records — carry a blank okres and are inert until the 1910 register establishes it.

And the German twin is a free second keying. Two compositors, two proof readings, one set of returns.


How it is read

The readers

Which models read the page today, in what order, and why — and what was tried and dropped on the way — is on the reading route. In short: every apparatus section is read four times, by Gemini 3.8 Flash once and Claude Haiku 5.5 three times; every mark is checked against the page image by an image encoder; what the readers do not agree on goes to Claude Opus 5.5 and then to the author. The rest of this section is about the page itself: where its geometry comes from, what its table must add up to, and why its apparatus is read section by section.

Geometry from the printed rules, not from the text

Three structural facts do the work, and none of them depends on reading a character correctly.

  1. The rules are in the ALTO. GraphicalElement entries taller than they are wide are the printed column rules. Boundaries are therefore measured. The engine finds all 18 on only about a fifth of pages, so the grid is fitted once over the 161 pages where the rule set is complete and then snapped to whatever each page did report — with the number of observed boundaries carried into the ledger, because an interpolated grid is weaker evidence than a printed one even when it is right.
  2. The area column is printed on municipality rows only. That is the block delimiter. Typography cannot be used for it: the NDK TextStyle entries are synthetic — Times New Roman, sizes 5–10 — and carry no weight.
  3. Column 1 runs two interleaved sequences, municipalities across the okres and osady restarting inside each. The OCR mangles them (o, í), Г) o), but the sequence is known a priori, so row identity is reconstructed rather than read.

A fourth fact had to be learned the expensive way. The register opens each district with a summary row — P. o. Náchod, I. s. o. Česká Skalice — and those rows carry an area, so they segment as municipalities. Summing okres Kraslice with its three summary rows left in gave roughly four times the true population.

Where the table ends and the apparatus begins is taken from the rules too (2026-10-14). The table's rows run from the rule that closes the column header to the foot of the column rules, and the apparatus opens on that same line; a divider the body band hid is found as the central rule whose foot is lowest on the sheet. Inside the apparatus the crops of a column meet edge to edge, so a line the text layer dropped is still on some image (D-76 to D-78). A page's geometry can also be read from the image alone, for volumes whose text layer cannot be trusted or does not exist (D-80, opt-in). The four pages of Prague and Liberec print their institutions as prose and are read as prose (D-82).

Since 2026-10-15 the crops are cut on the page image (D-83). The image gives the columns, their foot and the headings; the heading's words, from the text layer or from a read of that one line, label each crop. The legend under the apparatus is read from the image as well, and every pass records how many printed lines each crop shows against how many the reader returned (D-85).

The invariants are a blind grader

The table has to add up, in four independent ways, and the parser is graded against its own arithmetic rather than against a transcription nobody has made:

what it requires
I1_sex male + female = total
I2_confession catholic + evangelical + israelite + other = total
I3_language german + czech + other + foreigners = total
I4_block the settlements sum to their municipality
I5_sequence the interleaved sequence reconstructs
I6_okres okres totals reconcile against the 1910 census
I8_parts the Části osad house–population pairs match the table row
I9–I13 keys resolve, institutions parse, symbols join and agree
I14_accuracy each reader against the human transcription, pairwise, on the entries both read
I15_symbol_error the same comparison symbol by symbol — which glyph the reader is losing

Two rules keep the grade honest. A reader is never shown the invariants — a model told what the numbers must add up to returns numbers that add up, and the pass rate stops measuring anything. And a failing block is re-read, never repaired. Verdicts are pass / fail / unparseable / na, and na is never counted as a pass: a rate that silently absorbs unreadable rows measures nothing.

Its people are those of 31 December 1910, its districts those of end-1914. The German edition's preface says so, and its Änderungen list what moved in between. In Bohemia: two new political okresy, Loket (the court of that name, out of Falknov, from 2 December 1913) and Kralupy nad Vltavou (courts Kralupy and Velvary, out of Slaný, from November 1913); two new courts, Kralupy (out of Velvary, 1 March 1912) and Nové Město pod Smrkem (out of Frýdlant, 1 July 1913); and five political Exposituren (Vlašim, Mimoň, Bílina, Přelouč, Česká Kamenice) that stay inside their okres. The district backbone and the census are at 1910, so I6_okres has to pool Loket with Falknov and Kralupy nad Vltavou with Slaný, and a court-level check the new courts with the court they came out of (DECISIONS.md D-88).

Two parsing decisions are worth stating because they cut the other way. A printed dash is a zero, not an absence — the register sets it where a figure really is none, and reading it as unreadable threw away a value the press had stated. But 23S, íttiS, 58-4 and 07 must surface as unreadable, never coerced into a number.

The apparatus: sections, not columns

A footnote column is roughly 1,350 × 1,000 px of six-point type. glm-ocr reads it in one call and qwen3.5 does not — on page 270 it returned the Části osad section and stopped, one line short of the Úřady heading and everything under it. Halving the column changed the failure mode rather than fixing it: the bottom halves came back well-formed and empty, which is worse, because a region with no lines reads downstream as a part of the page that was blank.

Sending each Úřady, ústavy section as its own image instead gives a median of 478 px rather than a whole column, 1.77 crops a page instead of 3, a prompt that describes one kind of entry instead of five, and 56 symbol tokens in the vocabulary instead of 83.

Two rules found the same way. Headings are anchored on their tail, okres X:, not on their first word: that word is set in a larger face the engine handles worst — Úřady comes back as Hřady, tířady, TJřady, brady, I řady; Části as Ciísti, Gusti, t asti, (J àsti — and a rule keyed on it lost 137 of 1,146 headings (12 %) across the volume. A lost heading is worse than a lost line, because it does not drop that district's entries, it files them under the neighbour. And the line break is the entry boundary, not the full stop: the terminal period is small, it sits under the glyph noise, and a rule keyed on it merged 36 % of the offices entries into their predecessor.

A closed vocabulary, because there is nothing else down there

The table is graded by arithmetic. The apparatus has no arithmetic. So the only defence against a confabulated symbol is that the reader cannot name anything that is not on the list, and that an unknown mark has somewhere to go — [?] — that is not a wrong answer. build_symbol_atlas.py cuts the volume's own Výklad značek plate into 82 labelled crops, and --notes-atlas shows the models that plate instead of describing it in English.

The abbreviations are curated alongside, 194 of them, with a standing warning: the two editions disagree on letters. A building cooperative is St in the Czech volume and B in the German. Never join the editions on the letter.

Nor on the number. Each volume orders an okres's municipalities alphabetically in its own language, so the same place carries a different running number in each — Brzice is municipality 1 of soudní okres Česká Skalice in Czech and municipality 3 of Gerichtsbezirk Böhmisch Skalitz in German. A join on the printed number pairs unrelated municipalities while looking entirely well-behaved, so lexikon_1910_de.py matches by name, within an okres, and refuses a tie rather than breaking it on lookup order.


What actually fails

Measured over a 20-page pilot, 654 table rows.

The table reads well. I1_sex 96.9 %, I5_sequence 91.0 %, I3_language 86.7 %, I4_block 85.3 %, I2_confession 83.7 %, I8_parts 77.5 %. Between the printed rules the ALTO reads digits at about 88 %.

Below the rules it reads almost nothing worth having. The apparatus is small type, superscript keys and — for most of what it records — pictograms, which no amount of parsing recovers from a text layer that never identified them.

The plan that broke. It was supposed to be a division of labour: the text layer knows where every marker is, the image read knows what it is. Measured on 2026-08-06, it is false. The text layer emits 291 unresolved symbol slots where the image reads find 893 printed symbols (glm-ocr) and 545 (qwen3.5) — a threefold undercount, with only 16 of 221 municipality entries agreeing exactly and 185 holding more symbols than the ALTO saw. A pictogram the engine rendered as Щ, ф or $ becomes a slot; the same pictogram rendered as g, f, p or S — which is the commoner outcome — is indistinguishable from type and is never emitted at all. Which third of the markers the text layer knows about is decided by how the OCR happened to fail. So for the offices sections the image read has to be the primary record, and the slots are a lower bound.

The readers cannot name a glyph they have never seen drawn. Over the same pilot, qwen3.5 and gemma4:12b agreed on symbol identity 9.5 % of the time while agreeing on the words of the same entries 68.7 % of the time. They are not failing to read. Given the identical mark, two readers returned [doctor] and [rc-curate]. Across five passes, all 109 markers in the comparison are contested, none is unanimous, and 73 are claimed by exactly one pass. Note what these numbers are and are not: agreement between readers bounds accuracy without measuring it — two readers agreeing 21.5 % is equally consistent with both being 45 % right and independent, or both being 22 % right.

Template matching from the plate does not work at entry size, and this was measured rather than assumed. The plate sets its symbols at display size — [school] is 112 × 48 px — while an entry's whole printed line is 44 px tall, so the entries print the same design at roughly 0.45×. Matched against its own source crop a template scores 1.000. Matched against a real entry line at the correct scale it peaks at 0.644, and over a whole section the detections cluster 0.62–0.83 with no separation at all — the strongest hits being [osada], [knacker] and [single-house], settlement symbols that cannot occur in an offices section and are in fact correlating with ordinary letters. Nothing downstream consumes that output as an identification.

The German twin is harder, not easier. No ALTO and no printed rules, so the grid has to come from the words themselves; the scans are not consistently cropped (511–541 pt wide over a sample of 80 pages), so absolute positions smear into unusable bands; and its own text layer, on the Brzitz page, misreads the entry key 3, 1) as 4, —) and drops the two entries that follow it altogether.

And one reader settled an argument the models could not. The hand-keyed worksheet returns 286 symbols over 55 entries, 100 % marked certain, zero [?], zero [!]. The distinctions are on the paper at 399 DPI. So the limit is the readers, not the scan, and collapsing near-identical symbols into families is not the correct resolution of the source.


What the grader cannot see

Two failures satisfy every invariant by construction, and no pass rate will ever show them.

Compensating misreadings. Two errors in one row that cancel still sum correctly. I1–I4 cannot distinguish a correct row from a row wrong in two places.

A lost block. If a whole municipality is dropped off the page, every surviving row is still individually consistent and every block still sums. The question "is every municipality on this page in the block list?" is not one the arithmetic can ask.

Both are checked the only way they can be — by hand, against the print — which is what the transcription worksheet exists for.

1910 register — the reading routeHow it works

How the register is read today, step by step and why; what was tried and dropped; the gold's results; certification; notebook 15. · source: reference/LEXIKON_1910_ROUTE.md

In short

How the register's apparatus is read today: every section is read four times by two model families, every mark is checked against the page image, and what is in doubt goes to Claude Opus and, last, to the author. Nothing is guessed: every mark keeps the history of how each step read it. On the Czech gold the route adds no false claim and misses 2 of 881 (in-sample); on the eight sealed Czech certification pages, read once and scored once, it reads F1 0.996 (95 % interval 0.991–1.000) — the error rate the analysis may quote (D-127). The German half is not yet certified. Notebook 15 runs all of it, unattended, on the machine of record.

The route a mark travels ⇱ Interactive ↕ Hover for values

The eight steps below, and the five ways a mark can leave them. The spine is the runner's own order; the boxes on the right are the fates, with the share of marks that took each one in the run shown. Click a step for what it does and why it is there; the pills switch the edition and the page set. Every count is read from that run's own final_inventory.csv — the stages from run_frozen.py, nothing typed.

EditionPage set
THE FROZEN ROUTE — one edition, one page setnotebooks/15_frozen_run.ipynb, every step in order; click a stepHOW A MARK IS SETTLEDits five ways off the spine0Freezewhat is in force, recorded module by modulemanifest1Cut each page into sectionsone crop per heading, cut on the page imageheadingspage image2Read every section four timesGemini 3.8 once; Haiku 5.5 three times, with the contrast sheetHaiku ×3Gemini ×13Check every mark against the imagea box locator and DINOv3; and the printed order of the marksorder ruleDINOv3locator4Send what is in doubt to Opus — oncethe four readings disagree, or a check doubts what they agree onOpus 5.55The author rules what Opus cannot tellthe Token Review page, the print enlarged beside the platethe author6Keep every mark's history, then write the inventorythe ledger, and one row per claim with its count and courtledger7Set the two editions side by sideCzech against German; the German claims the Czech disputes go to OpusOpusboth editions8Measurethe gold, the sealed pages, and a validation sample of marksthe authorthe goldSettled by where the print sets ita look-alike in the slot the print keeps for its twin (D-169)— Accepted as the readers read itall four readings agree, and neither check doubts them— Never asked — the reader of record alonea claim with no second reading to disagree with it— Settled by Opusone call a part, a closed choice of tokens— Ruled by the authorwhat Opus cannot tell — the print, enlarged, beside the plate— 
Of the — marks this run settled, how each one was settled

Companion to Digitising the 1910 register, which describes the document itself. Every number below is from the decision named beside it in DECISIONS.md; the dated record of how the route came to be is at the foot of this page.


The route at a glance

step who or what decided in
1 cut each page into sections the page image, labelled by the headings D-76, D-83
2 read each section four times Gemini 3.8 Flash once, Claude Haiku 5.5 three times D-72, D-108
3 check every mark against the image a box locator and DINOv3; the printed order D-73, D-93, D-95
4 send what is in doubt to Opus Claude Opus 5.5, one call a part, once D-100, D-107, D-111
5 rule what Opus cannot tell the author, on the Token Review page D-111
6 keep every mark's history, write the inventory the ledger D-109, D-120–D-122, D-134
7 set the two editions side by side; anchor the German one on the Czech Czech and German Bohemia; Opus on each German claim the Czech inventory disputes D-58, D-116, D-129
8 measure the gold, the sealed certification pages, a validation sample D-55, D-120, D-121

The table under the apparatus — houses, population, confession, language — is read too, by Gemini and Haiku once each, and graded by its own arithmetic (Digitising the 1910 register).


The route, step by step

1. Cut each page into sections

What. The apparatus under the table is cut on the page image into one crop per section: one heading, one kind of section (Části osad, the named parts; Úřady, ústavy, the institutions), one court district. The heading's words label the crop — taken from the scan's text layer, or read from that one line when only the image sees it. The crops of a column meet edge to edge.

Why. A whole column per call made the readers stop early or return empty halves; a section is a median 478 px crop holding one kind of entry. Cut on the text layer, a line the OCR had dropped fell between two crops and was read by nobody; cut on the image, every printed line is inside some crop (D-75, D-76, D-83). The image cut costs nothing in accuracy: +0.002 F1 against the text layer's cut (D-97).

2. Read every section four times

What. Gemini 3.8 Flash reads each section once, thinking off: it is the reader of record. Claude Haiku 5.5 reads it three times, thinking on, with a contrast sheet — the look-alike symbol pairs set side by side. Each reader transcribes the section line by line with the volume's own symbol plate as its key, and may name only the plate's symbols (an unknown mark is [?], never a guess). The readers are never shown what the page must add up to, nor the order the marks are printed in.

Why. - Gemini is the most accurate single reader: F1 0.955 on the 881 claims of the Czech gold (D-97), 0.962 on the development gold against 0.872 for the best local model (D-54). - A second reader is worth what it catches of the first one's errors, not its own score (D-71). Another Gemini catches 26–37 % of Gemini's errors — they are the family's errors — while Haiku catches 58 % (D-97). - Three Haiku reads, because Haiku's reads differ from each other. With one, two and three Haiku reads beside one Gemini read, the gold claims no read makes fall from 18.7 to 14.0 to 10.7, and the false claims every read makes from 14.4 to 9.3 to 6.7 (D-108). - The contrast sheet goes to Haiku only. It more than halves Haiku's look-alike swaps (7.3 to 3.0 a pass) without adding errors; Gemini reads worse with it, in both layouts tried (D-102, D-106). - The table: the model reads 99.7 % of the number cells right, against 91.4 % for the scan's text layer (D-97). - Batched, at half price. Haiku through Anthropic's batches; Gemini through its native batch only once a check on the machine of record shows the batched reader is the live one (D-117, D-124).

3. Check every mark against the page image

What. A locator puts every mark a reader claimed on a box of the page image, and DINOv3, an image encoder, names each box by its nearest neighbours among glyphs cut from the gold pages: it agrees, disagrees, finds the mark not in the bank, or cannot place it (D-93, D-95). Separately, the printed order of each entry's marks is checked: the register prints them in a fixed sequence, and a mark out of place is flagged (D-73, D-74). On a German edition DINOv3 routes marks only once it has passed a gate on the German gold — it must keep and catch about as well as on the Czech gold, and judge as many marks (D-115, D-118).

Why. Readers that agree cannot show an error they share, and the gold has one every reader makes: an outline-roofed castle read as a poorhouse (D-97). Only the image sees it. DINOv3 separates the hardest pairs on cut glyphs — castle from poorhouse 95 %, station from stop 100 %, against 68 % and 85 % for plain pixels (D-91). The order is the one signal that depends on no reader: every disputed claim it flagged on the gold passes was false, 262 of 262 (D-73). A mark read twice is a signal of its own (D-149). The print sets a count before one mark (4 [doctor]) and repeats a mark in 1 run of 231 on the sheets (two named stations), so a reading with [doctor] [doctor] has usually read the vet as a doctor. Because claims are pooled per part, that error used to vanish into a count of two. Each repeat now puts the marks the order lets stand in its slot, and the mark's known look-alikes, to Opus as token questions. On the gold readings the missed mark is among them on 27 of 43 repeats. The order is learned from the gold sheets and, since D-149, also from the three introductions' worked examples (Sternberg, Odrau, Bílina). These contradict the sheets nowhere and give what ten pages never printed together: kindergarten before day nursery.

4. Send what is in doubt to Opus — once

What. A part goes to Claude Opus 5.5 when its four readings do not all agree, or when they agree on a mark DINOv3 doubts or the order flags. One call a part shows the municipality's whole entry, the asked marks outlined (D-107), and asks of each a closed question: one of the readers' tokens, DINOv3's, the tokens of the same base shape and the known look-alikes, none of these, or cannot tell (D-98). Each question is asked once (D-111). Where the German gate fails, Opus is asked whether a token is printed, not what an outlined box shows (D-118).

Why. On the 106 claims the readers disputed on the gold, Opus settled 99 % and was right on 98 %; taking Gemini's reading every time is right on 63 % (test B, D-100). It gave the same answer in all three repeats, so one call is enough (D-111). Ruling the Czech volume's disputes by hand would take 17–24 hours, against about $30 of Opus (D-97, D-100).

5. The author rules what Opus cannot tell

What. A mark Opus cannot tell — 0.9 % of the disputes on the gold, about 60 for the Czech volume — goes to the author on the Token Review page, the page beside it; the ruling becomes the mark's final value (D-111). Marks on the sealed certification pages are withheld until those pages are transcribed (D-120).

Why. The print settles every glyph at 399 DPI — the hand-keyed worksheet returned 286 symbols, all marked certain — so what no machine settles is few enough to read by eye.

6. Keep every mark's history, then write the inventory

What. The ledger keeps, for every mark, what each step read it as, in order (D-109). Its final value: a mark all four readings agree on, unflagged, stands (agreed); a mark Opus was asked about takes Opus's answer; where Opus settled nothing and the readers agree, their reading; anything else waits for the author. The final inventory has one row per claim — an institution of a municipality's part — with its printed count ("2 Sch"), the rule that settled it, the marks behind it, and the section's 1910 court (D-120, D-122).

Why. A claim is never decided by a vote among the readers: agreement routes a claim, it never validates one (D-55). The readers' majority enters only the printed count, a tie going to Gemini (D-121): on both golds it corrected three of Gemini's counts and overturned none of its right ones. The court ties every institution to a 1910 court district, the key to the census and to the other edition: 1,247 of the volume's 1,248 headings resolve, and three names that exact matching had sent to the wrong court are now right — on the Broumov pages Teplice is Teplice nad Metují, on the Děčín pages Benešov is Benešov nad Ploučnicí, on the Krumlov pages Planá is Horní Planá (D-122).

The institutions printed as words. Much of what a place has is printed as an abbreviation, not a pictogram: o. s. (district court), notář, 7 Adv., gw. Fortbsch., the cooperative letters after [cooperative]:. Every reading decodes them against the abbreviation table, and since D-134 they go the route's way too: an abbreviation all four readings make stands, one some but not all make goes to Opus as a token question, in a call of its own. They land in final_abbreviations.csv, keyed like the inventory, with the printed count, the expansion, the English and the category. The count is the number printed before the code, read past a qualifier (2 prům. pokr. šk. is two schools). A qualifier (měst., gw.) is never a claim. An abbreviation beside a pictogram is a claim of its own and leaves the pictogram's count alone. A course taught at the school (lw. Fortbk.) is listed, but it is not counted as an establishment. What it cannot see yet: roughly half the text items on the gold name something the table does not decode (Kreisgericht, BezKrankenk., Oberrealsch.), and a cooperative letter printed with its language suffix (Rč) was refused by the decoder — 16 of the Czech gold's 110 letters (D-134) — until D-139, which reads it.

7. Set the two editions side by side — and anchor the German one on the Czech

What. For Bohemia the German edition, the Spezialortsrepertorium, is read by the same route, apparatus only. The two final inventories are compared municipality by municipality, on the symbols both plates print. Municipalities are paired by court and by every name they print: each part name of the list of parts, and on the Czech side the table's name too. Where both editions read a municipality and the two inventories differ, the German claim is put to Opus as a token question on the German entry. Opus sees the German print only and is not told the Czech answer (D-129). The Czech inventory never changes. The questions are built from the German route without the anchor's own answers, so a later Run All builds the same questions and keeps their answers: built from the anchored inventory, the 20 questions of the gold (19 claims only the Czech side holds, 1 only the German) dropped to none once their rulings were in, and with them the claims they had added (D-164).

Why. The Czech route is certified (F1 0.996 on the sealed pages, D-127) and the German one is not, and the two editions print the same institutions: on the gold, 300 of 300 shared claims agree. Replayed on the gold re-run, the anchor finds 19 claims only the Czech side holds, and all 19 are on the German gold. That is every claim the German route missed on those municipalities (D-129). The editions were first kept apart as two measurements for a misclassification correction (D-58). With the Czech error rate this low, little is left for that correction to fix in Bohemia. The unreconciled German readings stay in the ledger, and the readers of record are still tested for independence (D-121).

8. Measure

What. On the gold, the route is scored claim by claim against the transcription. The certification pages — drawn at random, sealed, transcribed blind — give the error rate the analysis may quote. A validation sample of marks, stratified by how each was settled and shown to the author blind, measures the rest; it is never drawn on sealed or gold pages (D-55, D-120). It is drawn once per run and kept, it leaves out the marks Opus could not tell (those are the author's rulings), and an answer is scored, by mark, against what the inventory holds for it (D-164).

Why. The symbols are certified by a validation sample and used through debiased estimators (DSL, PPI), not by driving the reader to a fixed accuracy: with a validation sample a reader's error costs precision, not bias (D-55).


What was tried and dropped

tried what happened decided in
the scan's text layer for the symbols it names about a third of the marks the image shows (291 against 893 on the pilot), and which third depends on how the OCR failed the pilot, 2026-08
matching the plate's symbols as templates at entry size the matches do not separate (0.62–0.83, strongest on symbols that cannot occur there) the pilot
a whole column per call the readers stopped early or returned empty halves the pilot
local vision models as the reader the best read at 0.872 against Gemini's 0.962 D-54
Gemini thinking no gain, a loss on 3.8, up to six times the cost D-54
a second Gemini as the second reader it catches 26–37 % of the first one's errors: they are the family's D-71, D-97
the contrast sheet for Gemini the first layout made its castles worse, the second still failed the swap bar D-102, D-106
an image-only third reader (E3's independent arm) about 400 alarms a pass against a bar of 24 D-94, D-95
local models as a third reader all six failed: those that reach the residue alarm on half the claims, the accurate one shares the pair's blind spots D-105, D-113
asking Opus whose mark it is it did harm, and failed its no-harm bar D-101, D-110
Opus as the reader of the whole volume about $160–340 against about $6 for Gemini; not pursued D-54 (2026-09-22)
Gemini's OpenAI-compatible batch it refused the structured output, so Gemini was read live until the native batch D-117, D-124
the Czech-trained box locator on German pages it put German boxes on numbers and lines, so Opus rightly answered none of these 88 times; now gated D-118

Results so far — the gold

Czech gold German gold
pages, claims 12 pages, 881 claims in 171 entries 7 pages, 343 claims in 72 entries
Gemini alone, the reader of record F1 0.955 (31 false, 48 missed) 0.941
the route, first frozen gold run (2026-10-23) 0.995 (2 false, 7 missed) 0.899 (5 false, 59 missed)
the route with the fixes of 2026-10-23 and 24, replayed 0.999 (0 false, 2 missed) 0.953; at most 0.975 once its token questions are answered right
the gold re-run of 2026-10-25, both editions in one pass 0.999 (0 false, 2 missed of 881) 0.945 (4 false, 32 missed of 342)
the same re-run, every entry the sheets print 0.999 0.905 (61 missed of 371)
the gold re-read of 2026-10-30 on the pre-freeze route (D-164, D-159), every entry the sheets print 0.999 (0 false, 2 missed of 881) 0.977 (9 false, 8 missed of 372); 0.969 without the Czech anchor
the frozen route (2026-10-31), re-frozen 2026-11-01 after D-178/D-179 (no gold page takes a new path), every entry the sheets print 0.999 (0 false, 2 missed of 881; counts 875 of 879) 0.983 (7 false, 6 missed of 372; counts 364 of 366); 0.976 without the Czech anchor
printed count right 875 of 879 true claims the readers' majority right on 327 of 329
sections tied to a 1910 court 18 of 18 (the volume: 1,247 of 1,248 headings) 12 of 12
Opus on the gold 221 answers in 75 calls, none cannot tell; ≈ $0.77
the table's number cells (Gemini) 99.7 %, against 91.4 % for the text layer not read
text abbreviations, replayed offline (D-134) 195 claims: Gemini alone 0.974; agreed claims 0 false, 9 missed — all 9 among the 12 disputed, 5 calls; counts 195 of 195 91 claims: 0 missed, counts 91 of 91; the one extra claim is a gold typo

The two German lines are the same reading scored over two populations, and the gap between them is the finding. The score of record intersects the entries the gold and every reader reached, which is right for comparing readers and wrong for asking what the route will do to a volume: it drops the entries the route never reached, and on the German gold there are ten of them. One page carries 28 of the 61 claims that go missing, and what happens there is worth stating exactly. On scan 0380 the page image does not find the heading Ämter, Anstalten usw. im Gerichtsbezirk Reichenau an der Kněžna, so the section it opens runs on under the label of the one before it (D-38): the crop saved as rychnovnadkneznou_parts_3 holds that printed heading in its middle and every entry under it. A claim reaches the inventory only from a crop whose name says offices, so those 28 entries are not misfiled — they are deleted. Since 2026-10-25 a run says so: every court with osady on a page and no offices section beside them is flagged next to the claim count (D-128). That is segmentation on an edition with no text layer, not a reader misreading a glyph, and it is why the German number must be fixed before the sealed German half is spent on it (2026-10-25).

Sources: D-97, D-118, D-120, D-121, D-122, D-127, D-134. These are in-sample. The contrast sheet, the box locator, DINOv3's bank and the look-alike list were all built from the gold pages, and the German fixes were chosen after reading the German gold (D-118), so the German gold is no longer a clean test. The gold shows that the route works; it does not give the error rate to quote.


Certification — both halves read and scored

The pages. Eight Czech pages drawn at random and sealed (scans 0046, 0106, 0166, 0226, 0286, 0346, 0406, 0466) and eight German ones: Böhmen's four twins (0165, 0189, 0356, 0441), chosen by rule before any was looked at (D-121), and Mähren 37 and 105 and Schlesien 18 and 38 (D-136). They are outside every learning component and every sample. Four more, Mähren 71 and 139 and Schlesien 28 and 48, are sealed in reserve for the next certification (D-173).

The order. The certification run reads them right after the gold re-run, on the frozen route — section 5 of notebook 15 says whether its freeze is the gold re-run's, module by module (D-125) — and before the full run. The sheets are transcribed blind to its output and scored once. The full run's own readings of the same pages will be scored as a second measurement of the same route: a test–retest, not an independent measure. The Czech one is reported as the final route's Czech figure, since the Czech 0.996 was read on the route of 25 October.

What it can certify. Planned at about 590 Czech claims in about 115 municipalities and about 210 German claims (estimated from the gold's own mix); the Czech pages held 505 claims in 107 entries. That pins the overall error rate. It does not pin each symbol's rate, and was never meant to: most symbols have fewer than ten instances on eight pages.

Is it large enough? For the headline, yes, as a bound on the error counts: the upper end of their exact 95 % interval is 1.7 % for the Czech misses (3 of 505) and 1.1 % for its false claims (1 of 503), counting claims as independent, which understates the uncertainty (the 8-page bootstrap, with one to four errors, is anti-conservative: its upper end is 1.000). Whether that is small enough for a regression is not settled by the rate alone: misclassification of a binary regressor is not classical, and Schlesien's 0.909 in German round 1 shows errors cluster by geography. For per-symbol rates, no, and the entry-level sample that would give them is not drawn (D-174). Thirteen symbols, carrying four claims in five, have ten or more instances, and all but one are read without a miss; the other symbols have too few instances for any rate, and specificity for a rare symbol — where a handful of false claims would be a large share of its true ones — is not bounded at all by 107 entries. The tables below are built from each score file, the German half's by round (round 1, scored blind, is the record; round 2 is in sample, D-172); the last line says how many instances a symbol would need, read without a miss, for its sensitivity to be bounded.

The Czech result (2026-10-25, D-127). The eight sealed pages were read by the frozen run on 25 October and scored once by score_certification.py, whose metrics were written down before any of them was scored. Over every entry the sheets print: 505 claims printed, 503 by the route, 502 right, 1 false, 3 missed — F1 0.996, a 95 % page-clustered interval of 0.991 to 1.000, precision 0.998, recall 0.994. Entries reproduced exactly: 104 of 107. The printed count is right on 501 of the 502 true claims. Every token with ten or more instances except one is read at sensitivity 1.000 — fire brigade 86 of 86, school 79 of 79, cooperative 52, midwife 48, parish 32, post 28; the exception is the filial church, 11 of 12 (0.917); the one false claim and one of the three misses are the same mark — on 0286, municipality 6, part 3 the route reads [school] where the sheet has [school-exp], and the register there prints the plain school glyph beside the word exp., which the transcriber corrected to the bracketed form by decision, so the reader read what is printed. The other two misses are a [vet] on 0286 and an [rc-filial] on 0466, the latter on the single entry of 107 that the route never reached. Nothing on these pages was settled by the author, so the headline is the same with and without the claims that would not have been independent of the gold. This is the error rate the analysis may quote for the Czech symbols, and the interval, not the point estimate, is the result: eight pages are eight clusters.

Is the sample large enough? — Czech half, scored 2026-10-25

Computed at build time from certification_score.json. The page-clustered interval above is the result of record; the bounds here count claims as independent, so they are the floor of the uncertainty, not its ceiling.

observedrate95 % upper bound (exact)
false claims, of the route's claims1 of 5030.20 %1.10 %
missed claims, of the printed claims3 of 5050.59 %1.73 %
any error, per printed claim4 of 5050.79 %2.02 %
instances of the symbol on the sealed pagessymbolsclaimsmissedlowest 95 % lower bound on sensitivity
ten or more1341610.62
three to nine147420.09
one or two121500.02

A symbol needs about 36 instances, read without a miss, for its sensitivity's lower bound to reach 0.90, and 72 for 0.95.

Is the sample large enough? — German half, pooled (D-136), scored 2026-10-31, round 1 — blind, the record

Computed at build time from certification_score.json. The page-clustered interval above is the result of record; the bounds here count claims as independent, so they are the floor of the uncertainty, not its ceiling.

observedrate95 % upper bound (exact)
false claims, of the route's claims31 of 6294.93 %6.92 %
missed claims, of the printed claims23 of 6213.70 %5.51 %
any error, per printed claim54 of 6218.70 %11.19 %
instances of the symbol on the sealed pagessymbolsclaimsmissedlowest 95 % lower bound on sensitivity
ten or more14528170.18
three to nine127420.29
one or two151940.00

A symbol needs about 36 instances, read without a miss, for its sensitivity's lower bound to reach 0.90, and 72 for 0.95.

Is the sample large enough? — German half, pooled (D-136), scored 2026-10-31, round 2 — in sample (D-172), never quoted in place of round 1

Computed at build time from certification_score.json. The page-clustered interval above is the result of record; the bounds here count claims as independent, so they are the floor of the uncertainty, not its ceiling.

observedrate95 % upper bound (exact)
false claims, of the route's claims1 of 6190.16 %0.90 %
missed claims, of the printed claims5 of 6230.80 %1.86 %
any error, per printed claim6 of 6230.96 %2.08 %
instances of the symbol on the sealed pagessymbolsclaimsmissedlowest 95 % lower bound on sensitivity
ten or more1453020.66
three to nine127420.29
one or two151910.00

A symbol needs about 36 instances, read without a miss, for its sensitivity's lower bound to reach 0.90, and 72 for 0.95.

What the German half waited on (written before it was read, kept as the plan it followed). First, the German gold re-run on the route as changed on 25 October, so the sealed pages are not spent measuring a route about to change. Of the German gold's 61 missed claims, 28 came from the heading lost on 0380, which the geometry now finds (D-132). Another 19 came from Opus being shown only the list of parts for a numbered part, which is now fixed (D-130). And the Czech anchor reaches German misses the readers share (D-129). Second, the sealed German sheets must have their notes transcribed: the scorer refuses an edition whose sheets are not all complete. Their tables are drafted (D-135). Since D-136 there are eight: Böhmen's four (0165, 0189, 0356, 0441), Mähren's two (37, 105) and Schlesien's two (18, 38), every second of each volume's four drawn sealed pages. Böhmen is scored twice, with the Czech anchor (what Bohemia's inventory is) and without it (the route Mähren and Schlesien get, having no Czech twin). The German half's headline is the eight pages pooled, Böhmen without the anchor: eight clusters, with each volume's own figures reported beside it. For Mähren and Schlesien, two pages each are a check that the route still works on their scans, not a rate of their own. The symbol reading the four volumes share is measured per symbol by the entry-level sample, which draws from all of them. The Böhmen plate has no cooperative mark — it prints the letters (R, 2 K, Sch) where the Czech prints the mark and then the letters — so each Böhmen part with a cooperative letter is scored as the Czech [cooperative] claim, on the gold and the route alike (D-139); left out, the German headline would lose about a tenth of its claims, all of an easy kind, and read low against the Czech. Mähren and Schlesien print the mark before the letters, as the Czech does, and are scored as printed. Each of the three is read with its own plate — its own symbols (80, 88 and 86), its own key and a prompt that names it (D-146). A language letter is kept as printed (d deutsch, b böhmisch, and in Mähren and Schlesien p polnisch); a letter printed without one is a cooperative of no stated language, never a Czech one. The plates differ most in their Catholic marks — Mähren prints a Lokalie, a Kuratie, a Kaplanei, a Primissariat and an expositure, Schlesien a Lokalie, Böhmen none of them — so every score also carries a second block on the function each mark names (D-147): parish, vicariate, expositure, Lokalie, Kuratie and curate benefice as one resident priest, filial church and chapel as one church without a priest. A Lokalie read as a parish misses by symbol and hits by category. Any religious covariate that crosses a land border is built on these categories.

Later, once the full run exists (the plan as written; not drawn, see the next paragraph). A random sample of about 150 Czech municipalities drawn from its output, rare symbols oversampled — about 8 hours of the author's time — for the analysis variables' own error rates; Mähren and Schlesien 60–80 municipalities each. The sampling rule and the metrics are written down before the draw.

Not done: the entry-level sample (2026-10-31, the author's decision, D-174). About 290 whole entries (about 150 Czech, 60–80 each for Mähren and Schlesien) would take 12–16 hours of transcription, plus 2–3 for a stratum of route-negative entries, which is the only part that could see misses (3 of the 4 Czech sealed errors and 5 of the 6 round-2 German ones are misses, and the validation sample draws only marks a reader proposed). The author's time goes to the regressions instead, on the assumption that the remaining error is random with respect to the covariates. What stands in for it: the eight Czech and the eight German sealed pages (and the four sealed German reserve pages once transcribed) as the only independent error rates, the sealed abbreviation score, and the per-volume figures, which are the one place the assumption can be looked at: round 1's Schlesien 0.909 against Böhmen 0.976 shows error that was not the same everywhere. No per-symbol rate is available for a rare symbol, so any covariate built from one is reported with that caveat.

Czech German
certification run read 2026-10-25 (503 claims, 8 pages, 106 municipalities) read 2026-10-31 on the gold re-read's frozen route: Böhmen 4 pages (182 claims; 181 without the anchor), Mähren 2 (212), Schlesien 2 (209)
scored F1 0.996 [0.991, 1.000] F1 0.957 [0.927, 0.982], pooled over the 8 pages, Böhmen without the anchor (D-136): 621 printed, 598 right, 31 false, 23 missed; Böhmen 0.976, Mähren 0.984, Schlesien 0.909

The German result (2026-10-31, D-164's route). Scored once, as written down beforehand, and kept as the record of the route as it was run. Of the 54 errors, 40 come from four patterns, each looked at by the author against the print: a castle read as a poorhouse (7 of the 8 castles lost, Böhmen and Mähren; the order rule flagged the late poorhouse and Opus confirmed it), a savings bank the sheets do not print (14, all Schlesien: the registered-cooperative mark read as a savings bank), one Silesian entry whose marks were filed one part off (8, Geppersdorf), and a sanatorium read as baths (4). The author ruled the sheet right in 26 of the 32 contested parts and the route right in 2. The other 4 were "something else": Geppersdorf's three, which turned out to be the sheet's slip, and Kuttelberg, still open. So the Schlesien 0038 sheet is corrected — Gotschdorf's fire brigade, Heinzendorf's fire brigade, Geppersdorf's marks one part down — and against the corrected sheet the same reading scores F1 0.966 [0.949, 0.982] (604 right, 25 false, 18 missed; recomputed, the record of the first score unchanged; D-170). For the symbols an analysis leans on (schools, fire brigades, parishes, cooperatives, post offices), the German route reads as the Czech one does; castles (6 of 14 found) and savings banks (14 of 19 claims false) were not usable in the German volumes as read.

Fixed since (D-169, D-171), and measured a second time (2026-10-31, D-170). The two look-alikes are settled by where the print sets them: a mark before a cooperative list's colon is the cooperative mark, and a poorhouse in the castle's slot is a castle. Replayed on every reading on disk, the position rule fixes 10 claims and breaks none, and the colon rule removes the 14 phantom savings banks. The route was then read again from scratch on the same eight pages (round 2; round 1 is kept in de-pooled_certification_superseded_round1/). Round 2 is in-sample: both rules were found on these pages' failures and the author saw the pages while ruling on them. It is reported beside round 1 and never in its place; a blind measure of the fixed route would need sealed pages no run has read.

Everything certified, in one place (source: the certification_score.txt of each run folder; page-clustered 95 % interval, 8 pages, 10,000 draws, seed 1910).

Czech (blind) German round 1 (blind) German round 1, corrected sheet German round 2 (in-sample)
claims printed / by the route 505 / 503 621 / 629 622 / 629 623 / 619
hit · false · missed 502 · 1 · 3 598 · 31 · 23 604 · 25 · 18 618 · 1 · 5
precision · recall 0.998 · 0.994 0.951 · 0.963 0.960 · 0.971 0.998 · 0.992
F1 [interval] 0.996 [0.991, 1.000] 0.957 [0.927, 0.982] 0.966 [0.949, 0.982] 0.995 [0.991, 0.999]
entries exact 104 / 107 103 / 133 — 128 / 133
printed count right 501 / 502 592 / 598 — 616 / 618
by volume (F1) — Böhmen 0.976, Mähren 0.984, Schlesien 0.909 — Böhmen 0.998, Mähren 0.998, Schlesien 0.990

The sheet-corrected column is the recomputed round 1 (D-170); the author's rulings moved 8 of its 54 errors to the sheet.

The full run's readings of the same pages — test–retest (D-181). The full run reads the sealed pages again with fresh reader calls, on the route frozen at 14:31 on 1 November. utils/validation/score_sealed_retest.py scores those readings against the same sheets, with score_certification.py's own metrics, and puts them beside the certification run's (source: output/frozen/sealed_retest/<edition>.txt). This is in sample, like round 2: these pages shaped the rules. It measures how much two reads of one page on one route differ, and never replaces the record.

Schlesien, certification round 2 Schlesien, full run (2026-11-02)
claims printed / by the route 200 / 198 200 / 198
hit · false · missed 197 · 1 · 3 195 · 3 · 5
F1 [interval, 2 pages] 0.990 [0.986, 0.994] 0.980 [0.973, 0.989]
entries exact 27 / 30 25 / 30
printed count right 196 / 197 194 / 195

The two runs differ on six claims, from four marks. Three marks the full run gets wrong and round 2 got right (five claims): - a fire brigade missed (0018, Strumeň 10/2); - on 0038 15/4, a cadastre office read as a customs expositure, which the readers agreed on; - on 0038 6/1, a sanatorium read as baths, which Opus ruled. This is round 1's sanatorium confusion again, so the route has not settled it.

One mark the full run gets right and round 2 missed: the filial church on 0038 3/0. Both runs share three errors: a telegraph missed on 0018, and on 0038 an Evangelical parish (H.B.) read as a Catholic parish, which counts as one miss and one false claim. The other editions are added as their full runs finish.

The German table, against an outside transcription (D-183). Ostafin et al. (2020) typed every Silesian commune's 1910 census from the same volume, so the full run's table readings can be checked commune by commune over the whole of Schlesien, out of sample. utils/validation/check_ostafin_silesia.py joins them by court and German name (source: output/validation/ostafin_silesia.txt, run of 2026-11-03). - Gemini's reading equals theirs on 98.6 % of the 441 joined communes for total population, men, women, houses, the three languages and religion. Haiku agrees as often on population, and is weaker on the languages (94–96 %). - The six totals that differ are unit choices. On the page, the print sides with our reading: Ostafin files Pudlau apart from the commune of Oderberg, which the register prints as 5,810. - Not tested here. Area (theirs is measured on their maps), the apparatus (they carry no institutions), and the 55 communes the name join misses.

Per symbol, the ones an analysis leans on (certification pages only; hits of the sheet's instances, and the route's false claims; a rate on under ten instances is not a rate):

Symbol Czech (blind) German round 1 (blind) German round 2 (in-sample)
school 79 / 79, 1 false 103 / 104, 1 false 104 / 104, 0 false
fire brigade 86 / 86, 0 false 92 / 93, 3 false 95 / 95, 0 false
cooperative 52 / 52, 0 false 69 / 71, 0 false 71 / 71, 0 false
midwife 48 / 48, 0 false 60 / 62, 1 false 62 / 62, 0 false
Catholic parish 32 / 32, 0 false 41 / 41, 1 false 41 / 41, 1 false
post office 28 / 28, 0 false 34 / 35, 1 false 35 / 35, 0 false
castle 9 / 9, 0 false 6 / 14, 0 false 13 / 14, 0 false
poorhouse 12 / 12, 0 false 8 / 8, 7 false 7 / 8, 0 false
savings bank 3 / 3, 0 false 5 / 5, 14 false 5 / 5, 0 false

In the blind rounds, precision on the five covariate symbols (school, fire brigade, cooperative, midwife, post) is 0.97–1.00 in both languages, against 0.951 for the German route overall: the overall figure was pulled down by the symbols that were failing, not by these. What the zeros mean: no false claim was seen in about 100 school claims, which bounds the rate at about 3.5 % (exact two-sided 95 % upper end, treating the claims as independent, which they are not), and the cooperative colon rule and the castle/poorhouse position rule (D-169) were made on these pages, so the round-2 column is not evidence about unseen pages. The blind evidence is the Czech column and the German round-1 column.

Text abbreviations on the sealed pages (utils/reference_tools/places/score_sealed_abbreviations.py, 1.0.0; metrics written in its docstring before it was run; writes abbreviation_score.{json,txt} into each run folder; read-only against the sheets and the run, no paid call). 144 abbreviation claims on the eight German pages (Böhmen 48, Mähren 54, Schlesien 42): 46 institutions, 96 cooperative letters, 2 courses.

Round 1 (blind) Round 2 (in-sample)
all claims: hit · false · missed 144 · 1 · 0 139 · 1 · 5
F1 [page-clustered interval] 0.997 [0.988, 1.000] 0.979 [0.964, 1.000]
precision · recall 0.993 · 1.000 0.993 · 0.965
exact upper bound, false share of the route's claims · missed share of the sheets' 3.2 % · 2.1 % 3.3 % · 7.2 %
cooperative letters: hit · false · missed 96 · 0 · 0 91 · 0 · 5
language letters right on true cooperative claims 96 / 96 91 / 91
printed count right on true claims all all

The one false claim is the same in both rounds: a Bezkrankenk. in Schlesien 0038, 15,4. Round 2 lost five cooperative letters that round 1 read (Mähren 0037 two K; Schlesien 0018 an R and an L, 0038 an R): a re-read of the same pages by a route changed only for the pictograms read worse on the abbreviations, so the abbreviation readings vary between full reads and one score of one run is not the route's rate. The German abbreviation figures to quote are round 1's, blind. Czech sealed pages have no abbreviation file until the full run reads them (the 25 October run predates D-134). Per-symbol rates for the rare marks (under ten instances on eight pages) are not measured: the entry-level sample that would give them is not drawn (D-174).

Review before the full run (2026-10-31, Opus, read-only). Checked against the files; items 1 and 2 were re-checked by hand. Nothing below is done yet unless it says so.

Blocks the full run: 1. Done 2026-11-01: run_scale_e3_test.py moved to _archive/, the .tmp files deleted, notebook 15's settings reset to the gold re-run (PAGES="gold", SPEND=False, FORCE, NEW_ROUND and REREAD cleared) and the cz_certification test build removed from data/final/register/. The gold re-run and re-freeze (item 2) is the author's to run. Was: The tree would fail the freeze check. run_frozen._code_digests() digests every *.py in utils/lexikon_1910/; run_scale_e3_test.py was created after the gold's freeze (17:31), is not in the manifest and not in NOT_ROUTE_CODE, so the full run would read same_route: false. Move it to _archive/ (adding it to NOT_ROUTE_CODE edits run_frozen.py, which breaks the freeze too). Also: the certified route is uncommitted (HEAD is 10-25); commit and record the hash; reset notebook 15's saved settings (PAGES="certification", FORCE, NEW_ROUND="2", REREAD); delete the stale run_frozen.py.tmp.*; clear data/final/register/, which still holds the cz_certification test build. 2. Unspent pages exist — sealed 2026-10-31 (D-173). Mähren 71 and 139 and Schlesien 28 and 48 were role=dropped (D-136) and neither sealed nor in any sample, so the full run's review queue could have shown them to the author. They are now role=reserve: sealed_pages() includes them (out of the review queue and the validation sample), pages_for("certification") does not (the certification reads the same eight pages as before), and nothing may build, tune or train on them. They are the pages for the next certification: transcribed blind after the full run, then scored against the run's own readings. This supersedes the statement above that a blind measure needs pages no run has read. ⚠ It edits run_frozen.py, so the gold re-run's freeze no longer matches the tree: the gold must be re-run (from cache) and re-frozen before the full run. 3. The Czech 0.996 is the 10-25 route's. Opus questions, counts and the colon rule on the Czech plate changed since. Decide now to score the full run's readings of the eight Czech sealed pages and report that as the final route's Czech figure. It is a test–retest of one route, not an independent measure.

Statements to correct or state differently: 4. Done on the site 2026-11-01: build_docs.py reads every certification score, the superseded round 1 included, takes the round from the folder (…_superseded_round1, else the run's round.txt), and labels round 1 blind, the record and round 2 in sample (D-172); the route chart says the same of the German certification run. The score JSON itself still carries no round or blind field. Was: The docs site globs *_certification/ (build_docs.py), so it shows round 2 as the certification; the score JSON has no round or blind field. Add both; show round 1 as the record. 5. Done 2026-11-01 in Is it large enough? above (the blind-pool rule of three is not yet stated). Was: The 8-page bootstrap with 1–4 errors is anti-conservative (upper end 1.000). Lead with exact bounds on the error counts (the upper end of the two-sided 95 % Clopper-Pearson interval): Czech misses 3/505 (1.7 %), false 1/503 (1.1 %). Misclassification of a binary regressor is not classical and Schlesien's 0.909 shows errors cluster by geography, so "bounded well below the level that would bias a regression" is an overreach. The rule-of-three for school belongs to the blind pool: school 2 false of 184 route claims (upper 3.9 %), fire brigade 3 / 181 (4.8 %), post 1 / 63 (8.5 %). 6. "Every token with ten or more instances is read at sensitivity 1.000" is false for the filial church (11 of 12); fixed above, still repeated in DECISIONS, ROADMAP, VALIDATION and SITE_CHANGELOG. The corrected-sheet round 1 prints 622 claims where round 2 prints 623 on the same sheet: record the sheet digests and decoder version in the score file. The spec column mixes parts and municipalities, so report false claims per 1,000 non-printing parts instead. "0 claims set aside" is printed although the author ruled on 32 contested parts. 7. The corrections were real transcription slips (the author checked each against the print), so 0.966 is the better estimate and 0.957 stays the record of the first score. What the correction cannot show is a slip the route repeated, or one on a part the two agreed on; the sheet's own slip rate (8 of 621 claims, 1.3 %) is larger than the route's claimed error, so a second look at a random sample of agreed parts would bound it.

Gaps in what is measured: 8. Text abbreviations feed the register (cooperative-language counts, school classes) and had no sealed-page score; the gold score counts only entries every reader reached and is not written to a file. Built 2026-10-31 (above): German round 1 144 / 144 with 1 false, round 2 139 / 144 with 1 false. The Czech pages wait for the full run. The gold's German abbreviation precision (7 false of 133) is the weaker figure and is in-sample. 9. The validation sample draws ledger marks, so a mark no reader proposed is never drawn, yet misses are 3 of 4 Czech errors and 5 of 6 round-2 German errors. Commit the entry-level design before the run exists: whole entries, stratified on route-claimed rare symbols and route-negative entries, inclusion probabilities recorded for DSL/PPI, gold, sealed and reserve pages excluded. A sensitivity of 0.90 with zero misses needs 35 instances, 0.95 needs 72, which 150 municipalities cannot reach for most rare symbols: choose the covariates it must cover. 10. DINOv3 stays off for German for now. The gate was decided on Böhmen only (0.115 judged against 0.863, 4 false claims) and inherited by Mähren and Schlesien. Measured 2026-10-31 (D-173): the German gold sections rescaled to the Czech line height (66 px) lift the judged share only from 0.115 to 0.165 (keeps true claims 0.98, catches 4 of 22 false; the cover test still fails), so scale is not the main cause. A locator trained on the German gold places 70.7 % of office claims leave-page-out, exactly the Czech-trained locator's 70.7 % (parts 66 % → 79 %). Overlays of the placements show boxes on key digits and letters, not on pictograms: the placed share counts forced boxes, so the problem is upstream of the bank (candidate clusters and key-column geometry on the German print), not its training. The sharper cause (Opus review, same day): entry_mask returns no constraint when no cluster has an entry, and tag_entries gives the whole section up if the number of key lines differs from the number of printed entries by one; on the German pages 6 of 362 (entry, token) items could be located, so a claim can be placed anywhere in the section and the unsupervised training learns key digits as glyphs. place() also does not apply eligible(). The cheapest fix is a sequence alignment of detected key lines to the printed keys (a line may be skipped), then eligible() in place(); before any clicking, an oracle test: click about 30 glyphs by hand and see whether DINOv3 judges those correct boxes (if it does not, the bank is the problem and no locator fix passes the gate). For the click work there is a page (utils/reference_tools/places/german_click_page.html, strips cut by build_german_click_strips.py: 30 strips of 83 printed lines from gold pages 0142, 0276, 0480, offices sections only, never certification or reserve; published as the artifact German Glyph Clicks, clicks stored in its german_clicks collection in section-image coordinates). Part 1, the first five strips, is the oracle set: every glyph named, then DINOv3 is asked to judge those certainly-correct boxes. Part 2 is clicks only, the positives (and by omission the negatives) for a glyph / not-glyph classifier. The earlier 362-question queue (build_german_click_queue.py) is superseded by it. Measured against the author's 203 named clicks (2026-10-31, german_click_recall.py, german_candidate_diagnosis.py). Right glyph over all claims (an upper bound: any click of the claimed token inside a box padded by 0.25 line heights, on the same image): Czech-trained locator 27 %, German-trained leave-page-out 9 % (the Czech test A: 97.2 % of 141 clicked glyphs, on seven hard tokens). 58 of 203 clicks (28.6 %) fall in no candidate cluster at 0.1 line heights, against 0 of 141 on Czech: 24 on components the step keeps but that are too small to hold the click, 17 on components it throws away as full stops or commas, 4 as rules (the tall thin fire-brigade glyph), 11 on no ink. The first cause is the ink threshold: INK = 128 is fixed, and the German print is lighter (share of pixels under 128: 4.0 % against 5.6 % on the Czech offices sections), so its strokes fragment. Raised to 140 the same step holds 91.1 % of the clicks (0.1 lh) instead of 71.4 %; 144 gives the same, 150 and 160 are worse. The second cause is merging: only 43–58 % of the clicks sit in a cluster that is non-bracket and no wider than 1.3 line heights; JOIN_GAP from 0.12 to 0.24 does not change it, so the glyphs touch their neighbours as ink and connected components cannot split them. The German stations' sections are cut at a median line height of 47 px against 65. Neither fix is applied: either changes a constant of the route (a German-only value would keep the Czech path identical) and so the freeze. The oracle test (run_german_oracle.py: the Czech bank and threshold, the clicks as queries) is written and dry-run with the pixel encoder only (judged 0.14 against 0.93 Czech, raw 1-NN 0.45 against 0.97; midwife and post named right 100 %, school 23 %); the DINOv3 run, which decides between 'fix placement' and 'build a German bank', is for Heavyhorse6. The DINOv3 oracle result (2026-10-31, Heavyhorse6). With the box certainly right, DINOv3 ViT-B/16 against the Czech bank names the clicked German glyph right 85–93 % of the time (raw 1-NN, Czech bank against itself 97.4 %): school 100 %, fire brigade 98 %, midwife 95 %, rail station 100 %, revenue guard 100 %, post 83–92 %; weaker: revenue administration 33 % (named a ves), telegraph 40–80 % (poorhouse), parish 71 % (mill), doctor 75–80 %. But it judges only 9–10 % of them (Czech 93.5 %): the median nearest similarity is 0.75 against the 0.851 cutoff, so the bank discriminates but the German glyphs sit under the Czech-calibrated threshold. Two problems multiply: placement puts the box on the right glyph for 27 % of claims, and a right box is judged 9 % of the time, so fixing placement alone would not pass the gate. When it does judge, it is right (89–100 %). The next test is --german-bank: each German page named by exemplars from the other two pages (German only, and Czech + German), its threshold the 5 % quantile of their correctly named glyphs. German exemplars and placement (2026-10-31). With the author's 203 named glyphs as exemplars, each German page named by the other two (leave one page out), Czech bank + German exemplars judges 0.946 of the glyphs with agreement 0.943 (German only: 0.916 and 0.925; gate 0.835 and 0.936): on correctly boxed glyphs the keep test is not rejected, on three folds, in-sample, with the catch test untested. The judged share is set by the threshold rule (a 5 % quantile gives about 0.95 whenever the pages are alike), so it is not evidence; agreement 0.943 against 0.936 is within noise. Placement is the problem: at ink < 140 the entry finder tags no German cluster, and a whole-section alignment places the right glyph for 7.6 % of claims (Czech-trained glyphness) or 10 % (glyphness trained on the clicks); the 14 shape features flag the clicked glyphs at 55 % recall and 52 % precision at best. run_german_detect_by_naming.py reverses the order (embed every candidate, name it against the Czech + German bank, keep those above the page's threshold); with the pixel encoder alone it reached recall 0.4–0.7 at precision 0.6–0.9 at fixed cutoffs (at each page's own threshold precision was 0.27–0.56). DINOv3 detect-by-naming, run 2026-10-31 (v0.1, no reject class, section-wide matching): at each page's own threshold recall 0.83 / 0.74 / 0.86 but precision 0.095 / 0.26 / 0.17 (8.5, 2.1 and 4.3 false boxes per clicked glyph); at similarity 0.85 recall 0.62 / 0.65 / 0.52, precision 0.31 / 0.63 / 0.61. Letters and digits are named as the nearest pictogram. The criteria Opus set before this result was read (precision and recall at least 0.85 on every fold at the pre-declared threshold; a claim seen only by a box inside its own entry's line span; catch at least 50 % of the readers' false claims) are missed by a wide margin on the first, and the others need entry localisation that the German print does not yet give (no cluster is tagged at ink 140). German E3 is closed for this thesis (2026-10-31, D-175). Stopping rule fixed with Opus: one DINOv3 run against criteria written first; a miss closes it. It missed. What is established: DINOv3 names correctly boxed German glyphs well once German exemplars are in the bank; what is not: any way to find them. The candidate step loses 9–29 % of German glyphs (91 % held at ink 140), 43–58 % sit in a usable cluster, the entry finder tags no cluster at ink 140, and a bank with no reject class names letters as pictograms. A working version needs a German ink constant, key-line alignment, a reject class and a one-to-one match, a route change and a re-freeze, and German re-certification on the four reserve pages, for at most about 1 pp of F1 on symbols mostly not used as covariates. The German route stays as certified: DINOv3 recorded, routing nothing. The scripts, the 203 clicks and the page stay as the starting point if it is reopened. Reopened for one test: the reader gives the box (2026-10-31, bars written before any read). run_german_reader_boxes.py asks the reader of record (Gemini 3.8 Flash, thinking off, the German offices prompt and the key) for a box around every mark it writes, on the offices sections of the three clicked gold pages, and DINOv3 judges each box against the Czech bank, the German click exemplars of the other pages and a reject class (unclicked candidates on the other pages' exhaustively clicked strips), each page held out in turn. Each mark is put, one to one against the clicks, in right (its box holds a click of its token), misread (a click of another token), box off (no click inside, one of its token within 1.5 line heights) or phantom. The bars: box right / (right + box off) ≥ 0.90; cover, judged share of right claims ≥ 0.835; keep, agreement among judged right claims ≥ 0.936; catch, disagreement among judged misread + phantom claims ≥ 0.50, judged only with ≥ 10. A miss on any bar closes German E3 (D-175 stands); a pass is a reason to build it and certify it on the sealed reserve pages, not a certification. It cannot catch a real glyph filed under the wrong part, nor a mark no reader wrote. Expected gain at most about 1 pp of F1, mostly on symbols not used as covariates (the Czech gain was mostly poorhouse to castle, which the position rule covers); the reserve pages should not be spent on it unless the Böhmen cross-edition disagreements concentrate in what E3 could catch. For the regressions: land fixed effects, a Bohemia-only check, and the Czech-against-German disagreement rate in Böhmen regressed on the covariates to test non-differential error. Result (2026-11-01, run once on Heavyhorse6, output/glyph_e3_de/reader_boxes_none_dinov3.txt, read cost $0.06): keep fails, German E3 stays closed (D-176). The reader wrote 202 marks: right 139, box off 15, misread 30, phantom 18; it boxed and named 139 of the 203 clicked glyphs (0.685). Box 0.903 (139 / 154; exact 95 % 0.844–0.944): passes on the point estimate only. Cover 0.957: passes. Keep 0.872 (116 / 133; exact 95 % 0.803–0.924) against the bar 0.936: fails, and the bar lies above the interval's upper end, so the miss is not three folds' noise. Catch 0.857 (36 / 42 judged false claims; 0.715–0.946): passes. Held-out thresholds were 0.756–0.764 (Czech 0.851). In the route a DINOv3 disagreement drops nothing: it sends a claim every reader agreed on to Opus (routed_claims). So the trade is 17 of 133 judged true claims (12.8 %) flagged to catch 36 of 42 judged false ones. On the full German run, that is thousands of extra Opus questions on true claims, each a chance of a wrong overturn, to catch false claims all readers agree on, which run at about 1 in 620 claims at certification: about 0.1–0.15 pp of precision before Opus's own errors, and nothing for misses (5 of the 6 German round-2 errors). Of the 15 badly boxed true claims, 11 were disagreed: a box off its glyph is rejected, so box quality and keep are not independent. The test cannot say how the trade would net out on the route's own claims (they are text, not marks), and it was not made to: the bars were written to decide, and one failed. One declared follow-up (D-177, declared 2026-11-01 before any snapped judging). A free measurement on the cached replies found a defect in the test's design: the queries were cut from Gemini's raw box, while the bank's German exemplars are cut by build_glyph_bank.tight_box at the click. On the right claims, Gemini's box overlaps the ink-tight box of the matched click at a median IoU of 0.35 (1.6 times its width, centre 0.26 line heights off); re-cut by tight_box at its centre, the median is 1.00, and the share below 0.5 falls from 0.73 to 0.27. The rule, fixed now: each query is re-cut by tight_box on the same ink mask as the exemplars (threshold 128), at the integer centre of Gemini's box, with no search and no parameter; where no ink is near, the raw box is kept and counted. Categories stay as the raw box gives them, the D-176 bars and the threshold rule are unchanged, and it runs once (run_german_reader_boxes.py --stage judge --encoder dinov3 --snap, from the cached replies, no API cost). Expected (Opus, before the run): keep about 0.92, because the 27 % still badly boxed should stay near 0.87 and the oracle's ceiling is 0.943, so a pass is unlikely. A pass only reopens German E3; it cannot certify it, because the bank, the thresholds and the IoU diagnostic all come from these three gold pages. To be built at all it would then be parked until after the regressions and certified on the four sealed reserve pages (fresh Gemini box calls and about 200 clicks made before those pages are transcribed, gold kept out of the bank), and only if keep there is at least 0.95. A miss closes German E3 for good. The D-176 fail stays the record either way. Result (2026-11-01, run once on Heavyhorse6, output/glyph_e3_de/reader_boxes_none_dinov3_snap.txt): keep fails again, and German E3 is closed for good. 201 of 202 boxes were re-cut, and the one kept raw is a phantom. Cover 1.000 passes. Catch 0.891 (41 / 46; exact 95 % 0.764–0.964) passes. Keep 0.856 (119 / 139; exact 95 % 0.787–0.910) fails, below the raw-box 0.872. Tight cuts made every right claim similar enough to be judged (the 6 abstentions went), but not to the token it is. 17 of the 20 disagreements name the claim [not-glyph]; fire brigade, the tall thin glyph the candidate step also drops, accounts for 7 of 33. So with the box exactly where the glyph is, DINOv3's CLS feature puts one German pictogram in seven nearer the letters and digits around it than to the German exemplars of its own token. This is the same confusion that gave detect-by-naming its precision of 0.10–0.26; the oracle's agreement of 0.943 had no reject class to fall into. Nothing further is tried: the regressions run with German E3 off, as certified.

Before the full run: every page cut offline (2026-11-01, D-178, D-179)

The certification read 8 Czech and 8 German pages. The full run reads 1,117. So before anything is spent, every page was cut the way the route cuts it, with no model called. Three defects came out, none of which any gold or certification page could have shown.

  1. The statutory cities (D-178). Prague and Liberec (Czech 15–18, German Böhmen 21–24), the six Moravian (19–24) and the three Silesian (13–15) statutory cities print their institutions as running prose: no keyed apparatus and no pictogram. The German apparatus cut raised on those pages, and German Böhmen 21 is the volume's first page. The full run now reads each volume's table pages less these 17 (run_frozen 0.21.0, STATUTORY_TABLE_PAGES for Czech 15 and 18, whose city tables open the page above the prose). The register names the 11 cities and has no row for them: aggregated to its okres, a city's counts are missing, never zero (lexikon_1910_register 1.4.0). Their institutions are read apart, from the prose: two verbatim transcriptions (Gemini 3.8 Flash and Opus 5.5) with the text layer voting where they differ, and the author settling the rest (utils/reference_tools/places/statutory_prose.py, notebook 15 §20). Read 2026-11-01 for $1.63: of 113 disagreements, 67 were immaterial (a comma for a full stop, a district's place-type pictogram, a blank-strip remark, the running head) and settle by rule, 37 the text layer settled, and 9 were ruled against the scan (reference/places/statutory_prose_rulings.csv). The text layer never settles a figure: on Prague it read 19 girls' work schools as Gemini did, misreading 4 as 1, where the print has 49. Stage 2 (statutory_institutions.py, §21, about $2.50–3) lists every institution the verified text names (city, exact words, count, language split, category, code) by both readers, checks each row against the text by rule, compares the readers row by row, and checks Prague and Liberec Czech edition against German. Prague and Liberec are printed in both Bohemian editions, so their two transcriptions also check each other. Read 2026-11-01: readers $2.94; the open groups judged blind by Sonnet 5.5, the model that is neither reader ($0.70; the script's default, so a cached re-run is free), which settled 621 of 627, leaving 6 for the author (review.csv): its rows are accepted with a count printed after se/mit/( (se 7 pobočkami) and a code given as its vocabulary expansion (Steueramt → Sta.), while the readers keep the strict rule that decides what the judge is asked (0.4.0). The author ruled five of the six: a council with language sections is the council and a row for its sections, as a branch is (reference/places/statutory_institution_rulings.csv, status author, 0.5.0). One is open: two archpresbyteries named with no number printed. A total printed before its own breakdown ("181 registrierte … und zwar:") is flagged is_total; with totals left out, Prague's settled institutions agree across the editions within a few per category (cooperatives 186 / 185), transport too (93 / 94) once the German edition's 24 public telephone stations settled as a row of their own.
  2. German pages with faint rules (D-179). On some German pages the column rules are printed too faint to cross the header's foot. With fewer than the three crossings needed, no table was found. The apparatus cut then fell back to a text layer the German volumes do not have, and raised (Böhmen 73, 88, 232, 233, 237, 241, 243, 259 …), while the table reader returned no block on 48 Böhmen and 9 Mähren table pages. lexikon_1910_image 0.3.0 adds two German-only rules, each tried only where the strict rule failed: - the header's foot by position, when the frame's upper half holds exactly two frame-wide rules a header's height apart (HEADER_HEIGHT); - where only the top rule is frame-wide, one long rule (at least 60 % of the frame) a header's height below it, the foot printed broken (Mähren 28); - the column rules fitted to the volume's template (column_template in each German profile: the median boundary over 17–18 random pages, none straying more than 1 % of the frame's width). The frame is solved for rather than assumed: every pair of found rules set on every pair of template boundaries gives a frame, and the one putting the most rules on the template wins. That corrects a frame side found on an outer line (Mähren 42, 56, 84), on the page edge (Böhmen 273) or short of the frame (Böhmen 332). A fit is kept only with at least half the inner boundaries matched and at least 80 % of the rules inside the frame on the template; a page laid out otherwise keeps what it found, and says so.

Not one gold, certification or reserve page takes either path: all 27 go through the rules as certified, so the certification still describes the pages it measured. 3. The audit stays. utils/reference_tools/places/audit_full_run_pages.py (notebook 15 §19) re-cuts every German page and lists what would stop the run (cut fails), what loses a page silently (no table, no apparatus: no divider found) and what went through a 0.3.0 rule (fallback): output/frozen/page_audit.{csv,txt}. On the finished code (2026-11-01): 641 German pages (Böhmen 471, Mähren 133, Schlesien 37), 0 cut fails, 0 without a table, 0 without a divider, 57 through a fallback (48 Böhmen, 9 Mähren); median table blocks per page 18 / 23 / 14. Before the fixes, the cut raised on at least 9 table pages (Böhmen 73, 88, 232, 233, 237, 241, 243, 259 among the first 297 scanned, before that scan was stopped; Mähren 28) and 57 had no table. 4. The Czech volume needs none of it. Both Czech readers dry-run over 15–494: the table grid gives blocks on all 476 table pages (7,775; median 16), and the apparatus is cut on the image on 475 of 476, the one other falling back to its ALTO text layer. One weak page: 0360 (printed 346, three district totals), where the grid snaps 8 of 18 rules and gives 3 blocks and 9 unassigned rows. It is left to the certified route: the register takes a municipality the Czech volume misses from German Böhmen, whose matching page (0359) cuts cleanly (14 blocks).


Notebook 15 — how it runs

notebooks/15_frozen_run.ipynb runs the whole route, every stage in order, with its runner utils/lexikon_1910/run_frozen.py. Two settings choose what it runs on — the edition and the pages — and everything below them is the same for the gold, the certification pages and the full volume: what is tested on the gold is exactly what reads the volume.

Settings (section 1, the only cell to edit):

setting values
EDITION "all" (every edition the page set is for: the gold Czech and German Böhmen; certification the German half; calibration Mähren and Schlesien; full all four), "bohemia" (Czech, then German, cross-checked), "cz", "de-bohemia", "de-moravia", "de-silesia"
PAGES "gold", "calibration" (Mähren and Schlesien only, D-154), "certification", "full"
SPEND False: plan, check and score only; True: read and ask Opus. The notebook is saved with False
BATCH_READERS True: every reader through batches, at half price
WAIT True: the collect cells wait until their batches have ended, so Run All runs unattended
REREAD a date, once, after a change on the readers' side: the run's reads, batch queues, section crops, E3 and order flags are set aside and every pass runs again, from the cache where its prompt is unchanged (D-159). Never on certification
GEMINI_CHECK True once, before the first re-read: check Gemini's native batch against its live reads (≈ $0.66)
GEMINI_BATCH True only after that check printed PASS, then for every run — the freeze compares it (D-167); otherwise Gemini is read live
FORCE True only to run a finished certification again: without it, section 3 refuses one whose inventory or score is on disk (D-164)
ACCEPT_FAILED {}: every missing reply stops the ledger. {"de-silesia": ["<crop or block key>", "<Opus call id>"]} lets those named gaps through — a crop that cannot be read, say — recorded in the run's reads/completeness.json (D-180)

The sections:

§ what it does spends
2 the machine, packages and keys; how Gemini will read —
3 whether this edition and page set can run (a finished certification is refused), the readers' labels, and the re-read —
4 the gold frames, rebuilt from the sheets (gold only) —
5 freeze: the contrast sheet and the manifest of what is in force — every module's code among it; a certification or full run is compared with its gold re-run's freeze —
6 the reading plan and its cost estimate —
7 read: 7a queues each pass and sends it at once, 7b sends what an interrupted 7a left, 7c waits and collects, 7d finishes the passes from the caches (and will not run while a batch is processing) and then holds every pass to its queue — a request with no cached reply, whatever stopped it (an exhausted balance, an outage), marks its pass unfinished on a spending run, so 7d run again reads only the gap (D-180) — 7e the one-off Gemini batch check yes
8 check: E3 (locator, DINOv3), the order rule, the German gate GPU
9 route: what Opus is asked, and its estimate —
10 Opus: one call a part, batched yes
11 collect Opus: waits; a call that failed is sent once more —
12 ledger: every mark's history, the final inventory with each section's court, the place types, the text abbreviations, the author's queue — not built for an edition with a reply missing, a reader's or an Opus call's, unless named in ACCEPT_FAILED (D-180); 14b's anchored ledger likewise —
13 the score (gold only) and the validation sample —
14 the two editions side by side; on the gold, the independence test —
15 the author's queue for the Token Review page —
16 the author's decisions back into the ledger; the validation sample scored —
18 the register as municipal data in data/final/register/ — on "full" only (D-167; REGISTER_VARIABLES.md) —

How it is run. On the machine of record, Heavyhorse6, only (D-68). With SPEND = False nothing is sent. With WAIT = True, Run All runs top to bottom on its own: it stops at each collect cell until the batches are in — asking every two minutes, reporting every fifteen — and an interrupted cell resumes when it is run again (D-123). Everything a run writes is under output/frozen/<edition>_<pages>/; the batch queues, which hold every request with its images (about 135 MB a Czech page), are written outside Dropbox, in lexikon_1910_queues beside it.

The order from here (2026-10-31, D-169–D-171). The gold re-read with the new rules (REREAD), whose freeze every later run is held to; the German certification's second round on the same eight pages (NEW_ROUND = "2", FORCE = True: every call made again, the first round kept), scored pooled beside the first; then the full run of the three editions and the register. A change the second round calls for sends the gold round again first. The Czech half is not read again.

The order of use, as first written. "bohemia" on "gold" (re-run after every change, with REREAD after a change on the readers' side; the Gemini batch check 7e runs once before the first such re-read), then "de-bohemia" on "certification" (the Czech sealed pages are read and scored already, and the anchor uses that run). Mähren and Schlesien have no gold: the Böhmen German gold serves them (D-104). Each may first run "calibration", its four calibration pages only, looked at against the page image and not transcribed; it is optional, and their certification may go first (D-154, D-167). Then each runs "certification", its two sealed pages (D-133, D-136). Then the German half is scored pooled, and each volume is read on "full", after which section 18 builds the register. Nothing in the code may change between the gold re-run and the last certification run: each is held to the gold re-run's freeze. That freeze pins every German volume's key, so Mähren and Schlesien are compared with the entry for their own volume, and the scorers are recorded apart and not compared (D-164).

The cost. Reading about $215 for the four volumes with every reader batched (D-108), less with Gemini batched; Opus about $30–45 a volume (D-111). Sections 6 and 9 print the estimate before anything is sent.


The gold

What it is. Pages of the register read by hand off the scans, one Markdown sheet a page, in reference/places/transcription_gold/:

folder pages role
cz/calibration/ 8, drawn at random prompts, thresholds and the learning components are fitted here
cz/purposive/ 5 development pages, chosen for their difficulty
cz/certification/ 8, drawn at random sealed: transcribed only once the route is frozen and has read them
de/calibration/, de/purposive/ 7 the German twins, paired by court and name, never by page
de/certification/ 4 sealed: the twins of four sealed Czech pages (D-121)

The Czech and German calibration and purposive sheets are read, and so are the eight sealed Czech sheets (transcribed 2026-10-18, blind: no certification run existed). The four German sealed sheets are cut and blank, and the certification scorer refuses that edition until they are complete. A sheet's images are regenerated from the scans and are not tracked; the sheet is the artefact. The Czech sheets are read back by read_transcription_md.py, the German ones by score_r5_german_notes.read_gold.

While transcribing. Keep a German sheet blind to its Czech twin, or the two editions will seem to agree more than they do. Write what will be a regressor exactly as printed — a school's classes and its language note, a cooperative's letters and its language suffix. A part that opens its own line has no dash: 2) under 1) is part 2 of the same municipality. Note the minutes a sheet took when it is closed.

The dated plan

How the route came to be, revision by revision, from August to 15 October 2026 — kept as it was written, and superseded wherever the sections above say otherwise.

Show the dated plan

The pictures under the 1910 register's tables say what each place had — a school, a post office, a gendarmerie post. On the four development pages the reader now scores 0.972 (settlement F1); what it still gets wrong is the same handful of look-alike symbols in every repeat — a castle read as a poorhouse, a railway station as a stop — and nothing is certified yet. This sheet is the plan: where accuracy stands, how the pipeline changes next, and below that the dated record of how it got here — which began on 2026-08-14 with the best local model at F1 0.693 and 30 % of municipalities exactly right.

Companion to LEXIKON_1910.md (what the volume is and how it is read) and VALIDATION.md (the register of checks and their blind spots). Evidence lives in output/lexikon_1910_pilot/bakeoff/; the bake-off itself is notebooks/05_ocr_model_bakeoff.ipynb.


Revision, 2026-10-15 — the crops come from the page image

The pilot's default geometry is now the page image's, on the author's decision before the r5 re-read. Nothing was sent to a model. Figures are printed by measure_pilot_geometry.py, measure_running_heads.py and the pilot's own table pass. Logged as D-83 to D-87.

What the reader is shown. The apparatus is cut on the image's columns, foot and headings; a crop keeps the text layer's label where both found the heading, a text-layer heading the image missed stays a boundary, and the 38 headings only the image sees on the Czech table scans are read once in the pass before their section is labelled. The two geometries cut the same label sequence on 464 of 476 pages; the image's crops leave 76 specks and blots outside them and no printed line. The postal legend is read from the image as its own crop. A gold pass is 98 crops against 97.

What a pass now checks about itself. The crop audit sets the image's printed-line count against the lines returned, and any heading the reader writes out against the crop's label; on 101 sections of 30 pages the line count agrees with the text layer's at a median ratio of 1.00, so a flag means something.

Smaller fixes. The table ends at the rule foot on 0369 and 0472, where the body band overshot; a municipality split by a page break is graded as one (90 blocks); and a head that names an okres the authority lacks is unresolved rather than given the previous page's, with Prague and Liberec read from their heads. Loket and Kralupy nad Vltavou, which the register prints as okresy, resolve to the 1910 okres that held them (Falknov, Slaný): both were created in 1913, and the register draws districts as at end-1914 (D-88).

The r5 re-read is nine Gemini passes on Heavyhorse6 — 3.7 and 3.8 on the image geometry, and 3.8 on the old text-layer geometry as arm G, so the switch is measured on the gold rather than assumed.

Revision, 2026-10-14 — every printed line is inside a crop

A read-only audit of what the crops miss, and the fixes. Nothing was sent to a model and no gold sheet was edited. Figures are printed by measure_apparatus_coverage.py, measure_table_foot.py, measure_table_top.py, measure_running_heads.py, measure_image_geometry.py and measure_ink_accounting.py, run on ZenBookTH. Logged as D-76 to D-82.

What the crops missed

D-75 closed the gap under a column's last crop. The audit looked at the rest of the apparatus and found four more ways print escaped every crop. 22 pages were never read at all: the body band ran on into the apparatus, so the printed divider was rejected and the page was treated as having none. Lines between two sections fell outside both crops when the text layer dropped them (scan 0386, two Votice entries). Table rows sat inside the band on 227 pages and were sent to the reader as a carried offices section. And a heading the text layer missed altogether left its section filed under the one before it.

The fixes

lexikon_1910_notes 0.8.0 takes the divider by a fallback where the band hid it, opens the band at the foot of the table's own column rules, and tiles each column so every section runs to the next. Over table scans 15–494 that recovers the 22 pages (1,175 lines, 470 opening with an entry key, 68 headings), moves 318 table-row lines out of the apparatus with no entry key or heading among them, and puts 25 printed lines on 18 pages inside a crop for the first time. The table channel follows: lexikon_1910 0.2.0 ends the rows where the apparatus opens and 0.4.0 opens them under the header's closing rule — 137 + 233 rows that were never read, the purposive gold tables unchanged row for row. The okres comes from the running head through a folded matcher (24 pages change, 19 of them checked right; lexikon_1910_names 0.3.0). Both table fixes and the matcher are the default on the author's decision of 2026-10-14.

What stays open

Seven headings the text layer never saw (scans 0090, 0141, 0190, 0241, 0304, 0384, 0456 — all Části osad) are inside crops but still mislabelled; they wait for the author's ruling, and the image geometry of D-80 finds them and 34 more. The stored passes are stale: 48 of the 84 gold-pass crops change, so the bake-off moves to r5 and the gold is read again on Heavyhorse6 (≈ $3) once the transcription is finished (D-81). Any volume: profiles for four volumes and a page geometry read from pixels, with an ink account that lists any printed line outside every crop, are built and opt-in (D-80); on the image crops the only real print missed in four volumes is two lines in German Bohemia. The reader-side half — the reader reporting the headings and the column header it sees, and a printed-line count against the lines it returns — is designed, not built. Which channel to trust: on the purposive gold the whole-page vision read gets 99.1 % of the table's number cells right against 63 % for the text layer, and the post and station keys 100 % and 98 % against 39 % and 15 %; one run, four pages.

Revision, 2026-10-13 (later) — a section crop stopped short of the foot of its column

The last crop of an apparatus column ended at the last line the text layer recognised, so a printed line the engine dropped was outside the reader's crop and outside the gold crop alike. It was found collating the calibration sheets against the print. This revision fixes the geometry and counts how often it mattered. Nothing was sent to a model, nothing was spent, and no gold sheet was edited. Figures are printed by utils/lexikon_1910/_archive/measure_segment_foot.py and kept per page and column in output/lexikon_1910_pilot/segment_foot_gap.csv, as run 2026-10-13 on ZenBookTH (lexikon_1910_notes 0.7.0). Logged as D-75.

The defect, and the fix

A section is cut into one segment per column it occupies, and a segment's box was built from the lines the ALTO clustered: top of the first, bottom of the last. A short line that is only a key and a pictogram is the line the engine drops, and when it is the last line of a column nothing below it is recognised either, so the box ends above it. Scan 0076 prints 22, 3) [fire-brigade]. 39 px below the last recognised line of its right column; scan 0316 prints the lines for keys 35 and 36 in the 106 px under its own.

The reader and the transcriber were handed the same box, so neither could report the line and the two agreed on its absence. No comparison of a pass with the gold, and no comparison of two passes, can show a line that is on neither image.

From 0.7.0 the last segment of each column ends at the foot of the column — the divider rule's foot, which notes_boxes already takes from the printed rule and which is where the footer band opens, so the postal legend stays out. An earlier segment still ends where the next heading begins, which is a recognised line by construction. section_segments(..., extend_to_foot=False) is the old geometry. The extension is unconditional: the function is handed the text layer and no image, and the text layer is the channel that failed.

How often it mattered

Over the table scans 15–494: 454 pages with a section segment, 908 columns.

gap, old bottom to column foot columns
0 px or less 574
1–6 px 265
7–17 px 46
18 px or more — enough to hold a line 23

All 23 were read by eye off segment_foot_gap_strips.png (by Claude; the verdicts are the dated table EYE in the script, and the sheet is there for the author to rule on). 13 columns on 13 pages had print below the old crop, every one an offices section, 10 in the right column and 3 in the left:

scans (key)
12 whole entries 0076 (22, 3) · 0088 (29) · 0233 (9, 2) · 0254 (21) · 0269 (19) · 0316 (35 and 36) · 0322 (11) · 0350 (35) · 0427 (5) · 0463 (3) · 0473 (2)
2 wrapped tails 0368 — two marks closing entry 29; 0485 — one mark closing entry 19

The loss fell on one mark. Each of the twelve entries is a key and the same single pictogram as scan 0076's, and nothing else. It is the shortest line the register prints, the kind the engine drops, and it means the defect undercounted one mark and no other.

The scored pages are not touched. On scans 0118, 0270, 0450 and 0451 the largest gap is 2 px, so nothing was cut there and the r4 figures stand as they are. Of the nine other gold pages, 0076 and 0316 lost the three entries above; they are calibration sheets, not yet scored.

The screen against the eye

The script also looks for print automatically: the gap is thresholded at grey 95, inset 45 px from the column edges and 6 rows from the top, its dark pixels are summed by row, and a band at least 12 rows tall carrying at least 70 dark pixels is a line. The thresholds are read off the two known cases — one line of 114 px on scan 0076, two of 145 and 172 px on scan 0316 — and were not moved after the eye check.

columns
print by eye 13
of those, flagged by the screen 10
missed — the same one-mark line, printed faintly (scans 0350, 0427) and a one-mark tail (0485), 50–65 px 3
flagged with nothing printed — 91 px of specks over 50 rows (scan 0165) 1

Ink at one grey threshold does not separate a faint line from specks, so the screen is reported as it is and the count of entries is the eye's.

What it costs: the cached passes

The crop cache keys on the crop's bytes and on its part i of n caption, so any gap above zero moves the last crop of that column, print or none: 334 columns on 260 pages (228 offices, 106 parts).

a gold pass (calibration + purposive, both sections) crops match no cached entry scans
--notes-max-height 500 81 6 0076 (1), 0316 (2), 0376 (1), 0118 (1), 0449 (1)
--notes-max-height 250, the half variants 138 9 0076 (2), 0316 (4), 0376 (1), 0118 (1), 0449 (1)

Scans 0076 and 0316 change because print came into the crop; 0376, 0118 and 0449 by 2–8 px of blank paper. Each label that is replayed reads those crops again, on Heavyhorse6 (D-68). Nothing was re-read.

Blind to

  • Print below the divider's own foot. It lies in the footer band, not in this gap. A recognised line ends 11 px below the foot on scan 0076 and 6 px on scan 0316, so the columns do run a little past the rule.
  • A line dropped between two recognised ones. It is inside the crop under either geometry, and nothing here counts such lines.
  • A gap under 18 px, which was not read by eye. The screen flags none of them.
  • The certification pages. Four of their crops change by 1–4 px; the screen passes them and nobody looked.
  • Scans 495–756, which no pass reads, and the German edition.
  • The verdicts are one reading. The 23 strips were read once, by Claude.

What is and is not built

state
The geometry (section_segments, extend_to_foot) built, lexikon_1910_notes 0.7.0; 6 tests (tests/test_lexikon_1910_segment_foot.py)
The measurement, its CSV and its strip sheet built, utils/lexikon_1910/_archive/measure_segment_foot.py; fails if it does not reproduce the two known cases or meets a gap of 18 px it has no verdict for
The gold crops of scans 0076 and 0316 re-cut 2026-10-13; no sheet changed
Keys 22, 3 on sheet 0076 and 35, 36 on sheet 0316 not transcribed — the author's, from the re-cut crops
The stale crops of the cached passes not re-read; 6 crops a label
A switch for the old geometry in the pilot runner not built — a replay under 0.7.0 issues the changed crops as new requests

Revision, 2026-10-13 — the printed order of the marks, as a routing signal

The marks inside an offices entry stand in a fixed order, and a mark that stands where the print never sets it can be found without asking a model. This revision builds that check and scores it on the stored r4 passes. Nothing was sent to a model, nothing was spent, and no gold sheet was edited. Figures are read off output/lexikon_1910_pilot/bakeoff/rescore_r4_report.txt and the three rescore_r4_order*.csv files beside it, as written 2026-10-13 13:00 on ZenBookTH (rescore_passes.py 1.3.0, lexikon_1910_institutions 0.9.0). The check is wired into scoring only: nothing in the reading pipeline calls it yet. Logged as D-73; its place in the route, decided the same day, as D-74.

The rule, and where it comes from

The volume states the order by example. Its Návod (p. VIII) reads the entry of Bílina out in full: offices and courts; the revenue marks; gendarmes and the transport station; the churches; the schools; kindergarten, welfare, hospital, pharmacy, doctors, vet, midwives, fire brigade; then savings bank and the cooperative with its letters; then post, telegraph, telephone and railway. On the pages a castle and a ruin come last.

The check does not type that list in. It learns pairwise precedence from the gold sheets of the purposive and calibration roles — every filled line of section 2b, whatever the sheet's status, read by read_transcription_md.read_order_reference — and judges a pair only if the sheets print it one way at least twice and never the other (ORDER_MIN_SEEN = 2). The certification sheets are not read: the function refuses that role. Bracketed symbols only; abbreviations are decoded out of running words and hold no position.

the reference, 2026-10-13
sheets with filled offices lines 10 (five purposive, five calibration)
runs of marks (a numbered part, cut again at a named part such as Valdice:) 182
symbols 818
pair observations, all symbols 3,478 over 660 pairs; 72 against the majority direction
the same without [gendarme], [canteen], [transport-station] 2,902 over 552 pairs; 0 against

1. The order is exact on the ten sheets once three marks are set aside. All 72 contrary observations involve the gendarme, the canteen or the transport station. That block has two printed positions — after the offices at a court seat or beside state authorities, after the fire brigade elsewhere — and the check never judges it (ORDER_FLOATING).

2. The order restarts. At each numbered part, and at a named part inside an entry (Kartouzy: … . Valdice: [school] III …). Text in parentheses is left out.

Two verdicts

  • out_of_place — the smallest set of marks whose removal leaves no reversed pair. A castle read as a poorhouse at the end of a line stands after the post, the telegraph and the station, all printed after a poorhouse: one mark, in every reversed pair. A symbol question.
  • order_restarts — every reversed pair runs across one point: the marks before it are in order, the marks after it are in order, and only the two groups are out of order with each other, with at least two marks out of place (ORDER_RESTART_MIN). That is two parts read as one: a — 2) lost, two printed lines joined, a named part's colon dropped. A parsing question, and reported as one.

What it flags on the stored passes

Each page judged on the order learned from the other pages. Settlement level, against the 372-claim gold on four pages; the three repeats in order.

reader false claims of them flagged true claims of them flagged
Gemini 3.8, thinking off (reader of record) 10 / 10 / 11 1 / 1 / 1 361 / 363 / 361 0 / 0 / 0
Gemini 3.7, thinking off 11 / 11 / 12 1 / 1 / 1 360 / 361 / 363 0 / 0 / 0
haiku55nh 9 / 13 / 14 1 / 5 / 4 362 / 353 / 356 1 / 0 / 0
haiku55n 9 / 17 / 19 2 / 4 / 4 340 / 352 / 348 0 / 0 / 0
haiku55t 15 / 14 / 20 4 / 4 / 2 354 / 356 / 349 0 / 0 / 0
haiku55, thinking off 79 / 74 / 83 6 / 4 / 8 156 / 155 / 198 1 / 1 / 4

3. It catches little, and what it catches is false. One of a Gemini pass's 10–12 false claims, 1–5 of haiku55nh's 9–14; of about 360 true claims a pass it flags none for Gemini and one in one Haiku repeat. Most of what the readers still get wrong it cannot see — see Blind to.

4. Gemini's one is the castle. In all six Gemini passes the flagged false claim is the [poorhouse] at Trutnov 23, part 5 (scan 0450), a castle read as a poorhouse. Haiku's are the same pair the other way round: a [castle] written mid-line at Jirkov 9, Trutnov 17, Úpice 9 and Česká Skalice 3.

5. On a disputed claim a flag has not yet been wrong. Gemini 3.8 and haiku55nh dispute 42.3 claims a pair of passes, 19.7 of them false. The order flags 3.7 of them — 0.7 on a claim Gemini made, 3.0 on one Haiku made — and all 3.7 are false. Over the 105 pairs among the fifteen passes of the five readers that read at 0.94 or better, it flags 262 disputed claims and all 262 are false. With thinking-off haiku55 in the pair (48 pairs) 369 of 377 are.

6. It reaches an error both readers make. Two Gemini passes agree on 8–9 false claims, which the pair accepts unseen; the order flags 1.0 of them (the Trutnov poorhouse again). For Gemini 3.8 with haiku55nh: 1.3 agreed false, 0.3 flagged. It is the first signal here that does not depend on the readers disagreeing.

7. On the nine pages outside the scored gold it flags far more, nearly all one confusion. A Gemini pass has 17–21 marks flagged over the 13 pages, and 11–14 of them are a [poorhouse] standing last in its line. Where a calibration sheet transcribes the part (6–8 of them a pass), the sheet prints [castle] there every time. Counted at claim level: 16–20 flagged claims a pass outside the scored gold, 11–14 on a transcribed part, 4–6 of which the sheet does not have. Weaker evidence than the table above: those sheets are single-coded, two are still drafts, and they are the sheets the order was learned from (each page is judged on the others).

8. A missed boundary shows more often than not, and is rarely called one. Running two adjacent parts of the gold together, 28 of 35 joins leave a mark out of place; 7 get the order_restarts verdict. In 24 of the 35 the shorter part is a single mark, which reads as that mark out of place and cannot be told from a misread one.

9. The restarts in the stored passes: one the scoring path's own, one not a restart. Fifteen of the eighteen passes get exactly one order_restarts. In thirteen it is Aš 1 (scan 0019), where the named part Forst: opens with words (Forst: hlavní celní úř. II, král. bavorský [customs] …). A stored pass keeps each mark's twelve characters of context and not the entry's text, so the name is out of the first mark's sight and the two parts are judged as one. With the text the cut is made; order_runs takes either. In the other two, both thinking-off haiku55, it is Jirkov 9 (scan 0118): the reader wrote [customs] and [gauging] after the fire brigade, where the print has the gendarme and the canteen. Two neighbouring marks, both misread as marks that belong earlier, are the same picture as a second part, and the verdict cannot tell them apart.

Blind to

  • A same-slot substitution, which is most of the residue: railway station for stop, revenue office for customs, one church for another, a savings bank written directly before the cooperative. The wrong mark stands where the right one would.
  • Which of two. One reversed pair says one of the two marks is wrong, not which. The later one is returned and both are shown (against). The one true claim flagged above is this: a [telephone] at Úpice 9, reversed with a false [steamer] before it.
  • A missing mark, which reverses nothing — so in a dispute the check can only ever speak against the reader that wrote the claim.
  • The gendarme block, wherever it stands.
  • A pair the sheets show fewer than twice. The reference is ten pages. One, two or three sightings flag the same false claims on the scored pages, bar one more at one sighting in five of the six Gemini passes; four pages do not decide the constant.
  • The German edition. The order is learned from the Czech sheets only.
  • A mark flagged beside its twin. A second [poorhouse] at the end of a line whose part does have a poorhouse counts, at claim level, as a true claim flagged.

What the check found in the gold, and where the figures differ from the first probe

The scratch probe that prompted this (2026-10-13, 12:32–12:35) reported 815 symbols and 2,863 pair observations outside the gendarme block, 9 of them against the majority direction — all from two calibration entries. The author ruled on both by eye the same day and both were slips of the sheet, now corrected:

sheet entry was is
0136 Jičín 19 [rail-stop] 302 m, … [cooperative]: R. [rc-filial] 302 m, [school] III, [fire-brigade], 3 [gendarme], [cooperative]: R; [post].
0196 Nusle 4, part 2 [poorhouse] after the telephone [castle]

Three more cells of the same two sheets were corrected on his ruling that day, none of them found by the order: two misspelt tokens the sheet reader had been dropping as off-atlas ([coopearative] at 0196 Nusle 1, [ev-preahcer-hb] at 0136 Jičín 12), and a [solitary] in the institution list at 0136 Jičín 3, part 2, which is [ruin] Pařez. The figures in this revision are on the sheets after all five (0196.md saved 12:40, 0136.md 12:43): 818 symbols, 0 of 2,902, 28 of 35 joins where the probe had 27, and no gold line that the other pages' order flags. The per-pass and dispute figures are the same before and after.

The order's place in the route (D-74)

Decided the same day, by the author, after the check was scored. A design: the order-triggered call is not built and has never been run. Where this contradicts the route of 2026-10-08 below, this governs. Logged as D-74.

step what over state
1 Both readers read every crop blind: the order is not in the prompt, whole or as a cue for one pair the volume as the r4 passes were read
2 The order check over each reader's output. out_of_place: one verify call on that glyph — enlarged, a closed choice among the marks the sheets print in that position and the token the reader wrote. order_restarts: the parsing queue, no call every part check built (D-73); the call to build, and to test first
3 What the two readers still disagree on: the verify call of D-72 the disputed claims left to build; waits on test B
4 The author, by eye, on what the calls leave open — and on the disputes themselves if they number few enough the remainder —

The encoder of D-72 (its stage 2) is not touched by this and still waits on test A.

Why blind. The order is an invariant the reader is graded on, and a reader is never shown one (D-38). Stated in the prompt it would stop being evidence: a reader told the order writes to it where it has misread, may add the mark it expects — it already writes a [savings-bank] before a [cooperative] that has none — and may regularise the gendarme block. So the prompt arm of E2 is closed, not open.

Why a re-read and not a rule. The order says a mark is misplaced, not what it is. A rule exists for one pair: a [poorhouse] standing last in its line is a [castle] on every part the calibration sheets cover (finding 7). For any other flagged mark there is none, and a claim changed without the image is the repair D-38 and D-55 refuse. The rule is the bar the call has to meet on that pair.

What reaches each step. A Gemini pass has 17–21 marks flagged over the 13 pages (finding 7); scaled by crops, 2,991 in the Czech apparatus against 81 here, that is roughly 630–780 flagged marks a reader over the volume, 11–14 in every 17–21 of them the poorhouse. Of the ≈ 5,100 disputes D-72 counted, the order takes about a tenth before step 3 (3.7 of 42.3 a pair of passes, finding 5). What is left is more than the author reads by eye unless the verify call thins it.

Blind to: the call has never read a crop and joins test B; nothing produces a box per glyph, so its first crop is the printed lines of the entry; the choice it is offered is learned from ten pages; where one reversed pair does not say which of two marks is wrong, both are re-read; the volume figures are scaled from 13 pages, four of them purposive, and counted for Gemini only.

What is and is not built

state
The check, as pure functions (learn_order, order_runs, out_of_order, order_restart, check_order) built, lexikon_1910_institutions 0.9.0; 17 tests
Scoring: per pass, per pair, per flagged mark built, rescore_passes.py 1.3.0; run_e1b_second_reader.py --score prints the pair table
A call from the reading pipeline not built. Its place in the route is settled (D-74, above): after the blind read and ahead of the readers' disagreement; a flagged mark is re-read, and no claim is changed without the image (D-38, D-55)
A pass writing its entries' text, so a stored pass is cut at every named part not built; needs a replay from the crop caches on Heavyhorse6 (D-68)
The order stated in the reader's prompt not done, and closed (D-74): the readers read blind, so the order stays evidence. A stated order may also encourage the phantom [savings-bank] the reader already writes before a [cooperative]
A mark printed twice in one part as a sign of a missed boundary not built. One of 182 gold runs repeats a mark (Aš 1, two railway stations)

Revision, 2026-10-08 — the second reader screened, and the route for a disputed claim

Two things. The E1b screen was run on Heavyhorse6 with Claude Haiku 5.5 — twelve passes, four settings, three repeats each, the 81 crops of the r4 baseline. Its figures are read off output/lexikon_1910_pilot/bakeoff/e1b_second_reader_r4.txt as written 2026-10-08 10:07 (run_e1b_second_reader.py 1.2.0). And the route a claim takes when the readers disagree is laid out. The second half is a design: nothing in it is built or measured. Where this contradicts the revision of 2026-10-06, this one governs. Logged as D-72.

The screen — nine in ten of Gemini's errors caught, at three times the alarms the bar allowed

The screen did not run as step 4 below had it. The two default candidates, Claude Haiku 4.5 and local qwen3.8, have not been run. Claude Haiku 5.5 was run instead, on the same key, at $0.10 / $0.50 per M tokens — a tenth of Haiku 4.5. And it ran ahead of step 3, so it is scored on the four gold pages and not on twelve.

Settlement level, against the 372-claim gold; the default reader (Gemini 3.8, thinking off) as first reader, means over the nine pairs of repeats:

second reader setting F1 on its own, three repeats caught shared alarms F1 after a perfect review
Gemini 3.7 — the bar (D-71) thinking off 0.969 / 0.970 / 0.972 6.2 (30 %) 14.4 7.6 0.981
haiku55 thinking off 0.533 / 0.533 / 0.620 10.4 (52 %) 9.7 253.0 0.986
haiku55t thinking, medium effort 0.955 / 0.960 / 0.942 15.4 (75 %) 5.2 30.1 0.993
haiku55n the same, with the prompt's note for a reader that thinks 0.943 / 0.950 / 0.942 13.2 (64 %) 7.4 32.9 0.990
haiku55nh the note, high effort 0.974 / 0.957 / 0.960 18.0 (87 %) 2.7 24.3 0.996

1. The catch half of the bar is cleared and the alarm half is not. D-71 asked for a catch rate above 0.30 at no more than about eight alarms a pass. haiku55nh catches 87 % of the default reader's 20.7 errors and leaves 2.7 that the pair cannot see, where a second Gemini leaves 14 — and it raises 24 alarms, three times the limit. The limit was set by what a second Gemini does; a second reader this much better at catching was not what it was written for. What the alarms cost is the subject of the next section.

2. Thinking is what makes Haiku 5.5 a reader of these crops — the reverse of Gemini, where thinking bought nothing. Asked off, it reads at 0.56 and raises 253 alarms; with it, 0.94–0.97.

3. The shared blind spot is no longer visible in the test, which is weak. With haiku55nh the two readers both miss 1–2 of the 372 gold claims where 0.3–0.6 would be expected (p = 0.10–0.39 on the three pairs with the first Gemini repeat); with a second Gemini they both miss 4–7 (p < 0.0001). A large p on about ten misses is not independence.

4. One claim in nine is disputed, on the random pages as on the purposive ones. Disagreement needs no gold, so it can be counted on all 13 pages: Gemini 3.8 and haiku55nh differ on 137.7 of 1,240 claims a pass (11.1 %; 123–150 over the nine pairs) — 10.1 % on the four gold pages, 11.7 % on the nine calibration pages. Scaled by crops (2,991 in the Czech apparatus against 81 here) that is roughly 46,000 claims and 5,100 disputed over the volume. Counted 2026-10-08 from the stored inventories, keyed on page, district, municipality, part and symbol, presence only; not yet a column of --score.

5. In a dispute Gemini is right a little more often than not. Of the ≈ 42 disputed claims a pass on the gold (18.0 caught and 24.3 alarms), the default reader has 24.3 right: 57 %. That is the figure anything asked to settle a dispute has to beat.

6. Cost. A haiku55nh pass over the 13 pages is ≈ $0.13–0.16 and the Czech apparatus ≈ $5–6, about what Gemini costs; the twelve passes of the screen came to ≈ $1.0–1.4. Estimates: the token ledger in heavyhorse6/timings.json at the runner's list price, the lower figure with cache reads at a tenth of the input price.

Blind to: the rates rest on about 21 errors on four purposive pages, and the alarms of haiku55nh run from 17 to 29 between repeats; Haiku 5.5 refuses a temperature other than its default, so its repeats carry its own run-to-run spread; haiku55h — high effort without the note — is on file and unrun, so what the note does at high effort is not separated from what the effort does; and Haiku 4.5 and qwen3.8 are unread.

The route for a disputed claim (D-72)

stage what over state
1 Two readers of different families read every crop: Gemini 3.8 Flash, thinking off (the reader of record), and Claude Haiku 5.5, thinking at high effort with the note the volume Gemini frozen but for E2; Haiku provisional until re-scored on twelve pages
2 A box per glyph, and DINOv3 nearest-neighbour names every boxed glyph (D-69) the volume to build; waits on test A
3 Routing: the three agree — accepted. Any disagreement — stage 4 every claim to build
4 One verify call on Claude Opus 5.5: the glyph enlarged, a closed choice between the tokens the readers and the encoder gave, beside the page-cut exemplars the encoder retrieved ≈ 5,100 claims, more with the encoder's own disagreements to build; waits on test B
5 What Opus leaves open goes to the author, by eye, the enlarged print beside the plate the remainder —
6 The certification sample measures what is left (D-55) sealed pages waits on the freeze

Why the encoder reads everything and not only the disputed claims. Stages 1 and 3 cannot see an error both readers make; on the gold that is 2.7 a pass, some 300 over the volume. The encoder is the only piece here that shares neither architecture nor training with a language model, it costs nothing, and it gives the same glyph the same answer every time (D-68).

Why Opus, and only here. A whole volume on Opus 5.5 was priced at $160–340 and not pursued (2026-09-22). About 5,100 small calls are ≈ $30–90 on batch, from that revision's own line for verify calls ($5–16 for 900).

Three rules.

  • Every stage routes; none overwrites (D-38, D-55). A verify call that sides with one reader accepts that reader's claim, at an error rate measured on the gold among the claims so accepted. It never writes a claim no reader made.
  • The routing is code, not a model deciding in the loop. Which reader is called, on what, and when to stop is a fixed function of the readers' outputs, in a notebook, tuned on calibration pages, frozen, and run on Heavyhorse6 (D-68). A model that chooses its own next call is a degree of freedom inside the measurement, and it cannot be replayed.
  • Local language models are not the arbiter. Re-reading the disputed sections with them, repeatedly, was considered and not adopted: the best local pass stood at 0.872 (the r2 bake-off, old decoder and gold) and none has been read under r4; Ollama at temperature 0 returns the same answer each time, and correlated repeats bought +0.04 where they were tried; the plate atlas hurt three of five of them; and what is left to settle is fill pairs, the documented blind spot of the image encoders these models read through (Tong et al. 2024). qwen3.8 stays in test B, because it costs nothing to ask.

Two tests come first. Both are nearly free, and either can stop its half.

  • A — does an image encoder separate the fill pairs at all? Castle / poorhouse, ruin / solitary house, railway station / stop, myslivna / hájovna, cut by hand from the gold and calibration pages at printed size; DINOv3 and DINOv2 nearest-neighbour, each page classified from a bank that leaves it out. If the pairs do not separate, there is no whole-volume encoder pass and no box detector is built for one. No spend.
  • B — is Opus an arbiter, or a second Haiku? The verify call on the gold's disputed claims, ≈ 42 a pass, against the 57 % of always siding with Gemini; qwen3.8 on the same call beside it. Opus is Haiku's family, and one of the two readers in every dispute is Haiku: if its verdicts lean to Haiku on the claims Haiku had wrong, it is not arbitrating. Under a dollar. The bar is written down before the run.

What both wait on is where a glyph is. Nothing produces a box per glyph. Boxes taken from Gemini's own output let the encoder check the identity of a glyph Gemini found and never show it one Gemini missed — enough for the stable substitutions, which are most of the residue, and not for omissions. The first verify crop can be coarser: the printed lines of the disputed entry, from the text layer's line boxes.

Order of work, as amended

step what spend
1 ✅, 2 ✅ The r4 baseline; attributes and named parts scored —
3 — now Transcription of the notes on the eight calibration pages, then GB Trautenau in German. Unchanged the author's hours
4 — run 2026-10-08, ahead of 3 E1b on Claude Haiku 5.5. To be re-scored on twelve pages when 3 is done; haiku55h, Haiku 4.5 and qwen3.8 unrun ≈ $1.0–1.4 spent
5a Test A — the fill pairs on DINOv3 and DINOv2 none
5b Test B — the Opus verify call on the gold's disputed claims, qwen3.8 beside it under $1
5c If A holds: a box per glyph, the exemplar bank, the encoder over the 13 pages. Contrast pairs for the first reader (E2) cents a pass
6 Freeze; read the Czech volume once: both readers, the encoder, the verify calls, the review queue ≈ $6 + $5–6 + $30–90, and the author's hours on what Opus leaves open
— The German reader, alongside (D-58) —

What this revision is blind to

  • The design is unmeasured from stage 2 on. Opus has never read one of these crops; whether line-art embeddings separate an outline from a solid is untested; the licence of the DINOv3 weights is unread (D-69).
  • A third signal on every claim adds alarms, and how many is unknown until the encoder has read the 13 pages.
  • The author's hours. What stage 5 receives depends on how many of ≈ 5,100 disputes Opus settles at an acceptable error rate, and that is test B.
  • The volume figures are scaled by crop count from 13 pages, 4 of them purposive.
  • The choice of second reader rests on four pages — the caution of 2026-10-06 that a reader chosen on about twenty errors is chosen on noise still stands, and step 3 is what answers it.

Revision, 2026-10-06 — the baseline with every fix in: 0.972

The first pass read since the fixes of 2026-10-05, on Heavyhorse6 (D-68): both readers with thinking off, three repeats, the 13 gold-sweep pages, run tag r4 (notebooks/07_lexikon_1910_baseline.ipynb). Six passes, 81 of 81 crops each, ≈ $1.96 in all. Figures are read off output/lexikon_1910_pilot/bakeoff/rescore_r4_report.txt and heavyhorse6/timings.json. This is step 1 of the order of work below; the rest of the 2026-10-05 revision stands.

arm settlement F1, three repeats mean false claims, a pass missed, a pass misfiled
3.8, thinking off (default) 0.972 / 0.974 / 0.970 0.972 10.3 10.3 0
3.7, thinking off 0.969 / 0.970 / 0.972 0.970 11.3 10.7 0
the same two arms under r3 — 0.958 and 0.954 20.3 and 24.0 11.3 and 10.7 0

The heading rule did on a pass what it did on the text layer (D-66). No place-type symbol is filed as an institution any more, the false claims are halved, and scan 0451 — the page the damaged heading sat on — goes from 0.905 to 0.955–0.964 for the default reader. These are fresh readings, not a replay, so the step from r3 is the fix plus the reader's own run-to-run variation; the fix shows on its own in the claims that are gone.

What is left is the same few substitutions in every repeat. For the default reader, in all three repeats unless marked:

printed (gold) read as where
[castle] [poorhouse] Jirkov 6; Trutnov 23, part 5
[ruin] [solitary] Trutnov 30, part 1
[ev-preacher-ab] [rc-vicariate] Jirkov 13, part 2
[rail-station] [rail-stop] Úpice 1, part 1
[revenue-admin] [customs] Trutnov 29, part 3 (two of three)
[cooperative] alone [savings-bank] and [cooperative] Jirkov 1 part 1, Jirkov 3, Jirkov 4

Beside these, a [castle] and a [ruin] are missed in two repeats of three (Jirkov 1 part 3, Jirkov 3), and twelve claims are wrong in one repeat only. Three of the stable ones touch variables a regression will use: a railway station read as a stop, an evangelical preaching station read as a Catholic vicariate — the slip the first human reading made (D-65) — and a savings bank that is not there.

Before these become E2's targets, they are checked against the plate. A disagreement that is identical in three repeats is either a glyph the reader cannot tell apart or an error in the gold, and the gold has been the one at fault before. The entries of scan 0451 other than Trutnov 29 have been read by a person once only. Checked the same day: all five pairs were looked at against the plate, enlarged, and the gold stands in every one. The pairs are fill pairs — outline against solid (castle / poorhouse, ruin / solitary house, railway station / stop), a plain circle against a dotted one (revenue office / customs) — and one glyph in two parts taken for two glyphs (the cooperative).

Step 2 — what stands beside the glyph, and the named parts

Built and scored 2026-10-06 with no new request: the six r4 passes were replayed from their crop caches (inventories byte-identical to the ones Heavyhorse6 wrote). Figures from rescore_r4_attributes.csv, rescore_r4_named_parts.csv and rescore_r4_parts_symbols.csv. Logged as D-70.

A pass now writes two more files: attributes_<label>.csv, what it read beside the school and cooperative glyphs, and parts_<label>.csv, the named parts of Části osad with their place-type symbol and figures. Both are parsed by the code the gold sheets go through (lexikon_1910_attributes, lexikon_1910_notes.split_parts), so a difference between reader and gold is a difference in what was transcribed.

The attributes a regression will use are read right. On the entries of the four scored pages, in every one of the six passes:

attribute in the gold found count classes language
school ([school]) 56 56 56 56 56
burgher school (měšť. šk.) 5 5 5 5 5
cooperative kinds 55 55 (one pass: 50) 55 — 55

The one pass that found 50 kinds skipped a whole printed line of Trutnov 29 — a line dropped, not a letter misread. One count differed in every pass at first: Česká Skalice 3, where the print and the reader have 2 Sch and the sheet had Sch. That was the sheet's slip, corrected on the author's ruling the same day. The base under "language" is thin: the scored pages hold 16 cooperative kinds marked Czech and none marked German, and 3 schools with a language note, all three of them mixed (č. a něm.). That the reader copies the mark where there is one is shown; how it reads a cooperative's n is not.

The named parts are found, their figures are exact, and their symbols are the weak column. Of 175 named parts:

reader found houses and population place-type symbol agrees
3.8, thinking off (default) 175, 172, 175 every matched part, every pass 0.919–0.931
3.7, thinking off 175, 175, 175 every matched part, every pass 0.960–0.971

The three parts one pass does not find are three & residuals of scan 0451 whose ampersand it wrote as [?]; their figures are there and right.

The default reader's differences, three repeats pooled: [cottage-group] left unnamed 20 times (written ([?])), [huntsman] read as [gamekeeper] 9 times, [mesto] as [mestys] 3 times, and 7 others. The differences sit on 14 parts, 12 of them in all three repeats — they are stable, like the institution substitutions, so a second pass of the same reader will not find them.

The stable ones were put before the author, enlarged, beside the plate, and ruled on (2026-10-06):

what where ruling what it says about the reader
[huntsman] read as [gamekeeper] Jirkov 13 part 1 (Ochsenstall), Jirkov 15 (Töltscher), Trutnov 17 (Waldau) — both versions, all six passes huntsman in all three; the sheet stands the press, not the eye: the block under the antlers is meant solid and is mostly filled, but the ink leaves gaps inside it, and a block with holes reads as an outline. The plate's clean glyph is not what the page prints
[mesto] read as [mestys] Trutnov 17 (Pilnikov) — 3.8 only, all three repeats Pilnikov is město; Poříčí, two entries down, is městys; the sheet stands the reader gives the double circle and the circle-with-dot one name, two entries apart
[cottage-group] left unnamed seven parts, all in Trutnov 23 and 29 — 3.8 only cottage-group; the sheet stands the reader sees the parentheses — it writes ([?]) — and takes them for punctuation around a mark, not for part of the mark. 3.7 names six of the seven and calls the seventh [house-group]: the first measured reason to prefer 3.7 for anything
[scattered] read as [single-house] Trutnov 30 part 2 (Vlčice), the & — 3.7 in all three passes, 3.8 in two the readers are right: the print has one square. The sheet is corrected —

Two versions of one reader agreeing against the gold was the gold's fault once in four here — which is the reason not to settle such a case by counting votes.

What this hands to E2. Three more targets, each with a different remedy:

  • myslivna / hájovna joins the fill pairs — but its exemplars have to be cut from pages, not from the plate: a clean solid block teaches nothing about one the press left half-inked. The same holds for castle / poorhouse and ruin / solitary house, and it is the case for collecting a handful of printed instances of each fill-pair glyph.
  • the parenthesised marks need a rule in words before an exemplar: parentheses around squares belong to the glyph — a cross of four inside them is [cottage-group], a block of four inside them is [colony], a block of four with none is [house-group].
  • město / městys needs the pair shown side by side; Trutnov 17 and 19 are that pair, two entries apart on scan 0450.

Four parsing defects were found on the way, and every one had passed for a reader error. In each the reader had copied the print faithfully and the splitter did not expect what the print does:

what the print does what the reader wrote what the splitter made of it
the plate draws the cottage-group and colony marks inside parentheses Hammerwerkflur ([cottage-group]) 10—84 the token became part of the name
a full stop stands where the list's comma should (1—9. Stachelmühle) the same two parts glued into one
the residual is &, which the plate names mimo to [besides] [scattered] 2—7 a part named [besides], and the & "missed"
a speck after a mark (○.124—998, scan 0451) [ves].124—998 the token cut in half inside the name

The first two were 22 of the 23 parts the default reader seemed to miss (152 of 175 found before, 174 after; lexikon_1910_notes 0.6.0); the last two were every other "missed" part in the six passes but the three above (0.6.1). Not one named part on the four pages was missed by a reader. The rule this leaves for the whole-volume read: a part reported missing is a parsing question first.

Step 3 prepared — the bar a second reader has to clear

Measured 2026-10-06 on the six r4 passes, no request sent. Figures from e1b_second_reader_r4.txt (run_e1b_second_reader.py --score). Logged as D-71.

The second reader routes and never overwrites: a claim both readers make is accepted, a claim only one makes goes to review. So it is not chosen on its own accuracy. It is chosen on which of the first reader's errors it disagrees on — second_reader in lexikon_1910_institutions 0.8.0 counts, for a pair of readers against the gold:

  • caught — errors of the first that the second disagrees on; they reach review;
  • shared — errors both make; nothing in the pair shows them, and they are what a perfect review still leaves in the data;
  • alarms — claims the first had right and the second did not; review load for nothing.

What reading again with Gemini buys, the default reader (3.8, thinking off) as first:

second reader the first's errors caught shared alarms F1 → after a perfect review
the same reader, another repeat 20.7 5.3 (26 %) 15.3 5.3 0.972 → 0.979
Gemini 3.7, the other version 20.7 6.2 (30 %) 14.4 7.6 0.972 → 0.981

Means over the pairs with each of the three repeats (6 and 9 pairs); settlement level.

Seven in ten of the reader's errors are invisible to a second Gemini. The test D-58 asks for says the same thing from the other side: of the 372 claims the gold has, the two versions each miss 9–12 and miss the same 4–9, where 0.3 would be expected if their misses fell independently (Fisher's exact test, one-sided, p < 0.0001 on each of the nine pairs); and of the 10–12 false claims each makes, 8 or 9 are the same ones. That is the stable substitutions of the table above, counted.

So the bar for E1b is a catch rate above 0.30 at no more than about eight alarms a pass — and a reader that is worse than Gemini on its own can clear it, if it is wrong somewhere else. The ceiling such a pair reaches is the F1 after review; with a second Gemini it is 0.98.

The screen is ready and has not been run. notebooks/08_lexikon_1910_second_reader.ipynb on Heavyhorse6, SPEND = False as shipped. The two candidates that run by default need no new account — that was the author's condition (2026-10-06): no further key, no further billing, until a reader on hand has been shown not to do.

candidate reached through $ / M tokens in / out in the default set
Claude Haiku 4.5 (claude-haiku-4-5) the Anthropic key already on file 1.00 / 5.00 yes
qwen3.8:27b Ollama on Heavyhorse6 free yes
Qwen3-VL 235B Instruct (qwen/qwen3-vl-235b-a22b-instruct) OpenRouter — a new key 0.21 / 1.90 no
MiniMax M3 (minimax/minimax-m3) OpenRouter 0.30 / 1.20 no
Kimi K2.5 (moonshotai/kimi-k2.5) OpenRouter 0.45 / 2.25 no

OpenRouter's ids and prices as its own list has them on 2026-10-06; they supersede the list prices in the table of 2026-09-22 below.

One pass each. The Haiku pass is about $0.60 — an estimate: 5,900 tokens in and 380 out a crop over 81 crops gives $0.63, and the pilot's own dry run, from the crops' pixel sizes, $0.51. Before a hosted pass the runner sends one tiny request as the pass would send it, so a model or a parameter the endpoint refuses costs one request and not a pass. Blind to: Haiku on these crops at all — its only figure here is 0.573 on whole pages, and the API shrinks an image wider than 1,568 px, which these strips are, so its glyphs arrive smaller than Gemini's; whether OpenRouter honours reasoning_effort = none for MiniMax and Kimi; and four pages are a thin base for a rate built on twenty errors.


Revision, 2026-10-05 — the residue was the decoder and the gold; the reader stands at 0.958

Written after building the scorer the 2026-09-22 revision asked for, re-scoring every cached pass with it, and following what it showed. No new request was sent: the twelve E1 passes were replayed from their crop caches. Where this contradicts the revisions below, this one governs. Logged as D-63 to D-69. Every figure here is read off output/lexikon_1910_pilot/bakeoff/rescore_r3_report.txt and e1_gemini_version.txt as written on 2026-10-05.

Where the readers stand

Settlement-level F1 against the gold as it now is — 372 claims over 61 entries on scans 0118, 0270, 0450 and 0451 — three repeats an arm:

arm settlement F1, three repeats mean municipality F1 institutions misfiled, a pass
A — 3.7, thinking on 0.972 / 0.966 / 0.948 0.962 0.962 0
B — 3.8, thinking on 0.893 / 0.909 / 0.897 0.900 0.933 11
D — 3.7, thinking off 0.957 / 0.955 / 0.951 0.954 0.954 0
E — 3.8, thinking off (default) 0.961 / 0.955 / 0.958 0.958 0.959 0
C — 3.8, whole page (v2) five repeats, 0.812–0.892 0.851 0.851 —

These are not comparable with the 0.915 of 2026-09-23: the decoder and the gold both changed. On the gold of that date the decoder change alone takes the default reader from 0.915 to 0.961. The settlement figure now equals the municipality figure in every thinking-off pass, which is what "the part is filed correctly" looks like in the numbers.

Four findings

1. The undiagnosed ~0.05 was the decoder again (D-64). None of the three candidates named on 2026-09-23 was the cause. Read off the image of scan 0118: the register prints the dash of — 2) only when a part runs on inside a line. A part that opens a new line is set as a bare 2) under the 1), with no dash, and _PART_CONT_RE — fitted to the text layer's dashed form — never split on it. 207 of the 211 misfiled institutions in the twelve passes sat in three entries (Jirkov 13, Trutnov 23 and 29). lexikon_1910_institutions 0.7.0 splits on the line-opening form when the number rises, and tolerates a speck before it (scan 0118 has a spot of ink there; one pass transcribed it as °2)). Arm B still misfiles Jirkov 13, because 3.8 with thinking on writes that speck as *2) and an asterisk is refused.

2. The gold was the first reading, and it had its own errors (D-65). inventory_human.csv was still the August free-form worksheet — 286 claims over 47 entries — a month after the whole-page sheets for the same pages were finished, because two scripts wrote the one filename. The two readings of the same 47 entries agree at settlement F1 0.977: the gold's disagreement with itself was the size of the reader's remaining error. Seven claims were settled against the legend plate. The arch-and-cross followed by A is [ev-preacher-ab] (Jirkov 9 and 13, Česká Skalice 3); the first reading had called it [rc-vicariate] and the second [ev-parish-ab] twice. So the reader's "missed [rc-vicariate], 0 of 3" was the gold's error, and the reader had been right. The scored gold is now the sheets, scan 0449 held out as the few-shot page.

3. A damaged heading files a whole section under its neighbour (D-66). On scan 0451 the text layer reads Části osad v soudním otřesu Trutnov, the heading test needs the word okres, and the parts section was carried into Trutnov's offices section from scan 0450. The reader was given the offices vocabulary for it, and its place-type symbols were filed as institutions: about 13 of the default reader's 18–22 false claims a pass. 34 of the volume's 1,238 headings are damaged the same way (26 parts, 8 offices). The heading test now accepts them — over all 99,240 text-layer lines it gains those 34, changes none and loses none — but no pass has been read since, because the prompt differs by section kind.

4. A crop cache belongs to the machine that made it (D-68). The cache key hashes the crop's PNG bytes. On a second PC 26 of 81 crops came out with different pixels, so a third of the cached answers were not found. The replay here matched those crops to their answers by position and was accepted only because the old decoder then reproduced all twelve stored inventories byte for byte. Paid passes and replays run on Heavyhorse6 from now on.

Where the default reader errs now

Three repeats pooled, place-type symbols from finding 3 left out:

glyph printed, three repeats found false claims
[castle] 15 4 0
[ruin] 12 2 0
[poorhouse] 15 15 6
[customs] 6 5 4
[revenue-admin] 6 3 1
[ev-preacher-ab] 9 7 0
[rc-parish] 48 48 2
[rail-station] 36 35 0
[revenue-guard] 15 14 0

Seven rarer glyphs carry one or two errors each over the three repeats: [baths], [sanatorium], [rc-parish-exp], [rc-vicariate], [rc-pilgrim-chapel], [rc-pilgrim-church] and [rail-stop].

Every glyph that will enter a regression and occurs at all — [school], [fire-brigade], [cooperative], [midwife], [post], [doctor], [telegraph], [telephone], [gendarme], [rc-filial] — is found every time it is printed, with no false claim. The errors that remain are the rare glyphs and the fill pair ([castle] read as [poorhouse]), which is the input E2 was already waiting for. They matter for a published dataset even where no regression uses them.

How the pipeline changes

Stage by stage, for the Czech edition's symbols: what the pipeline does today, what changes, and whether the change exists yet. The last column is kept current — as of 2026-10-08.

stage today what changes state
1 — cut the page into sections headings found in the text layer a heading whose okresu is damaged opens its section (D-66); and a new check, that the heading the reader transcribes names the kind its crop was read as rule in force since the r4 baseline; check to build
2 — cut sections into crops strips of 500 px, enlarged, the legend plate sent with each nothing in the cut; every pass and replay runs on Heavyhorse6 (D-68) in force
3 — read a crop Gemini 3.8 Flash, thinking off, one section a call it stays the reader of record; a cheap reader of another family is screened on the same crops (E1b); contrast pairs for the confusable glyphs (E2); a box per glyph (E3) baseline read (0.972); the screen run 2026-10-08 on Claude Haiku 5.5 — 87 % of the default reader's errors caught, 24 alarms a pass, on four pages; to be re-scored on twelve (D-72); E2 and E3 to build
4 — decode the reading entries, then parts, then symbols both printed forms of the part marker split (D-64); the parts-section symbols written out as an inventory of their own; school classes and language, cooperative type and language parsed from the text beside the glyph done (D-64, D-70)
5 — second look none each boxed glyph classified by nearest neighbour on an image encoder (D-69); where it and the reader disagree, a zoom-verify call; what is still open goes to a person to build; laid out 2026-10-08 (D-72): the encoder over every glyph, Opus 5.5 as the verify call, and two tests before either
6 — score (entry, part, symbol) against the whole-page sheets (D-63, D-65) the same scorer over the parts symbols and the attributes; specificity over every part, not only the parts that carry a symbol parts symbols and attributes scored (D-70); what a second reader brings to review measured (D-71); specificity over every part to build
7 — check the whole volume the table's own sums and the census structure (kind against heading, rising part numbers, every key has a table row); the parts' figures against the table; Czech against German; outside knowledge, such as a district seat having a court to build
8 — certify nothing is certified an entry-level sample on the sealed pages, drawn after the freeze, and estimates through DSL / PPI (D-55) waits on the freeze
the German edition no reader sections cut from the image, a reader of its own and a gold of its own (D-58, D-67) seven sheets cut, none read; reader not started

What the rows leave out:

  • The scorer exists (D-63). score_against_reference reports the settlement figure, the printed count, per-glyph sensitivity and specificity, a page-clustered interval and the list of misfiled parts. Specificity is a lower bound until the parts ledger supplies the units that carry no symbol.
  • The gold is read differently (D-67). The German edition gets its own gold — Mähren and Schlesien exist only in German, so the German reader cannot borrow the Czech one — matched by court district and municipality name, never by page. Notes are transcribed before tables. The queue is places/transcription_gold/README.md.
  • ~~The parts-section symbols become an output. The reader reads them and the sheets hold 268 gold rows, but a pass writes only the parts check (footnote_parts_*.csv), not an inventory, and nothing scores one.~~ Done 2026-10-06 — see Step 2 above.
  • ~~What a regression will use is not yet scored. The school's class numeral and language note and the cooperative's type and language suffix are text beside the glyph. The reader transcribes them (5 [school] (1 č., 4 něm.) XXVI, [cooperative]: Rč); the scorer sees only the glyph.~~ Done 2026-10-06 — see Step 2 above.
  • The second reader is still E1b, and moves up: a full-volume pass by Opus stays out of reach on cost, so the second family is a cheap reader chosen by the screen, with a costly model looking only at single glyphs two readers disagree on.

E3's second method: nearest neighbours on an image encoder (D-69)

The 2026-09-17 revision asked for "a second method, not only a second model", and the 2026-09-22 one for a non-CLIP nearest-neighbour check. It is named now: DINOv3, a self-supervised image encoder, with DINOv2 run beside it as the comparison.

  • What it does. Each boxed glyph is cropped, enlarged to the encoder's input size and embedded, then classified by its nearest neighbours in a bank of labelled glyphs. The bank is cut from entries, at printed size — never from the legend plate, whose glyphs are set larger and against which template matching topped out at 0.644 (LEXIKON_1910.md).
  • Why this method. It shares neither architecture nor training signal with a language model, and shared architecture is what correlates readers' errors (Kim et al. 2025). The question it answers is the one still open: which of a closed set of 88 printed symbols this is. It runs on Heavyhorse6 with no API, so the same glyph gets the same answer every time (D-68).
  • What it needs first. A box per glyph, which nothing produces yet — the first half of E3. And the bank: labelled glyphs from calibration pages only, harvested while their notes are transcribed, never from a page it is then scored on.
  • How it is judged. By the same scorer, per glyph, on the gold; by the independence of its errors from the reader's (D-58's permutation check); and against two cheaper baselines — DINOv2, and a small classifier trained on the same bank. The baselines are not a formality: a glyph is a few dozen pixels of line art, far from what these encoders were trained on, and the distinctions that fail are fine detail — an outline roof against a solid one, round brackets against square.
  • What it cannot do. Read text. The class numeral, the language note and the cooperative letters stay with the language model; this checks the glyph's identity and nothing else.
  • How its answer is used. It routes and never overwrites (D-38, D-55). Reader and encoder agree: nothing happens. They disagree: the zoom-verify call is made, and it is shown the glyph beside the exemplars the encoder retrieved for its leading candidates. Still unresolved: a person.
  • Blind to. Whether embeddings of line art separate the fill pairs at all — untested. And the licence: DINOv3's weights are gated under Meta's own terms, which have not been read for this use.

Order of work

step what spend
1 ✅ 2026-10-06 The r4 baseline on Heavyhorse6: the default reader and 3.7, thinking off, three repeats, 13 pages (notebooks/07_lexikon_1910_baseline.ipynb) ≈ $2
2 ✅ 2026-10-06 Write the parts-section symbols out and score them; score school classes and language, cooperative type and language none — replays from step 1's cache
3 — now Transcription: the notes on the eight Czech calibration pages (scans 0019, 0076, 0136, 0196, 0256, 0316, 0376, 0436), then GB Trautenau in German (0478–0480). Every pass already reads these pages; with their gold the six r4 passes are re-scored on twelve pages instead of four the author's hours; no spend
4 — run 2026-10-08 on Claude Haiku 5.5, ahead of 3; see the revision of 2026-10-08 E1b, the second-reader screen, on the same crops (notebooks/08_lexikon_1910_second_reader.ipynb); the bar it has to clear is measured ≈ $0.60 for the screen (Claude Haiku 4.5 on the key on file, and local qwen3.8, free), then three repeats of a finalist
5 E2 and E3 for the rare glyphs: contrast pairs; then a box per glyph, the DINOv3 nearest-neighbour check (free, local) and a zoom-verify call cents a pass
6 Freeze; read the Czech volume once ≈ $6 on batch
— The German reader, alongside: sections cut from the image first (D-58) —

Reordered 2026-10-06, on the author's call: transcription moved ahead of every further model run. A second reader chosen on about twenty errors on four purposive pages would be chosen on noise; the calibration pages are a random draw, already read by every pass, and their gold makes each later comparison — E1b, E2, E3 — a comparison on twelve pages.

What this revision is blind to

  • Four pages. The gold is 61 entries on four purposive pages and certifies nothing. The default reader's page figures run from 0.905 to 1.000 in its first repeat, and the low one is scan 0451.
  • The heading change is unmeasured on a pass. It is validated on the text layer only.
  • The replay was not the ordinary one. A third of the crops were matched to their cached answers by position, not by hash. The byte-identical baseline is the evidence that the match is right; it is not the same as a cache that simply hits.
  • The gold was corrected by one person looking at the plate, with the reader's answer and both earlier readings in view. That is adjudication, not a blind third reading.
  • Arm B's figure is partly the asterisk, not only the model: 11 of its false claims a pass are Jirkov 13's part.

Written after re-reading the pipeline, re-scoring the E1 passes at the unit the analysis will use, and a second, wider pass through the literature. Nothing was built. Where this contradicts the 2026-09-17 revision below, this one governs. Logged as D-56, D-57 and D-58.

Three measurements that change the order of work

1. At settlement level the error is the decoder, not the reader. The scorer keys a claim on (page, district, muni_seq, code): which part of the municipality a symbol belongs to, and how many were printed, are not in the key. Re-scored with both (E1 passes, mean of three repeats; the same 280-claim gold):

reader, section crops municipality + count settlement settlement + count
Gemini 3.7 Flash 0.964 0.960 0.856 0.832
Gemini 3.8 Flash 0.947 0.923 0.842 0.805

The model reads the part markers; the decoder drops them. The lines Gemini returns for scan 0118 read 1, 1) [rc-filial] 243 m, … — 2) [fire-brigade]. — 3) [doctor], [post], [telegraph], … and — 2) [revenue-admin] II, … — correct. But _ENTRY_KEY_RE (utils/lexikon_1910/lexikon_1910_institutions.py:70) recognises only the m, n) form, so the continuation — n) a municipality uses for its second and later parts is never split, and everything after it is filed under the first part. In the first 3.7 repeat 35 of 273 correctly read claims (12.8 %) sit on the wrong part, every one shifted towards part 1 (human part 2 → model part 1 on 18, 3 → 1 on 6, 5 → 4 on 5). It is the "rule narrower than its source" shape, it is invisible to every figure this document has reported, and it costs nothing to fix: the cached answers re-decode for free.

Counts add a smaller, model-specific loss: requiring the printed count to match costs 3.7 about 0.004 F1 and 3.8 about 0.024 — the newer model misreads 2 [midwife] more often.

2. The gold is the binding constraint, and it is stalling. Three weeks after D-45, two of the eight calibration sheets have been started — 0019 about 28 of 36 offices lines, 0136 about 32 of 64 — and the other six and all eight certification sheets are untouched. minutes: is empty on every sheet, so the 48–74 h budget is still a projection. The transcriber copies every text abbreviation beside the symbols (0019's entry 1 is a paragraph of them), which is most of the reading time and none of the symbol channel.

3. Four pages rank readers; they do not measure them. Per-page entry_exact over the six E1 passes: 0450 0.93 in every pass, 0270 0.69–0.77, 0118 0.61–0.83, and 0451 0.00 in every pass — 0451 is a single entry, Trutnov town, with 27 claims, and no reader finishes it. Page effects that large make four pages behave like four observations, and they make entry_exact a poor headline: it weights a 27-claim town exactly like a hamlet with one school.

What the wider literature adds

source what it means here
Johansen, Koschnick & Vedel (2026), How to deal with machine learning bias in economic history Prediction errors correlated with covariates can distort and even reverse coefficients in highly accurate models; the remedy is a small, randomly sampled, expert-coded set plus debiasing. Supports D-55. Vedel is also an author of OccCANINE, which the occupation work already uses.
Battaglia, Christensen, Hansen & Sacher (2025) The institution dummies will be generated regressors. They give an explicit bias correction and a joint-likelihood alternative, which fit this use better than a PPI mean.
Kane, Rouse & Staiger (1999); Black, Berger & Scott (2000); Mahajan (2006) Two error-prone measures of a binary regressor bound, and with more structure identify, the misclassification rates. This is the formal role of the Czech and German editions — conditional on their errors being independent given the truth.
Tong et al. (2024), Eyes Wide Shut? — "CLIP-blind pairs" The two confusions E1 found ([castle] outline roof against [poorhouse] solid roof; [rail-station] against [rail-stop]) are a documented blind spot of the encoders these readers use. Self-supervised features (DINOv2-type) close it: the case for the second method.
Yang et al. (2023), Set-of-Mark prompting Number the detected glyph boxes on the image itself and ask for one label per mark — closed classification per glyph without losing the line's context. The cheapest form of Stage 1.
Gemini's native box output (box_2d, 0–1000) Detection needs no training, but the boxes are approximate and vary run to run; they must be tightened locally before anything is cropped from them.
Griesshaber & Streb (2025), German patents 1877–1918 Gemini-2.5 built 306,070 records from 9,562 scans at higher quality than research assistants, 205× cheaper: independent confirmation that a flash-tier reader is the right tool for this kind of source.
Ren (2026), Double Triangle Annotation — French medical directories 1887–1906 Two architecturally independent models, agreement auto-accepted (85 % of 13,595 fields), disagreement to a human: word error rate 0.003. Its one assumption is error independence, which E1's shared confusions violate here — so agreement routes; it does not validate.
Berzak et al. (2016); Mikulová et al. (2023) — pre-annotation Showing annotators the machine's reading speeds them up and pulls them towards its errors. Keep the validation sample blind; allow assisted labelling where nothing is being measured.
IPUMS double-keying practice Two independent entries, disagreements settled against the source, the illegible flagged rather than guessed — the template for double-coding, and the same rule as [?].
Selective conformal risk control (2025); PPI++ (Angelopoulos, Duchi & Zrnic 2023); stratified PPI (Fisch et al. 2024) Set the auto-accept threshold so that the error rate among accepted claims is guaranteed; estimate efficiently when the validation sample is stratified by router state.
Martí-Henneberg's European railway HGIS, 1830–2010 An external, convergent check for the two rail glyphs.

The revised strategy

Stop. Same-model repeats and majority votes (E1: no gain); temperature experiments; reading symbols with the whole-page pipeline; the local-model bake-off.

Change.

  1. Measure at the analysis unit before any new model run (D-56). Fix the — n) continuation in the decoder; extend the scorer to (entry, settlement, symbol) with the printed count, keeping the municipality figure beside it; report page-clustered intervals and per-glyph sensitivity and specificity for every institution that will enter a regression; stop using entry_exact as the headline. Then re-score every cached pass — no spend.
  2. Split the gold by purpose (D-57). * Symbol validation: a blind, random sample of entries across many pages, a few minutes each, drawn with known probabilities — which may lean on reader disagreement, never on village size (D-55). * Table accuracy: D-45's whole-page protocol, kept for the pages where the table's arithmetic is being graded. * Exemplars and cluster labels: may be labelled with the model's reading shown, because anchoring does no harm where nothing is measured. * Every sheet: log the minutes per layer from now on. Finish the symbol sections of the eight calibration pages first; the text abbreviations can wait.
  3. Attack the known confusions structurally. Contrast pairs for the fill pairs ([castle]/[poorhouse], [rail-station]/[rail-stop]) and for the cooperative glyph; an explicit instruction that a beehive followed by waves is one token; fix the three truncated labels on atlas_prompt.png; once boxes exist, a non-CLIP nearest-neighbour check on the fill pairs as the second method. Never a post-reading repair (D-38).
  4. The German edition, in the order it can actually be done (D-58). It has no ALTO, and E1 showed the section crop is the lever — so its sections must first be found from the image (Gemini's boxes can locate the printed headings). Then a different model family, the conditional-independence test on the gold, and only if that passes, the two-measure methods above.
  5. Converge the code. About 20,000 lines serve the register across two stacks — the bake-off path (run_lexikon_1910_pilot.py, bakeoff.py, lexikon_1910_notes.py, _vision.py, _institutions.py) and the whole-page path (lexikon_1910_pages.py, 3,458 lines) — and most of it is uncommitted, beside a Dropbox conflicted copy of notebook 05. Commit, then keep one reader path: the section-crop view with the whole-page path's batch transport and cost ledger. Split this document into a one-page current specification, a dated experiment log (E0–E5) and a history appendix.

Order. Free first: the decoder and scorer, and a re-score of everything cached. Then the transcriber's time: the symbol sections of the calibration pages, then double-coding. Then cents each: contrast pairs (E2), boxes and the verify call (E3). Then German sections, the independence test, the freeze, the random validation sample, and debiased regressions.

What running the full pipeline on Claude Opus 5.5 would cost (batch)

Priced from the crops the pipeline actually cuts: the pilot's --dry-run over the 13 gold-sweep pages with the E1 flags gives a median 2,421 image tokens a crop upscaled (666 at native width), 81 crops, i.e. ~2,991 crops for the Čechy apparatus. Opus 5.5: $4 / $20 per Mtok, cache reads $0.20, Message Batches 50 % off. Thinking cannot be switched off on Opus 5.5, so it is priced at two effort levels.

component calls low effort, prefix cached medium effort, no cache
CZ apparatus, section crops 2,991 $36 $92
DE apparatus, section crops (assumed the same size) ~2,991 $36 $92
DE table, whole page (no ALTO; 480 pp assumed) 480 $59 $76
CZ table escalation, blocks failing arithmetic (~15 %) ~1,060 $11 $29
verify calls, zoomed glyph + candidates (~15 % of crops) ~900 $5 $16
calibration experiments E2–E4 (~15 passes of 13 pages) ~1,215 $15 $38
total ≈ $160 ≈ $340

Assumptions: key image 2,030 tokens + system prompt ~1,500 as the cacheable prefix; ~400 answer tokens a crop (Gemini's 290 × 1.35 for the tokenizer on Czech); thinking 300 (low) or 1,500 (medium) tokens a crop; the German volume the same size as the Czech, which has never been measured. Native-width crops cut the apparatus lines by about $10–20 each, and whether upscaling helps a Claude reader is untested.

For scale, Gemini Flash with thinking off costs the Czech apparatus ≈ $12 sync, ≈ $6 batch — and ≈ $36 on batch with thinking on, as E1 actually ran it (see Correction below: the ledger's $0.32 a pass left the thinking out). The cost difference is real but small next to the transcriber's hours; what is not known is whether Opus 5.5 reads these crops better. The only Claude figures are whole-page ones (Sonnet 5 0.744, Haiku 4.5 0.573) — the view E1 showed costs a reader about 0.12 F1. One 13-page pass on the section crops costs about $1–2.50 on batch and would settle it; it is also the natural candidate for the "different family" of D-58.

Decided 2026-09-22: not pursued. Gemini stays the default reader (3.7 Flash, 3.8 as fallback). The "different family" for D-58 is to come from a cheap reader instead, chosen by a screen on the same crops (E1b below) rather than from a public leaderboard. The leaderboards do not transfer: on OmniDocBench 1.5 Gemini 3 Flash scores 0.121 on a higher-is-better scale — a metric mismatch in the aggregation, not a reading — while Gemini reads these crops at 0.96.

E1b — the cheap-reader screen. The 81 section crops of E1, identical flags, one pass per candidate (three repeats only for a finalist), scored with the D-56 scorer, and each candidate's errors tested for independence from Gemini's on the gold (D-58). Candidates, all of them a different family from Gemini:

candidate why list price $/Mtok in / out route
Qwen3-VL 235B-A22B Instruct 0.920 on OCRBench; open weights 0.20 / 0.88 OpenRouter or Alibaba Model Studio
qwen3.8:27b, local best local reader already (F1 0.872 in r2); free 0 Ollama on Heavyhorse6
Kimi K2.5 0.923 on OCRBench, the top score not listed there the pilot's existing kimi preset
MiniMax M3 0.916 on OmniDocBench 1.5, the top score 0.28 / 1.10 OpenRouter
GPT-5.6 Luna cheapest OpenAI tier; batch halves it 0.20 / 1.20 OpenAI or OpenRouter
Claude Haiku 4.5 key already on file; never tried on crops (0.573 on whole pages) 1.00 / 5.00 (batch half) the pilot's claude reader

Excluded: dedicated OCR models (Mistral OCR 3, glm-ocr, PaddleOCR-VL, dots.ocr) — they transcribe words and drop pictograms, the D-39 failure; Gemini Flash-Lite — the same family, so no use for D-58, and Flash with thinking off is already ~$6 a Czech apparatus on batch. At these prices every candidate reads the Czech apparatus for ≈ $3–12, so the choice is made on accuracy at settlement level and on error independence, not on cost. The screen itself is well under $5, and a single OpenRouter key reaches four of the six through the pilot's openai-compatible reader.

Correction, 2026-09-22 — Gemini's thinking tokens were never counted

The E1 ledger recorded $0.32 a Gemini 3.8 pass, and this document repeated it. It is several times too low. The pilot reaches Gemini through the OpenAI-compatible endpoint and books prompt_tokens + completion_tokens; on that endpoint completion_tokens is the answer only, while the thinking the model does by default is billed as output and shows up only in total_tokens. Measured on one E1 crop (scan 0270, 2,576 × 918 px, atlas on):

prompt answer hidden thinking wall clock
Gemini 3.8 Flash, default (as E1) 4,205 239–326 ~5,000–5,700 30–35 s
Gemini 3.7 Flash, default (as E1) 4,205 323 ~1,350–3,500 14–21 s
either, reasoning_effort="none" or thinking_budget=0 4,205 323 0 10–13 s

The native SDK confirms it: thoughts_token_count 2,221 on a comparable call. So one 3.8 pass as E1 ran it cost ≈ $1.95, not $0.32. It also explains why 3.8 took twice 3.7's wall clock at the "same" tokens. Thinking can be switched off on this route (reasoning_effort="none", or extra_body={"extra_body": {"google": {"thinking_config": {"thinking_budget": 0}}}}). Measured the same evening (E1, thinking off, below): accuracy holds on 3.7 and improves on 3.8, so thinking stays off. The pilot's OpenAICompatibleReader should book total_tokens − prompt_tokens as output; until it does, every hosted-reader cost it prints is a floor.

What the Czech apparatus (~2,991 crops) costs, reader by reader:

reader in / crop out / crop sync batch
Gemini 3.8 Flash, thinking on (as E1) 3,837 ~5,650 $72 $36
Gemini 3.7 Flash, thinking on (as E1) 3,837 ~2,690 $39 $19
Gemini 3.7 / 3.8 Flash, thinking off 3,837 290 $12 $6
— the same from 1 Jan 2027 (list price doubles) $24 $12
Qwen3-VL 235B-A22B Instruct (OpenRouter; no thinking) ~5,900 ~380 $4.5 —
Qwen3-VL-Plus (Alibaba Model Studio) ~5,900 ~380 $5.3 $2.7
Qwen3-VL-Flash (Alibaba Model Studio) ~5,900 ~380 $1.3 $0.7
MiniMax M3, thinking off 4,000–6,000 ~380 $5–7 —
MiniMax M3, thinking on (+~2,000) ~6,000 ~2,380 $14 —

Prices: Gemini $0.75 / $3.75 (intro, to 31 Dec 2026); Qwen3-VL 235B $0.20 / $0.88; Qwen3-VL-Plus $0.20 / $1.60; Qwen3-VL-Flash $0.05 / $0.40; MiniMax M3 $0.30 / $1.20. The Gemini rows are measured; the others are estimates: Qwen's image tokens at one per 32 × 32 px (crop ≈ 2,310, atlas ≈ 1,488, text ≈ 2,100), MiniMax's tokenization is unpublished (hence the band), and both answers are taken at ~1.3× Gemini's 290 for their tokenizers on Czech. OpenRouter has no batch tier; Alibaba halves batch; MiniMax's batch discount is not published.

What it means. Per crop, the cheap readers cost a third of Gemini with thinking off, and a sixteenth of Gemini as E1 actually ran it. But the whole Czech apparatus is under $15 on every row except thinking-on Gemini, and none has been measured on these crops except Gemini. Gemini with thinking off was measured next and holds its accuracy (E1, thinking off: F1 0.962 on both versions at $0.33 a pass), so the default reader is ≈ $6 a volume on batch and the question of price is closed. What remains for the E1b screen is choosing a second reader — Qwen3-VL or MiniMax M3 — on accuracy and on error independence from Gemini (D-58), not on price.

What this revision is blind to

  • ~~The decoder diagnosis rests on the pattern, not yet on a re-decode.~~ Confirmed and fixed 2026-09-23 — see Two defects fixed, and E1 re-decoded.
  • The settlement-level figures use the old free-form gold. The new worksheets key a part by number and name (m,n › name), and a settlement-level scorer has to follow that ledger, including the __,+ named parts that have no number.
  • The cost table rests on unmeasured terms: Opus 5.5's thinking tokens on this task, the tokenizer factor, whether batch requests actually hit the cache (best-effort), and the German volume's size. The price is from the Anthropic API reference cached 2026-06-24, where Opus 5.5 is marked as launching — confirm before a volume run.
  • Several of the new sources are 2025–26 preprints.

Literature, added 2026-09-22

  • Angelopoulos, A. N., J. C. Duchi and T. Zrnic (2023), "PPI++: Efficient Prediction-Powered Inference," arXiv:2311.01453.
  • Battaglia, L., T. Christensen, S. Hansen and S. Sacher (2025), "Inference for Regression with Variables Generated by AI or Machine Learning," Cowles Foundation DP 2421; arXiv:2402.15585.
  • Berzak, Y. et al. (2016), "Anchoring and Agreement in Syntactic Annotations," arXiv:1605.04481.
  • Black, D. A., M. C. Berger and F. A. Scott (2000), "Bounding Parameter Estimates with Nonclassical Measurement Error," JASA 95(451).
  • Ren, Y. (2026), "Double Triangle Annotation: A Scalable Human-in-the-Loop Framework for High-Precision Historical Document Annotation," arXiv:2605.25781.
  • Fisch, A. et al. (2024), "Stratified Prediction-Powered Inference for Hybrid Language Model Evaluation," NeurIPS 2024.
  • Griesshaber, N. and J. Streb (2025), "Multimodal LLMs for Historical Dataset Construction from Archival Image Scans: German Patents (1877–1918)," arXiv:2512.19675.
  • Johansen, T. S. D., J. Koschnick and C. Vedel (2026), "How to deal with machine learning bias in economic history," arXiv:2606.28063.
  • Kane, T. J., C. E. Rouse and D. Staiger (1999), "Estimating Returns to Schooling When Schooling is Misreported," NBER WP 7235.
  • Mahajan, A. (2006), "Identification and Estimation of Regression Models with Misclassification," Econometrica 74(3).
  • Martí-Henneberg, J. (2013), "European integration and national models for railway networks (1840–2010)," Journal of Transport Geography 26; with the HGIS of Morillas-Torné (2012), JGIS 4.
  • Mikulová, M. et al. (2023), "Quality and Efficiency of Manual Annotation: Pre-annotation Bias," arXiv:2306.09307.
  • Tong, S. et al. (2024), "Eyes Wide Shut? Exploring the Visual Shortcomings of Multimodal LLMs," CVPR 2024.
  • Xu, Y., W. Guo and Z. Wei (2025), "Selective Conformal Risk Control," arXiv:2512.12844.
  • Yang, J. et al. (2023), "Set-of-Mark Prompting Unleashes Extraordinary Visual Grounding in GPT-4V," arXiv:2310.11441.

Revision, 2026-09-17 — the readers got good, so the gold is the constraint now

Everything after this section was written against local models at F1 ≈ 0.70. Two cloud runs since — Gemini on the bake-off's own crops (2026-08-30), and three cloud models on whole pages in notebooks/06_lexikon_1910_v2.ipynb (2026-09-08 to 09-10) — moved the reader past the point the architecture below was designed for, and neither was written down. This section records them, re-reads the plan against the econometrics of AI-generated measurement (Dell & Rambachan's 2026 NBER Methods Lectures and the papers they draw on), and sets the order of work. Where it contradicts a stage below it says so; the stages are otherwise left standing as the record of how the plan got here. Logged as D-54 and D-55.

What the cloud runs measured

Every row is lexikon_1910_institutions.score_against_reference against the same gold as every other number in this document — inventory_human.csv, 280 claims over 47 entries on the four purposive pages — so the rows compare with the local passes and certify nothing. All recomputed 2026-09-17 from the stored inventories and raw payloads.

The best reader is not the one the v2 pipeline runs.

pass model what one call is shown P R F1 entry_exact
gemini37_r3atlasg Gemini 3.7 Flash one apparatus section, the bake-off's strips, atlas 0.974 0.954 0.964 0.830
gemini37_r3thinkg Gemini 3.7 Flash the same, thinking on 0.957 0.957 0.957 0.787
v2 halfbands Gemini 3.8 Flash the page's bands, each halved; whole-page schema 0.865 0.825 0.845 0.596
v2 columns Gemini 3.8 Flash bands, apparatus split at its column rule 0.821 0.800 0.810 0.553
v2 bands Gemini 3.8 Flash table / apparatus / legend 0.772 0.739 0.755 0.340
v2 native Gemini 3.8 Flash the page whole 0.750 0.615 0.676 0.244
v2 halfbands Claude Sonnet 5 native-resolution tiles 0.792 0.701 0.744 0.385
v2 halfbands Claude Haiku 4.5 native-resolution tiles 0.674 0.498 0.573 0.140
qwen38_r2hatls best local pass the bake-off's strips 0.907 0.839 0.872 0.553

The first row is a single draw, decoded by the code of 2026-08-30. Three repeats under today's decoder (E1, below) put Gemini 3.7 Flash on these crops at 0.964 / 0.780, and Gemini 3.8 Flash at 0.947 / 0.730.

Three readings.

1. Less page per call is the lever that moved. Down the Gemini rows the reading improves every time a call carries less of the page: 0.676 → 0.755 → 0.810 → 0.845 → 0.964. The last step changes two other things. The model version goes backwards (3.8 → 3.7), and the task shrinks: the v1 prompt asks for the apparatus only, while the v2 schema asks for the 17-column table, both apparatus documents, the legend and the footnotes in one response — 10–11k output tokens a page. Both differences point the same way, and the mechanism is established for multimodal models in general: accuracy on a detail depends causally on the detail's size in the image, and cropping restores it (Zhang et al. 2025). E1 has since separated the version from the rest — see E1 — the version or the view? below: about seven-eighths of the gap is the view.

2. Gemini is not deterministic, and its repeats are correlated. Stage 3's "the rig is deterministic at temperature 0" was true of Ollama and is not true here. The 2026-09-10 repeats (output/lexikon_1910_v2/raw/pool*, var*, temptest):

five repeats of single runs, F1 ≥ 3 of 5 5 of 5
halfbands 0.804–0.878, mean 0.830 P 0.900 / R 0.839 / F1 0.869 P 0.953 / R 0.650
bandsfrac 0.724–0.795, mean 0.758 P 0.844 / R 0.736 / F1 0.786 P 0.963 / R 0.568

Temperature 0 / 0.5 / 1.0 on bandsfrac gives F1 0.736 / 0.751 / 0.725 — flat, and inside the run-to-run spread. A majority of five repeats gains about +0.04 on the average run and does not beat the best one. That is what votes from one model buy.

3. The Claude models were never tested where Gemini won. Both read native-resolution tiles of the whole page, and both fell well behind Gemini on the same geometries. Neither has read a single section crop, so neither is ruled out as a second reader.

Which kind of measurement problem this is

Dell & Rambachan (2026) sort AI-assisted measurement by what ground truth exists, and the tools that are legitimate differ by case. The symbol channel sits in their easiest case.

A criterion measurement exists and can be applied to a sample. A person reading the scan at 399 DPI settles these glyphs — the worksheet returned 286 symbols, 100 % marked certain — and the mark is caused by the institution: the compositor set a post horn because there was a post office. That is their post-outcome measurement, the case in which the channel from truth to pixels has a physical reason to be stable from page to page. The design that goes with it: the model reads every entry, a person reads a probability sample, and the downstream estimates are corrected with that sample — MAR-S (Carlson & Dell 2025), design-based supervised learning (Egami et al. 2023), prediction-powered inference (Angelopoulos et al. 2023). MAR-S also covers the case of granular validation and an aggregated estimand, so an okres-level use of the register is inside the same framework.

Agreement between readers is not validation. Their central empirical finding is that errors are strongly correlated across models and more so across prompts to one model — in their two replications, within-model prompt pairs sit at 3–6× and 8–20× the critical value for conditional independence — and across 350+ models the more accurate ones make more similar mistakes (Kim et al. 2025). Estimators that assume independent readers (Dawid & Skene 1979) should not be applied to them. This project met it at small scale: every local model under-read [fire-brigade]. The k-of-N tiers of Stage 3 stay, as a router; accuracy is only ever read off the gold.

The target is not 99 %. Under a validation sample, reader error stops biasing the estimate and costs precision instead. The correction term's variance is roughly proportional to the reader's error rate — for a binary claim, E[(Y − Ŷ)²] = P(Y ≠ Ŷ) — so halving reader error, 3.6 % → 1.8 %, is worth about as much as doubling the hand-read sample. Every reader improvement is therefore priced in transcription hours, which is the only scarce input left: a Czech edition costs ≈ $12–13 per geometry on Gemini batch (notebook 06 §7, measured), a gold page 2–3.5 h. For scale, entry_exact 0.95 at ~6 claims per entry needs ≈ 99.1 % per claim, a fourfold cut in error from the best pass.

None of this withdraws the settlement-level argument below — the entry is still the unit of error — it changes the remedy. What Stage 5 called the honest fallback, the dataset at its measured accuracy with a validation-sample correction, becomes the design, and accuracy work is justified by the precision it buys.

Five proposed levers, assessed

1. More exemplars per symbol, from other pages and volumes — yes, cut from entries and from calibration pages only; not from other volumes. Many-shot in-context learning does improve multimodal classification in Gemini-class models (Jiang et al. 2024), and exemplars retrieved to resemble the query beat a fixed set. The caution is VL-ICL Bench (Zong et al. 2025): exemplars often teach output format rather than perception, and more of them can hurt — which is what the plate atlas did to three of five local models. This project also has its own measured reason: the plate prints glyphs at display size and entries at ~0.45×, and template matching topped out at 0.644 (LEXIKON_1910.md).

  • 3–5 exemplars per glyph, cut from entries on calibration pages at printed size, concentrated on the eight contested glyphs and the rc-* family, and shown as contrast pairs ([rail-station] solid beside [rail-stop] outlined).
  • Not from Mähren or Schlesien: another print run imports a shift the gold cannot see. German Böhmen exemplars only for reading the German edition.
  • Two defects regardless. atlas_prompt.png truncates three labels ([rc-pilgrim-cha, [revenue-techni, [transport-static). And v2's worked example is scan 0449 — soudní okres Trutnov, the district of scored pages 0450–0451. Not the same entries, but a leak to close before calibration numbers are compared.

2. More zoomed-in symbols in the atlas — no; zoom the query. The ladder above is the zoom that matters, and it is on the page, not on the key. Enlarging only the exemplars widens the very scale gap that broke template matching. Zoom belongs in a verify call: a tight, enlarged crop around one contested glyph beside same-scale exemplars of its two or three candidate tokens. Gemini's media_resolution setting controls tokens per image, and Google's own guidance is that raising it rarely helps ordinary OCR; the crop is the lever, not the setting.

3. More transcribed notes — yes, the highest-return lever, with three changes.

  • Double-coding is no longer optional. The transcriber's own error is put below at plausibly 2–5 %; the best reader is at 3–5 %. Past this point a reader improvement cannot be told apart from noise in the gold.
  • For the symbol channel, sample entries, not pages. Eight certification pages are eight clusters — print quality, district and reader context are shared within a page — while ±1.8 pp treats ~560 claims as independent. One entry takes minutes to read, so a few hundred entries drawn across as many pages cost less than eight whole pages and give an interval that means what it says. Whole-page reading (D-45) stays right for the table, where the arithmetic grades the transcriber. The three uses of the gold — exemplars and prompt tuning, thresholds, certification — stay apart, as D-48's seal already requires.
  • Let the draw lean on reader disagreement, with known probabilities. Active statistical inference (Zrnic & Candès 2024) oversamples where the readers are uncertain and reweights by 1/π, and MAR-S admits annotation probabilities that depend on observables. That is the rigorous form of this plan's rule against population priors: attention is routed by the readers (CZ ≠ DE, a run-to-run flip), never by village size, with a floor so no stratum goes unsampled — and the estimate stays unbiased.

4. More re-runs of the same page by the same model — little as votes, useful as an uncertainty signal. Repeated prompts to one model yield only a count, and a count does not identify accuracy; what carries information is which different readers agree (Chen, Rambachan & Tamer 2026). The +0.04 of a 3-of-5 majority is what correlated repeats buy, and more samples raise coverage while leaving selection as the bottleneck (Brown et al. 2024) — the 23-pass local union covered 274 of 280 claims while asserting 1,280. What repeats do supply is uncertainty: consistency across samples is a better confidence signal than a model's self-report (Xiong et al. 2024), and self-report here is high on every claim. If repeating, vary the view (crop offset, geometry) rather than the seed, and prefer the German edition to a third repeat.

5. A better prompt — structural changes yes, wording search no. Formatting alone moves accuracy by tens of points (Sclar et al. 2024), and across 37 replicated annotation tasks model and prompt choices overturn roughly a third to a half of conclusions (Baumann et al. 2025). Wording is a researcher degree of freedom: tune on calibration, freeze, certify. The changes worth making are structural — one job per call; the closed vocabulary with [?] and [!] explicitly available; written contrasts for the confusable pairs; and a bounding box per glyph, which is what makes the verify call and clustering possible.

Three levers not on that list

  • The German edition, read by a different model family. It is the one genuinely independent route to the same facts: separate composition, proofing, paper, scan and ordering make print- and scan-induced errors independent across editions. Glyph confusions are not independent when one model reads both, so a different family breaks both correlations. Whether it pays is testable rather than arguable: on calibration pages, run Dell & Rambachan's check — the total-variation distance between the joint distribution of two readers' labels and the product of their marginals, given the gold, against a permutation null — on CZ-Gemini against DE-other. A disagreement still has two causes, a reader error or a genuine difference between the editions, and neither edition is truth.
  • A second method, not only a second model. Crop every glyph box and classify it by nearest neighbour against the bank of entry-scale exemplars — the EffOCR design (Carlson, Bryan & Dell 2024), recognition as retrieval against exemplar embeddings. Shared architecture is what drives error correlation (Kim et al. 2025), so a different method decorrelates more than a different vendor. This is Stage 2 with the cloud reader doing the detection.
  • External checks per glyph — convergent and known-groups validity, never label-setting (Dell & Rambachan's construct-validity lecture). [rail-station] / [rail-stop] against the 1910 railway network; [post] / [telegraph] / [telephone] against the postal legend on the same page (Stage 4, still unbuilt); an okresní seat must carry a court, a tax office and a gendarmerie post. These test the reading; they must not change it, for the reason given under Out of bounds.

Order of work

Every model-side step costs cents to a few dollars. Transcription time sets the order.

step what answers
E0 Finish the offices and parts sections of the 8 calibration pages; double-code two of them the gold every later step is scored on, and its own error rate
E1 ✅ The bake-off's section crops on Gemini 3.8 Flash, symbols-only prompt — done 2026-09-17: the view, not the version (below) model version against crop and task load
E2 Exemplars on those crops: none / plate / entry-scale / contrast pairs — labels fixed first whether exemplars help, and in which form
E3 A box per glyph; the zoom-verify call on disagreements; nearest-neighbour classification as a second method the verifier Stage 1 asked for
E4 German edition on matched pages, a different family; the conditional-independence check whether the second edition is worth reading for the volume
E5 Freeze. Entry-level certification sample with known probabilities; per-glyph error rates; downstream estimates by DSL / PPI the number the paper reports

E1 — the version or the view? (run 2026-09-17)

Six passes over the bake-off's 81 section crops on the 13 gold-sweep pages, every flag identical and only the model changed; arm C is the five whole-page repeats of 2026-09-10. Runner utils/lexikon_1910/run_e1_gemini_version.py, report and per-pass CSV output/lexikon_1910_pilot/bakeoff/e1_gemini_version.{txt,csv}. The flags were verified before anything was spent: re-running the 2026-08-30 pass against a copy of its crop cache served 81 of 81 crops, and the cache key covers the crop bytes, the prompt, the model, the temperature and the atlas.

arm reader, view repeats F1 mean [range] entry_exact mean [range] majority vote F1 / exact
A Gemini 3.7 Flash, section crops 3 0.964 [0.958, 0.975] 0.780 [0.745, 0.830] 0.961 / 0.766
B Gemini 3.8 Flash, section crops 3 0.947 [0.944, 0.949] 0.730 [0.723, 0.745] 0.946 / 0.723
C Gemini 3.8 Flash, whole page halfbands 5 0.830 [0.804, 0.878] 0.507 [0.426, 0.596] 0.869 / 0.596
difference F1 entry_exact what moves
A − B +0.017 [+0.005, +0.028] +0.050 [+0.007, +0.106] the model version, on identical crops
B − C +0.117 [+0.090, +0.138] +0.224 [+0.105, +0.345] the whole page: crop size, task load and thinking together

Pooled over repeats; 95 % intervals from 2,000 bootstrap draws over the 47 entries. The entries sit on four pages, so the intervals are a lower bound on the uncertainty.

What it settles.

  • The view, not the version. Of the 0.134 F1 between arms A and C, 0.117 — about seven-eighths — remains when the model is held at 3.8 and only the view changes. The version effect is real and small: 3.7 reads better on every repeat, and in half the wall clock (507–550 s a pass against 907–1,006 s, at the same reported tokens).
  • A majority vote over repeats of one model buys nothing on the crops. ≥ 2 of 3 lands at or below the mean single run for both versions (A 0.961 against 0.964, B 0.946 against 0.947). Repeats of one reader err together — D-55's rule, now measured on the configuration that will actually be used.
  • The 2026-08-30 pass was the lucky draw. Re-decoded by today's code it scores 0.975 / 0.830; the two new 3.7 repeats score 0.959 / 0.766 and 0.958 / 0.745. The single figure first quoted for this reader (0.964 / 0.830, from the older decoder) overstated entry_exact by about 0.05. The honest figure is the mean: 0.964 / 0.780.

What it does not settle. Inside B − C three things still move together: how much page one call is shown, how much it is asked to return (the whole-page schema's 10–11k output tokens a page against ~290 a crop), and thinking — off in the whole-page runs, the provider default in the crop runs. None of the three changes the decision, since each argues for section crops read in their own call, but they are not separated.

Two systematic confusions, identical in both versions — the input E2 needs.

  • [castle] read as [poorhouse], or dropped — 4.0 misses a repeat for 3.7 and 4.3 for 3.8. The two glyphs are the same building and differ only in the roof: outline for a castle, solid for a poorhouse. Checked on scan 0118, Jirkov entries 1, 3 and 6 against entry 9: the gold is right and both readers are wrong. It is the fill distinction that also separates [rail-station] from [rail-stop].
  • The cooperative glyph read as two. A beehive followed by waves is [cooperative]; both versions also emit [savings-bank] — the beehive alone — for it, 3.0 and 5.0 false claims a repeat. On the whole page the same pair fails the other way: [cooperative] missed 12.2 times a repeat, [savings-bank] asserted 10.6 times.

3.8 additionally drops [rc-vicariate] (3.0 misses a repeat against 1.0). The two shared confusions are errors that a vote cannot fix and a newer version did not fix, which is exactly what E2's contrast-pair exemplars are for. They must not be repaired after reading (D-38): a rule deleting [savings-bank] beside [cooperative] would also delete the entries that print both, as Jirkov entry 12 does.

Cost. ~~Gemini 3.8 Flash: 310,768 input and ~23,600 output tokens a pass, $0.32 at the $0.75 / $3.75 sync rate.~~ Corrected 2026-09-22: the ledger counted the answer and not the thinking, which Gemini bills as output — ~5,000–5,700 tokens a crop on 3.8, so a pass as run cost ≈ $1.95, and the Čechy apparatus ≈ $72 sync / $36 batch with thinking on, ≈ $12 / $6 with it off. 3.7 is priced the same ($0.75 / $3.75 to 31 Dec 2026, doubling from 1 Jan 2027) and thinks less (~1,350–3,500 a crop). See the revision of 2026-09-22, Correction.

Consequence. ~~E1 is done. On this evidence the reader for E2–E4 is Gemini 3.7 Flash on the section crops, with 3.8 as the fallback.~~ Superseded the same week by the thinking-off arms below: the "version effect" was a thinking effect.

E1, thinking off (run 2026-09-22)

Arms D and E repeat A and B — the same 81 crops, every flag identical, three repeats each — with Gemini's thinking switched off (--notes-reasoning-effort none). Report and CSV output/lexikon_1910_pilot/bakeoff/e1_gemini_version.{txt,csv}, runner v1.2.0. Before anything was spent, a thinking-on pass was re-run against its own cache with a dummy key and served 81 of 81 crops, so the new cache-key term does not disturb any existing entry.

Figures as re-decoded on 2026-09-23 from the cached answers, after the two defects in the next section were fixed; decoding is now byte-for-byte reproducible. They supersede the live-run figures reported the evening before, which differed by at most ~0.01 F1.

arm reader, section crops F1 mean [range] entry_exact mean [range] settlement F1 cost a pass
A 3.7, thinking on 0.964 [0.958, 0.975] 0.780 [0.745, 0.830] 0.912 ≈ $1.1
B 3.8, thinking on 0.948 [0.944, 0.952] 0.730 [0.723, 0.745] 0.883 ≈ $1.95
D 3.7, thinking off 0.965 [0.963, 0.970] 0.787 [0.766, 0.809] 0.907 $0.33
E 3.8, thinking off 0.962 [0.959, 0.966] 0.802 [0.787, 0.809] 0.915 $0.33
difference F1 entry_exact what it is
A − D −0.001 [−0.014, +0.010] −0.007 [−0.078, +0.050] what thinking buys 3.7: nothing
B − E −0.014 [−0.025, −0.003] −0.071 [−0.142, −0.014] what thinking buys 3.8: it hurts
D − E +0.003 [−0.006, +0.012] −0.014 [−0.071, +0.035] the version effect, thinking off: none
E − C +0.132 [+0.104, +0.153] +0.295 [+0.170, +0.426] crops against whole page, thinking off on both sides

Bootstrap over the 47 entries as before, so the intervals are a lower bound.

What it settles.

  • There is no version effect. With thinking off, 3.7 and 3.8 read these crops identically (D − E ≈ 0). The +0.016 that E1 credited to 3.7 was 3.8's thinking costing it accuracy: B − E is significantly negative, and thinking also triples 3.8's wall clock and multiplies its bill by six.
  • The view is the whole story. Holding thinking off on both sides, section crops beat the whole page by +0.132 F1 and +0.295 entry_exact — more than the +0.117 / +0.224 E1 measured with thinking confounded in.
  • The price question is closed. A thinking-off pass costs $0.33 (measured with the corrected booking), so the Czech apparatus is ≈ $12 sync, ≈ $6 batch, and the same again for the German edition — cheaper than any credible alternative once accuracy is counted, and D-58's second reader is a question of independence, not of cost.
  • One confusion goes, one stays. 3.8 without thinking stops splitting the cooperative glyph ([savings-bank] false claims 5.0 → 0.0 a repeat). The outline-roofed [castle] is still missed 2.3–3.7 times a repeat by both versions, with the matching [poorhouse] false claims — E2's contrast pairs are still needed for it, and [rc-vicariate] joins the list.
  • Settlement level was the decoder, not the reader. Before the fix every arm sat at 0.846–0.856 whatever the reader setting; with the decoder fixed it is 0.907–0.915 for the thinking-off readers (next section).

Consequence. The reader for E2–E5 is Gemini Flash on the section crops, thinking off — 3.8 by default, as the newer model with the best and least variable entry_exact, and 3.7 interchangeable with it on this evidence. Every Gemini call in the pipeline sends reasoning_effort="none"; a pass that leaves it out is a different configuration and is labelled as one.

Two defects fixed, and E1 re-decoded (2026-09-23)

Both were silent — plausible output, no error — and both were found by scoring at the unit the analysis uses. Neither required a new request: every E1 pass was replayed from its crop cache with a dummy key (81 of 81 crops served, zero spend).

1. The part marker (D-56) — lexikon_1910_institutions 0.5.0. The register opens a municipality's second and later parts with — n). _ENTRY_KEY_RE knew only m, n), so the continuation stayed inside the first part's text and everything after it was filed under part 1. The model's lines had the markers; the decoder dropped them. Three changes:

  • parse_entries splits on _PART_CONT_RE into one entry per printed part. The pattern was fitted to the whole volume's text layer — 733 continuations on 454 pages, the dash read as - 420 times, — 297 and _ 16 — and may not follow a letter or digit, so the house–population pair (217—2266) and a hyphenated place name (Záluží-Ledenice) never split an entry. Over the volume it splits 527 multi-part municipalities; 28 have a part number that does not rise, almost always because the OCR destroyed a later municipality key, and those are flagged in seq_repaired, never renumbered.
  • build_inventory emits one row per (municipality, part, symbol) instead of one per (municipality, symbol), which had filed a fire brigade printed under parts 1 and 2 as one row on part 1.
  • join_symbols gives each joined marker its own part instead of the municipality's first.

Municipality-level scores do not move: replaying the same answers through a text-for-text reconstruction of 0.4.0 gives identical claims on the four scored pages (285 = 285), plus five symbols 0.5.0 recovers on unscored page 0019. Settlement-level F1 does:

arm settlement F1, decoder 0.4.0 settlement F1, 0.5.0
A — 3.7, thinking on 0.856 0.912
B — 3.8, thinking on 0.842 0.883
D — 3.7, thinking off 0.846 0.907
E — 3.8, thinking off 0.854 0.915

2. Strips assembled in the order they finished — run_lexikon_1910_pilot.py. A section taller than 500 px is cut into strips that share the section's slot, and the reader collects replies with as_completed, i.e. in completion order. The code's own comment said the strips were joined "in job order, which pool.map preserves" — true of pool.map, false of the loop beneath it. With four workers a section's second strip regularly finished before its first, and its entries landed under the wrong municipality. It showed as the same cached answers scoring differently live and on replay (one pass 279 claims live, 285 replayed). Replies are now sorted back into submission order before assembly, and two consecutive replays of all twelve passes produce byte-identical inventories. The consequence for the record: part of the run-to-run spread attributed to Gemini in E1 was this assembly noise, which is why the re-decoded ranges above are tighter than the live ones.

What is still open. Settlement F1 is 0.915, not the 0.962 of the municipality level: about 0.05 of part attribution is still wrong and has not been diagnosed. Candidates, not findings: named parts without a number (Forst:, Kartouzy:), a symbol the print assigns to several parts (the gold's @1+3), and the old free-form gold's own part coding. The main scorer (score_against_reference) still keys on the municipality; the settlement figure lives in the E1 runner until D-56's scorer change is made. And every other pass on disk — the local-model bake-off, the 2026-08 runs — was decoded by 0.4.0 and has not been replayed.

Beside entry_exact, report per-glyph sensitivity and specificity for every institution that enters a regression. A specification uses a handful of institution dummies, and the bias from misclassifying a binary regressor depends on exactly those two rates and on whether they vary with the covariates — which entry_exact over 83 tokens cannot show.

What this revision is blind to

  • Every number above is on four purposive pages — 280 claims, one coder — and the comparison has already been used to choose between configurations. It ranks; it does not certify.
  • The geometry ladder confounded three things — crop size, model version, task load. E1 separated the version; crop size, task load and thinking are still confounded with each other.
  • The variance argument is for a mean-type estimand and a binary claim. For a regression coefficient the form differs and the direction does not; the actual trade-off should be computed on calibration data, not assumed.
  • The conditional-independence check needs gold, so it is only as good as E0.
  • The German edition's pictograms are assumed to be the same designs as the Czech. Nothing has checked that glyph by glyph, and the abbreviations are already known to differ (St against B).

Literature

  • Angelopoulos, A. N., S. Bates, C. Fannjiang, M. I. Jordan and T. Zrnic (2023), "Prediction-Powered Inference," Science 382(6671).
  • Baumann, J. et al. (2025), "Large Language Model Hacking: Quantifying the Hidden Risks of Using LLMs for Text Annotation," arXiv:2509.08825.
  • Brown, B. et al. (2024), "Large Language Monkeys: Scaling Inference Compute with Repeated Sampling," arXiv:2407.21787.
  • Carlson, J., T. Bryan and M. Dell (2024), "Efficient OCR for Building a Diverse Digital History," ACL 2024.
  • Carlson, J. and M. Dell (2025), "A Unifying Framework for Robust and Efficient Inference with Unstructured Data," arXiv:2505.00282, rev. Feb. 2026.
  • Chen, X., A. Rambachan and E. Tamer (2026), "Partial Identification from LLM Prompts," arXiv:2606.15031.
  • Dawid, A. P. and A. M. Skene (1979), "Maximum Likelihood Estimation of Observer Error-Rates Using the EM Algorithm," JRSS C 28(1).
  • Dell, M. (2025), "Deep Learning for Economists," Journal of Economic Literature 63(1).
  • Dell, M. and A. Rambachan (2026), NBER Methods Lectures: "Measurement in the Age of AI: Discovery, Definition, and Observation"; "Discovery and Hypothesis Generation with Unstructured Data"; "Measurement Without Random Validation Samples"; "Construct Validity: Credibility When Measurement Is Cheap." Slides.
  • Egami, N., M. Hinck, B. M. Stewart and H. Wei (2023), "Using Imperfect Surrogates for Downstream Inference: Design-based Supervised Learning for Social Science Applications of Large Language Models," NeurIPS 2023.
  • Jiang, Y. et al. (2024), "Many-Shot In-Context Learning in Multimodal Foundation Models," arXiv:2405.09798.
  • Kim, E. M., A. Garg, K. Peng and N. Garg (2025), "Correlated Errors in Large Language Models," ICML 2025.
  • Sclar, M., Y. Choi, Y. Tsvetkov and A. Suhr (2024), "Quantifying Language Models' Sensitivity to Spurious Features in Prompt Design," ICLR 2024.
  • Xiong, M. et al. (2024), "Can LLMs Express Their Uncertainty? An Empirical Evaluation of Confidence Elicitation in LLMs," ICLR 2024.
  • Zhang, J., M. Khayatkhoei, P. Chhikara and F. Ilievski (2025), "MLLMs Know Where to Look: Training-free Perception of Small Visual Details with Multimodal LLMs," ICLR 2025.
  • Zong, Y., O. Bohdal and T. Hospedales (2025), "VL-ICL Bench: The Devil in the Details of Multimodal In-Context Learning," ICLR 2025.
  • Zrnic, T. and E. J. Candès (2024), "Active Statistical Inference," ICML 2024.

Where the run left it (2026-08-14, local models)

Twenty-three accuracy passes and eight throughput passes, four models × six crop geometries, every pass graded against 280 hand-read claims on four pages.

Best single pass qwen36_r2hatls — P 0.746 / R 0.661 / F1 0.701
Best pass worth running qwen36_r2atlas — F1 0.693 at 4.0 h for the volume
Municipalities read exactly right 14 of 47 = 29.8 %
Mean per-entry overlap (Jaccard) 0.63
Run-to-run noise floor 0.000 — base and again are byte-identical

The atlas is the only lever that paid for every model (+0.105 mean F1; it also cut unresolved rows 17→2 for qwen3.6, 109→20 for glimmer). Halving the crop lost for all four. Upscaling is a wash.

The three things that decide the architecture

1. The information is legible; the models over-assert. Of the human's 280 claims, 274 (97.9 %) were made by at least one of the 23 passes — only six were never seen by anything. But the union asserts 1,280 claims for 280 true ones. These readers are not half-blind, they are 4.5× incontinent. The missing component is a verifier, not a better reader.

2. Agreement across passes is a real certainty signal. Self-reported confidence is not. Every scored pictogram claim in every pass carries confidence=high, source=image, corroborated=False — one bucket, zero variance, at precision 0.73. The schema's hook for corroboration is never set for a symbol. Cross-pass agreement, by contrast, separates cleanly:

rule (four best passes) precision recall F1
best single pass 0.746 0.661 0.701
≥ 2 of 4 0.856 0.749 0.799
≥ 3 of 4 0.937 0.482 0.636
4 of 4 1.000 0.126 0.223

3. Ensemble plus adjudication reaches ≈ 0.95 and stops there. Auto-accept ≥3-of-4 (127 claims, 8 wrong) and settle the contested band perfectly, and the arithmetic gives R 0.935 / P 0.967. That is the ceiling of a design built only from these readers, and it assumes an adjudicator that never errs. Passing it requires a channel that can see what none of the models saw.


Why settlement level raises the bar

The analysis will use the register at settlement level, not as okres shares. That removes the one thing that would have made 70 % accuracy survivable: aggregation. Error that is independent across entries averages out of an okres mean and does not average out of the entry itself. At settlement level the unit of analysis is the unit of error, so per-entry exactness — today 30 % — is the binding constraint rather than a diagnostic.

Two consequences the plan is built around:

  • The target is the entry, not the claim. An entry is right when its whole symbol set is right. Report entry_exact beside precision and recall from here on; an F1 that improves while entry_exact does not has moved claims around without finishing a municipality.
  • The place join is a second error source of the same size class. A perfectly read entry attached to the wrong settlement is wrong in the regression. The gold pages must therefore be scored on place_name / place_level as well as on symbols — the worksheet already carries both columns, and nothing has ever graded them.

Out of bounds: the population prior

The idea that a village of 500 is unlikely to have a poorhouse is true, and it must not be allowed to set a label.

If presence is predicted from size, then every regression of institutional provision on population, wealth or region is confirmed by construction — the estimate recovers the prior that was injected. Worse for this design, it converts classical measurement error (attenuating, predictable, conservative) into error correlated with the regressor, which biases in an unknown direction and cannot be signed in a robustness table.

The trap survives a weaker version. "Use the prior only to flag for review" still fails if only surprising presences are re-examined: false positives get deleted in small villages, false negatives are never hunted, and the size-correlated error appears anyway. If priors route attention at all, they must flag surprising absence exactly as hard as surprising presence, or review must run at a uniform rate within strata.

What is legitimate is context from the same page. If the entry's own Czech text reads pošt. úřad, then [post] is corroborated by the source rather than by a belief about villages. That is Stage 4 below, and it is currently doing nothing at all.


The plan

Stage 0 — a gold set that can certify 95 % (blocks everything)

Four purposive pages, 47 entries, 280 claims, single-coded, cannot certify anything about the volume. An accuracy measured on pages chosen for difficulty estimates nothing; one measured on pages chosen for legibility estimates upward. Drawn sample and protocol below.

Gate: ≥ 600 claims from a probability sample, two disjoint halves, ≥ 2 pages double-coded.

Widened 2026-08-29 (D-45): the reading is the whole page, not the symbol column — the table, both halves of the two-column apparatus, the postal legend and the asterisk footnotes, over 21 pages. The reason is not more symbol claims (there are none: certification is still ~560 claims at ±1.8 pp) but that the register's own arithmetic then grades the transcriber. See What it buys, and what it costs.

Stage 1 — detect, then classify

The 4.5× over-assertion is a property of the protocol, not the optics: an open-ended "name every glyph in this band" has no denominator, so precision collapses. Segment the printed marks first — they are discrete pictograms with whitespace around them — so the count per entry is known, then pose a closed 56-way choice per detected box. Generation becomes bounded classification, and per-box logprobs become a certainty worth iterating on.

Gate: detected-box count within ±1 of the human's symbol count on ≥ 90 % of calibration entries.

Stage 2 — cluster the marks, label the clusters

lexikon_1910_symbols.py already reached this conclusion and recorded it as a measured negative: matching the Výklad značek plate against entries fails, because the plate sets glyphs at display size and the entries print the same design at ~0.45×. Self-match scores 1.000; entry match peaks at 0.644 with detections clustered 0.62–0.83 and no separation at all.

The module's own docstring names the fix, and it is the strongest idea available: match like with like. Cluster the small marks the pages actually print, label each cluster once by hand, let every instance inherit its cluster's name. The plate becomes the reference for naming clusters rather than the matcher.

The arithmetic is what makes this the centre of the plan. The volume holds ~850 offices crops at ~9.5 entries each ≈ 8,000 municipality entries, and at 8.6 contested claims per entry a human adjudication pass is ~71,000 decisions. Cluster labelling is ~50 decisions, once.

Gate: ≥ 95 % of detected boxes fall in a cluster whose hand-assigned label matches the human reading, on calibration pages.

Stage 3 — the ensemble as router, not as answer

Keep the k-of-N tiers, fit the thresholds on calibration pages only, and use them to sort each claim into auto-accept / verify / reject. Two rules:

  • Votes stay independent. Do not show model B what model A said. Sequential cross-review invites anchoring and destroys the property that makes the agreement count informative.
  • Perturb the view, not the question. The rig is deterministic at temperature 0 — base and again are byte-identical — so re-asking the same model the same way returns the same answer. Variation must come from crop geometry, the atlas, or a different model. (2026-09-17: true of the Ollama rig, not of Gemini, whose repeats differ but make correlated errors — the rule stands for a different reason. See the revision above.)

Gate: ≥ 2-of-4 reproduces P 0.856 / R 0.749 on calibration; thresholds never touched after the certification pages are opened.

Stage 4 — make corroboration real

Wire channel B's words to the symbol claims and populate corroborated. A symbol backed by its own entry's text is near-certain; a symbol asserted by image alone against silent text is the review queue. Both channels already run in every pass — this is bookkeeping that was never connected, not new machinery.

Gate: corroborated=True on a non-trivial share of claims, and measurably higher precision in that stratum than in the image-only stratum.

Stage 5 — the residual, and what it costs

Whatever survives goes to human adjudication, budgeted from the calibration set rather than discovered. If Stages 1–4 land at ~90 % with a flag that catches most of the remainder, the human queue is small enough to close; if they do not, the queue is the finding and the honest move is to report the dataset at its measured accuracy with a validation-sample correction rather than to claim 95 %.


The gold sample

Drawn 2026-08-14 by utils/lexikon_1910/draw_gold_sample.py, seed 1910, recorded in places/gold_sample_pages.csv. Re-running the script reproduces the draw exactly.

Design. Systematic sample of 16 pages from the 480 table scans (15–494), step 30, random start. Systematic rather than simple random because a clustered draw would hide the error this design most fears — a reading that degrades with print or scan quality, which varies down the volume and therefore across okresy. Alternate pages are assigned to the two halves so each keeps the spread. Pages 118, 270, 450 and 451 are excluded: they are the development set, and purposive.

role scans printed pages use
calibration 19, 76, 136, 196, 256, 316, 376, 436 33 … 450 thresholds, cluster labels, prompt changes
certification 46, 106, 166, 226, 286, 346, 406, 466 60 … 480 sealed — read once, at the end, reported
purposive 118, 270, 449, 450, 451 104, 256, 435, 436, 437 outside both halves — four dev pages, plus 449 to close Trutnov

The five purposive pages are draw_gold_sample.ALREADY_READ, so they sit outside the frame the sample was drawn from and change nothing about the draw or the interval — verified by re-running the draw with 449 excluded and diffing: the 16 drawn rows come back byte-identical. They earn their place three times over.

  • 0270 carries Brzice, the municipality the Czech volume numbers 1 and the German volume numbers 3 — the page that tests the CZ/DE join.
  • 0118, 0270, 0450 and 0451 already carry an old free-form hand transcription, so reading them whole measures the transcriber against themselves across two protocols, at no cost to the sample.
  • 0449 completes soudní okres Trutnov. Measured on the text layer 2026-08-29: 448 is Maršov's last page, Trutnov runs 449–451 and nowhere else, and 451 hands off to Úpice. 449 adds 18 Trutnov municipalities (and 3 of Maršov) to the 16 on 450 and the 4 on 451. A whole okres is the only configuration in which the postal legend can be gold-read at all: its numbering is district-wide, which is why 85 of 535 pilot keys come back legend truncated.

Scan 16 was drawn and replaced by 19: scans 15–18 print no footnote apparatus at all — the volume opens its table before it starts printing the apparatus beneath it — so there is no offices section on any of them. They are excluded from the frame in the draw script rather than drawn and then abandoned, which would have left a hole in the sample that nothing afterwards could see.

They must not be the same pages. A threshold chosen on the pages it is then scored against is not a measurement, and a 95 % obtained that way is a fact about the fitting.

Prague was considered and refused (D-40). The obvious thought — the largest city will print the widest variety of symbols — is wrong for this volume. Prague is not in the alphabetical body at all: it is the front matter, Města s vlastním řádem obecním, scans 15–17, and that section is not the numbered apparatus. It is continuous prose with every institution spelled out in full words, down to hlavní celní úřad I se 7 pobočkami, and not even the abbreviations appear. Glyph-shaped characters on the text layer: 4 on scan 16 and 13 on scan 17, mostly quotation marks, against 92 on scan 19 and 46 on scan 136. It would have added ~zero symbol claims. The symbol-dense page is the okresní seat, and the draw already holds four of them. Prague's prose is worth reading for a different job — it is the fullest expansion of the abbreviations institution_abbreviations.csv is otherwise inferring — and if any purposive page is ever added here it goes outside both halves, never in place of a drawn one.

Protocol

The worksheet is built by make_gold_worksheet.py into reference/places/transcription_gold/cz/<role>/, which is fresh ground — transcription_gold/_superseded/freeform_2026-08/ holds the first four pages in the old free-form format, and nothing writes there. It is read back by read_transcription_md.py.

python utils/lexikon_1910/make_gold_worksheet.py --role calibration
python utils/lexikon_1910/make_gold_worksheet.py --role certification
python utils/lexikon_1910/make_gold_worksheet.py --role purposive
python utils/lexikon_1910/read_transcription_md.py --role calibration --write

One Markdown file per scan, four sections, mirroring the register's own division (Návod p. VI, recorded at LEXIKON_1910.md) — three documents plus the asterisk footnotes:

---
scan: "0019"          printed_page: "5"       role: calibration
districts: [AŠ]       status: draft
minutes: {table: , parts: , offices: , legend: , footnotes: }
---
# 1 — Tabelární část                    ← the 17-column table, one pipe row per line
# 2 — Dvousloupcová část
## 2a — Části osad …                    ← the named parts, and their figures
## 2b — Úřady, ústavy a t. d. …         ← the institution inventory
# 3 — Jednosloupcová část pod čarou     ← the postal legend below the rule
# 4 — Poznámky pod čarou  (*), **))     ← nobody has ever read these

Every data cell is blank, the name and the register's own sequence number included. The only thing pre-filled in the table is r1, r2, r3 …, which is a position on the page rather than a reading of it. Column 1 is the most OCR-damaged text in the volume and one of the things being graded, so pre-filling it would anchor the reading against the channel it grades — the rule D-38 already enforces on the models. Row counts per district are pre-filled from the roster so the table has the right number of blank rows; a row that turns out not to exist is deleted, a missing one added.

Sections 2a and 2b share the ledger below, and the answer decides which document it is. In 2b the answer is the symbol list; in 2a it is the named parts the key prints, each as name <mark> houses—population, comma-separated. Části osad is not names and digits: every part name carries a topographic mark from the same atlas — Hazlov, Haslau [ves] 217—2266, Petermühle [mill] 1—11 — so it is a second symbol channel. & is printed as an entry in its own right and is transcribed as &; what it is has never been documented anywhere in this project, and the recon page settles it.

The sheet is a named-part ledger, and every line carries both names.

== 18  Kbelnice, Kbelnitz Ш   [apparatus: 18,1]
   18,0    Kbelnice             › (obec as a whole)                    >
   18,1    Kbelnice             › Kbelnice, Kbelnitz                   >
   18,2    Kbelnice             › Soudná, Soudna O                     >
   18,?    Kbelnice             › (part figure unreadable)             >

Four things about that layout are decisions, and each one answers a defect.

obec › část on every line, because the municipality used to live in a block heading. Twenty rows into Jičín's twenty-two municipalities the transcriber had to scroll up to find out which block they were in. The names are pre-filled from the printed table, so the redundancy costs nothing to write and removes the one navigation the sheet was forcing.

The register addresses parts by NAME, so the sheet has to receive a name. Inside one printed key it prints a part's name and then that part's institutions:

17, —) Kartouzy: † 314 m, věznice pro muže s nemocenským odděl., [midwife].
       Valdice:  [school] III, [pharmacy], [fire-brigade], [post], [rail] 301 m.
18,  1) [fire-brigade].
       Zebín: † 400 m.

Zebín is the case that settles the design: printed, with a church, and the table gives it no number anywhere in the volume. A sheet keyed only on m,n has nowhere to put it. The __,+ escape takes it — written once per section as a blank template rather than as two dead rows on every municipality, because per-municipality it cost 44 always-blank rows on scan 0136 against ~32 that meant anything, and a column that is always blank teaches the eye to skip the column. m,? stays per municipality but only where the table gives it numbered parts, which is the only place a part figure exists to be unreadable.

The number column fails in both documents at once, so the roster now repairs it rather than dropping the block. The old rule dropped a table block whose sequence number the OCR destroyed, on the reasoning that parse_entries would carry it instead and flag it !roster. But the apparatus keys are the same column one document lower: when it goes, it goes in both. Scan 0136 reads Jičín's municipalities 4, 6 and 9 as i, <> and í); all three are printed in the apparatus with symbols (4, 1) [pharmacy], [fire-brigade]) and all three had no row on the sheet at all — the reading was bounded by the channel it grades, one level up from the defect that had already been fixed. Reading order is the numbering, so a number that sits between two legible ones is now reconstructed where the arithmetic is unique (3, ?, 5 can only be 4), left unaddressed where it is not, and marked suspect where it parses but goes backwards (15, 10, 17 — the 16 read as a 10). Jičín goes from 18 municipalities on the sheet to 22.

The same argument runs one level down and the old code had it backwards: parts run 1..n densely inside a municipality, so printed order beats the OCR of a single damaged character. Preferring the parsed figure had put Tíhotka at 2,9 under Hodkovičky, a number that municipality does not have. Position now wins and a disagreement is flagged !seq, which fires 5 times across the eight pages — one of them exposing a table block split down the middle of Planá nad Lužnicí, Plana an der Lužnitz.

A symbol can belong to several parts, and the sheet can say so. Written on the line of the key it is printed under, with @ and the parts it covers joined by +:

1,1   Albrechtice › Albrechtice     > [school] II @1+3, [fire-brigade]

The register's own visible form of this is the cadastral span the table prints beside a name — Oslov, Woslow [cadastral](1, 2) — which the sheet pre-fills. The part column is never truncated, because a fixed cut had reduced Červená, Čerwena 3. odd. (viz Milevsko 14 a 52) to (viz Milev — removing the cross-reference the protocol explicitly asks the transcriber to honour, from the one column it is printed in.

And a case neither sheet anticipated. A settlement can be divided between municipalities and across soudní okresy: scan 0316 prints that Červená in full — the third division, the others sitting under two municipalities of a different court. The convention is to record against the printed key only, never to copy the institution onto the divisions it cross-references and never to delete it as a duplicate on meeting it there. Which division got the school is the fact being collected, and it bears directly on what a settlement-level observation is.

  • Fill from the image, never from the text layer. The offices hint block quotes the text layer's own read of the section; it is garbage where the symbols are, and it is carried in an HTML comment so it reads as orientation rather than as content. It undercounts badly and is silent about the rest — a hint and a floor, not an answer.
  • A blank cell is data where the register prints nothing there. A printed dash is a zero, not a gap. Anything printed but unreadable is [?]; never guess a digit.
  • Record the minutes per layer in the front matter as you go. That is what the remaining pages are budgeted from, and the whole-page estimate below is a projection until the recon page replaces it.
  • Write tokens from symbols/atlas.png. [?] for genuinely illegible — that is data, and it is the answer to the resolution question: if a person at 399 DPI cannot separate two glyphs, no method will, and collapsing them becomes the correct reading of the source rather than a compromise. [!] for printed-but-not-in-the-atlas, which says the curated list is short rather than that the reading failed.
  • If a drawn page turns out to have no Úřady, ústavy section, replace it with the next free scan forward and record it in the replaced column — never substitute silently.
  • Double-code two certification pages: transcribe them twice, blind, well apart. Until that is done the transcriber's own error rate is unknown, and it is plausibly 2–5 % — the whole margin under discussion.

What it buys, and what it costs

On the symbol channel, nothing has changed. At the observed ~70 claims per page, 16 drawn pages ≈ 1,100 claims; the 8 certification pages ≈ 560, which certifies 95 % to about ±1.8 pp. Widening the reading adds records and self-checks, not statistical power on the symbol channel, and that figure must not be restated as improved.

What the whole page buys is a graded transcriber. Run the gold rows through the register's own arithmetic — grade_row, grade_block_sum, and the parts sums — and I1 (m+f = total), I2 (confessions), I3 (languages + foreigners), I4 (settlements sum to municipality) and I8 (the parts pairs sum to their settlement's row) all fire on the human reading, per page, while the image is still open. read_transcription_md.py does exactly this and prints the failures. A failure is either an OCR error in the printed table — which is a finding, since it is what the gold exists to measure — or a slip of the transcriber's, and which one has to be settled by eye before the page is closed. That is the term the blind-spot list below called "plausibly 2–5 % — the whole margin under discussion", and symbols-only reading could not see it at all.

It also converts the table's inferred ~88 % digit rate into a rate measured against a person, which is what makes channel A worth switching on: the argument for leaving it --a-on-fail was that "a second billed reading of a block that already satisfies every arithmetic identity buys confirmation of what was not in doubt", and a human table reading is exactly what voids it.

Cost, named. Measured over the 8 calibration pages: offices 177 lines / 93 entries, parts 198 lines / 118 entries, and the table ~35 rows × 15 numeric columns ≈ 525 cells a page. Budget 2–3.5 h a page, so 48–74 h over the 21 — against 8–12 h for the symbol column alone. Recon first: transcribe scan 0019 (Aš) across every layer with the clock running per layer before committing to the remaining 19. 0019 is 17 offices and 14 parts entries, one segment each, and it is calibration page 1 regardless; 0136 is the richest offices page in the draw but has only 2 parts entries, so it cannot time the layers being added — read it second, where its documented defects make disagreements interpretable.

Reading the same page with the models

Two changes, both so the gold has something to grade.

The parts channel is switched on. SECTIONS moves from "offices" to "all" (D-46). Everything it needs was already wired — lexikon_1910_vision.SECTION_HEADS carries parts beside offices, --notes-sections already fans out to both kinds, SECTION_CATEGORIES already restricts the vocabulary per kind, and VOLUME_SECTIONS_PER_PAGE already holds the measured parts rate. The bake-off simply never asked. It is close to free and it roughly doubles the graded surface.

Channel A runs on the gold pages. It needs no code change: --a-on-fail is a restriction, so naming A in --channels already reads every block. What changed is the argument for leaving it restricted — "a second billed reading of a block that already satisfies every arithmetic identity buys confirmation of what was not in doubt" — which a human table reading voids. It is billed, so price it first:

python utils/lexikon_1910/run_lexikon_1910_pilot.py --pages 19     --channels B,A,G --notes-sections all --dry-run

Then drop --dry-run. This is the only way the table's ~88 % stops being an arithmetic self-grade and becomes an accuracy against a person.

The scope is frozen before any certification page is opened and is identical across both halves. Deciding after calibration is transcribed means re-opening calibration or shipping an asymmetric instrument. Calibration pages first — they unblock Stages 1–4; certification is not read until the pipeline is frozen.


What this plan is blind to

  • One volume, one edition. Čechy, Czech printing. Mähren and Schlesien are on the shelf in German and have not been read; nothing here transfers to them untested.
  • 560 claims cannot certify a per-glyph rate. The eight contested glyphs ([pharmacy], [fire-brigade], [midwife], [mill], [rail-stop]/[rail-station], [post-agency], [telephone]) are 32 % of the human's claims but carry 55 % of the false positives and 44 % of the misses. The certification set sizes the aggregate, not each glyph.
  • Deleting the hard glyphs does not rescue the rest. Excluding all eight still leaves P 0.818 / R 0.714 — the residue is not clean, and "settle the confusable pairs" is worth real points but not the target.
  • The transcriber is only partly graded. Whole-page reading (D-45) makes the register's own arithmetic fire on the human reading, so a slip that breaks a sum is caught per entry while the page is open. It is silent where the arithmetic is: I8 grades only settlements that have named parts, and I1–I4 cannot see an error that preserves a sum — two digits transposed between the male and female columns passes I1. Double-coding two certification pages is still required.
  • The legend is certifiable for exactly one soudní okres. Its numbering is district-wide, so a page-level gold read is only well defined where the sample holds the whole district — which is why 85 of 535 pilot keys come back legend truncated. With 0449 added, Trutnov (449–451) is complete and is the only such district in the set. On the other eighteen pages the legend keys point outside it, and a legend read there is a partial reading that cannot be scored as right or wrong.
  • The two halves are not equally well-formed, and it is the sealed one that is worse. A key whose municipality has no row in the page's table window gives the transcriber a number with no name to check it against. Measured 2026-08-29 once every sheet was built: calibration 2.2 % of keys, purposive 1.5 %, certification 8.7 %. Two structural causes were found and fixed — an apparatus heading was joined to the table by requiring the two OCR readings to be byte-identical (scan 0106's Mechanice against the table's Nechanice stranded 46 keys), and _repair_run let the first legible sequence number anchor a whole page (scan 0451 rewrote a correctly-read 31, 32, 33 into 81, 82, 83). Those took certification from 19.4 % to 8.7 %. What is left is page damage, not an instrument defect: scan 0466 prints no legible sequence number at all in nineteen consecutive blocks. It is a real asymmetry and it belongs beside the certification figure — the sealed half is being read off worse pages than the thresholds were set on.
  • 21 pages of 480 is 4.4 %. Whole-page reading makes those 21 authoritative. It says nothing more about the other 459 than the symbol-only design did.
  • The German volume is not gold-read. Brzice on 0270 tests the CZ/DE join, but the German edition has no ALTO — only UBW_Spezialortsrepertorium-Boehmen-1910_de_plaintext.
  • A quarter of the model's output has no ground truth at all. 112 of qwen3.6's 457 rows are letter/abbreviation codes; inventory_human.csv holds only pictograms, so those rows are scored neither as hits nor as errors.

When it lands

Write the outcome into DECISIONS.md with the configuration it was obtained under and the certification figure with its interval, and into VALIDATION.md as a check with its blind spots. A winner at 0.95 on calibration and a winner at 0.95 on sealed certification pages are different findings, and only the second one is about the volume.

1910 register — the municipal dataHow it works

The register as analysis tables in data/final/register: every place linked to settlements_1910, the German volume through the Czech one, every column. · source: reference/REGISTER_VARIABLES.md

In short

The 1910 Podrobný seznam míst / Spezialortsrepertorium is read by the frozen vision route (utils/lexikon_1910/run_frozen.py, notebook 15, reference/LEXIKON_1910_ROUTE.md). A run settles what the register prints for each part of each municipality and writes it to output/frozen/<edition>_<pages>/. This page describes the step after that: four normalised tables in data/final/register/, each place linked to the place authority the legionnaires' birthplaces already resolve to (data/final/geo/settlements_1910, key settlement_id), so the later merge onto persons is a plain join. Decisions D-148 (the build) and D-150 (German Böhmen through the Czech volume).

Built by utils/lexikon_1910/lexikon_1910_register.py, from section 18 of notebooks/15_frozen_run.ipynb, which reads every edition's finished run for the notebook's page set and replaces what data/final/register/ holds. Since the pre-freeze review (2026-10-30, A10/D6) section 18 runs only when PAGES = "full": a gold, calibration or certification Run All skips it, so a handful of sampled pages can never replace the full register (and a certification run never builds it from sealed pages). The keys are asserted twice: by the build itself, and by the QA gate (utils/validation/data_checks.py, place_checks() / run_checks()), which also fails a register row whose settlement_id is no longer in settlements_1910. Tests: tests/test_register_build.py.

Build order

  1. Notebook 02, §3c writes data/final/geo/settlements_1910 (the place authority).
  2. Notebook 15, §18 (PAGES = "full") anchors the register to it and writes data/final/register/.
  3. Notebook 02, §3e builds the map crosswalk data/final/geo/settlements_1910_geo, which breaks ties by the register's court (ruian.register_courts reads register_places).

Out of this order, a link can sit on the wrong settlement without any key check seeing it: a settlement table rebuilt after the register moves anchors (5 of 288 on the certification register of 2026-10-29), and a crosswalk built before the register reads the old one. Section 18 prints a warning when the crosswalk is older than the register it has just written (C17, lexikon_1910_register.crosswalk_stale).

Numbers from the build

Everything numeric on this page sits in the block below, which section 18 rewrites on every build (write_build_block). The two exceptions are marked where they occur, with their source.

Generated by lexikon_1910_register.write_build_block (1.1.0) on 2026-10-29 — do not edit by hand.

data/final/register/ holds: cz_certification. Text abbreviations read for: no run on disk.

table rows columns key
register_places 288 22 edition, court_key, muni_seq, settlement_seq
register_institutions 503 19 edition, court_key, muni_seq, settlement_seq, mark, source
register_cooperatives 0 16 edition, court_key, muni_seq, settlement_seq, coop_code, language_letter
register_municipalities 123 101 settlement_id

Claims by the hand that settled them: opus 59, reader_of_record 1, readers 2, agreed 441 (author-ruled 0; disputed 62 of 503). Places with no claim: 154. Municipalities with a cure seat (rc_cure_seat): 31 of 123; with a parish in the strict sense: 31. Cooperative rows: 0.

Table rows the anchor could not place in a section: {'cz_certification': 6}; municipalities refused for sharing a settlement_id within one edition: 0.

Anchoring, every run on disk (each page set built together, so German Böhmen is crosswalked to the Czech run of its own pages):

run place_level places anchored rate ambiguous matched matched_court parts_disagree unmatched via_parts no_name via_czech_order unanchored_partitioned
cz_certification municipality 128 123 0.961 2 121 1 1 2 1 0 0 0
cz_certification part 160 150 0.938 5 148 2 0 5 0 0 0 0
cz_gold municipality 197 187 0.949 2 185 2 3 5 0 0 0 0
cz_gold part 203 193 0.951 8 190 3 0 2 0 0 0 0
de-bohemia_gold municipality 102 86 0.843 1 32 1 1 0 35 14 18 0
de-bohemia_gold part 78 56 0.718 2 56 0 0 2 0 18 0 0

Crosswalk to the Czech volume, gold:

crosswalk to the Czech volume (German Böhmen places):
    municipality  102 places, paired 67 {'settlement': 48, 'order': 19}; inherited 18; both anchored 0, agree 0
    part           78 places, paired 36 {'settlement': 36}; inherited 0; both anchored 0, agree 0
    order rule, leave-one-out on 48 known pairs: predicted 34, of those correct 34
    order step by court: {'filled': 5, 'no anchors': 1}

How a claim becomes a variable

  1. The run settles the claim. A claim is one institution printed in one part: (page, section, municipality number, part number, mark). Pictograms come from final_inventory.csv (one row per claim, with the printed count and the rule that settled it); text abbreviations and cooperative letters come from final_abbreviations.csv, where the run wrote one (D-134). Each section's 1910 court comes from courts.csv (D-122).
  2. The place gets its key. A place is keyed by its section's court, not by the printed label, which the text layer damages (mechanice for Nechanice; D-121, D-127). A section with no court is keyed unresolved:<label> and pairs only with itself.
  3. The place gets its printed name. The run folder holds no names. They are in the readers' pass files: the table reader's rows (row 0 = the municipality, row k = part k; every edition since D-160, whose German district totals name no place and whose parts carried over a page break continue the municipality before them), and the reader of record's parts list (every edition: the named components of each part, each with its type). A place takes its table name where there is one. Otherwise it takes every component printed as a village, town or market town, or failing those a hamlet-like one. The list is alphabetical, so its first line is not the place (German Jičín 20/1 opens with a mill).
  4. The place is anchored to settlements_1910 (lexikon_1910_names.build_authority + anchor_blocks): the candidates are every settlement of the okres of the section's court, under every name the project knows for it (modern Czech headword, the 1910 name, the German name, and the ČSÚ viz aliases in settlement_aliases_1910.csv, which carry most German forms). A match needs similarity ≥ 84 and must beat the runner-up by 5. Ties are refused, never broken on lookup order (D-22). Each settlement takes at most one municipality and at most one part per okres. A tie at okres level is retried among the settlements whose coordinate lies in the section's own court (matched_court): the court is evidence the entry carries, so this is the same move as breaking a tie with the record's own municipality (D-22). A municipality its own name does not anchor takes the obec that all its anchored parts belong to (via_parts: a part that is an obec is its own owner, an osada is owned by its 1910 parent). German municipalities with numbered parts need this, because no list a reader writes prints their name. 4b. German Böhmen is crosswalked to the Czech volume (D-150, utils/lexikon_1910/lexikon_1910_crosswalk.py). The two volumes print the same municipalities, each under both names, but each numbers them in its own alphabet: Dílce is No. 7 of court Jičín in the Czech volume and No. 9 in the German. Many German places have no line on the German reader's parts list, and the German table is never read, so they have no name to anchor. Per court, a German place is paired with a Czech one (a) by a shared settlement_id, (b) by name against the Czech cell's forms (≥ 84, ties refused), and (c) by position: the German volume numbers a court in alphabetical order of the German names, accents folded, and the Czech table prints every German name. Between two paired municipalities the German numbers are a closed run of slots. They are filled with the Czech municipalities whose German name sorts between the two pairs, in order, only if there are exactly as many as there are slots; that count proves no municipality is missing from the Czech pages read. A court's ends close only on a full run, and a court whose pairs do not rise with the German numbers is refused the order step. A German place its own names left unanchored takes the Czech place's settlement_id (via_czech_name, via_czech_order). The order rule is tested by leave-one-out (each known pair dropped and found again from the others), in the block above. Parts are paired by settlement and name only, since their German order is not verified on any page yet.
  5. The claim gets its category (D-147). A religious mark takes its common category from reference/places/religion_categories.csv: parish, vicariate, expositure, Lokalie, Kuratie and curate benefice → [rc-cure-seat]; filial church and chapel → [rc-church-no-priest]; Kaplanei and Primissariat (Mähren only) → no category. Any other pictogram is its own category. A text abbreviation's category is txt: plus its English (txt:district court), so the Czech o. s. and the German BG. meet while a printed word never merges with a pictogram.
  6. Claims are summed per municipality. A part's claims belong to its municipality (same court and municipality number), and the municipality's row carries the sum. Only anchored municipalities have a row.

The tables

register_places — one row per register municipality or part

Key: edition, court_key, muni_seq, settlement_seq.

column meaning
run the run folder it came from (cz_certification, de-bohemia_full, …)
edition cz, de-bohemia, de-moravia or de-silesia
court_key the section's 1910 court id as text, or unresolved:<label>; never missing
court_id the 1910 court id (districts_1910.court_id); <NA> when unresolved
court_cs the court's Czech name, from courts.csv
okres_id the 1910 political okres (court_id // 1000), the anchor's constraint
muni_seq the municipality's number in its court, as printed
settlement_seq the part's number; 0 is the municipality itself
place_level municipality (settlement_seq 0) or part
pages the scan(s) the place was read on, ;-joined
district the section label as the source row wrote it
printed_name the name cell used for anchoring, cleaned of leaders and symbols (Czech + German forms, ,-joined)
name_source table (the edition's table reader, Czech or German since D-160), parts (the parts list) or none
components the parts list's named components, Name [type]; …
settlement_id the settlements_1910 key the place anchors to; <NA> if refused
anchor_score best similarity (0–100); 0 for via_parts and for places never scored
anchor_runner_up the second-best settlement's similarity
cz_muni_seq, cz_settlement_seq German Böhmen only: the Czech volume's numbers for the same place (step 4b); <NA> if unpaired or another edition
crosswalk how the pair was made: settlement, name or order
crosswalk_agrees where both places anchored on their own and were paired by name or order: whether they anchored to the same settlement (never set on a settlement pair, which agrees by construction)
anchor_status anchored: matched, matched_court, via_parts, and for German Böhmen via_czech_name, via_czech_order (inherited from the paired Czech place). Not anchored: ambiguous (a tie, refused), unmatched (nothing ≥ 84), parts_disagree (its parts point at different obce), no_name, no_court, outside_authority (the okres has no settlement in the lexikon)
area_ha, houses, pop_total, pop_male, pop_female the table's figures for the place (1.5.0): its own row, the municipality's (settlement_seq 0) being the municipality's total. A row is read when pop_total is a number above 0; then a cell printed empty (nothing, a dot, a dash) is 0, while a cell that is not a number is <NA>. area_ha is printed for municipalities only
conf_catholic, conf_evangelical, conf_israelite, conf_other confessions, as printed
lang_german, lang_czech, lang_polish, lang_other, foreigners the Umgangssprache columns and foreign nationals. lang_polish is printed by the Silesian volume only and is <NA> elsewhere; lang_czech is the census's böhmisch-mährisch-slowakisch
figures_check the register's own identities on the row (lexikon_1910.INVARIANTS I1–I3): "" all hold; else the failing ones (sex, confession, language); unchecked where a figure in one is <NA>; not read with no population
post_office the post office the table's post key names, through the reader of record's legend; key N where no legend read names it
stations, station_nearest, station_hours the railway stations the table names with their walking distance in hours (Name 1.75; Name 2.5), the nearest, and its distance (<NA> where none is printed). The distance is the table cell's, else the legend entry's (the German volumes print it there). On a page sample a key often points past the part of the court's legend that was read (key N: 339 on the certification pages, 0 within the legend read); the full run reads every page
school_classes, burgher_school_classes the classes of the place's elementary schools ([school] III → 3) and burgher schools, summed, from the reader of record's attributes (attributes_<label>.csv); the school count is the settled one (n_mark_school). 0 where the place has none, <NA> where the run wrote no attributes

register_institutions — place × mark

Key: the place key + mark, source. Foreign key: the place key → register_places.

column meaning
mark the pictogram token ([school]) or, for text, txt: + the code as printed (txt:o. s.)
source pictogram (final inventory) or text (final abbreviations)
count the printed count (2 Sch = 2); a claim with no count written counts 1
count_source how the run set the count (readers' majority, tie, …; none written: 1)
rule the run's rule string, every rule if the claim was read on two pages
settled_by the strongest hand in the rule: author > opus > reader_of_record > readers > agreed
n_pages how many final-file rows were folded into this one (a place read across two pages adds its counts)
entry the way back to the run (1.7.0): the claim's key in the run's final_inventory.csv or final_abbreviations.csv (page/court_id/muni_seq/settlement_seq), \|-joined where rows were folded
marks the way back to each glyph (1.7.0): the marks behind a pictogram claim, each a key of the run's ledger/ledger_<run>.csv, which keeps every step the mark went through (each reader, E3, the order rule, Opus, the author, with module versions) — a box on a section crop (0018_skocov_offices_4.png#1053,184,1085,254) or, for a claim no box holds, its unplaced mark (0018/skocov/3/0#[post]#0); \|-joined. Empty for text abbreviations, which are read as text and have no box: their way back is entry
author_ruled settled_by == "author": the author ruled on the claim by hand
disputed the readers did not all agree (settled_by != "agreed")
english a text abbreviation's English (empty for pictograms)
establishment False for a course (taught at another institution, D-134); True otherwise
qualifier a qualifier printed before a text abbreviation (měst., fil.), never a claim; a qualifier that names the kind (prům. pokr. šk., gw. Fortbsch.) is part of the code (D-156)
common_category the cross-volume category (step 5); <NA> for Kaplanei / Primissariat
rc_cure_seat the mark is a Catholic cure seat (D-147's analysis flag)
rc_parish_strict the mark is a parish in the strict sense (parish or pilgrimage parish church)

register_cooperatives — place × cooperative letter × language letter

Key: the place key + coop_code, language_letter. Foreign key as above.

column meaning
coop_code the letter as printed: Czech H J K P R Sch St, German B G K L R S Sch
language_letter the suffix as printed (č/n Czech edition; b/d/p German); empty = not stated, its own value
language czech, german, polish or not_stated (empty letter); never Czech by default (D-146)
coop_kind the English kind (Raiffeisen credit union, agricultural cooperative): the Czech H and the German L meet here
count cooperatives of this kind and language in the place. Since the pre-freeze review (A9) the route writes a count per printed language letter (final_abbreviations.csv, column language_counts, "none:1\|d:1"), so R, Rd is one cooperative of no stated language and one German, and Rb, 2 Rd is three
count_split True only on a run written before language_counts, where one claim carried several letters: each distinct letter then takes 1 and the rest are not_stated, because that run's split cannot be recovered
count_source, rule, settled_by, n_pages, author_ruled, disputed, entry, marks as in register_institutions (marks is empty: the letters are read as text)

register_municipalities — one row per anchored 1910 municipality

Key: settlement_id. Built by build_municipalities from the three tables above and nothing else.

column meaning
settlement_id the municipality's settlements_1910 key
edition, run where the row's counts come from. When two editions read the same municipality (the Czech volume and German Böhmen print the same places) the certified Czech volume wins (EDITION_PRIORITY)
editions_seen every edition that anchored a municipality to it, ;-joined
court_id, court_cs, okres_id, muni_seq, printed_name, anchor_status, anchor_score the register municipality's own, from register_places
n_parts numbered parts the run named or claimed for
n_parts_anchored of those, how many anchored themselves
abbreviations_read the edition's run wrote final_abbreviations.csv; if False, every text and cooperative count is <NA> (not read is not zero, D-52)
n_claims claims in the municipality (institutions + cooperative rows)
n_claims_disputed, n_claims_author, n_claims_opus, n_claims_reader_of_record of those: disputed, author-ruled, settled by Opus, left to the reader of record
rc_cure_seat, rc_parish_strict any mark of the municipality carries the flag
n_rc_cure_seat the summed count of cure-seat marks
n_coop cooperatives, all kinds and languages (<NA> if abbreviations not read). The cooperative column comparable across volumes (C19): it counts the letters, which every plate prints. n_cat_cooperative / n_mark_cooperative count the registered-cooperative mark, which the Czech volume, Mähren and Schlesien print and German Böhmen does not (D-139), so they are 0 for every Böhmen municipality by its plate, not by its cooperatives
n_cat_<category> summed counts by common category (n_cat_rc_cure_seat, n_cat_school, n_cat_txt_district_court)
n_mark_<mark> summed counts by mark as printed (n_mark_rc_parish, n_mark_txt_o_s)
n_coop_kind_<kind>, n_coop_lang_<language> cooperatives by kind, and by language (n_coop_lang_not_stated)
the figures, figures_check, post_office, stations, station_nearest, station_hours the municipality's own row (register_places, settlement_seq 0), which prints its total
school_classes, burgher_school_classes summed over the municipality and its parts
n_school_<type> schools by type (1.6.0), the municipality and its parts: every school pictogram and school abbreviation typed on the legionnaires' own education ladder (SCHOOL_TYPES → the keys of utils.parsing.education.INSTITUTIONS): elementary, elementary_branch (an expositure), burgher, continuation, continuation_ind, commercial, industrial, trade, agricultural, gymnasium, realschule, secondary (a girls' lyceum), teacher, seminary, and off the ladder preschool, music, handicraft, unspecified (the bare šk. / Schule). Every type is a column in every build. The types a plate prints only as text are <NA> where the edition's abbreviations were not read
school_top_rung the highest rung any of those schools stands on (EDUC_ORDER: 1 elementary, 2 burgher, 3 vocational secondary, 4 upper secondary, 5 tertiary; continuation schools are no rung, D-08); 0 where none is printed; <NA> where the abbreviations were not read, since a higher school would be a text mark. On one scale with the legionnaires' own education_ord, and the types with their education_top

A spread column exists for every category, mark, kind and language claimed somewhere in the build. Within a build, a 0 is a true zero for a municipality that was read; a build with other runs can have other columns. A column's name is the value's ASCII stem; where two values of the code table share a stem (Gymn. and gymn., hosp. pokr. šk. and hosp. pokr. k.: 19 such stems before the pre-freeze review crashed the build, B1) each takes six hex of its own hash as well, decided over the whole code table so a name never depends on what one build holds. register_columns.csv, written beside the tables, maps every spread column back to its table, column and value (key column).

register_statutory — the statutory cities' institutions, from their prose (D-178)

Key: edition, city, row. Prague, Liberec and the nine Moravian and Silesian statutory cities print no pictogram; their institutions are read from the prose (statutory_prose.py, stage 1, verified transcription; statutory_institutions.py, stage 2, one row per printed mention by two readers, paired by where their words sit in the text). Here: the rows both readers gave alike, and the rows a third, blind reading settled where they did not (status adjudicated), each passing every rule (the words are in the text, the count is the printed one, a language split sums, the code is in the vocabulary), and the rows the author ruled where neither settled (status author, reference/places/statutory_institution_rulings.csv: a council with language sections is the council and a row for its sections).

column meaning
edition, city the volume and the city as printed (Praha and Prag are both here: the two Bohemian editions)
row the mention's order within the city
okres_id the city's own okres (STATUTORY_OKRES), the join key: Prague has no single settlement
quote the mention's words, exactly as the verified transcription prints them
count the number the mention opens with, 1 where none
languages the language split where one is printed (german 14, czech 4)
category, code the common category and the institution_abbreviations.csv code, where one fits
is_total the row is a total the text prints before its own members ("181 registrierte … und zwar: 74 …", "5 železničních stanic: …"), which are mostly rows of their own: sum either the totals or their members, never both. With totals left out, Prague's settled institutions agree across the two editions within a few per category (cooperatives 186 / 185, transport 93 / 94)
school_type the row's school type (SCHOOL_TYPES) where its code names a school; "" otherwise, and for a school the readers coded "" (its type is then only in the quote)

Not comparable with the pictogram counts: the prose names far more kinds of institution than the plates print. Use it for the cities themselves, or with the categories both sources share.

Checks against sources outside the book (register_checks, 1.5.0)

  • Okres reconciliation (okres_check): per pooled okres (census_okres_id), the anchored municipalities' pop_total against the census; graded only where every municipality of the okres is anchored and read, since a partial okres under-sums by construction.
  • Birthplace join (birthplace_join): of the legionnaires born in a Cisleithanian okres, how many name a settlement of it uniquely, how many of those the register holds, how many were born in a statutory city (7,193 of 101,274). A measure, not the link: the person table should carry the settlement key from notebook 02's own resolution.
  • Not built: altitude. The register prints it for about a third of the municipalities (2,699 Czech altitudes for some 7,800); an elevation model on the settlement coordinates covers all of them.

Caveats

  • Cross-volume comparability (D-146, D-147). Each German volume is read with its own plate, and the plates differ most in their religious marks. Any covariate that crosses a land border uses common_category, rc_cure_seat or rc_parish_strict, never n_mark_*; the plate-specific marks are for comparisons within one land. Still open (D-147): whether Böhmen's parish mark also covers Bohemian Lokalien and expositures.
  • The bare cooperative letter (D-146). A letter printed without a suffix is a cooperative of no stated language: not_stated, never Czech. A frequent d in German Moravia says nothing about a default. Böhmen prints its letters bare and without the mark (D-139); Mähren and Schlesien print the mark before them.
  • Text abbreviations: the code table is grown from a census, and the census is never empty. The print sets institutions out of its own abbreviation parts (hosp. pokr. k., Waisenh.), and a combination the code table lacks reaches no claim. Since D-156 the table holds the combinations read on the gold and certification readings, and what no code reaches is listed by notebook 15 §8b. On the four readings of each run, items no code reaches went 167 → 33 (Czech certification), 414 → 72 (Czech gold) and 190 → 9 (German gold), and what is left is mostly names; the full run will print institutions no gold page has, so treat n_cat_txt_*, n_mark_txt_* and the cooperative counts as lower bounds until its census has been read. Hand-typed, from D-156's measurement, 2026-10-29. No run on disk has written final_abbreviations.csv yet, so in the current build these columns are <NA> throughout.
  • Reading error, and the D-55 correction still to come. Every count here is the route's output, which reads with error. The certification measured that error on the sealed Czech pages (D-127); the German volumes are not certified yet. Any regression on these variables needs the measurement-error correction (D-55) that is still to be built; until then, treat them as error-prone proxies and report the certification's figures beside any result.
  • Anchoring is a refusal-first match. An unanchored place is missing, not zero, and it has no row in register_municipalities. Refusals cluster where names are hardest: homonyms within one okres with no coordinate (ambiguous), and German places whose reader listed no components (no_name) and whose position the Czech pages read cannot close (step 4b). The block above shows how many there are. German Mähren and Schlesien have no Czech volume in this project, so step 4b cannot reach them; they rest on their own names and the ČSÚ aliases.
  • Těšín Silesia. The ČSÚ lexikon covers present-day Czechia. Okres Bílsko went to Poland whole and has no settlement in it, so its places are outside_authority. Okresy Těšín and Fryštát were split, so an unanchored place there may be Polish or may be misread (unanchored_partitioned in the anchoring table). The city of Bílsko is a curated row of its own.
  • Page samples cut municipalities. On the gold and certification samples, a municipality that runs across the edge of a sampled page loses the parts on the unread page. The full run does not have this problem.
  • settlement_id stability (D-148). The id is the headword's ASCII slug plus its ordinal among identical slugs in register order. A matcher, Wikidata or court change never renumbers it. A parser fix that makes a new entry of an existing headword start shifts that one name's later ordinals, and an old id can then name the other homonym, which no key check can see. So rebuild the register (section 18) after any rebuild of settlements_1910: the QA gate warns when the register is older than the settlement table, and fails a row whose id no longer exists.

Amateur-theatre measureHow it works

Czech amateur theatre per place and year, 1780–1913: sources, rulings, certain vs possible, where it stands. · source: reference/THEATRE_MEASURE.md

In short

Czech amateur theatre, place by place and year by year, 1780–1913: a treatment of Czech-language nationalism before the war.

In plain terms

The national amateur-theatre database (NIK) has a page for every Czech amateur theatre company, and every town and village that had one. For each company active before 1914 it tells us three things:

  1. When it was active: the year it started, the decades it played (the 1880s, 1890s, 1900s), and sometimes the year it ended.
  2. What it did: a short chronicle of dated notes (1887 founded, 1901 gave 12 performances, 1905 gave to the National Theatre fund).
  3. Where: the town or village it belonged to.

From that we count how much Czech theatre went on in each village and district, year by year, before the war. That is our measure of local Czech national feeling, to be linked to where each legionnaire was born.

How it is built

It is built from the Databáze českého amatérského divadla (NIK, formerly NIPOS; Data sources #22) by utils/parsing/amateur_theatre.py (0.6.0). The runner is utils/reference_tools/places/run_amateur_theatre_pilot.py. Everything it builds lands in data/interim/theatre/. Nothing is joined to the persons yet.

The database records Czech theatre only. A German-speaking village without a Czech company is therefore a true zero of the treatment, not a gap in coverage. Exposure scales with the Czech-speaking share, which is a control, not a reason to drop German areas.

What the database gives

Source Scraper step What it holds Rows
Place page B the place's first surviving report (první zpráva), its chronicle, bibliography and archive citations 7,368 places
Ensemble list B every company filed under a place, with a start and (sometimes) an end 34,569 rows
Ensemble page C the decades a company was active (Působení), its end or last report, and a chronicle of its own 7,103 pages (every pre-1914 company outside puppet theatre)
Abbreviation key — the database's own shorthand (DS, SDO, ÚMDOČ…) 309 entries + 43 sigla

A první zpráva is the first report that survives, not a founding date. A fuzzy year (191x, 19xx, kolem 1850) keeps its width all the way through: it is never coerced to a single year.

From text to a panel

  1. Lines. chronicle_lines() reads every chronicle line with its leading date as a start and an end interval. A line is a dated event, an undated note, a periodical citation, an admin note, a heading or a separator (D-137).
  2. Clauses. chronicle_clauses() cuts a line wherever a new date opens after a comma, semicolon or full stop (D-140). It never cuts inside brackets or quotes, after an abbreviation, or inside a dated newspaper report.
  3. Codes. code_clauses() applies the author's codebook (D-141): which event types put Czech theatre in a place and year, and how.
  4. Evidence. evidence_table() puts every dated record into one interval form, keyed evidence_id, and gives each company one company_id across sources.
  5. Panel. place_year_panel() turns the evidence into one row per place and year, every measure certain and possible.
  6. Areas. area_year_tables() sums the panel to the 1910 settlement, court and okres, keeping every unit (zeros included).

The ensemble pages enter at steps 2–4 (D-145). Their chronicles are cut and coded exactly as a place's are.

Certain and possible

Every measure comes twice. Certain counts only what the record pins down; possible counts everything it allows. Certain always lies inside possible (asserted).

A record that is… Certain in Possible in
a span with a printed end (1887-1934 SDO) from its latest start to its earliest end its whole width
open, no end (1887 zal. SDO) — B1 its start year, if printed exactly its start to 1913
open, but its page lists decades — D-145 its start year, if printed exactly its start and its listed decades only
a listed decade (189x in Působení) — D-145 no year (the decade is kept in activity_decade_certain) the ten years
a point printed to the year that year that year
a decade or kolem point — B10 no year its width
known only by its end (do 1910) no year the end year
hedged (patrně, snad, ?) — B4 no year as above

A company is certain from its earliest certain start to its latest certain end, since it existed in between. It is possible only on the union of its records' ranges plus that certain stretch. A gap between two listed decades therefore stays empty (D-145).

A recorded pause turns certain activity into possible for its years (B12).

From records to the panel, place by place ⇱ Interactive ↕ Hover for values

Every dated record of one place, one lane each: the pale bar is the years the record makes possible, the solid bar the years it makes certain. The last two lanes are what the place gets in the panel — the union over its Czech-activity records. The places are picked by rule: those with the most kinds of evidence, the ensemble pages among them, and 5–14 records. Hover a lane for what the record printed and how it was read.

Ensemble listListed decade (ensemble page)Ensemble's own chroniclePlace chronicleFirst report (první zpráva)

The rulings

The author ruled each event type's role and twelve design calls on the codebook page (2026-10-28, D-141), and four more on the ensemble pages (D-145).

Roles. Activity and a column: listed, active, founded, performance, count, statutes, permit, first, school and church theatre, travelling companies. A column only: quoted titles (a title also counts as a performance), attendance, proceeds, National Theatre fund, German, Latin. Coded only: venue, building, members, renamed, ended, pause, ban.

Design calls.

Call Ruling
B1 A record with no end is certain only in a start year printed exactly, and possible to 1913.
B2 One company in the list and the chronicle is matched on starts within a year and ends that don't contradict, never on names: SDO names many companies.
B3 The place's first report is activity certain in its year.
B4 A hedged record is never certain.
B5 ČOS stays the reading-and-dramatic society.
B6 School and church theatre enters the Czech measure from 1848.
B7 Travelling companies count as activity.
B8 German and bilingual theatre stay out of the Czech counts, in columns of their own.
B9 The panel runs 1780–1913.
B10 A date known only to its decade is possible across it and certain nowhere.
B11 Undated clauses that print a year go to the model pass.
B12 A recorded pause turns certain activity into possible.
D-145 · decade A listed decade makes the company possible across it, certain in no year.
D-145 · open end A page that lists decades replaces B1's open end; "…" between decades adds nothing.
D-145 · end A printed end or last report closes the span, certain from start to end.
D-145 · chronicle The page's own chronicle is coded like a place's; its company clauses belong to that ensemble.

The panel's columns

Key (geo_id, year), balanced: 7,368 places × 134 years. Each count comes as _certain and _possible.

  • any_activity — some Czech-activity record (the first report included).
  • ensembles_active — distinct companies active in the year.
  • performances_min — performances printed to one year, each counting the number it states or 1.
  • performances_period_avg — a count stated over a span (1883-1910 173 her) spread over its years; an average, never a minimum.
  • plays_distinct_certain, attendance_max — distinct quoted titles, and the largest stated audience.
  • One count each for foundings, statutes, permits, firsts, proceeds, the National Theatre fund, school and church, travelling companies, German, bilingual and Latin.
  • activity_decade_certain — Czech activity certainly somewhere in this year's decade.
  • puppet_* — puppet theatre, kept apart from everything above.

The area tables add places_active_* (places with activity) and places_in_database.

Where it stands

Three builds, each run with --all --reuse-tables: - 0.5.0 (D-141): no ensemble pages. - 682 pages (D-145, 2026-10-28): only the pre-1914 companies with no end in the list. - All pages (2026-10-29): every pre-1914 company.

The 682-page panel was rebuilt from that build's saved evidence and reproduces the current runner's panel exactly when given the current evidence.

0.5.0 682 pages all pages
places with Czech activity, certain 3,063 3,068 3,077
places with Czech activity, possible 3,242 3,242 3,242
place-years certain 52,700 53,403 53,696
place-years possible 93,113 91,651 94,294
company-years certain 78,493 83,410 88,236
company-years possible 195,589 191,247 195,631
performances, certain (minimum) 2,917 11,322
places with a certain performance 915 1,680
distinct plays, certain 769 1,625
foundings, certain 3,574 5,981
National Theatre fund, certain 274 675
1910 settlements ever certain 2,774 2,779 2,786

The 682 companies printed no end, so their pages mattered for when: listed decades replaced an open end to 1913. The rest already had an end in the list, so their pages matter for what: performances, plays, foundings, the national fund.

What the 7,103 ensemble pages hold: - an end or last report: 5,426 pages; - listed decades: 6,058; - a decade after the start's and before 1914: 2,807; - a last decade ending before 1910: 2,587; - a dated chronicle line after the start and before 1914: 2,926; - nothing new: 1,340.

The model pass

Built 2026-10-29 (D-157), nothing sent yet. Module utils/parsing/amateur_theatre_model.py (0.1.0), notebook notebooks/16_theatre_model_pass.ipynb.

The rules leave a clause open (needs_model) when it is undated but prints a year (B11), or dated but no cue reaches it. The model reads such a clause and reports what it says: - its event types, in the cue vocabulary (listed, active, founded … ban); - for an undated clause, the interval its years give, fuzzy years kept wide, or source_year when the only year dates a source reporting the activity; - whether it hedges, the play titles it names, and who performed.

The readings replace the clause's cues and missing date, and code_clauses runs again: the rulings still decide what counts. A reading that fails its checks (years out of range, a start after an end, evidence not in the clause) is kept and not applied.

Scope Place clauses Ensemble clauses All
pre1914 (default): may attest the years before 1914 1,077 5,148 6,225
all: every open clause 4,734 13,193 17,927

Each clause goes with its place, its ensemble, the two lines before and one after, and the database's key to the abbreviations used. One clause a request, the codebook as a cached system prompt, the reply as a JSON schema, sent in batches; every reply is cached under its request's hash, so nothing is paid for twice.

The gold: 308 clauses drawn by source × kind (reference/places/theatre_model_gold.csv, drawn 2026-10-29), ruled by the author blind, halved. They are ruled on a private artifact page built by utils/reference_tools/places/build_theatre_gold_page.py; it stores one document a clause (rulings/<clause_key>), and only the owner can write. amateur_theatre_model.fill_gold_sheet writes the ruled documents into the worksheet. The prompt is fitted on the calibration half; the certification half is scored once the prompt is frozen and a bar is written. Haiku 5.5 and Sonnet 5.5 read it at the same effort. Offline estimate for pre1914, batched: about $0.90 and $18.

The readings enter the measure through the runner:

python utils/reference_tools/places/run_amateur_theatre_pilot.py --all --reuse-tables --model-readings data/interim/theatre/model_pass/readings_haiku.csv

Czech theatre over time ⇱ Interactive ↕ Hover for values

Every place-year summed over the database's 7,368 places. The solid line is what the records make certain, the dashed edge of the band what they make possible; the gap between them is the uncertainty the rulings keep. In 1913 Czech theatre is certain in 1,870 places and possible in 2,618. Performances count only those printed to one year, so they are a floor.

Where Czech theatre was ⇱ Interactive ↕ Hover for values

Each 1910 court district (soudní okres) shaded by the share of its 1910 settlements with Czech theatre in the chosen year — hamlets and parts included, so the shares are low and best read against one another. White lines are the political okresy. The colour scale stops at 80%, the 95th percentile of possible shares in 1913. A German-speaking district with no Czech company is a true zero of the treatment, not a gap; hatched districts have no 1910 settlement in the frame to count. Drag the year, or press play.

1913080%+

Open

  • The model pass is built, not run. Notebook 16, with SPEND = True on Heavyhorse6: the gold stage first, then the full stage for the chosen reader. For the author: whether post-1914 clauses of a pre-1914 company are worth reading (scope all), whether source_year should count as an end, and the bar. companies_performing waits on it: the readings carry performer_kind and performer_name, not yet matched to a company.
  • One-off events on an open record. A founding, statutes, a first or a school play takes its record's possible range. On a record with no end that range runs to 1913 (B1), so founded_possible counts a founding as possible in every year after it. Closing open ends (D-145) is why the full build's founded_possible is lower than the 682-page build's (37,298 → 20,606), even though it has more foundings. Whether an event's possible years should be its date's width only is for the author to rule.
  • The settlement key. Settled 2026-11-03 (amateur_theatre 0.6.1): the saved settlement table is keyed by settlement_id, the settlement table's own key; settlement_row, a position, is used only inside one build. The rebuild that day also caught up with the settlement table of 29 October 18:59, which the 10:13 build predated: 6,668 places tied (6,614 before), certain company-years 1880–1913 60,544 → 61,047, okres ranking Spearman 0.999.
  • Persons. Nothing reaches a legionnaire until the person files carry a birth settlement. An okres or court link was possible on 2026-10-29: 101,201 legionnaires (76 %) reach the okres panel and 58,000 (44 %) the court panel. The author set it aside as too coarse and waits for the settlement list. The lead treatment is to be presence and duration, with certain and possible kept as bounds. Performance counts are a robustness check, controlled for documentation.
  • Language. It is known only at okres level until the 1910 register is merged.

Run it (Anaconda PowerShell Prompt on Heavyhorse6, or any machine with the cache; about 3 minutes):

python utils/reference_tools/places/run_amateur_theatre_pilot.py --all --reuse-tables

Matice measureHow it works

Members of the Matice česká and Matice moravská, read from the scans, placed in 1910 settlements and summed per okres and court; with an interactive map. · source: reference/MATICE_MEASURE.md

In short

Committed Czech nationalism before 1914, place by place: who paid to join the two Czech national book societies, the Matice česká (Prague, from 1831) and the Matice moravská (Brno, from 1849), and where they lived.

In plain terms

Both societies printed lists of their members, each with an occupation and a place of residence. A Matice česká founder paid a one-off contribution of 52 zl. 50 kr. — weeks of a teacher's pay — so a name on its list is a costly, revealed choice, not a signature. We read the lists back from the scans, find the 1910 settlement each member lived in, and count members per political okres and per court district, against the 1910 census population. The result sits beside the amateur-theatre measure: theatre is mass, participatory nationalism; the Matice is the élite that paid for it.

The sources

List Printed What it holds
Matice česká, Jména p.p. zakladatelův … na konci r. 1873 1874 living founders: name, occupation, residence, entry year, membership number; the corporate founders
the same, na konci r. 1890 1891 living founders, every deceased founder since 1831 (number, year of death), corporate founders
Matice moravská, Seznam údů and Do pokladnice … složili 1876–1878 founding, active and contributing members, with the amount paid and the year it was for
Matice moravská, Seznam členů 1891 (state at the end of 1890) the full roster, and the additions of 1891
Matice moravská, Seznam členů dle stavu ze dne 1. března 1909 1909 the full roster, and its own tabulation by place and occupation — the list the measure uses for Moravia and Silesia (D-182)
Matice česká, Zpráva o Matici České in the Časopis Musea království Českého 1886–1914 everyone who paid into the society, month by month: name, occupation, residence, amount, and the month's total — the list the measure uses for Bohemia, as the founders who joined in 1895–1909 (D-184, D-188)

Provenance, rights and the download are in Data sources #28, #29 and #31. The membership number was kept for life, so it links a Matice česká founder across the two issues — together with the surname (founder_key), because the OCR misreads a digit: in the 1890 living list 58 of the 59 numbers printed twice belong to two different people.

From the scans to the entries

utils/parsing/matice.py reads each item's OCR with its ALTO word coordinates into one row per printed entry, keeping the entry's own text beside every field read from it. Three layouts occur: one founder per line (Matice česká), one member per line with a hanging indent in two columns (Matice moravská 1909), and members run on in a paragraph (Matice moravská 1876–1891), cut where a residence or an amount has been read and a new name opens. Each list is held to the total its society printed (D-158); the table below is the build's own count.

The Matice česká payment lists are a fourth layout: entries run on and separated by dashes, each ending in its amount (21 K., 10 zl. 50 kr., counted at 2 K to the gulden), each month closed by its total (Úhrnem), against which every month is checked. Until 1888 a period is a range of months; until 1892 names are printed given name first, from 1893 surname first, and each month's order is read off the majority of its names. Schools, libraries, offices and societies pay in the same lists and are set apart from persons when the name holds an institution anywhere (Knížecí arcibiskupská knihovna, Výbor okresní) and neither a given name nor a person's occupation. The founder's fee, 52 zl. 50 kr. or 105 K, could be paid in instalments, so payment_persons gathers a payer's payments under one key: surname, first initial and okres. A joiner is a person whose first payment falls in the window (D-184). Dating by completion instead would keep few: instalments ran for years, often 5 K a month, and a payer the lists name differently splits in two.

Entries found against the printed totals

listpartprintedfound
mc1873founder2,6332,61499.3 %
mc1873founder corporate146155106.2 %
mc1873founder deceased81579797.8 %
mc1890founder2,6582,65099.7 %
mc1890founder corporate26725595.5 %
mc1890founder deceased1,6171,59998.9 %
mm1891founding181794.4 %
mm1891active14613793.8 %
mm1891contributing64159592.8 %
mm1909honorary33100.0 %
mm1909founding1010100.0 %
mm1909active10310198.1 %
mm1909contributing75773597.1 %

11,128 entries in all, built by the runner from the scans. Two printed totals are read rather than taken (D-158).

Where the residences landed

matchedpragueoutsideambiguousunmatchednoneland only
Matice česká52.0 %17.5 %8.4 %9.3 %6.4 %6.1 %0.3 %
Matice moravská77.7 %4.8 %2.1 %10.6 %3.6 %1.1 %0.0 %

matched: one 1910 settlement; prague: the city; outside: abroad; ambiguous: several settlements and nothing printed to choose between them; none: no place printed. 867 residences were re-read from the scans (D-162).

Where a member lived

The lists print a residence after a preposition, so in the locative (v Brně, ve Vodňanech) or, for an origin, the genitive (z Hostomic). utils/parsing/locative.py declines every 1910 name forward into those forms and matches the printed one against them; it never guesses a nominative back (D-161). A settlement is found by its present Czech name, its 1910 name, its older Czech names, a short list of historic names, its German name (as written and as a Czech list spells it), abbreviations, and nineteenth-century spellings. A name several settlements share is decided only by what the entry prints — the land, a qualifier (u Příbrami, located itself and taken as within 30 km), the settlement's own name, a court seat — and refused otherwise. OCR misspellings are matched only when very close, or when the qualifier corroborates them. Residences the OCR left unplaceable were re-read from the scans by a vision model and count only if they then match (D-162). Places abroad (Vienna, Galicia, Upper Hungary …) are recognised and left out. An abbreviation is read even when the list's own punctuation swallowed its last full stop (Kr. Vinohr.) or it shortens a river to one letter (Kostelci n. L.); a place printed … v Praze that is no settlement is a street or quarter of Prague (locative 0.4.0). In the payment lists, a residence refused as ambiguous or not found takes the place that the same payer's other payments print under the same name, where they agree on one (matice.borrow_places); this is evidence from the lists, never a prior.

The measure

Members are summed to the 1910 census okres (148 pooled units) and to the court district (330), with the 1910 census population as the denominator, and the rate per 1,000 is shrunk toward the mean in proportion to its noise (Marshall 1991), since most units hold a few dozen members. The two societies split by land — Matice česká recruited in Bohemia, Matice moravská in Moravia and Silesia — so the combined measure, matice_own_z, takes each unit's own land's society and standardises it within that society's territory.

The construction follows Squicciarini and Voigtländer (2015, QJE; NBER w20219, §3.1), who measure the Enlightenment élite of a French city by its subscriber density to the Encyclopédie, ln(1 + subscriptions per 1,000 inhabitants), "to reduce the influence of extreme values", and put a Paris dummy in every regression (D-185):

matice_own_zi=xi−x¯TsT,xi=ln(1+100·r^i),

where r^i is unit i's shrunk member rate per 1,000 inhabitants in its own land's society, so 100·r^i is members per 100,000, and x¯T, sT are the mean and standard deviation over the society's territory T (Bohemia; Moravia and Silesia). Per 100,000 rather than their per 1,000, because a Bohemian median of 0.03 per 1,000 sits where ln(1+x)≈x and the log would change nothing. Prague, the seat of the Matice česká, where students and officials from all of Bohemia paid while they lived there, stands at about +3 on this scale (+8 in levels), and every regression on the measure carries a Prague dummy (prague). The dummy is the city only: Karlín, Královské Vinohrady, Smíchov and Žižkov were okresy of their own. Robustness forms sit in the same tables: the level z (matice_own_z_level), any member at all (matice_own_any), the log of the shrunk rate, and the sample without Prague. The measure is also carried per Czech speaker (matice_own_z_czech, D-186), built the same way with the 1910 Czech speakers as the denominator, over the units with at least 1,000 of them: per inhabitant a German okres's zero is a true zero, as in the theatre measure, while per Czech speaker the measure asks how committed the Czech population was where it lived. Inside the Czech-majority okresy the two hardly differ (Spearman 0.96); outside them the per-Czech form rests on a few members, most of them posted. Moravia and Silesia are measured by the Matice moravská roster of 1 March 1909, the one nearest the 1910 census (D-182); Bohemia by the Matice česká joiners of 1895–1909, the fifteen years to that roster and the census (D-184, D-188). A decade was too few people — the median Bohemian okres had two joiners in 1900–1909 — and the wider window ranks okresy closer to their joining outside it (1886–1894 and 1910–1914): Spearman 0.83 against 0.79, and 0.63 against 0.56 inside the Czech-majority okresy. The joiners are a flow from a narrow window, so founders who died or moved after joining need no correction, as Squicciarini and Voigtländer's (2015) Encyclopédie subscriptions need none. The founders of 1831–1890 and the joiners who paid the full fee within the series stay in the tables beside it. Clergy, teachers and officials, whose residence an employer chose, are counted apart from local members.

The measure on the map ⇱ Interactive ↕ Hover for values

Each 1910 court district is shaded by its political okres or by its own value. Combined is matice_own_z: each land's own society's shrunk member rate as ln(1 + members per 100,000), standardised within its territory (Bohemia: Matice česká joiners; Moravia and Silesia: Matice moravská), as Squicciarini and Voigtländer (2015) build Encyclopédie subscriber density (D-185) — red above the territory's mean, navy below, capped at −2 and +3 (Prague, at about +3.1, is drawn at the cap). The two society views show the shrunk rate per 1,000 inhabitants, the scale stopping at the 95th percentile. The circles are the members placed in a settlement — 988 Matice česká founders who joined 1895–1909 and 578 Matice moravská members of 1 March 1909 — area proportional to the count up to 22 and 36 members; an outlined dot is larger than that (Prague, Brno, Olomouc …). Hover for the numbers.

A check against the theatre measure

Spearman correlation across okresy between each Matice rate (shrunk, per 1,000) and Czech theatre companies active per 1,000 inhabitants (certain), averaged over the measure's own years: 1895–1909 for the joiners and the 1909 roster, 1880–1890 for the founders and the 1890 roster. Across all okresy both track the Czech share of the population; inside the Czech-majority okresy of Bohemia the joiners barely follow theatre.

all okresyCzech share >= 0.5Böhmen, Czech >= 0.5Mähren, Czech >= 0.5
Matice česká joiners 1895–1909 · theatre 1895–1909+0.58+0.22+0.02+0.08
… joiners who paid in full · theatre 1895–1909+0.34+0.15+0.04-0.07
Matice česká founders 1831–1890 (not used) · theatre 1880–1890+0.66+0.41+0.27+0.03
… local founders only · theatre 1880–1890+0.53+0.36+0.23+0.21
Matice moravská 1890 (not used) · theatre 1880–1890+0.19-0.03+0.27+0.45
Matice moravská 1909 · theatre 1895–1909+0.20-0.20-0.00+0.24
combined · theatre 1895–1909+0.60+0.10+0.02+0.24

Open

  • Rulings. Whether posted members count is for the author to rule. They are the society's core: of the 973 joiners of 1895–1909 placed in a Bohemian okres, 329 are priests, teachers and officials, 323 students, 212 other occupations and 109 unknown; leaving the posted out keeps 644 (Spearman 0.90 with the full measure), keeping only the other occupations 212 (0.74, with 50 of 103 okresy at zero). In the German-majority okresy 20 of 29 joiners are posted. Ruled: the denominator, both (D-186); which Matice moravská list stands for Moravia, 1909 (D-182); Bohemia's, the joiners (D-184), of 1895–1909 (D-188).
  • The joiner, defined. Dated by the first payment, not by completion, and counted over 1895–1909 (both ruled 2026-11-03, D-184, D-188).
  • What the joiners carry. They rank okresy much as the old founders do across Bohemia, but inside its Czech-majority okresy they do not track Czech theatre of the same years (the pilot sets each measure against theatre of its own years, THEATRE_YEARS). Many joiners are pupils and students whose parents paid.
  • Unmatched. What is left is mostly small places the 1910 table does not hold and places abroad with no marker; their readings are kept for a look by eye.
  • Persons. Nothing reaches a legionnaire yet; the tables stay in data/interim/matice/.

Run it (offline, about a minute):

python utils/reference_tools/places/run_matice_pilot.py

Occupation → HISCOHow it works

Czech occupation string → HISCO → social class; §9 summarises the three checks. · source: reference/OCCUPATION_CODING.md

In short

How the free-text Zaměstnání field on the legionnaire records is turned into HISCO, and from there into HISCLASS and SOCPO (HISCAM was dropped — D-30). The sibling of PLACE_MATCHING.md: that document explains how a birthplace resolves to a 1910 okres, this one how an occupation resolves to a social class. Sources and licences are in DATA_SOURCES.md §3a.

Explain it simply

Every soldier's card has one word for what he did: rolník, zámečník, obchodní příručí. To ask whether poor men deserted more often than comfortable ones, those words have to become something comparable — a rung on a ladder that a farmer in Moravia and a clerk in Prague can both stand on.

That is what HISCO is: an international coding scheme where every historical job has a five-digit number, and every number maps to a social class from 1 (elite) to 13 (unskilled). The trouble is that HISCO was built from Dutch, English and German records and contains no Czech at all. So the dictionary from Czech word to code is written here, by hand, one entry at a time — about 500 entries, which is enough to cover 98.5 % of the men because occupations follow a brutal power law: a handful of trades account for most of a population.

The matching itself is deliberately stupid. It will accept a phrase only if a whole run of words is already in the dictionary — never a fragment inside a word. That stupidity is the safety feature: a cleverer matcher, allowed to look inside words, once found the rank major hiding inside generálmajor and demoted every general in the army. Here, if nothing matches, the answer is nothing.

And the honest part: about a fifth of the coded records rest on a judgement that could have gone the other way. Rolník — a sixth of the whole sample — is read as a farmer who works his own land rather than a hired farmhand, which is a four-class difference. That single call was tested against the 1910 census's own employment columns before it was allowed to stand, and it is flagged in the data so anyone can re-run the analysis without it.

How a Czech word becomes a social class ⇱ Interactive ↕ Hover for values

One lane, four rules, tried in the order shown — an earlier rule wins, and the later ones only ever see what it left. The ribbon down the left is what is still uncoded; every step in it is a rule doing its work, and the bar inside a box is that rule against the largest. ⊘ marks a stage with explicit refusal rules. On the right, where the resolved codes land on the HISCLASS ladder. Click anything for the detail; untick a rule to drop it from the gradient below.

EliteLower middle classSkilled workersFarmersUnskilled
EVERY RECORD → A HISCO CODErules in the order the matcher tries them; an earlier rule wins132,993 inWHERE THE CODE LANDSHISCLASS, coloured by the 5-class collapseNo occupation recorded14,044 · 10.6% of all records—left <NA>, deliberatelythis rule against the largest⊘1 refusal rule(s) — click the stageTick to keep this rule in the sample belowThe whole key is curated104,293 · 87.7% of non-emptyexactone dictionary entry, no compoundingthis rule against the largest⊘2 refusal rule(s) — click the stageTick to keep this rule in the sample belowTwo curated keys, both coded7,039 · 5.9% of non-emptydualfarms his fields and works a tradethis rule against the largest⊘2 refusal rule(s) — click the stageTick to keep this rule in the sample belowAdjective(s) + a curated head noun3,790 · 3.2% of non-emptyheadthe head noun carries the codethis rule against the largest⊘1 refusal rule(s) — click the stageTick to keep this rule in the sample belowA curated key + something uncurated2,018 · 1.7% of non-emptyprefixthe key carries itthis rule against the largest⊘1 refusal rule(s) — click the stageNo rule matched — left <NA>1,809 · 1.5% of non-emptynoneguessing is not codingthis rule against the largest⊘1 refusal rule(s) — click the stage1869Higher managers — 869 (0.8%)24,736Higher professionals — 4,736 (4.2%)3866Lower managers — 866 (0.8%)45,060Lower professionals, higher and middle clerical and sales personnel — 5,060 (4.5%)56,171Lower clerical and sales personnel — 6,171 (5.4%)6751Foremen — 751 (0.7%)742,295Medium skilled workers — 42,295 (37.2%)822,957Farmers and fishermen — 22,957 (20.2%)915,959Lower skilled workers — 15,959 (14.0%)10253Lower skilled farm workers — 253 (0.2%)11340Unskilled workers — 340 (0.3%)121,110Unskilled farm workers — 1,110 (1.0%)1312,268Unskilled workers not specified — 12,268 (10.8%)−13,505a code, but no class — students and the other deliberate −1s

Does the coding drive the result? ⇱ Interactive ↕ Hover for values

The three compound rules — dual, head, prefix — are the least externally-anchored part of this method: nothing outside the project vouches for them the way the German bridge and OccCANINE vouch for the codes themselves. So the question worth asking is whether they are manufacturing the class gradient. Untick them and watch. The rates are unadjusted means with 95% Wilson intervals, which is the interval that behaves when the sample shrinks and the event is rare — as the bars widen rather than move, that is the answer.

The rule that fired is a single column in the data (occupation_match), which is what makes the filter above reproducible in three lines. Russia carries essentially all Legion desertion, so these levels are descriptive only — they are not conditioned on theatre, enrolment date or unit.


Last updated: 2026-08-02.


1. The problem

Zaměstnání is free text: 118,949 non-empty values (89.4 % of 132,993 records) over 8,001 distinct strings. It is the natural pre-war SES control — without it, "who deserted" cannot be separated from "which social stratum volunteered".

The field is heavily concentrated, which is what makes a curated dictionary tractable:

share of non-empty records
rolník alone 18.0 %
top 200 normalised keys 88.1 %
top 1,000 normalised keys 93.9 %
top 2,000 normalised keys 95.7 %

The curation skeleton carries the top 2,000 keys, of which 495 have been coded — the rest of the skeleton's records are reached by the compound rules in §4 rather than by a dictionary entry of their own, which is why coverage (98.5 %) runs well ahead of the share of skeleton keys that carry a code.

There is no Czech HISCO dictionary anywhere. The History of Work title table covers FR, SW, NL, UK, PT, NO, GE, DK, SP, CA, GR — not CS. OccCANINE, the transformer that codes occupational descriptions automatically, is trained on 13 languages and Czech is not one of them. So the Czech→HISCO step is authored for this thesis (utils/reference_tools/occupations/czech_glosses.py → occupation_hisco_curation.csv, 495 curated keys over a 2,000-key skeleton); everything downstream of the code is off-the-shelf.

2. The chain

Zaměstnání (raw Czech)
   │  _norm()                     lowercase, split on , ; / , expand marked abbreviations
   ▼
normalised key
   │  split_occupations()         exact → dual → head → prefix, token boundaries only
   ▼
curated key  ──► occupation_hisco_curation.csv ──► HISCO + status/relation/product
   │                                                        │
   │                                              hisco_authority.csv
   ▼                                                        ▼
occupation_hisco, _en, _hisco_major        _hisclass, _hisclass_5, _socpo

utils/parsing/occupations.py does the work; notebook 02 §1 calls it in the demographics cell, after the generic clean, so ? and - are already <NA>.

3. Normalisation

Lowercase, collapse whitespace, drop the connectors a / i / nebo, strip surrounding punctuation. Then abbreviations: only tokens carrying a trailing period, and only inside a longer phrase, are expanded (obch. příručí → obchodní příručí, merging 666 records with the 1,002 written out in full). Where one stem abbreviates two words, the next word decides: zem. before dělník is zemědělský (62105, HISCLASS 12), not zemský (ABBREV_CONTEXT, 1.4.0 — the 18 zem.dělník records had been an unspecified 99900, HISCLASS 13).

A list is coded one segment at a time (1.4.0). A string with , ; or / — rolník, tovární dělník — is split there and each segment matched on its own; the first coded segment is the primary occupation and the next one the secondary, as a dual. Until 1.4.0 the separators were dropped before matching, so the compound rules saw one long phrase, coded it on the last segment's head noun and filed the first job as its "qualifier" (rolník, tovární dělník → 99900 with qualifier rolník tovární): 464 records, audit 2026-09-22 B4.

That anchor is not cosmetic. Several abbreviation stems are themselves complete occupations: expanding on the bare stem rewrites cihlář (brickmaker, 164 records) into cihlářský and stavitel (master builder, 111) into stavitelský, destroying both. This is the same unanchored-matching failure the 2026-07 audit found twice elsewhere in the repo (generálmajor scoring as major); the period is the anchor here.

Diacritics are kept, with a folded fallback at lookup time, so rolnik/rolník and čišník/číšník resolve together without merging genuinely different words.

Abbreviations reach only 3.6 % of records, and case folding merges only 53 keys — normalisation is worth about half a percentage point of coverage, not more. The work is done by the dictionary and the compound rule.

4. Matching: exact, on token boundaries only

A key matches only if the whole normalised string, or a whole run of its tokens, is itself a curated dictionary entry. No substring test is ever performed. zámečník cannot be found inside a longer word; major cannot be found inside generálmajor (asserted as a unit check). This is the deliberate difference from education.py, which uses ordered substring rules.

Compounds get one of four treatments, recorded in occupation_match so any analysis can restrict to whatever it trusts:

occupation_match rule share of non-empty example
exact the whole key is curated 87.7 % rolník, akademický malíř, strojní zámečník
dual splits cleanly into two curated keys, or a list whose segments code 5.9 % rolník zámečník, rolník a obuvník, rolník, tovární dělník
head adjective(s) + curated head noun; longest matching suffix wins 3.2 % mlynářský dělník → dělník
prefix curated key + something uncurated; longest matching prefix wins 1.7 % soustružník železa → soustružník
(none) unmatched — every output column stays <NA> 1.5 %

exact gained ~3,250 records and head lost them in occupations 1.2.0: the high-volume qualified compounds are now curated whole rather than decomposed (see The qualifier is kept, below). The modifier is recorded either way — occupation_qualifier does not depend on which row of this table a string lands in.

Total coverage 98.5 % of non-empty values; HISCLASS assigned to 95.5 % (the gap is students and the other deliberate -1 codes, see §6). Every share here is measured on the 118,949 non-empty values, not on all 132,993 records — an empty cell is the absence of an occupation, not an unmatched one.

Two guards on the compound rules

Decomposition is the default, not the answer to everything, and both exceptions exist because the rules were caught inventing a class.

A qualifier that changes the trade gets the whole string curated. mlynářský dělník is a worker in a mill and the head noun is right; akademický malíř is an academically trained fine artist and the head noun is not. Left to decompose, all eleven malíř compounds — 214 records — took 93120 Building Painter, HISCLASS 9. They are now curated whole (akademický/umělecký malíř → 16130 Painter, Artist; malíř písma → 93950 Sign Painter; malíř skla → 89520; malíř porcelánu/keramiky → 89530), so they match exact and never reach the head rule. Same for brusič drahokamů → 88030 Gem Cutter (not the bare brusič's metal grinder) and zubní lékař → 06310 Dentist (not lékař's physician). The German link bridge independently returned the same code for six of these nine, which is why they carry a link_hisco beside the hand assignment.

A curated key that is really an adjective may never carry a compound. ADJECTIVE_KEYS = {lesní, mlynářský, sladovnický} in occupations.py: each is a curated key, and each is far more often a qualifier than the noun it is curated as. lesní is the forest master in 6 records and the adjective "forest-" in about 150. Because dual is tried before head, the adjective was winning the compound and becoming its primary occupation — lesní dělník (a forest labourer) and lesní zřízenec (an attendant) both coded 63220 Forest Supervisor, HISCLASS 3. Of 384 records on that code, ~150 were labourers, clerks and gamekeepers promoted to lower managers.

The rule: an adjective key is eligible only as an exact whole-string match. It can still be a dual secondary, but never a dual primary, a head or a prefix, so the compound falls through to the noun that carries it. hostinský looks like one of these and is deliberately absent — it is a genuine noun (innkeeper) and its duals are correct. The cost is 19 records over 16 strings whose head noun is not in the dictionary (lesní taxátor, lesní hosp.); they are now <NA> rather than wrongly class 3, which is the same policy as §8.

Falling through to the head noun was the right rescue, not the right answer. The adjective guard stopped forest labourers being promoted to lower managers, but it left them on whatever the bare head supplied — lesní dělník landed on 99900, the residual for a worker of unknown sector (HISCLASS 13), which is no more true of a forestry worker than class 3 was. occupations 1.2.0 curates the string properly: lesní dělník → 63230 Forestry Worker, HISCLASS 12. The same release curates lesní úředník → 63220, following lesní adjunkt, lesní správce and lesník, which reverses for that one string (12 records) the incidental 3 → 5 move the guard produced. The guard was right about the labourer and over-reached on the official; both now carry a curated code rather than a fall-through. Flagged review.

Dual occupations

rolník zámečník is a man who farms his fields and works a trade for extra money. HISCO practice (and HSN's DOUBLE table) codes the first title; here both are coded — the first into occupation_hisco, the second into occupation_hisco_2, each with its own HISCLASS — and the analytical choice is deferred rather than made by the parser. 7,039 records (6,518 before lists were coded segment-wise). occupation_hisclass_high is a convenience column carrying the higher-status of the two (numerically the lower), and equals occupation_hisclass wherever only one occupation was recorded.

The qualifier is kept (occupations 1.2.0)

head and prefix matching finds the noun and throws the modifier away. That is fine for the code and wrong for the data: strojní zámečník and zámečník are different jobs, and they desert at different rates. Within Russia, the machine fitter deserts at 3.38 % and the jobbing locksmith at 6.06 %; the bank clerk at 0.54 % against 1.64 % for a generic one. Same HISCO code until this release, same HISCLASS after it. The class scale cannot see this, and it is the largest source of within-class variation in the dictionary.

Two things changed.

1. The modifier is recorded. occupation_qualifier holds it on 13,042 records (11.0 % of non-empty, 1,534 distinct modifiers; 13,559 before 1.4.0 stopped filing the first job of a list as a modifier) — obchodní 1,973, strojní 1,427, tovární 954, bankovní 761, železniční 470, hospodářský 482.

It is extracted independently of how the string matched, by locating the curated head noun inside the key: the longest proper suffix that is a curated key, else the longest proper prefix. Keying it off the match kind — "whatever head/prefix discarded" — is the obvious implementation and is a trap: promoting a compound to a curated exact key would then silently stop recording the qualifier for exactly the records that motivated recording it, which is what step 2 does to seventeen of the highest-volume ones. dual is excluded: in rolník zámečník the leading run is a second occupation, already in the *_2 columns, and calling it a qualifier would turn a man's farm into an adjective describing his workshop. A QA invariant asserts no qualifier ever appears on a dual.

2. Where the qualifier changes the code, the compound is curated whole. Twenty-five strings, ~3,250 records, all previously head or prefix. Compounds whose code was already right (soustružník železa, tovární dělník, soukromý úředník) were deliberately not curated — the qualifier column records the modifier on head/prefix matches too, so curating them would buy nothing.

curated whole n was now HISCLASS
strojní zámečník 1,204 83930 Locksmith 84100 Machinery Fitter 7 → 7
bankovní úředník 715 39310 Office Clerk 33140 Bank Teller 5 → 5
železniční zřízenec 324 99900 residual 36090 railway operating staff 13 → 9
hospodářský úředník 124 39310 Office Clerk 21230 Farm Manager 5 → 3
stavební inženýr 123 02000 Engineer, unknown 02210 Civil Engineer 2 → 2
poštovní úředník 116 39310 Office Clerk 33170 Post Office Clerk 5 → 4
strojní inženýr 93 02000 Engineer, unknown 02410 Mechanical Engineer 2 → 2
hospodářský dělník 69 99900 residual 62105 Farm Worker 13 → 12
železniční úředník 69 39310 Office Clerk 39960 Railway Clerk 5 → 5
stavební kreslič 65 03110 Draughtsman 03140 Civil Eng. Draughtsman 4 → 4
poštovní zřízenec 55 99900 residual 37030 Postman 13 → 9
hospodářský příručí 55 45190 Shop Assistant 39310 Office Clerk 5 → 5
strojní technik 34 03200 Technician 03510 Mech. Eng. Technician 4 → 4
zubní technik 32 03200 Technician 84250 Dental Prosthesis Maker 4 → 7
stavební technik 31 03200 Technician 03310 Civil Eng. Technician 4 → 4

plus stavitelský kreslič, inženýr chemie, poštovní sluha, lesní dělník, lesní úředník, hospodářský pomocník / asistent / praktikant, lesní asistent / praktikant. 1,058 records changed HISCLASS — 417 of them 13 → 9 (railway and postal attendants), 178 5 → 3 (estate and forest officials), 126 5 → 4 (postal clerks), 91 13 → 12 (farm and forest labourers).

Amended 2026-08-26 (D-41): the estate-and-forest group is largely reversed. Adjudication put hospodářský úředník (124), hospodářský praktikant (10), hospodářský asistent (10), lesní praktikant (9) and lesní asistent (6) on 39310 Office Clerk, General — which is what the bare head úředník gives — so 159 of those 178 records go back to HISCLASS 5. Only lesní úředník (12) stays at 63220 and class 3. The compound curation's net class effect is therefore about 899 records, not 1,058. The curation is not thereby pointless for these keys: occupation_qualifier still records the modifier, and the entries now say that the compound was examined and lands on the head's code, which is a different statement from never having asked.

A further 270 moved because the newly curated keys became available to the compound rules: rolník železniční zřízenec used to collapse to the zřízenec head (class 13) and now splits as a genuine dual — farmer primary, railway attendant secondary. That is the compound machinery working, not a side effect to undo.

What the qualifier does not settle. Eleven of the fifteen above keep their HISCLASS. For those the work buys a correct code and a recorded modifier, and changes no class-based result. The honest summary: the qualifier is worth carrying as its own variable, not as a route to a different class.

Career stage, and why HISCO's status modifier could not carry it

occupation_status is HISCO's own slot for master / journeyman / apprentice / subordinate. It is populated on ~2.7 % of coded records once students are excluded, and no amount of curation will fix that: csol.cz records a job title, not a labour-force return, so for ~97 % of men the source simply never states a position.

The Czech titles do carry the ladder where they carry it at all — tkalcovský mistr, hospodářský adjunkt, poštovní aspirant, stavební podnikatel — so occupation_career_stage reads it directly off the normalised key as a whole-token scan (never a substring: mistr must not be found inside ministr). 3,502 records, 2.9 %: assistant 2,824 · master 359 · apprentice 153 · senior 123 · owner 37 · independent 3 · leaseholder 3. Where a title carries two stage words the highest rung wins.

Three limits, stated because they bound what the variable can be used for:

  • It is descriptive. It never touches hisco or the class. A master weaver and a weaver are the same HISCO code; whether the rung should move the class is an analytical question, not a coding one.
  • The ordinal mixes two ladders. occupation_career_stage_ord (1–8) folds the guild ladder (učeň → tovaryš → mistr) together with the clerical/estate one (praktikant → adjunkt/asistent/příručí → vrchní), and ownership is a third axis rather than a rung. Use the string column for anything that matters.
  • 2.9 % is the ceiling, not a starting point. A naive token scan suggests 14.5 %, but 16,224 of those records are dělník, zřízenec or příručí standing alone — occupations in their own right, not stages of a trade. Counting them would double-code. nádeník (day labourer) is excluded for the same reason: it is an employment status and a job, and it belongs with the 1910 census day_laborers column, not here.

5. Where the codes come from

Three sources, in this order of authority:

  1. German link bridge. Each Czech key gets a German gloss (obuvník → Schuhmacher). The gloss is matched exactly against the 1,199 German labels in hisco_occupation_link_combined.csv; an unambiguous hit is taken as the code. German is the right bridge — these are 1910 Bohemian, Moravian and Silesian titles whose German equivalents the HISCO project already coded. A gloss resolving to more than one code is not auto-assigned; it is flagged for review.
  2. English label match against the authority's own label_en, exact only.
  3. Hand assignment (HISCO_OVERRIDE) for the 434 keys the German list does not reach, and for every judgement call. Each code was read off hisco_authority.csv — none was recalled from memory — and each carries a one-line note.

Codes decided by hand always win over the bridge, so a documented judgement cannot be silently overwritten on a re-run.

A fourth thing runs after all three and assigns no codes: ADJUDICATED, the answer sheet of the 2026-08-26 conflict workbook (D-41). It records keep, recoded or open for each of the 181 keys a human was asked about, writes that to the curation's verdict column, and is the only writer of review — so a word still flagged is one nobody has settled, not one nobody has read. The code a recoded verdict implies lives in HISCO_OVERRIDE like every other hand assignment, never in the register, and run_link_bridge fails outright if a recoded key has no override entry. 41 keys were recoded (4,359 records; 23 of them move HISCLASS, 1,434 records), 130 kept, 10 left open. Three answers were reconciled the same day against the rest of the dictionary rather than applied as given — knihvedoucí back to účetní's 33110, profesor back to 13290 (a gymnázium master was styled profesor in 1910), and hospodářský úředník onto 39310 rather than a code naming a government body he did not work for.

Three of the recodes buy a sharper five-digit code at the cost of the class: HSN gives 95530 Stucco Plasterer (štukatér, 95) and 79475 Glove Cutter (rukavičkář, 44) no HISCLASS at all, and 95240 Cement Finisher (cementář, 55) only under a modifier the source does not supply. That is 199 records that now carry a better code and no class — a deliberate trade, named here because nothing downstream will say so.

6. Judgement calls worth knowing about

These move HISCLASS for a large share of the sample and should be checked before any result leans on them. They were all flagged review in the curation until the 2026-08-26 adjudication (D-41); each now carries a verdict instead, and the ones below were all answered keep.

  • rolník (21,633) → 61110 General Farmer, HISCLASS 8 (settled 2026-08-02, D-33). The single most consequential decision: 18 % of the sample. The alternative, 62105 Farm Worker, General (HISCLASS 12), would move a fifth of the Legion from "self-employed farmer" to "unskilled farm worker". rolník in Czech usage denotes a holder — a plnoprávný hospodář on his own land, living from its yield — not a hired hand; zemědělský dělník, čeledín and nádeník carry the labourer codes separately. domkář (cottager) and malorolník are deliberately not 61110 — they take 61115 Small Subsistence Farmer (Husbandman), HISCLASS 10, the rung HISCO provides between the farmer and the farm labourer. A cottager held a cottage and a garden plot and lived by craft or day-labour; ranking him with holders of ten hectares would be the same category error in the opposite direction.

This was the dictionary's one load-bearing decision that no check could reach, and it now has a primary source. The German bridge only confirms Landwirt → 61110, OccCANINE is fed that same gloss, CZ-ISCO does not contain the word, and education cannot discriminate (rolník 1.13 against domkář 1.00 — schooling did not track landholding). The 1910 Volkszählung Berufsstellung columns can. Within-agriculture composition over 99 okresy and 15,584 farm-occupation legionnaires gives Spearman +0.402 between the legionnaire own-account share and the census Selbständige share, monotone from estate-dominated okresy (census 0.241 → legion 0.862) to smallholder ones (0.643 → 0.931), with flat placebos on nádeník and dělník. The level gap (0.907 vs 0.463) closes to +0.273 once mithelfende Familienangehörige are counted as own-account, and the residual is selection into the Legion rather than a coding defect. Decisive either way, because 62105 is hired labour and neither a holder nor a farmer's son on the family holding is that. Ecological, at okres level — see VALIDATION_OCCUPATIONS.md Check 6 and run_census_berufsstellung_check.py. - dělník (9,627) → 99900 Worker, No Further Information, HISCLASS 13. Class 13 is HSN's own addition for exactly this case: unskilled, but whether industrial (11) or agricultural (12) is context-dependent and the source does not say. Leaving it at 13 is the honest answer; forcing 11 would invent information. 12,268 records land here (build of 2026-09-23). - student (2,678) and the other education statuses → HISCO −1, status 41. HISCO does not treat study as an occupation, and the HSN scheme deliberately excludes status 41 from HISCLASS. They therefore carry no social class — but they remain identifiable through occupation_status, rather than being dropped or forced into a class. - zámečník → 83930 Locksmith, on a smaller and cleaner set than before. Schlosser also links to 84100/84105 Machinery Fitter and 87105 Plumber; in 1910 Bohemian industry the trade is often a metal fitter. Two things have since narrowed this. The 1,204 strojní zámečník records — the ones the machinery-fitter reading was really about — are now curated to 84100 in their own right (D-29), leaving ~1,900 bare zámečník. And HISCAM, which was the only scale on which the choice moved anything (60 vs 66), is no longer emitted (D-30). HISCLASS is 7 on every candidate, so what remains at stake is the 5-digit code alone. Still flagged review. - hudebník (567) → 17190, HISCLASS 2 (higher professionals), which flatters a village bandsman. HISCO has no lesser musician code. - hajný (139) → 64960 Trapper and Hunter, HISCLASS 10 (settled 2026-08-02). A forest warden is estate staff — sworn, housed in a hájovna, patrolling against poachers — so 63220 Forest Supervisor (HISCLASS 3) makes him a lower manager and 63230 Forestry Worker (HISCLASS 12) ranks him with the men who fell the timber. 64960 is the rung between them, and the OccCANINE second coder returned exactly 64960 for Waldheger independently. Same reasoning as domkář → 61115 above. SOCPO (2) and HISCLASS_5 (5) are identical under 63230 and 64960, so only the 13-class HISCLASS moves. myslivec (6) goes with it, and for that word the hunting reading is unambiguous. - železničář (47) → 99900, not 99999. The German bridge sends Eisenbahner to 99999 Workers Not Elsewhere Classified, a unit-group code HSN never gives a HISCLASS — so the railwayman was being coded and then silently declassed. 99900 is the documented residual for exactly this case, the same one dělník and zřízenec take. - zřízenec (683) → 99900. "Attendant" names an employment grade, not a task. So does čsd (State Railways) — coded −1 and flagged, because it says who employed the man, not what he did. - abiturient (18) and žák (5) carried status 43, which does not exist. The HISCO status list is 11–13, 21–24, 31–34, 41, 42, 51, 52. An abiturient has passed the maturita and is a graduate (42, as absolvent); a žák is a pupil (41). - Master craftsmen. tkalcovský mistr, zednický mistr, tesařský mistr take the trade's code plus HISCO status 21 (Master), which is what the modifier axis is for — the grade is recorded without the word mistr swallowing the occupation.

7. Why the authority is keyed on four fields

hisco_authority.csv is keyed on (hisco, status, relation, product), not on the HISCO code alone. In the HSN release that four-field key determines HISCLASS, HISCLASS_5, HISCAM and SOCPO exactly (HISCAM is still built into the authority; it is simply no longer emitted as a column — D-30) — 5,762 keys, zero conflicts — whereas 806 of 1,346 codes (59.9 %) take more than one HISCLASS depending on the modifiers. Status can move an occupation to another class; any value on relation excludes it from HISCLASS entirely; status 41 (student) likewise. Keying on the code alone would silently pick one of several classes. build_hisco_authority.py asserts the dependency rather than trusting it.

8. What is deliberately left <NA>

  • 14,044 records with no Zaměstnání at all. code_occupation(pd.NA) returns None and every output column stays <NA> — asserted in the notebook 02 QA gate, because this is the exact shape of the audit's worst defect (~26k empty cells fabricated into exit_N).
  • 1,809 non-empty values (1.5 %) that no rule matched — the surviving tail averages barely over one record per distinct string: misspellings, one-off compounds, and genuinely rare trades.
  • Every HISCO sentinel carries a code and no stratification scale at all — not HISCLASS, not HISCLASS_5, not SOCPO (occupations 1.3.0, D-42). -1 is "no occupation", -2 "an employer or institution was named instead of a job", -3 "nothing recorded"; a scheme that ranks occupations cannot rank a refusal to name one. label_en and hisco_major are kept, since they describe the code rather than the man.

This was believed to hold before 1.3.0 and did not. For a sentinel there is no occupation, so the status modifier alone carried the scales, and HSN's crosswalk reads a modifier as a social position where this project reads it as a refusal to code. student (−1, status 41) had no HISCLASS — which is why the exclusion looked enforced — but carried SOCPO 42, middle class, supervisors of skilled workers, on 3,098 records, 19 % of that cell. absolvent and abiturient (−1, status 42) were worse and in the headline variable: HISCLASS 2, Higher professionals and SOCPO 5, Elite, for 160 men whose qualification was a school-leaving certificate — 3.3 % of HISCLASS 2 and 5.3 % of the elite. HSN's values are real, with 378 and 9 observations behind them; they answer a different question. A QA invariant now asserts the exclusion on both scales and both occupation fields, which no check previously did — SOCPO was not covered by any invariant at all.

The tail is left uncoded rather than guessed, following the same policy as religion.py.

9. Validation

Three external checks and three self-checks. Full results and the disagreements are in VALIDATION_OCCUPATIONS.md; the same evidence sits beside the unit and rank checks in the VALIDATION.md register.

External — a source outside this project that could contradict it:

Check Tests Result Blind to
German link bridge (run_link_bridge.py) the HISCO code for a German gloss 39 of 501 keys coded by the HISCO project itself, not by us; 15 ambiguous glosses refused rather than guessed the 460 hand-assigned keys, every qualified compound (no Maschinenschlosser in the list) — and it starts from a gloss we wrote
OccCANINE (run_occcanine_check.py) HISCLASS and the HISCO major group 88.2 % of records on HISCLASS, 96.7 % on the major group Czech is not among its 13 languages, so it had to be fed our German gloss
CZ-ISCO (run_czisco_check.py) the Czech reading itself — what the word means in English, per ČSÚ reaches 57 % of coded records; 15 keys / 457 records disagree the historical core (rolník, hajný, příručí, tkadlec, mlynář, cihlář) is absent; no path to a HISCO code

The three are not redundant: they test different links in the same chain. The first two both begin from a German gloss this project authored, so neither can test whether the Czech word was read correctly — which is exactly and only what the third does.

Self-checks — these catch a build disagreeing with its own rules, not with the world:

Check Holds
QA gate invariants (data_checks.py v1.4.0) missing stays missing; every emitted code exists in the authority; HISCLASS ∈ 1..13, HISCLASS_5 ∈ 1..5; coverage ≥ 93 %; occupation_hisclass_high never invents a class; every coded record carries a major group; every emitted status/relation is a real HISCO modifier
matcher_selftest(), 17 cases generálmajor ≠ major; cihlář survives normalisation; akademický malíř matches whole; lesní úředník reaches úředník; hostinský, řezník stays a dual
Coding robustness the desertion gradient across classes does not move when the compound rules are switched off

The last two QA invariants were added on 2026-08-02 after both failed silently for months.

10. Rebuilding

python utils/reference_tools/occupations/download_hisco_sources.py
python utils/reference_tools/occupations/build_hisco_authority.py
python utils/reference_tools/occupations/build_occupation_curation.py --top 2000
python utils/reference_tools/occupations/run_link_bridge.py

Then re-run notebook 02. build_occupation_curation.py is non-destructive: it recomputes frequencies and appends new keys, but never overwrites a hand-entered code.

Education ladderHow it works

The 1869 Austrian school ladder — and why the apprentices' continuation school is not a rung on it. · source: reference/EDUCATION_LADDER.md

Status. Built. utils/parsing/education.py is v2.1.0 (v2.0.0 rebuilt it on the two sources below; 2.1.0 added the tie-break, semesters and word anchoring, D-51); schema.py carries the columns; notebook 02 was last re-run 2026-09-23 and data/final is current, QA gate 107/107 — the first build to carry the 2.1.0 columns (education_semesters, education_top_imputed), which the 2026-09-03 build predated (audit 2026-09-22 A6). D-08 stays Open — not because the code is unfinished, but because the ladder is established for Cisleithania only and the Hungarian half of the sample rides on it unvalidated (§5).

Sources. Three: two secondary works that divide the ladder between them, and — since 2026-09-07 — the primary source that checks it.

  • Moore, Scott O. (2020). Teaching the Empire: Education and State Loyalty in Late Habsburg Austria. West Lafayette: Purdue University Press (Central European Studies). library/books/Moore-2020_Teaching-the-Empire_Habsburg-Schooling-and-State-Loyalty.pdf — the general ladder (§1). Page references are printed book pages (add 11 for the PDF page).
  • Lassnigg, Lorenz (2015). 'Muddling through' and historical institutionalism. IHS Sociological Series WP 108, Vienna. library/literature/Lassnigg-2015_Muddling-Through_Austrian-VET-System.pdf — the vocational branch (§2). Page references are printed pages (add 10 for the PDF page).
  • k.k. Statistische Zentralkommission (1912). Statistik der Unterrichtsanstalten in den im Reichsrate vertretenen Königreichen und Ländern für das Jahr 1908/1909 (Österreichische Statistik, XCIII. Band, 1. Heft). Wien: k.k. Hof- und Staatsdruckerei. library/education/OeStat-1912_Bd-XCIII-1_Statistik-der-Unterrichtsanstalten-1908-09.pdf — the administration's own taxonomy and counts (§2a). Roman folios are the Einleitung, arabic the tables; reference/education/unterricht_page_index.csv maps both to PDF pages.

Moore is a study of civic education, not of school administration, but chapter 1 ("The Development of Education and Civic Education in Austria", pp. 17–49) sets out the structure of the Cisleithanian system in the exact terms our field uses: which school, entered from where, for how many years, at what ages. That is what the Vzdělání strings encode.


1. The ladder, 1869–1918

Everything below is the settlement reached by the Reichsvolksschulgesetz of 14 May 1869 and still standing in 1914 — i.e. the law under which every man in our sample was schooled (birth cohorts ≈ 1875–1900).

Rung German Czech (as abbreviated in Vzdělání) Entered from Length Ages Moore
Compulsory elementary Volksschule ob. — obecná škola age 6 5–8 yrs 6–14 30
Compulsory upper elementary Bürgerschule mě. — měšťanská škola after 5 yrs Volksschule 3 yrs 11–14 30
Academic secondary Gymnasium gym. age 10 (from Volksschule) 8 yrs 10–18 30
Technical secondary Realschule reálka age 10 7 yrs 10–17 30
Hybrid secondary Real-Gymnasium (rare) age 10 8 yrs 10–18 30–31
Girls' secondary Lyzeum (absent — male sample) — — — 31
Teacher training Lehrerbildungsanstalt uč.ústav, učitelský ústav after Bürgerschule, age 15 4 yrs 15–19 29, 31, 184
Tertiary Universität / Technische Hochschule univerzita, technika Gymnasium/Realschule Matura — 18+ 30, 24

Four things in that table matter for the coding, and all four are counter-intuitive:

  1. The eight compulsory years are ob. 5 + mě. 3. The 1869 law added two years to the Theresian six and set compulsory attendance at ages 6–14; "students could achieve this by attending Volksschule for five years followed by an additional three years at a Bürgerschule" (p. 30). So the most common structured string in our data — ob. 5 mě. 3 (983 records, plus every spacing variant) — is the legal minimum, not an achievement. Equally, ob. 8 (2,813) is the same eight years taken entirely in a Volksschule with an upper stage. ob. 8 and ob. 5 mě. 3 are equal in years and unequal in curriculum; they must not be coded as if the second were a full rung above the first.
  2. The Bürgerschule is not "lower secondary" in the modern sense. It is the compulsory upper elementary stage, terminal by design: it "provided additional general and vocational education to those students not planning to attend university" (p. 24, on the 1849 Entwurf that created it). It is a branch of the compulsory school, not a step toward the Gymnasium.
  3. Gymnasium and Realschule are entered at age 10, in parallel with — not after — the Bürgerschule (p. 30). The ladder forks at ten. A record showing ob. 5 gym. 8 (72) or ob. 5 reálka 7 (81) is a boy who left the compulsory track at ten; ob. 5 mě. 3 is one who did not. This is a genuine ordinal break, and the one the field measures most reliably.
  4. Teacher training sits on top of the Bürgerschule, not on top of a Matura. Teachers "began their training after completing Bürgerschule at the age of fifteen, receiving an additional four years of schooling at a teacher training institution" (p. 29), and Austrian policy deliberately refused to route them through Gymnasium and university (p. 29; repeated p. 184). uč.ústav (1,583 records) is therefore not tertiary and not equivalent to a Realschule leaver — it is a four-year vocational secondary qualification entered two rungs lower. The current module has it in the same bucket as gym.

Administration, and why the ladder is uniform enough to code

The May Laws kept a federalised system: crownland → district → local school board, with the ministry controlling only "the universities, Gymnasien, Realschulen, and other secondary schools" and Volksschule/Bürgerschule matters left entirely to the provincial boards (p. 31). Despite that, Moore finds the elementary curriculum "remarkably similar throughout Austria" from the 1870s to the 1910s (p. 32), and the ministry's policies "applied to all public schools, regardless of its language of instruction" (p. 32) — so the Czech-language schools our legionnaires attended ran on the same ladder as the German ones. That is the licence to treat ob./mě. as one national scale.

The variation Moore says is real, and that we can measure

  • Urban vs rural is the binding constraint — not province, gender or language (p. 33). School hours were set by school size: a one- or two-room rural school taught lower and upper grades in shifts, and after the conservative turn of the 1880s–90s provincial boards let rural schools drop to half-day attendance — in Upper Austria, 98 of 124 one-room schools by 1913 (p. 34). Two men both recorded ob. 8 did not receive the same schooling, and the difference runs along the urban/rural axis we already carry at okres level. An argument for using birth_okres covariates alongside the education ordinal, not for abandoning the ordinal.
  • Eight years was politically contested throughout (pp. 34–35): Das Vaterland argued six years sufficed and that more schooling made worse recruits — "additional years in school would actually harm the quality of recruits, because they would become too inquisitive and prone to question authority". Useful framing for the paper — contemporaries themselves theorised schooling → obedience — but not a coding input.

2. The vocational branch, which Moore does not reach

Source. Lassnigg, Lorenz (2015). 'Muddling through' and historical institutionalism — Explanatory devices for the long term development of the dualistic Austrian VET system. IHS Sociological Series Working Paper 108, Institut für Höhere Studien, Vienna. library/literature/Lassnigg-2015_Muddling-Through_Austrian-VET-System.pdf. Page references are printed page numbers (add 10 for the PDF page).

Lassnigg is an institutional history of Austrian vocational education written to explain the present-day system, so most of it is about the Republic. Its historical section (pp. 11–16) covers exactly the ~17 % of our field that Moore leaves dark, and it settles the largest open question in §5 below.

The pok. question is settled: the Fortbildungsschule is compulsory schooling, not secondary

Three findings, each independently decisive:

  1. It was legally part of compulsory mass schooling. Lassnigg names as a critical juncture "the very early decisions to treat the accompanying part-time school as part of compulsory (general mass) schooling, and consequently to enclose these institutions into the decentralised local governance structures of compulsory schooling, whereas the fulltime schools were developed from the central level" (p. 18). The Fortbildungsschule sat in the same communal governance box as the Volksschule and Bürgerschule — not with the Realschule and Gymnasium, which Moore (p. 31) puts under the ministry.
  2. It selected on nothing. "Access to an enterprise based apprenticeship only depends on the decision of the training enterprise, without being based on formal educational credentials; as a second step the obligation to attend Berufsschule takes effect after an apprenticeship contract is concluded" (p. 14). By contrast, the full-time VET schools required "academic lower secondary or good grades at Bürgerschule/Hauptschule, plus entrance exams" (p. 15). So pok. in a record carries no information about prior school performance; obch., prům. and odb. do.
  3. Its ancestry is popular, not academic. The predecessors "were originally rather related to popular mass education (Sunday and evening schools)" (p. 11) — Hankiewicz's genealogy runs Christenlehre → Wiederholungsschule → Sonntagschule → Zeichenschule → … → Fortbildungsschule (p. 11 n. 5). Apprenticeship itself descends from the guilds and was regulated by the Gewerbeordnung, i.e. trade law, not education law (p. 11).

That places pok. (8,805 records) squarely inside the compulsory-plus tier, next to mě., and confirms it is currently promoted seven rungs too far. It also explains pok. vs pok.prům.: two categories evolved, "general (across a number of trades: allgemein-gewerbliche Fortbildungsschulen) and specialised (for a certain trade), depending on the local numbers of apprentices (except in cities the former dominated quantitatively)" (p. 13).

The rest of the branch

Token Institution What Lassnigg establishes p.
prům., vyšší prům. Staatsgewerbeschule Full-time, central jurisdiction, created by the Dumreicher plan from the 1880s as "a network of trade and engineering schools across the monarchy"; the higher tier linked upward to the Polytechnic/Technische Hochschule. A genuine secondary institution — and a different thing from pok.prům. 13
obch. Handelsschule Medium level, mostly private, "much more stand-alone medium level schools than higher level colleges existed" 13, 15
obch.ak. Handelsakademie Higher level college; the private commercial schools "tried to acquire state recognition for their certificates"; the Matura exam here was voluntary 13, 15
odb., hosp. Fachschulen (trade, commerce, agriculture, home economics) Four sectoral traditions grew "at local level in close relation to the economic activities", with workshops at the core; agriculture stayed outside the education ministry entirely 13
reálka Realschule (1805) Originally the vocationally oriented alternative; became fully academic only in stages (1848, 1869, 1927, 1962). In our period it is explicitly "a mediating mechanism between VET schools and higher education" 13
mě. Bürgerschule (1869) "Established at the three upper grades (6–8) of primary school for purposes of commerce and administration" — sharpens Moore's terminal-branch reading with a purpose 13

Two things that bear directly on interpretation

  • The full-time/apprentice split is partly a liquidity split, not an ability split. Full-time VET schools "provided the same credentials as an apprenticeship, however, whereas apprentices received a wage, attention to fulltime schools had to be paid for" (p. 15). A man with prům. or obch. rather than pok. differs from an apprentice in family resources as well as in schooling. This is a caveat the paper should state wherever the education variable is read as human capital.
  • Regulation was regional in our period. The Fortbildungsschule was governed at Land and communal level, with the regional laws set "between 1907 and 1931" (p. 13) and the apprenticeship/full-time separation not completed until 1925 (p. 13). So for the 1875–1900 birth cohorts the vocational branch was genuinely heterogeneous across crownlands, in a way the compulsory ladder (§1) was not. Cross-okres comparisons of pok. are weaker than cross-okres comparisons of ob./mě.

What Lassnigg cannot give us

His quantitative series (Table 1) starts in 1924 and covers the Republic of Austria, not the Bohemian lands — so it is background, not a benchmark. It is still worth one line in the paper: his generational reconstruction (Table 2) puts the cohorts born 1865–1888 and 1883–1923 in upper secondary education in 1880–1907 and 1898–1942 respectively, which brackets our sample, and the first observation (1924) finds 82 % of 14–18-year-olds not in education at all, with apprenticeship at 11 % against 2 % each for medium VET, higher VET and the academic track. Even allowing for territory and date, that is the order of magnitude: post-compulsory schooling of any kind was rare, and our gym./reálka/univerzita records are a thin, selected top.

He is silent on Hungary, and on the Czech lands specifically.


2a. The administration's own taxonomy (1908/09)

Everything above §2 is secondary literature. Moore describes the law; Lassnigg's quantitative series begins in 1924, in the Republic of Austria, on territory this sample does not cover. §5 of the first version of this document said as much, and named the filler: "the Austrian statistical administration's own school taxonomy … which classifies schools rather than describing them."

That source is now in hand — Österreichische Statistik XCIII/1 — and it was read against the ladder on 2026-09-07. The full results are in VALIDATION_EDUCATION.md; what bears on the ladder itself:

The state files the continuation school apart, in both branches. p. XX divides the vocational branch into a) Fachschulen für gewerbliche Hauptgruppen, b) Fachschulen für einzelne gewerbliche Zweige, c) Handwerkerschulen and d) gewerbliche Fortbildungs- und Zeichenschulen; p. XVI divides the commercial one into 24 höhere Handelsschulen, 115 kommerzielle Tagesschulen and 168 kaufmännische Fortbildungsschulen. And in both it is the mass institution: Böhmen has 504 gewerbliche Fortbildungsschulen with 50,836 pupils against 9 Staatsgewerbeschulen with 3,518 and 85 Fachschulen with 4,970 — 84.0 % of all vocational pupils in the Land. §2's reading of pok. is the administration's own, and the v1.0.0 reading would have put the largest and least selective school kind in the empire level with the Gymnasium.

The Bürgerschule was a selective step, not the ordinary sequel to the Volksschule. Table 34 counts 1,266 Bürgerschulen against 20,975 allgemeine Volksschulen — 5.7 % of public elementary institutions. In Böhmen 608, of which 374 čecho-slavisch and 234 deutsch. Rung 2 has to be read at that rate.

⚠ The eight-year standard behind education_complete does not describe a school that existed. Table 34 also counts allgemeine Volksschulen by number of ascending classes:

classes 1 2 3 4 5 6 7 8
Cisleithania 6,940 6,433 2,372 1,914 2,624 620 62 10
Böhmen 1,112 1,703 951 562 1,114 97 4 2

Ten eight-class Volksschulen in all of Cisleithania; two in the Czech lands; 1,114 five-class schools in Böhmen alone. Moore's eight years (p. 30) are a legal obligation spanning institutions — Volksschule 5 + Bürgerschule 3 — and Table 34's classes are an institutional form. Vzdělání's bare ob. N does not say which of the two it records. This is logged as D-53 and is not yet fixed; see §5.

The liquidity caveat has a number. At the Staatsgewerbeschulen (p. XXII), 65.0 % of pupils were wholly exempt from Schulgeld and 1.2 % half — so §2's "full-time VET had to be paid for" is true of the fee schedule but not of most pupils' families, and the ability/liquidity distinction is weaker than the fee alone suggests.

Parallel entry, in the source. p. VIII tabulates the Vorstudien der Geprüften for Gymnasium entrants as allgemeine Volks- oder Bürgerschulen against Privatunterricht — Böhmen 3,191 of 3,223. §1's age-ten fork, from the administration rather than from Moore.

Two things the volume adds that the ladder had no place for. The Notschule (emergency one-teacher school, 379 in Cisleithania) and the Handwerkerschule (13 in the empire). Neither is tokenised; neither matters for this sample — 1 Notschule in the Czech lands against 277 in Tirol — but a study of Alpine births would need the first.

⚠ The volume is Cisleithanian by definition ("in den im Reichsrate vertretenen Königreichen und Ländern"), so none of the above reaches Hungary, and D-08 stays open for exactly the reason it already was.


3. What the field contains

Vzdělání is non-empty on 105,992 of 132,993 csol records (79.7%), across 10,060 distinct strings. It is not a level: it is an itinerary — a sequence of school types, each optionally followed by the number of years completed (ob. 5 mě. 3 pok. 3). Eighteen token families reach 98.9% of the non-empty strings:

Token family Institution Records % of non-empty
ob. / ob 5 / 8 ob. Volksschule 77,275 72.9%
mě. / 3mě Bürgerschule 23,517 22.2%
pok. pokr. pokrač. pok.prům. Fortbildungsschule (see §2) 8,805 8.3%
obch. obch.šk. obchodní škola Handelsschule (2 yrs) 4,141 3.9%
reálka Realschule 4,138 3.9%
prům. průmyslovka vyšší prům. Staatsgewerbeschule 3,853 3.6%
gym. Gymnasium 3,540 3.3%
odb. Fachschule 2,281 2.2%
hosp. hosp.šk. landwirtschaftliche Schule 1,676 1.6%
uč.ústav Lehrerbildungsanstalt 1,583 1.5%
univerzita (+ práva, medicína…) Universität 1,493 1.4%
obch.ak. obchodní akademie Handelsakademie (4 yrs, Matura) 1,348 1.3%
technika Technische Hochschule 1,033 1.0%
analfabet — 445 0.4%
kadetka, vojenská Kadettenschule 256 0.2%
konzervatoř, umělecká conservatory / art academy 92 0.1%
? - placeholder 190 0.2%
bohoslovecká, seminář seminary 10 0.0%
unmatched — 1,135 1.1%

(Families overlap — a string names several schools — so the column sums past 100%.) The unmatched tail is mostly spelled-out or truncated variants of the same institutions (učit.ústav, práv.fak., lesnická škola, střední škola, umprum, selské učiliště): a dictionary problem, not a new rung.

The year counts are usable. Because the law fixes the length of every rung, a count that falls short of it is informative: ob. 3 (1,212) or ob. 2 (703) is an interrupted compulsory education, and under the half-day regime above it plausibly means very little schooling at all. A count that meets it (ob. 5, mě. 3, reálka 7, gym. 8) means completion. A truncated secondary count (gym. 4, reálka 4) means the lower Gymnasium/Realschule without Matura — a real and recognised exit point in the Austrian system, not a parsing artefact.


4. What this catches in the current implementation

utils/parsing/education.py v1.0.0 collapses everything into a five-level ordinal by first-match substring rules. Against the ladder above, four defects:

  1. pok. is scored "Upper secondary" and is not secondary at all. The pokračovací (živnostenská) škola — the gewerbliche Fortbildungsschule — is the compulsory part-time continuation school for apprentices, attended a few hours a week alongside a trade apprenticeship after the eight compulsory years. It is the bottom of the vocational branch, not a rung above the Bürgerschule. Because its rule fires before mě. and ob., every ob. 5 mě. 3 pok. 3 record — the classic apprentice itinerary — currently lands in the same bucket as gym. 8,805 records, 8.3% of the non-empty field. This is the largest single error in the variable, and it confirms ROADMAP B3.
  2. Dot-less and long variants fall through. pokr., pokrač., 3mě, 2mě do not contain the literal tokens pok. / mě.; hosp., odb., kadetka, konzervatoř have no rule at all, and the 1.1% unmatched tail returns None unless an ob. elsewhere in the string rescues it at Primary.
  3. uč.ústav is coded level with gym./reálka. Per Moore p. 29 it is entered from the Bürgerschule at fifteen, runs four years, and is explicitly not an academic track. It belongs a rung lower, or on a separate vocational axis.
  4. First-match-wins on a string that describes a whole career. reálka technika (99), gym. univerzita (44), ob. 5 mě. 3 uč.ústav 4 (38) are itineraries; the rules read only the first token they happen to hit. The field supports highest rung reached plus years at each rung, and should be parsed that way rather than classified.

This is the same failure shape logged elsewhere in the project: an unanchored substring rule that is narrower than its source (cf. ranks 1.4.0, occupations 1.1.0).

5. What is still open

Revised 2026-09-07, after the external check in §2a. Three of the four items below moved; one did not, and one is new.

  • The vocational branch — the taxonomy is closed, the duration is not. §2a settles where the administration itself put each vocational institution, which is what the ordinal needed and what §5 previously asked this class of source for. What the 1908/09 volume still does not give is duration: it classifies and counts schools without stating the Fortbildungsschule's weekly hours or the Fachschulen's course lengths, which varied by Land and by trade (regional laws 1907–1931). So pok. 3 and odb. 2 remain years of a thing whose size we do not know, education_complete stays <NA> across that branch, and no education_years_total is emitted.
  • Hungary. Unchanged, and now the only reason D-08 is open. Moore is explicit that his account "only appl[ies] to Austrian schools and not those in Hungary", the Ausgleich having handed Hungarian schools to Budapest (p. 36). Lassnigg is silent on Hungary too, and the 1908/09 volume is "in den im Reichsrate vertretenen Königreichen und Ländern" — Cisleithania by definition, so it cannot help either. The ~10,500 Slovakia-born records are excluded from every test in VALIDATION_EDUCATION.md. Slovak and Rusyn legionnaires were schooled under a different law (Eötvös 1868, then the Apponyi laws of 1907), with a different ladder and a Magyarising language policy. One ordinal cannot silently span both frames; at minimum this wants a Cisleithanian/Transleithanian indicator interacted with the level, the way birth_okres and birth_jaras_id are already kept apart. The fix is a Hungarian source, not another pass over the strings. Sources located 2026-11-03. (1) The law itself, Act XXXVIII of 1868 on public elementary education, in full on Ezer év törvényei (net.jogtar.hu, docid 86800038.TV; its robots.txt bars AI agents, so the text is not in library/ — save it by hand). As read there: school is compulsory from 6 to 12, or to 15 (§ 1). The everyday elementary school runs 6 years, against Bohemia's 5, and is followed by up to 3 years of a part-time ismétlő iskola (repetition school), 5 hours a week in winter and 2 in summer (§§ 48–52), which is the nearest Hungarian counterpart of the pok. continuation school. The felső népiskola (upper elementary school) is entered after the six classes and runs 3 years for boys (§§ 61–62). The polgári iskola (burgher school) is entered after the fourth class, not the fifth, and runs 6 years for boys and 4 for girls (§§ 68–69). Act XXX of 1883 governs the 8-class gymnasium and real school. (2) An English overview, Kornis, Education in Hungary (Teachers College, Columbia, 1932), free on the Hungarian Academy's REAL-EOD (record 8622); in library/education/ since 2026-11-03, not yet read. (3) The state's own figures, the counterpart of §2a: the 1910 Hungarian census, the first to ask completed schooling (secondary classes completed), by county and town; its volumes are in the KSH collection on Hungaricana (Magyar Statisztikai Közlemények, n.s. 64 per Karády 2025). (4) The Slovak side, Puttkamer, Schulalltag und nationale Integration in Ungarn (Oldenbourg, 2003), on Slovak and Romanian schooling under the Hungarian state idea, 1867–1914. The records already follow the law (build of 2026-09-23). Of Slovak births whose top rung is the elementary school, 56.2 % state 6 years, against 26.2 % at 5 and 32.5 % at 8 for Czech-lands births. A Slovak burgher school runs to 4 years (34 % of those stating years), a Czech one to 3 (62 %). So the rungs carry over, and the years at each are the Hungarian law's. What does not carry over is the completion standard: 789 Slovak records with the full six-year everyday course are scored education_complete = False, held to the Austrian length. The fix belongs with D-53 below: each frame scored against its own law.
  • ⚠ Whether a bare ob. N counts years or classes. Newly opened, and it breaks a column. §2a's Table 34 shows the eight-class Volksschule education_complete scores against existed 10 times in Cisleithania and twice in the Czech lands. Among 11,986 Czech-lands-born records with education_top == "elementary" and a stated year, 3,894 are scored complete against that form and 3,137 reading ob. 5 — the modal full Bohemian Volksschule — are scored incomplete. Logged as D-53 with two candidate resolutions: (a) set education_complete to <NA> for a bare ob. N, refusing to judge as the vocational branch already does, or (b) re-base the standard on 5. (a) is recommended; (b) resolves by assumption exactly where the source shows the ambiguity is real. Not yet applied — it changes an emitted column.
  • ⚠ How completely pok. is recorded at all. New. The Czech lands had 5.3 continuation pupils per full-time vocational pupil in 1908/09; the records name them at 0.86 to one, about 6× fewer. The continuation school was compulsory, part-time and unremarkable, and men appear simply not to have written it down. education_apprentice (6.6 %) is a lower bound on apprenticeship, not a rate, and must not be read as the share of the cohort that served one. Flow-vs-stock and selection into the Legion also contribute, and nothing available can apportion the three.
  • Whether ob. 8 ≡ ob. 5 mě. 3. Narrowed. Moore gives us that they are equal in years (p. 30) and differ in curriculum (p. 30: the Bürgerschule added practical agricultural and industrial subjects). §2a adds that they were nothing like equally common — the Bürgerschule was 5.7 % of public elementary institutions, and the state examined its teachers separately (4,618 Volksschule against 865 Bürgerschule Lehrbefähigungsprüfungen, p. XVI). So mě. marks a selective step and not the ordinary sequel. Which is the higher signal is still an assumption to be stated, not a fact any of the three sources supplies; internal evidence — rank at entry, occupation, HISCLASS — can adjudicate it the way D-33 adjudicated rolník.
  • Attendance vs enrolment. Narrowed, not closed. The half-day rural regime (Moore p. 34) means recorded years overstate contact hours, unevenly and in a direction correlated with rurality. Table 39 of the 1908/09 volume defines the thing exactly — schools where "die Kinder ganzer Ortschaften oder Weiler die Schule wöchentlich nur ein bis dreimal besuchen" — and counts them, but by Land, not by Bezirk. So there is now an administrative definition and a crownland magnitude, and still no okres covariate and nothing at the individual level.

6. The revision, as implemented (education.py 2.0.0, extended in 2.1.0)

Parse, don't classify. The module tokenises the itinerary — institution names and the numbers attached to them — and reports the highest institution attended, the years at each rung, and the track. Eleven columns, educ_clean / educ_ord keeping their old names so schema.py and notebook 03 keep working.

ord Label Institutions on this rung
0 No schooling analfabet; žádné
1 Elementary Volksschule (ob., obecná, národní škola)
2 Burgher school Bürgerschule (mě., měšťanská)
3 Vocational secondary Handelsschule (obch.) · Staatsgewerbeschule (prům.) · Fachschule (odb., lesnická, učiliště) · landw. Schule (hosp., zemědělská, selská)
4 Upper secondary Gymnasium (gym.) · Realschule (reálka) · Handelsakademie (obch.ak.) · higher Staatsgewerbeschule (vyšší prům.) · Lehrerbildungsanstalt (uč.ústav) · Kadettenschule (kadetka) · conservatory (konzervatoř, umprum) · střední škola
5 Tertiary Universität (univerzita, vš, fakulta) · Technische Hochschule (technika) · theology (bohoslovecká)
— off the ordinal Fortbildungsschule (pok., pokr., pokrač., pok.prům.) — carried by educ_apprentice

ladder_report() prints this table with the native names, tracks and standard course lengths.

6a. What 2.1.0 added (D-51)

Three things the 2.0.0 rewrite left unresolved, all found by running the parser back over the raw field rather than by reading it:

The tie-break. 1,512 records name two schools on the same rung — reálka + obch. akademie, gym. + uč.ústav, odb. + hosp. — and max() alone resolved them by insertion order, i.e. by which school the clerk happened to write first. The same pair came out both ways: obch. + odb. gave commercial on 20 records and trade on 164. It is not cosmetic: it fixes educ_track (academic vs teacher vs military) and educ_complete, whose standard course lengths differ across the tied schools (Realschule 7, Lehrerbildungsanstalt 4). The order is now written down in education.TIE_BREAK, on two principles:

  1. A generic word yields to a specific one. odb. (odborná = "specialised") and střední ("secondary") are what the source writes when it does not name the school — "odborná hospodářská škola" is one school, not two. střední is dropped outright when the record names anything on rung 3 or above (83 records move; "ob., střední" with nothing else post-compulsory still stands on its own).
  2. The terminal professional school outranks the general school that feeds it. A man who wrote "reálka, uč.ústav" finished as a teacher — §1 has the Lehrerbildungsanstalt entered from the Bürgerschule at fifteen, and it is where his schooling ends. Same for the cadet school, the commercial academy and the conservatory.

Principle 2 rests on reading the cell as a chronological itinerary, which §3 argues from the field's structure and which the data supports: on 92.4 % of multi-rung records the ordinals never fall along the string.

Semesters are not years. sem./semestr and its number are lifted out into educ_semesters (722 records) before the itinerary is scanned. They used to attach backwards to the preceding institution and land in educ_years_secondary, so "ob. 4 gym. 8 univerzita 8 sem." recorded eight years at the university. Universities counted in semesters and the Mittelschulen in years; the two must not share a column.

The scan is word-anchored. Every institution pattern now has to start at a word start. Without it technik matched inside hydrotechnika and ob inside pob. — the unanchored substring shape that ranks 1.4.0 had to remove after Fidler caught generálmajor scoring as major. The number group is deliberately left unanchored so "reálka4" still yields 4. Eight records stop parsing as a result, six of them correctly (pob., iob., dělostř.š.); the two všeob. records are a genuine loss and are left unparsed rather than guessed at.

Anchoring the scan exposed three abbreviations the source uses and the patterns did not cover, now added: univ. (55 records, previously only univer…), lék.fak. / fil.fak. / práv.fak. (the faculty abbreviation, previously only the full fakulta), and vyš.prům. (37 records, previously only the full vyšší, so they fell to the 3-year Fachschule instead of the 4-year Staatsgewerbeschule). Coverage 99.07 % → 99.10 % of non-empty cells.

A flagged floor. Where the only school named is the continuation school the ordinal is floored at Elementary — 1,112 records where educ_top names a school educ_institutions does not list. Those now carry educ_top_imputed = True and the track continuation rather than compulsory, and the QA gate asserts that this is the only case where the two disagree.

One dead branch removed. educ_complete computed the Volksschule standard as "5 if a Bürgerschule follows, else 8". The five can never be reached: the Bürgerschule outranks the Volksschule, so a record naming both has burgher as its top rung and is scored against its standard of 3. Measured on the full field: 0 records. The Volksschule standard is simply 8.

Design decisions worth naming, each traceable to §1 or §2:

  • The ordinal is attendance, not attainment. A bare reálka does not say whether the man finished, so completion is a separate column (educ_complete) that is allowed to be missing — rather than a guess folded into the level. educ_complete is pd.NA for the whole vocational branch, because §5's residue is exactly that no source gives those course lengths.
  • pok. sets a floor of 1, never a rung. A continuation school followed the eight compulsory years, so a record naming only pok. implies elementary schooling; it can raise a record from unknown to Elementary, and can never raise it past what the string states.
  • The Volksschule standard is itinerary-dependent — 8 years alone, 5 when a Bürgerschule follows (§1.1). ob. 5 alone is therefore incomplete; ob. 5 mě. 3 is complete.
  • prům. vs vyšší prům. splits on the qualifier, one rung apart, the same way occupation_qualifier splits zámečník from strojní zámečník (D-29).
  • Years attach positionally, backwards to the institution they follow (ob. 5) and forwards when the string opens with a number (8 ob., 3mě), so the dot-less variants that fell through v1.0.0 now parse.

Result on the full csol frame (132,993 records; not yet written to data/final):

v1.0.0 v2.0.0
Non-empty strings parsed 96.7 % 99.1 %
Records the pok. rule put in the gym. bucket 8,117 0

The v1.0.0 Upper secondary bucket (18,756) redistributes: 7,579 stay, 3,973 move to Vocational secondary, 2,606 to Burgher school, 4,598 to Elementary. Ladder after the rewrite: Elementary 64,876 (48.8 %), Burgher school 19,884 (15.0 %), Upper secondary 9,320 (7.0 %), Vocational secondary 7,845 (5.9 %), Tertiary 2,630 (2.0 %), No schooling 449 (0.3 %), unparsed/empty 27,989 (21.0 %). educ_apprentice is true on 8,815 records (6.6 %).

Landed 2026-09-02. Notebook 02 re-run, QA gate 66/66, data/final/persons/ and the data dictionary rebuilt with all eleven columns. One label changed between the first run and the second: rung 0 was called None, which in any value_counts is indistinguishable from a missing value — the same shape as the fabricated-exit_N bug the 2026-07-16 audit found. It is No schooling now, and in the built frame the two read apart cleanly: No schooling 449, missing 27,989.

Still to do: notebook 03's education figures import EDUC_ORDER symbolically, so they will run against the six labels, but they were composed for five and the palette and stacking want an eye. Those figures have not been re-run since 2026-08-02.

Citation

@book{moore2020teaching,
  author    = {Moore, Scott O.},
  title     = {Teaching the Empire: Education and State Loyalty in Late Habsburg Austria},
  series    = {Central European Studies},
  publisher = {Purdue University Press},
  address   = {West Lafayette, IN},
  year      = {2020}
}

@techreport{lassnigg2015muddling,
  author      = {Lassnigg, Lorenz},
  title       = {'Muddling through' and historical institutionalism -- Explanatory devices for the long term development of the dualistic Austrian VET system},
  institution = {Institut f\"ur H\"ohere Studien (IHS)},
  type        = {IHS Sociological Series Working Paper},
  number      = {108},
  address     = {Vienna},
  year        = {2015},
  url         = {https://irihs.ihs.ac.at/id/eprint/3735/}
}

@book{ost1912unterrichtsanstalten,
  author    = {{k.k. Statistische Zentralkommission}},
  title     = {Statistik der Unterrichtsanstalten in den im Reichsrate vertretenen K\"onigreichen und L\"andern f\"ur das Jahr 1908/1909},
  series    = {\"Osterreichische Statistik},
  volume    = {XCIII. Band, 1. Heft},
  publisher = {k.k. Hof- und Staatsdruckerei},
  address   = {Wien},
  year      = {1912}
}

The ladder in the data

Built by notebooks/03_visualisation.ipynb from data/final. The stack is coloured on the sequential ramp, not the categorical palette, because the variable is ordinal: light at the bottom rung, navy at the top. Click to enlarge.

Educ by theatre
Figure 1 — Educ by theatre
output/figures/descriptives/composition/educ_by_theatre.png

Rank laddersHow it works

The two rank systems and the reset. · source: reference/ranks/rank_ladders.md

In short

Reference capture of the military rank hierarchies relevant to the thesis. The core mechanism (relative rank deprivation on Legion enrollment) depends on placing every habsburg_rank and every legion_rank on a comparable ordinal scale, so the two ladders are recorded here with German equivalents to make the crosswalk explicit.

Explain it simply

The central question of this project is what a demotion does to a man. A legionnaire had already deserted the Austrian army once; some of them were sergeants or lieutenants when they did. In the Czechoslovak Legion many started again as privates. Did the ones who fell furthest desert again?

To ask that, the two armies' rank systems have to be laid side by side, and they do not line up on their own — different languages, different numbers of grades, and titles that mean different things in each. So both are placed on one ordinal scale using the NATO officer codes as the bridge, and the drop is simply the difference: a Habsburg sergeant who becomes a Legion private has a rank reset of minus several rungs.

The trap here is a title that is not a grade at all. Some men were entered as pracovník (labour service) or občan (civilian) — administrative holding categories on the way in, not punishments. Scored naively, a Habsburg colonel entering as a labourer looks like the most spectacular demotion in the dataset, and it never happened. Those 7,696 records are flagged, and the headline result is computed without them.

The two ladders, and what the reset subtracts ⇱ Interactive ↕ Hover for values

Left, the k.u.k. ladder a man was on when he was captured; right, the Legion ladder he entered on. Neither army's own numbering can be subtracted from the other's, so both are pinned to one common scale down the middle, on which the same Czech word always scores the same rung — and rank_reset is nothing more than the step between the two. Each rung is labelled with its NATO band (APersP-01), which is the coarse robustness axis; where consecutive rungs carry the same band the band cannot tell them apart, and the three subaltern rungs all reading OF-1 is exactly why the fine scale exists — the bare numeral on each ladder's outer edge is that finer rung. Hover a rank to light its rung and its counterpart in the other army. Click either side: a Habsburg rank shows where those men went, a Legion rank shows where they came from. Bars are √n; counts are from data/final.

One rung is contested. svobodník is coded OR-3 here, but §G2 above reports the APersP-01 CZE note as vojín, svobodník = OR-1, and Bednařík's 1916 POW-camp pay anchor pays “pěšák a svobodník 15” — private and svobodník identically, with the first real step at desátník/četař. The band axis for the enlisted rungs is therefore the weakest part of this figure; it is logged as an open item in Decisions (D-15) and affects only rank_band, not the headline rank_reset, which rides on the numeral rather than the band.

non-combat status (legion_noncombat) — scored at grade 1, but not a demotionin the ladder, absent from the dataOF-3 and above — click for the individual records
HABSBURG LADDERrank at captureNATO BANDone rung per gradeLEGION LADDERrank on enrolmentOF-1022polní maršál0Field marshal (Feldmarschall) — common grade 22 (OF-10) · n = 0OF-921generálplukovník0Colonel general (Generaloberst) — common grade 21 (OF-9) · n = 021armádní generál0Army general — common grade 21 (OF-9) · n = 0OF-820generál pěchoty0General of the infantry/cavalry — common grade 20 (OF-8) · n = 0OF-719podmaršálek0Lieutenant field marshal (Feldmarschalleutnant) — common grade 19 (OF-7) · n = 019generálporučík0Lieutenant general — common grade 19 (OF-7) · n = 019divizní generál0Divisional general — common grade 19 (OF-7) · n = 0OF-618generálmajor0Major general (Generalmajor) — common grade 18 (OF-6) · n = 018generálmajor5Major general — common grade 18 (OF-6) · n = 518brigádní generál0Brigadier general — common grade 18 (OF-6) · n = 0OF-517plukovník1Colonel (Oberst) — common grade 17 (OF-5) · n = 117plukovník14Colonel — common grade 17 (OF-5) · n = 14OF-416podplukovník8Lieutenant colonel (Oberstleutnant) — common grade 16 (OF-4) · n = 816podplukovník24Lieutenant colonel — common grade 16 (OF-4) · n = 24OF-315major11Major — common grade 15 (OF-3) · n = 1115major31Major — common grade 15 (OF-3) · n = 31OF-214štábní kapitán4Staff captain (Stabshauptmann) — common grade 14 (OF-2) · n = 4OF-213setník98Captain (Hauptmann) — common grade 13 (OF-2) · n = 9813kapitán140Captain — common grade 13 (OF-2) · n = 140OF-112nadporučík459First lieutenant (Oberleutnant) — common grade 12 (OF-1) · n = 45912nadporučík70First lieutenant (Oberleutnant) — common grade 12 (OF-1) · n = 7012podkapitán4Sub-captain — common grade 12 (OF-1) · n = 4OF-111poručík1,444Lieutenant (Leutnant) — common grade 11 (OF-1) · n = 1,44411poručík586Lieutenant — common grade 11 (OF-1) · n = 58611kornet4Cornet (cavalry sub-lieutenant) — common grade 11 (OF-1) · n = 4OF-110podporučík762Second lieutenant (sub-Leutnant) — common grade 10 (OF-1) · n = 762OR-99praporčík1,015Ensign (Fähnrich) — common grade 9 (OR-9) · n = 1,0159praporčík475Ensign / officer candidate (Fähnrich) — common grade 9 (OR-9) · n = 475OR-88důstojnický zástupce12Acting officer (Offizierstellvertreter) — common grade 8 (OR-8) · n = 128důstojnický zástupce12Acting officer (Offizierstellvertreter) — common grade 8 (OR-8) · n = 12OR-87kadet1,391Cadet (Kadett) — common grade 7 (OR-8) · n = 1,3917kadet105Cadet — common grade 7 (OR-8) · n = 105OR-76kadet-aspirant157Officer cadet (Kadettaspirant) — common grade 6 (OR-7) · n = 1576rotmistr94Warrant officer / senior NCO — common grade 6 (OR-7) · n = 946štábní rotmistr0Staff warrant officer — common grade 6 (OR-7) · n = 06podpraporčík26Sub-ensign (Unterfähnrich) — common grade 6 (OR-7) · n = 26OR-65šikovatel876Sergeant major (Feldwebel) — common grade 5 (OR-6) · n = 8765šikovatel239Sergeant major (Feldwebel) — common grade 5 (OR-6) · n = 2395strážmistr238Cavalry sergeant major (Wachtmeister) — common grade 5 (OR-6) · n = 2385ohněstrůjce84Artillery sergeant (Feuerwerker) — common grade 5 (OR-6) · n = 845střelmistr52Master gunner — common grade 5 (OR-6) · n = 52OR-54četař3,617Sergeant (Zugsführer) — common grade 4 (OR-5) · n = 3,6174četař1,250Sergeant — common grade 4 (OR-5) · n = 1,250OR-43desátník8,346Corporal (Korporal) — common grade 3 (OR-4) · n = 8,3463desátník2,774Corporal — common grade 3 (OR-4) · n = 2,774OR-32svobodník6,339Lance corporal (Gefreiter) — common grade 2 (OR-3) · n = 6,3392svobodník1,899Lance corporal (Gefreiter) — common grade 2 (OR-3) · n = 1,8992nápředník153Senior gunner (Oberkanonier) — common grade 2 (OR-3) · n = 153OR-11vojín73,914Private — common grade 1 (OR-1) · n = 73,9141vojín96,982Private — common grade 1 (OR-1) · n = 96,9821pracovník7,406Labourer (non-combatant) — common grade 1 (OR-1) · n = 7,4061občan290Citizen (non-rank status) — common grade 1 (OR-1) · n = 290

  • ordinal columns are within-system ranks (1 = lowest). They are not directly comparable across the two tables — use the NATO/German equivalent as the bridge. Officer equivalences follow ALT 1 (name-anchored, §G): a Habsburg poručík and a Legion poručík are both OF‑1 (Leutnant).
  • Czech terms are the exact strings found in data/final/legionnaires_clean.csv (habsburg_rank, legion_rank, last_legion_rank); counts (n≈) are from the full dataset, terms with n≥20 shown. Branch synonyms and the j.d. modifier are handled in their own sections below.
  • ⚠️ flags a gloss I am not certain of — verify before relying on it.

Sources (full APA in ## References). §A is anchored on the bilingual Czech↔German rank table in Dolejší et al. (2003, p. 408); §B on the scholarly VHÚ monograph Purdek (2013) and the wartime Legion orders it reproduces (Rozkaz č. 15 / č. 117 plates, Klub přátel pplk. Karla Vašátka, 2009), with the post-1918 framework in Vládní nařízení č. 141/1927 Sb. The three Legion chronicles supply ordering evidence — Boháč (1938, Rota Nazdar = France), Bednařík (1927, V boj = Italy), Vaněk et al. (1925–1929, Za svobodu = Russia) — and the k.u.k. German ladder is corroborated by Rest et al. (2002, Des Kaisers Rock) and Rothenberg (1976). The legal recognition of prior rank is treated in the companion memo rank_recognition_1918_1938.md. Counts are from the csol.cz legionnaire dataset (Československá obec legionářská, n.d.).

OCR status of Des Kaisers Rock (the …Anna's Archiv (1).pdf copy): an OCR text layer now exists across all 498 pages. The German rank ladder in §A is confirmed by its prose insignia descriptions (pp. 61–64): Korporal → Fähnrich → Zugsführer/Feldwebel → Subalternoffizier (Leutnant, Oberleutnant) → Hauptmann → Stabsoffizier (Major, Oberstleutnant, Oberst). Caveat: the dense visual plates OCR'd to noise — the rank-Distinktion insignia plate (pp. 48–51) and the regiment Egalisierungsfarben tables (pp. 55–56, 164–187) are unusable from the text layer and need visual page reading. The book is German, so it does not itself resolve the Czech-specific ⚠️ terms below.


A. Habsburg (k.u.k. / k.k. Landwehr / k.u. Honvéd) ladder

Applies to habsburg_rank (rank held at capture). 98,656 non-null, 438 raw variants — the long tail is OCR noise, branch synonyms, and j.d. compounds.

Authoritative source (P408): Dolejší et al. (2003), Pod císařským praporem: Historie habsburské armády 1526–1918, table "Hodnosti rakousko-uherské armády," p. 408 — a bilingual Czech↔German rank table with the command echelon each rank held (Velitelský stupeň). This resolves the ⚠️ flags from earlier drafts (notably nápředník and závodčí, both corrected downward to Gefreiter-level).

Confidence column (Conf · src). Every ladder row carries a 0–100 % confidence in its ordinal placement (not the rank's mere existence) plus a short source key: - 100 % — read straight off an authoritative rank table; - ~95 % — scholarly/archival source (e.g. Purdek 2013) with corroboration; - ~90 % — attested + corroborated, standard placement; - ~75 % — branch-rank placed at a known level by clear analogy; - ~55 % — rank attested but its rung reconstructed; - ~40 % — genuinely uncertain.

Citation keys (full entries in ## References): P408 = Dolejší et al. (2003, p. 408); DKR = Rest et al. (2002, pp. 61–64); ROTH = Rothenberg (1976); R15/R117 = Rozkaz č. 15 / č. 117 plates (Klub přátel pplk. Karla Vašátka, 2009); Purdek = Purdek (2013); 141/1927 = Vládní nařízení č. 141/1927 Sb.; APersP = APersP-01 / STANAG 2116 (NATO Standardization Office, 2022) — the OF/OR codes; ZS#:line = Vaněk et al. (1925–1929), OCR locator; RN = Boháč (1938); VB = Bednařík (1927); DATA = csol.cz dataset; INF = author's inference (no external source).

Rows below ~90 % also carry an alt? pointer to §H (alternative orderings).

n (data) columns give the count in legionnaires_clean.csv (habsburg_rank, parser-aggregated over spelling/j.d. variants); the four §A tier totals reconcile to the parser's rank_tier.

A1 — Generals & officers (high to low), with command echelon

ord Czech (German) NATO Command level (velitelský stupeň) n (data) Conf · src
20 Polní maršál (Feldmarschall) OF‑10 army group (skupina armád) 0 100 % · P408
19 Generálplukovník (Generaloberst) ¹ OF‑9 field army 0 100 % · P408
18 Generál pěchoty / jezdectva (General der Infanterie / der Kavallerie) OF‑8 field army 0 100 % · P408
18 Polní zbrojmistr (Feldzeugmeister) OF‑8 field army / army corps 0 100 % · P408
17 Podmaršálek (Feldmarschall-Leutnant) OF‑7 division 0 100 % · P408
16 Generálmajor (Generalmajor) OF‑6 brigade 0 100 % · P408
15 Plukovník (Oberst) OF‑5 regiment (pluk) 1 100 % · P408
14 Podplukovník (Oberstleutnant) OF‑4 independent battalion 8 100 % · P408
13 Major (Major) OF‑3 battalion (prapor) 11 100 % · P408
12 Hejtman / setník, Rytmistr (Hauptmann, Rittmeister) OF‑2 company (rota) 119 100 % · P408
11 Nadporučík (Oberleutnant) OF‑1 deputy company cdr 459 100 % · P408
10 Poručík (Leutnant) OF‑1 platoon (četa) 1,445 100 % · P408

Generals (ord 16–20) are n = 0 — in the ladder for completeness, but no ex-Habsburg general served as a legionnaire. The officer total (Σ n = 2,043) equals the parser's Officer tier.

A1b — Officer candidates (důstojničtí čekatelé, ord 6–9)

Ordered by p.408's command echelon (adopted — see §H#1): kadet-aspirant = deputy platoon cdr (lowest); praporčík/Fähnrich + Offizierstellvertreter = platoon cdr (highest). The parser (ranks.py v1.1.0) uses this order.

ord Czech (German) n (data) Conf · src
9 Praporčík (Fähnrich) 1,026 75 % · P408-echelon
8 Důstojnický zástupce (Offizierstellvertreter) 12 75 % · P408-echelon
7 Kadet (Kadett; +záložní kadet/Reservekadett, kadet-šikovatel/Kadettfeldwebel) 1,391 75 % · P408-echelon
6 Kadet-aspirant (Kadettaspirant) 156 75 % · P408-echelon

Σ n = 2,585 = parser's Aspirant tier. Medical: Nadlékař (Oberarzt, n = 20) — officer-equivalent, 90 % · P408, outside the candidate band; deliberately left unscored by the parser (decision 2026-07-16): medical officer-equivalents sit outside the combatant command ladder the reset is built on, so the 20 records stay <NA> and surface in the residual report rather than being force-ranked.

A2 — NCOs & enlisted (poddůstojníci a mužstvo), by branch

Aligned by row = same rank level. Czech terms in the dataset given in bold.

ord Pěchota (Inf.) Polní myslivci (Jäger) Jezdectvo (Cav.) Dělostřelectvo (Arty) German n (data) Conf · src
5 šikovatel nadmyslivec strážmistr ohňostrůjce Feldwebel / Oberjäger / Wachtmeister / Feuerwerker 876 100 % · P408
4 četař četař četař četař Zugsführer 3,617 100 % · P408
3 desátník, kaprál podmyslivec desátník desátník Korporal / Unterjäger 8,346 100 % · P408
2 svobodník, frajtr závodčí závodčí nápředník, dělovod Gefreiter / Patrouilleführer / Oberkanonier 6,337 100 % · P408
1 pěšák (gen. vojín) myslivec dragoun, hulán, husar / jezdec dělostřelec Infanterist / Jäger / Dragoner,Uhlan,Husar / Kanonier 73,916 100 % · P408

n (data) is the level total (= the parser's canonical count, which collapses all branch terms at that level). Σ matches the parser tiers: L1–2 = Mannschaft 80,253; L3–5 = NCO 12,839.

Notes from the table, reconciling our data: - vojín (n≈72,834) is the umbrella term for the level‑1 branch privates (pěšák/Infanterist, etc.); střelec/Schütze and zákopník/Pionier are further level‑1 branch privates not printed in this table. - Raw branch-term counts (in habsburg_rank): dělostřelec 390, střelec 255, pěšák 107, dragoun 54, jezdec 25, zákopník 21 (L1); nápředník 138, závodčí 22 (L2); strážmistr 98, ohněstrůjce 12 (L5). - ✅ nápředník = Oberkanonier — artillery, level 2 (Gefreiter-equiv.), not a senior NCO. (Earlier draft had it at ord 6 — corrected.) Synonym dělovod. - ✅ závodčí = Patrouilleführer — cavalry/Jäger, level 2 (Gefreiter-equiv.). (Earlier draft had it at ord 3 — corrected.) - ✅ strážmistr = Wachtmeister = cavalry/arty Feldwebel (level 5) — confirmed. - setník = hejtman = Hauptmann (OF‑2) — confirmed synonym (data n≈61).

✅ podporučík confirmed absent from the k.u.k. ladder. The lowest k.u.k. officer is Leutnant = poručík (commands a platoon). So habsburg_rank == "podporučík" (n≈202) is a Legion/ČSA reading mis-filed (or a Landwehr reserve rendering) — flag and inspect, do not score as OF‑0. Likewise rotmistr is not a k.u.k. rank here (the cavalry captain is rytmistr = Rittmeister); treat Habsburg rotmistr as either an OCR/spelling slip for rytmistr or a Legion-side rank (see §B). The same treatment applies to podpraporčík (n≈9): no k.u.k. Unterfähnrich existed — a Legion/ČSA reading mis-filed, dropped to <NA> (not folded into praporčík, which would score it three common grades above its Legion sense; ranks.py ≥ v1.3.1).

¹ Generaloberst (generálplukovník) was created on the German model only in 1915 (first: Archduke Eugen and Gen. Conrad). At the 1918 armistice Habsburg had six field marshals besides three archdukes (Conrad, Kövess, Krobatin, Rohr, Böhm-Ermolli, Boroević). — footnotes, Dolejší et al. (2003), p. 408.


B. Czechoslovak Legion / ČSA ladder

Applies to legion_rank and last_legion_rank. Within-Legion ordinals; the NATO equiv. column follows ALT 1 (name-anchored, §G): poručík↔poručík (OF‑1 Leutnant), nadporučík↔Oberleutnant (also OF‑1, senior sub-grade — APersP-01, §G2), and Legion podporučík = a sub-Leutnant grade the Habsburg system lacks. n is from last_legion_rank.

Primary authority for §B — Purdek (2013), VHÚ. The Legion sleeve-insignia ladder (Rusko 1917–1920) is given verbatim in Purdek (2013, p. 73) — the scholarly twin of the Rozkaz č. 15 / č. 117 plates — and it confirms the Oct-1918 transitions (podpráporčík and podkapitán "od októbra 1918 zrušená" / → kapitán; archival source: rozkaz velitele Čs. AS, 15./30. 12. 1917). The interwar 1918–1939 ladder is in Purdek (2013, p. 105): vojín < svobodník < desátník < četař < rotný < rotmistr < štábní rotmistr < praporčík < podporučík < poručík < nadporučík < kapitán < štábní kapitán < major < podplukovník < plukovník │ generals. Note the 1924/1925 split: rotmistr was a single grade until 1924, then split into rotmistr / štábní rotmistr / praporčík from 1925 (Purdek, 2013, p. 105). The post-1918 legal frame is Vládní nařízení č. 141/1927 Sb. (library/reference-texts/Vladni-narizeni-141-1927_branne-predpisy.pdf); the official current-army ladder (Ministerstvo obrany ČR, n.d.) corroborates rotmistr/nadrotmistr/praporčík as the warrant tier. (Two popular blogs — Kutilův zápisník, n.d.; Magazín Reenactor, n.d. — agree but are secondary.)

ord Czech term (data) NATO equiv. (ALT 1, §G) Tier n Conf · src alt?
1 vojín / střelec (+ dělostřelec, zákopník, jezdec — branch, §D) private (OR‑1) Mužstvo 61,260 100 % · R15/R117
1 pracovník labour — unarmed, treat as base Mužstvo 5,152 90 % · DATA
1 občan "citizen" — non-rank status Mužstvo 318 60 % · ⚠ §H
2 svobodník Gefreiter (OR‑3) Mužstvo 8,466 100 % · R15/R117
2 nápředník Oberkanonier — artillery Gefreiter-level (§A) Mužstvo 749 75 % · analogy
3 desátník Korporal (OR‑4) Poddůstojník 11,934 100 % · R15/R117
4 četař Zugsführer (OR‑5) Poddůstojník 7,061 100 % · R15/R117
5 šikovatel Feldwebel (OR‑6) Poddůstojník 1,838 100 % · R15/R117
5 strážmistr (cav) · ohněstrůjce, střelmistr (arty) branch Feldwebel-equiv. Poddůstojník 904 · 526 75 % · analogy
6 rotmistr / štábní rotmistr senior NCO / warrant officer (rotný<rotmistr<štábní rotmistr; one grade until 1924, split from 1925); cavalry-officer sense = §B-cav minority Poddůstojník 1,057 / 45 95 % · Purdek p105
6 podpraporčík Unterfähnrich (OR‑7) — Rozkaz č. 15 only Poddůstojník 28 100 % · R15
7 praporčík (+ kadet, důst.čekatel, důst.zástupce — candidates) Fähnrich / officer-candidate Aspirant 352 90 % · R15/R117
8 podporučík sub-Leutnant (OF‑1⁻) — Legion-only junior commission (§G) Důstojník 1,227 100 % · R15/R117
9 poručík Leutnant (OF‑1) — = Habsburg poručík (§G) Důstojník 1,775 100 % · R15/R117
10 nadporučík Oberleutnant (OF‑1, senior; sub-grade OF‑1⁺ — §G/§G2) — senior lieutenant, confirmed interwar rank Důstojník 768 95 % · Purdek p105
11 kapitán Hauptmann (OF‑2) Důstojník 1,202 100 % · R15/R117
11⁺ štábní kapitán Stabshauptmann (OF‑2, senior) — own grade, between kapitán and major Důstojník 91 95 % · Purdek p105
12 major Major (OF‑3) Štábní důstojník 328 100 % · R15/R117
13 podplukovník Oberstleutnant (OF‑4) Štábní důstojník 150 100 % · R15/R117
14 plukovník Oberst (OF‑5) Štábní důstojník 54 100 % · R15/R117
15 generálmajor · brigádní generál Major general / brigadier (OF‑6) Generál 10 · 3 95 % · Fidler 2025 §B-gen
16 generálporučík · divizní generál Lieutenant / divisional general (OF‑7) Generál 2 · 1 95 % · Fidler 2025 §B-gen
17 armádní generál Army general (OF‑9) Generál 0 90 % · Purdek p105 §B-gen
— milosrdná sestra · adjutant · žák, repatriant, zajatec non-ranks / statuses — exclude — 33 · 53 · 38/47/41 —
— generál (bare) · kandidát na X · požitky X · nebyl X tier not grade / annotations about a rank — exclude — 1 · 9 · 2 · 1 — §B-gen

(The officer order is unchanged (podporučík < poručík < nadporučík < kapitán); ordinals were renumbered only to split podpraporčík (NCO, ord 6) from praporčík (officer-candidate, ord 7), matching the Rozkaz plates. NATO labels follow ALT 1, §G.)

Senior-NCO / warrant band (pinned by Purdek, 2013, p. 105): šikovatel ≈ rotný (5) < rotmistr ≈ podpraporčík (6) < štábní rotmistr (6⁺) < praporčík (7, officer-candidate). Legion šikovatel (rozkaz 117) is the equivalent the interwar army renamed to rotný/rotmistr.

B-gen — Generals (ord 15–17), added in ranks 1.4.0

Unlike the Habsburg generals (§A1, deliberately n = 0 — no ex-Habsburg general served as a legionnaire), Legion generals are present in the data. The Legion commissioned its own: Šokorov, Syrový, Čeček, Ditěrichs, Kolomenskij all appear as generálmajor or generálporučík in Fidler (2025), which records 20 general officers.

Two naming systems coexist and are paired by NATO grade, not by word:

OF Russian style (Legion in Russia) French style (post‑1918 ČSA)
OF‑6 generálmajor brigádní generál
OF‑7 generálporučík divizní generál
OF‑9 — armádní generál

Bare generál is deliberately unscored — it names the tier, not a grade, the same refusal applied to nadlékař (§A1b). So are annotations about a rank: kandidát na poručíka (candidate for lieutenant), požitky poručíka (paid at a lieutenant's rate), nebyl vojínem (was not a private). Each of these was previously scored as the rank it names.

Why this section exists. Before 1.4.0 the Legion ladder simply stopped at plukovník, and because the matcher worked by substring, generálmajor resolved to major (ord 12, OF‑3) and generálporučík to poručík (ord 9, OF‑1) — a general silently recorded as a field officer or a subaltern, on the very variable rank_reset differences. The matcher now requires a word-start match, so an unlisted word can no longer inherit a shorter rank it happens to contain. Full account and record counts: reference/VALIDATION_FIDLER.md §2.

A note on the nad- seniors. nadstrážmistr (n ≈ 57), nadpraporčík, nadrotmistr share their base grade's rung rather than getting one above it. They are not 1918 Legion grades at all — nadstrážmistr is the inter-war gendarmerie/ČSA senior grade, and nearly every instance sits in last_legion_rank (the man's final, often post-war, rank). Their position on the §B ladder is therefore not determinable from the Rozkaz plates, and inventing a rung would be worse than the tie. They are listed as explicit tokens so the match is intentional rather than a substring accident.

B-cav — Cavalry officer track (jízdní pluk)

The infantry insignia plates (Rozkaz č. 15 / 117) don't cover cavalry. Za svobodu shows a parallel cavalry officer ladder (Russian-derived), in which rotmistr is an officer, unlike the infantry senior-NCO usage. Note: in the unified interwar ČSA, rotmistr = NCO/warrant (Purdek, 2013, p. 105) — the cavalry rotmistr = Rittmeister (officer) is the older branch convention, so most data rotmistr (esp. last_legion_rank) are the NCO sense.

Czech (data n≈) role ≈ NATO Conf · src evidence (Za svobodu)
kornet (4–5) cavalry sub-lieutenant (Russ. корнет) — commands squadron/recon OF‑1 70 % · ZS _2:70589, _4:22014, _4:23789
štábní rotmistr (45) staff cavalry captain (штабс-ротмистр) — commanded a battalion / division OF‑2 70 % · ZS _2:10985, _4:23801
rotmistr (1,057) cavalry captain in cavalry; but a senior NCO in infantry (§B-evidence) OF‑2 / OR‑6 40 % · ⚠ ZS _2:442, _4:23801

B-evidence — what Za svobodu 2–4 (Russia chronicle) corroborate

These narrative volumes (Vaněk et al., 1925–1929, vols. 2–4; ~2,770 OCR pp.) give ordering evidence, not a rank table:

  • Rank-ordered casualty list _2:442: praporčík → důst. zástupce → rotmistr → četař → desátník → svobodník → střelec. Places rotmistr above četař (4) but below the officer-candidates ⇒ infantry senior-NCO ≈ ord 5–6.
  • podkapitán (data n≈4) — confirmed a real Legion field officer between poručík and kapitán; commanded companies/battalions (_4:34819, _3:45036). A Rozkaz č. 15 grade.
  • ohněstrůjce — confirmed artillery crew rank (armoured train Orlík, _4:5170).
  • důst. zástupce sits just below praporčík in the casualty ordering (officer-candidate / warrant band).
  • Insignia transition _2:63714: the 1917 stripping of Russian shoulder-boards (pogony / nárameníky) and the National Council ordering new ČS insignia "designed by a military commission" — the moment that produced the Rozkaz č. 15 (Jan–Oct 1918) and Rozkaz č. 117 (Oct 1918–) plates that are §B's authoritative source.

✅ Resolved by Purdek (2013, pp. 73, 105): Legion šikovatel = interwar rotný (ord 5); rotmistr / podpraporčík = ord 6. The diary ordering above is consistent with this (rotmistr above četař, below the candidates).


C. Theater-specific notes

The Legion's rank system was not uniform across theaters — this matters for the rank-reset measure, which should be computed within theater.

  • France (Rota Nazdar, 1914–15; Boháč, 1938): soldiers served inside the French Foreign Legion and carried French ranks — caporal (kaprál), sergent, sergent-major, adjudant. Foreigners passed through "all NCO ranks" before any officer commission, and non-citizens were capped at capitaine (rota_nazdar:9952, :9962). First NCO appointments 5 Oct 1914; the "aféra kaprálů" (corporals' affair) is a named episode (:31398).
  • Russia (Za svobodu; Vaněk et al., 1925–1929): the dominant theater (~66%); ranks follow the Czech/Russian-influenced ladder in §B. NCO authority over privates is discussed at za_svobodu:50018.
  • Italy (V boj; Bednařík, 1927): see the pay anchor in §E.

D. Branch-of-service synonyms (all = private level, ordinal 1)

These are not separate ranks — they name the arm of service of a private. Collapse to ordinal 1 for the rank scale, but preserve separately as a branch field (artillery vs. infantry vs. cavalry may matter on its own).

Czech term branch German
vojín / pěšák infantry Infanterist
dělostřelec artillery Kanonier
střelec rifles / Landwehr Schütze
dragoun cavalry (dragoons) Dragoner
jezdec cavalry Reiter
zákopník engineers/sappers Pionier
pracovník labour Arbeiter

E. j.d. modifier and the pay anchor

  • j.d. = jednoroční dobrovolník (German Einjährig-Freiwilliger): the educated one-year-volunteer track, a status, not a rank level. It appears both standalone (j. d., n≈308) and compounded (desátník j.d., j.d. svobodník, …). Recommendation: strip to a boolean one_year_volunteer flag and resolve the underlying rank separately. Plausibly correlated with education and with the desertion outcome — worth keeping.

  • Pay anchor (Italy, Habsburg POW camp pay, from April 1916), giving an economic ordering of the lower ladder — Bednařík (1927), v_boj OCR:5351:

    „pěšák a svobodník 15, desátník a četař 30, šikovatel a výše 50 centesimů denně."

i.e. {private, Gefreiter} = 15 < {Korporal, Zugsführer} = 30 < {Feldwebel and above} = 50 centesimi/day. Useful if a cardinal (not merely ordinal) rank value is wanted.


F. The officer cross-system subtlety (resolved → §G)

The Czech officer ladder has three subaltern grades (podporučík < poručík < nadporučík); the German/Habsburg system has two (Leutnant < Oberleutnant). Earlier drafts function-anchored the bridge (lowest officer ↔ lowest officer), which forced the counter-intuitive "Legion poručík = Oberleutnant."

Resolved in §G via ALT 1 (name-anchored): - Habsburg poručík ↔ Legion poručík = OF‑1 (Leutnant) — same word, same slot. - nadporučík ↔ Oberleutnant — the literal calque ("over-lieutenant"); also OF‑1 (senior sub-grade), not OF‑2 (APersP-01, §G2). - Legion podporučík = a sub-Leutnant grade the k.u.k. system lacks (§A note).

Consequences for the reset: Habsburg poručík → Legion poručík = 0; Habsburg poručík → Legion podporučík = a genuine −1. Map officers through the NATO column (ALT 1), not by raw Czech string — the only word needing care is podporučík (Legion-only).


G. Officer cross-mapping — the chosen common scale (ALT 1)

For the rank-reset (reset = legion − habsburg) the two within-system ordinals (§A 1–20, §B 1–17) are not directly subtractable; they are bridged onto one shared NATO/OF scale (STANAG 2116 / APersP-01, see §G2). Of the options considered, ALT 1 (name-anchored) is adopted:

Czech Habsburg Legion NATO band (APersP-01) ALT 1 reset sub-grade
podporučík — (not k.u.k.) sub-Leutnant OF‑1 OF‑1⁻
poručík Leutnant Leutnant OF‑1 OF‑1
nadporučík Oberleutnant Oberleutnant OF‑1 OF‑1⁺
setník / kapitán Hauptmann Hauptmann OF‑2 OF‑2

⚠️ Correction (APersP-01, 2026-06-29). Earlier drafts of this table labelled nadporučík = OF‑2 and Hauptmann = OF‑2/3. That is wrong by the NATO standard: both Leutnant and Oberleutnant are OF‑1, and Hauptmann is OF‑2 — confirmed for modern Germany and the modern Czech Army (§G2). So the three Czech subaltern grades (podporučík < poručík < nadporučík) all sit inside one NATO band, OF‑1; the standard code cannot separate them.

Why ALT 1: keeps poručík↔poručík and nadporučík↔nadporučík (intuitive, name-preserving) and gives a clean reset sign — the only Legion-specific grade, podporučík, sits just below poručík (a junior/probationary commission), so a Habsburg lieutenant re-entering as podporučík shows a true small demotion. The reset's monotone ordering does not depend on the OF label; it rides on the within-OF‑1 sub-grade column (= the within-system ordinal), with the coarse NATO band as the robustness fallback.

Rejected alternatives (kept for the record): - Function-anchored (lowest-officer ↔ lowest-officer): maps Habsburg poručík ↔ Legion podporučík; produces the odd "poručík = Oberleutnant" and leaves nadporučík without a clean slot. (The earlier-draft mapping.) - Coarse bands / raw NATO OF code (OF‑1, OF‑2, … as in APersP-01): robust and authoritative, but collapses podporučík = poručík = nadporučík into a single OF‑1 → 0 reset among all subalterns. Keep as the robustness variant (= rank_band). - Stretched-fine (podporučík = OF‑1⁻, poručík = OF‑1, nadporučík = OF‑1⁺): the adopted ALT 1 sub-grade column — monotone within the OF‑1 band, no longer mislabelling nadporučík as a captain-grade.

To add when building the reset: a fine rank_common column (the ALT 1 sub-grade ordering above) and a coarse rank_band column (raw APersP-01 OF/OR code) on both ladders; then reset = legion_common − habsburg_common, with the OF-band reset as a robustness check. Worked examples (sub-grade scale, as implemented in RANK_COMMON): Habsburg četař (common 4) → Legion vojín (common 1) = −3; poručík → poručík = 0; poručík → podporučík = −1; poručík → nadporučík = +1 (a within-OF‑1 promotion the raw OF code would miss). The enlisted rungs are consecutive integers — order is certain, cardinal spacing is an analyst convention (§G3).

G1 — Institutional warrant for the common scale

The ALT 1 cross-system mapping is not only an analyst's reconstruction; the Czechoslovak state itself enacted it. After 1918 the army was the merger of the Legions and (admitted) ex-Habsburg personnel into one origin-blind rank ladder: Zákon č. 195/1920 Sb. placed officers of both origins into the same statutory rank-classes II–XI and credited both Austro-Hungarian war-years and Legion years toward one seniority clock. The reconciled post-war officer ladder — podporučík < poručík < nadporučík < kapitán — is precisely the contemporary synthesis of the two predecessor systems: Habsburg poručík = ČS poručík and Habsburg nadporučík = ČS nadporučík (both within OF‑1; see §G2). That is ALT 1. So treating a same-level rank from either origin as equivalent has explicit institutional backing (see the companion memo, rank_recognition_1918_1938.md).

Scope discipline (read with G1). This warrants only rank-level commensurability — a shared vocabulary and one ladder — not symmetric valuation: admission and seniority were deliberately asymmetric (legionary service was rewarded — waivers, reserved posts, seniority back-dated to 1914; ex-Habsburg officers were admitted at ministerial discretion under a loyalty screen, Zákon č. 194/1920 Sb.). And it is post-war evidence that the rank vocabularies are commensurable — it supports the comparability assumption behind the reset, but is not the wartime Habsburg→Legion conversion rule (that was set by each legion's own wartime regulations). Cite 195/1920 for the common scale's legitimacy; do not cite the 1918–1938 statutes as the reset mechanism.

G2 — The OF/OR code system itself (authoritative: APersP-01 / STANAG 2116)

The "OF/OR" codes used throughout this file are not an ad-hoc invention; they are the NATO standard rank-comparison codes defined in APersP-01, NATO Codes for Grades of Military Personnel, Edition A Version 3 (NATO Standardization Office, 2022), which implements STANAG 2116. The scale:

  • Officers OF‑1 … OF‑10 — Junior OF‑1–2, Senior/field OF‑3–5, General OF‑6–10.
  • Other ranks OR‑1 … OR‑9 — OR‑5–9 are Non-Commissioned Officers; officer candidates sit in the OR band, not a separate code (e.g. DEU Fähnrich = OR‑6, Fahnenjunker = OR‑5, Oberfähnrich = OR‑8 — APersP-01 nat. note DEU).

How we assign each Czech/German rank its code (two hops): Czech term → German k.u.k. equivalent (Dolejší et al., 2003, p. 408) → the established APersP-01 code for that grade. The publication's per-nation annexes pin the anchors directly:

  • Germany (DEU): Leutnant = OF‑1, Oberleutnant = OF‑1, Hauptmann / Stabshauptmann = OF‑2, Major = OF‑3, Oberstleutnant = OF‑4, Oberst = OF‑5.
  • Czech Republic (CZE) — the successor army, decisive here: poručík = OF‑1, nadporučík = OF‑1, kapitán = OF‑2, major = OF‑3, … . Other ranks (CZE national note): vojín, svobodník = OR‑1; desátník, četař, rotný = OR‑3–4; Warrant Officers rotmistr, nadrotmistr, praporčík, nadpraporčík, štábní praporčík = OR‑5–9.

Two consequences for this thesis: 1. The modern Czech state codes poručík and nadporučík identically (OF‑1) — the institutional-warrant point of §G1, now from the live NATO standard, not just the 1920 statutes. It also confirms the §B warrant band: rotmistr/praporčík are OR‑5–9 (warrant), exactly the senior-NCO/candidate placement (ord 6–7). 2. The raw OF code is too coarse for the reset — it cannot separate the three subaltern grades the double-desertion design turns on. Hence ALT 1's within-OF‑1 sub-grade column (§G), with the raw OF/OR rank_band kept as the robustness check.

Caveat (anachronism). APersP-01 is a 2022 standard; it is used here only to fix the OF/OR code conventions and to show the successor state's own equivalencing of the (unchanged) Czech rank vocabulary — not as a 1914–1920 source. The wartime ladders remain anchored on §A (Dolejší p. 408) and §B (Purdek 2013 + the Rozkaz plates).

G3 — Reliability of the reset: how sure are the demotions?

The name-anchored common scale is only as trustworthy as its weakest rung, so the reset's credibility rests on where the demotion mass actually sits, not on the whole ladder being equally certain. Tagging every demotion (rank_reset < 0) by the weakest-confidence endpoint of its two ranks — using the per-rank Conf grades in §A/§B — gives (n = 17,328 demotions of 91,580 resolved resets; legionnaires_clean, csol.cz, computed 2026-07-09; the 2026-07-16 rebuild shifts this by −9 rows to 17,319 of 91,571 — the dropped Habsburg podpraporčík — leaving every share below unchanged):

Demotion by weakest-endpoint confidence n share
both endpoints 100 % (core spine) 14,244 82.2 %
90–99 % (warrant / candidate band) 1,452 8.4 %
60–89 % (branch analogy / občan) 1,632 9.4 %

Two robustness facts: - Direction is near-model-free. The fine ALT-1 reset and the coarse raw-NATO-band reset (rank_reset_band) agree on demoted-vs-not for 99.8 % of resolved rows; only 169 flip — the within-OF‑1 subaltern cases (poručík→podporučík etc.), a demotion by definition under name-anchoring (§G) but 0 under band-anchoring. - The mass is the certain part. The bulk of demotions is NCO→private (desátník / svobodník / četař → vojín), all on the 100 %-confidence spine; officer demotions are a 7.7 % minority.

What remains uncertain is magnitude, not sign. (i) The enlisted rungs are scored as consecutive integers, so četař→vojín = −3 (= the §G worked example): order is certain, cardinal spacing is an analyst assumption. (ii) The reconstructed candidate/warrant band (common grades 6–9) rests on p.408 command-echelon order (75 %) and branch analogy. Two interpretive caveats, both robustness-check material: 11.7 % (2,025) of demotions land in pracovník/občan (possibly labour reassignment, not rank punishment), and the 169 subaltern cases are ALT-1-definitional.

Recommendation: lead with the demotion indicator (safe — 82 % rock-solid, 99.8 % sign-robust) plus rank_reset_band as the coarse robustness axis; report a pracovník-excluded cut as a footnote. Machinery: add_rank_reset in utils/parsing/ranks.py.


H. Alternative orderings — open and resolved

Resolved (no longer open): - ✅ Habsburg officer-candidates (§A1b). The p.408 command-echelon order is now adopted: kadet-aspirant(6) < kadet(7) < důst.zástupce(8) < praporčík(9); applied in ranks.py v1.1.0. - ✅ Infantry rotmistr (§B). Resolved → ord 6 (senior NCO / warrant); Purdek (2013, p. 105): rotný < rotmistr < štábní rotmistr, above četař. - ✅ Legion nadporučík (§B). Resolved → within-system ord 10, a confirmed officer rank between poručík and kapitán (Purdek, 2013, p. 105). NATO code corrected to OF‑1 (senior sub-grade), not OF‑2 — APersP-01 (§G2). - ✅ Senior-NCO band fine order (§B). Purdek (2013, pp. 73, 105) pins Legion šikovatel = interwar rotný (5) and rotmistr = ord 6 (= podpraporčík level); current §B ordinals confirmed.

Resolved (2026-07-21): - ✅ občan / pracovník (§B, ord 1) — flagged, not excluded. These are non-combat statuses, not grades: pracovník is the labour-service entry track (in Italy the pracovní prapory were a transitional stage that merged into the combat zvláštní pluky by April–May 1918), and občan is civilian status. The qualitative record does not support reading a "colonel → labourer" record as a genuine 16-grade demotion — systematic officer degradation was an explicitly-rebutted rumour (V boj; see reference/VALIDATION_FIDLER.md §3). Decision: keep them on the ladder at rung 1 and keep rank_reset computed, but add_rank_reset now emits legion_noncombat (True for pracovník/občan at Legion enrolment, 7,696 records). Lead the headline reset on ~legion_noncombat (combat ranks, ~85.7k resolved); the full column is the inclusive robustness variant. ranks.py v1.4.1.


Status / next steps

  • This file is the captured ladder, reconstructed from data + diaries + references — and the authority §A is built against.
  • ✅ Built (both sides): utils/parsing/ranks.py v1.3.1 encodes §A (Habsburg, 1–20) and §B (Legion, 1–17 since the general grades were added in v1.4.0) as system-keyed ordinal parsers (add_rank_columns(..., system=...), clean_rank, resolve_ordinals, branch_of, rank_report). Habsburg habsburg_rank → habsburg_rank_{clean,en,ord,ord_low,jd,branch,tier,common,band} (98.8 % coverage); Legion legion_rank (enrolment) and last_legion_rank → the same nine-column families (99.6 % / 99.5 % coverage). All three wired into notebooks/02_data_cleaning §1 and registered in utils/parsing/schema.py. Compounds keep both ends (ord = highest reached, ord_low = lowest listed); j.d. is a flag.
  • Habsburg drops podporučík, rotmistr and podpraporčík to <NA> (none is a k.u.k. rank, §A note; podpraporčík added v1.3.1); nadlékař is deliberately unscored (§A1b). v1.1.0+ adopts the p.408-echelon candidate order (§A1b/§H).
  • Legion scores podporučík (sub-Leutnant, ord 8) and rotmistr (warrant NCO, ord 6) as real ranks; pracovník/občan → base mužstvo (1); non-ranks (zajatec/repatriant/žák/sestra/adjutant) → <NA>. The within-Legion ordinal follows this §B table; štábní kapitán is collapsed to the kapitán rung (11), podkapitán to the nadporučík rung (10), and štábní rotmistr ties with rotmistr at 6 (the §B "6⁺" is not separately encoded) for the integer scale.
  • ✅ Cross-system bridge built (v1.3.0, 2026-07-09). add_rank_columns also emits the system-blind {prefix}_common (ALT 1, §G — RANK_COMMON) and the coarse {prefix}_band (raw APersP-01 OF/OR); add_rank_reset differences the common columns into rank_reset (+ rank_reset_band robustness). The two within-system ordinals remain not cross-subtractable (intro, §F) — the reset rides on _common only. A machine-readable reference/ranks/rank_ladders.csv remains optional (the module embeds the maps, mirroring education.py).
  • ✅ Resolved via Pod císařským praporem p. 408: nápředník (=Oberkanonier, arty Gefreiter-level), závodčí (=Patrouilleführer, Gefreiter-level), strážmistr (=Wachtmeister), setník (=Hauptmann), and podporučík / rotmistr absent from the k.u.k. ladder.
  • ✅ Resolved Legion-side via Purdek (2013, VHÚ) + Vládní nařízení č. 141/1927 Sb. (§B header): rotmistr = senior NCO/warrant (ord 6), nadporučík = OF‑2 (ord 10) — superseded: the APersP-01 check two entries below puts nadporučík at OF‑1, which is what _COMMON_TO_BAND encodes — štábní kapitán between kapitán and major; the Rozkaz 15→117 transitions and the senior-NCO band order are all pinned (Purdek, 2013, pp. 73, 105).
  • ✅ The common scale (ALT 1) now has institutional backing — Zákon č. 195/1920 Sb. merged both origins into one rank-ladder (§G1); cite it (not the post-1918 statutes) for the reset's comparability assumption.
  • ✅ OF/OR codes grounded + corrected (§G2, APersP-01 / STANAG 2116, 2026-06-29). The NATO codes are now from the authoritative standard, which also fixed an error: poručík and nadporučík are both OF‑1 (modern DEU and CZE), kapitán = OF‑2 — so the three subaltern grades share one NATO band and the reset rides on the within-OF‑1 sub-grade (rank_common), with the raw OF/OR code as rank_band robustness. Officer-candidates (praporčík etc.) are OR‑5–9 (warrant band), not OF.
  • ⚠️ Still open (Legion-side, minor): střelmistr exact level and občan handling.
  • The German backbone of §A is independently corroborated by the OCR'd Des Kaisers Rock (pp. 61–64) — see the OCR-status note at the top.

References

Czech/Slovak titles keep original orthography. Locators like ZS 2:442 are OCR line numbers in library/diaries/ (the digitised chronicles), not print pages.

  • Bednařík, F. (1927). V boj! Obrázková kronika československého revolučního hnutí v Italii 1915–1918. Praha: Za svobodu. (VB; OCR: v_boj_ocr_fixed.txt)
  • Boháč, J. (1938). Kronika československé legie ve Francii: Kniha prvá — Rota Nazdar 1914–1916. Praha: Naše záloha. (RN; OCR: rota_nazdar_ocr_fixed.txt)
  • Československá obec legionářská. (n.d.). Databáze legionářů [Dataset]. https://www.csol.cz/ (DATA)
  • Dolejší, J., Fučík, J., Havel, P., & Pernes, J. (2003). Pod císařským praporem: Historie habsburské armády 1526–1918. Praha: Elka Press. (P408)
  • Klub přátel pplk. Karla Vašátka. (2009). Hodnostní označení čs. legií na Rusi 1918–1920. http://www.karelvasatko.cz/uniformy-ruske-legie/hodnostni-oznaceni-rlvseobecne (R15/R117 — the Rozkaz č. 15 / č. 117 insignia plates)
  • Ministerstvo obrany ČR. (n.d.). Vojenské hodnosti. https://doarmady.mo.gov.cz/o-armade/poznejte-armadu/vojenske-hodnosti (official current-army ladder; corroboration)
  • NATO Standardization Office. (2022). APersP-01: NATO codes for grades of military personnel (Edition A, Version 3). North Atlantic Treaty Organization. (STANAG 2116; the OF/OR code definitions + per-nation rank annexes incl. DEU and CZE — §G2)
  • Purdek, I. (2013). Československá vojenská symbolika v rokoch 1914–1939. Bratislava: Vojenský historický ústav. (Purdek; library/reference-texts/Purdek-2013_Ceskoslovenska-vojenska-symbolika-1914-1939.pdf)
  • Rest, S., Ortner, M. C., & Ilming, T. (2002). Des Kaisers Rock im 1. Weltkrieg: Uniformierung und Ausrüstung der österreichisch-ungarischen Armee von 1914 bis 1918. Wien: Verlag Militaria. (DKR)
  • Rothenberg, G. E. (1976). The Army of Francis Joseph. West Lafayette, IN: Purdue University Press. (ROTH)
  • Vaněk, O., Holeček, V., & Medek, R. (Eds.). (1925–1929). Za svobodu: Obrázková kronika československého revolučního hnutí na Rusi 1914–1920 (Vols. 1–4). Praha: Památník odboje. (ZS; OCR: za_svobodu_{1–4}_ocr*.txt)
  • Zákon č. 195/1920 Sb. z. a n., o úpravě služebních požitků československého vojska [Act No. 195/1920 Coll., on the Regulation of the Service Emoluments of the Czechoslovak Army] (1920). Sbírka zákonů a nařízení, částka 41. (rank-classes II–XI; the common-scale warrant, §G1)
  • Zákon č. 194/1920 Sb. z. a n. (1920); Zákon č. 462/1919 Sb. z. a n. (1919) — admission of ex-Habsburg gážisté / legionnaire definition; see the companion memo.
  • Vládní nařízení č. 141/1927 Sb. z. a n., jímž se vydávají nové branné předpisy. Sbírka zákonů a nařízení, částka 64 (1927). (141/1927; library/reference-texts/Vladni-narizeni-141-1927_branne-predpisy.pdf)
  • Legal recognition of prior military rank in the Czechoslovak army, 1918–1938 [Project research memo, 2026]. rank_recognition_1918_1938.md.
  • Secondary (popular, corroborating only): Kutilův zápisník (n.d.), České vojenské hodnosti používané v letech 1918–1937; Magazín Reenactor (n.d.), Hodnostní označení čs. armády ve třicátých letech 20. století.

Biblatex cite-keys (→ Zotero)

All of these live in reference/references.bib, which is generated on every docs build from the master bibliography behind the References sheet — the same list that sheet renders, so the two cannot disagree and neither can omit a work the site cites. Import that one file into Zotero; the keys below are pinned, so a re-import updates the existing records rather than forking them. (It supersedes the former hand-maintained rank_sources.bib, whose entries were folded into the master.) Keys marked (new) were the ones not yet in the library when this table was written.

doc key source (short) citekey in Zotero?
P408 Pod císařským praporem pernes_pod_2003 yes (Ranks) — ⚠ see flag
DKR Des Kaisers Rock rest_des_2002 (new)
ROTH Rothenberg 1976 rothenberg_army_1976 (new)
R15/R117 Vašátko plates klub_hodnostni_2009 (new)
Purdek Purdek 2013 purdek_ceskoslovenska_2013 (new)
APersP NATO OF/OR codes (STANAG 2116) nato_apersp01_2022 (new)
141/1927 nové branné předpisy narizeni_141_1927 (new)
195/1920 služ. požitků (§G1) zakon_195_1920 (new)
(194/1920) admission of ex-Habsburg zakon_194_1920 (new)
(462/1919) legionnaire definition zakon_462_1919 (new)
— MO ČR (current army) mocr_vojenske_nd (new)
— companion memo haufer_legal_2026 (new)
ZS Za svobodu vanek_za_1925 yes (Legions)
RN Rota Nazdar bohac_kronika_1938 yes (Legions)
VB V boj! bednarik_v_1927 yes (Legions)
DATA csol dataset csol_legionari_nd (confirm in Zotero) yes (Legions)

⚠️ Author flag (P408). Zotero files Pod císařským praporem under the sole author Pernes, but the in-text citations here use the full list Dolejší et al. (2003). Align one to the other before export — either add the three co-authors to the Zotero record, or switch the doc's in-text to Pernes et al. (the key pernes_pod_2003 is unaffected either way).

Units & armiesHow it works

regiment vs unit_key, and which units Fidler confirms.

Explain it simply

A soldier's record says he served in the 1st Regiment. Which one? The Czechoslovak Legion in Russia had a 1st Regiment. So did the Legion in Italy. So did the French Foreign Legion, in which a Czech company also fought. Three different wars, three different sets of men, one number.

Treating them as one unit would be a real error, not a cosmetic one, because unit is how soldiers are grouped for almost every comparison in the project — how big was his company, how many of his comrades died, did his regiment desert as a bloc. Mix two regiments together and every one of those numbers is wrong.

The fix is to stop using the number alone and use regiment plus army as the identity. It sounds trivial; it moved 4,347 records, and it revealed that the records labelled 1st Regiment are mostly Foreign Legion rather than the Czechoslovak 1st Rifles that everyone assumes.

Unit identity — regiment, army, and unit_key

The raw enrolment field is label ( formation ), e.g. 1.stř.pl. ( Čs. legie v Rusku ). The parenthetical is the part that is easy to misread: it names an army, not merely a theatre. Regiment numbering restarts inside each army, so the bare number is not a unit identifier.

Across the 132,993 records there are 37 distinct armies / formations. 4,262 records (3.2%) sit under a non-Czechoslovak army — the French Foreign Legion (whose 1st regiment, rota Nazdar included, is not the Czechoslovak 1st Rifle), the French, Serbian, Russian and British armies. Collapsing on regiment alone would merge those with Legion units of the same number.

Army / formationRecords%
CS Legion (Russia)85,08764.0%
CS Legion (Italy)28,45821.4%
CS Legion (France)9,7007.3%
— (none recorded)5,4864.1%
Russian Army2,1241.6%
Serbian Legion8550.6%
French Foreign Legion4600.3%
British Army3060.2%
French Army1610.1%
Russian Army (Kornilov)830.1%
US Army790.1%
Polish Legion410.0%
Canadian Army340.0%
Romanian Army280.0%
Czechoslovak Army (post-1918)230.0%
Australian Army130.0%
Italská armáda110.0%
Kolčakova armáda70.0%
MRA60.0%
Děnikinova armáda40.0%
Domobrana v Itálii40.0%
DM30.0%
LP20.0%
LLR20.0%
ČH20.0%
L20.0%
S10.0%
ČNR10.0%
lLR10.0%
LER10.0%
LKR10.0%
v10.0%
RM10.0%
Lf10.0%
S X10.0%
2.stř.pl.10.0%
BEL10.0%
LRF10.0%

The three columns, and which to use

ColumnWhat it isUse for
regimentdisplay label, army stripped (6,058 distinct)reading, plot facets
legion_armythe formation, normalisedsplitting Legion from foreign service
unit_keyregiment @ army (6,594 distinct identities)unit fixed effects, clustering, size denominators

6,058 labels resolve into 6,594 distinct unit_key identities — the gap is the regiment numbers shared across armies. See Pipeline → units.py and Variables §5.

Which units actually existed — the Fidler cross-check

Every Czechoslovak-Legion record whose regiment carries a number is checked against the unit authority derived from Fidler (2025): did that regiment number exist in the theatre the record claims? Support companies, foreign-army units and unnumbered formations carry no number to check and are marked not checkable — that is not the same as wrong.

StatusRecords% of recordsDeserters
Verified — number exists in this theatre71,58853.8%2,136
No Fidler entry for this number2,5581.9%0
Conflict — number never existed in this theatre2660.2%0
Not checkable (support unit / foreign army / no number)58,58144.0%1,581

The conflicts are records whose regiment number never existed in the tagged theatre — the 21st–23rd rifle regiments were French, the 31st–39th Italian, yet a handful are tagged Russia. These are contradictions inside csol.cz, reproduced rather than silently corrected, and flagged by a soft QA check. The validation sheet has the full account.

Regiment labelTagged theatreRecords
23.stř.pl.Russia34
21.stř.pl.Russia28
23.p.pl.Russia27
21.p.pl.Russia25
33.p.pl.France17
4.p.pl.Italy16
22.stř.pl.Russia9
33.p.pl.Russia9
3.p.pl.Italy9
1.p.pl.Italy7
23.p.pl.Italy6
32.p.pl.Russia6

What this feeds

The Descriptives → analysis sample waterfall lets you require a verified unit (or merely a non-contradicted one) and see the cost in sample size — the borderline decision this cross-check exists to inform.

Habsburg army & recruitmentHow it works

Ergänzung system: how a unit ties to a home district (+ regiment explorer). · source: reference/units/ERGAENZUNG.md

In short

How the Austro-Hungarian army was organised, how each unit was raised from a place of origin (the Ergänzung system), and how that lets a Habsburg unit be tied back to a 1910 census district. Grounded only in repository sources (library/ah-army/, library/books/); the crosswalk file is reference/units/ergaenzungsbezirk_1914.csv.

Explain it simply

The Habsburg army recruited locally on purpose. Each regiment had a home district it drew its men from, so a regiment was in effect a slice of one region — its language, its trades, its villages. That is the Ergänzung system, and it is why a regiment number is quietly also a place.

That link is worth a great deal here. It lets a man's pre-Legion unit be tied back to a census district even when his own birthplace field is unreadable, and it gives a way to ask whether men from the same home district behaved alike — the kind of comparison the whole literature on unit cohesion turns on.

It also explains the ambiguity next door: the Monarchy ran three parallel armies — the common army, the Austrian Landwehr and the Hungarian Honvéd — each numbering its own regiments from one, each with its own command language. A bare number is genuinely ambiguous, which is not sloppiness in the records but a fact about the state that kept them.

The three pillars

The armed forces had three parallel components, each its own numbering and command language — so a bare regiment number is ambiguous and the army qualifier is essential (this is exactly what unit_key = regiment × army encodes):

Pillar Command language 1914 infantry
k.u.k. Heer (common army) German 110 regiments + 30 Feldjäger battalions
k.k. Landwehr (Austrian) German 40 regiments
k.u. Honvéd (Hungarian) Magyar / Croatian 32 regiments

Plus Bosnian-Herzegovinian troops. Peacetime → war hierarchy: 16 corps (= Militärterritorialbereiche) → divisions → brigades → regiments → battalions → companies; field armies formed only on mobilisation (Ehnl (1934), pp.4–15).

The Ergänzung system — a unit ⇒ a home district

  • The Monarchy was divided into 16 corps areas, each subdivided into Heeres- and Landwehr-Ergänzungsbezirke (recruiting districts) that coincided (Ehnl (1934), p.9).
  • Core rule: each Heeres-Ergänzungsbezirk raised exactly one common-army infantry regiment — so a k.u.k. IR ⇒ one home district, regionally (hence ethnically) homogeneous by design. Landwehr IR drew from several districts; cavalry/artillery from the whole corps area; railway/telegraph from the whole Monarchy (Ehnl (1934), p.9).
  • Totals: 109 Heeres-, 59 Landwehr-, 47 Honvéd recruiting districts; the Czech crown lands held 24 Heeres districts (Ehnl (1934), pp.9–10).
  • Recruitment ≠ garrison. Only IR 14 and 41 were strictly territorially garrisoned; units "drew their replacements from outside the region in which they were normally stationed" (Ehnl (1934), p.10; Rothenberg (1976), p.99). A regiment therefore predicts a soldier's home district well, his garrison poorly.
  • Wartime dilution. Depots (Ersatzbataillone) fed Marschbataillone to the front; heavy 1914–15 losses and lowered standards eroded the regional/ethnic homogeneity over the war (Rothenberg (1976), pp.128, 199, 211). So homogeneity is a decaying quantity — date matters.

What the data already carries

The legionnaire records parse the Habsburg unit at two moments — this is the recruitment- vs-field distinction above, already in the data:

Field Coverage Meaning
habsburg_unit_peacetime 47.6% peacetime service regiment — closest to the recruiting regiment ⇒ Ergänzungsbezirk
habsburg_unit_capture 75.6% wartime field unit at capture; equals the peacetime regiment for early captures, drifts later

The abbreviation encodes the pillar (p.pl. = common-army IR, zem. = Landwehr, dom. = Landsturm, stř. = Schützen, drag. = dragoons). The most common capture-units — IR 28 (Prague), 11, 3, 88, 21, 75, 36, 98 — are precisely the Bohemian common-army regiments, confirming the field is the recruitment-linked regiment. For a home-district signal, prefer habsburg_unit_peacetime, fall back to habsburg_unit_capture with a war-timing caveat.

Two crosswalk files

1. ergaenzungsbezirk_1914.csv — district gazetteer. Recruiting-district towns from the ErgBezKmdo. <town> tokens in the 1914 common-army Schematismus OCR, matched (umlaut-folded, rapidfuzz ≥ 92) to data/final/geo/districts_1910: 19 Czech-lands districts resolved to a 1910 okres; the rest flagged out-of-region (Galicia, Hungary, Austria, Bosnia — no repo GIS).

2. The regiment-level tables (this is the good part). Ehnl's Tabelle I was re-read directly from the PDF page scans at 300 DPI (the fold-out text layer is corrupt), giving, per regiment: Inhaber, founding year, Ergänzungsbezirk, garrison, corps/division/brigade, and the full Umgangssprache nationality breakdown (11 languages, % of the Grundbuchstand) — plus derived dominant_lang, dominant_share, herfindahl, pct_slav, and an okres join. This resolves the regiment ⇄ district pairing the Schematismus OCR could not, and supplies the per-regiment ethnic-homogeneity measure. All 1914 infantry, 179 regiments across four files (→ okres = Czech-lands 1910 okres; → járás = Slovak/Subcarpathian 1910 járás — both Czechoslovak territory 1918–38):

File Pillar Regiments → okres → járás Nationality mix
ehnl_tabelle1_infantry.csv k.u.k. common army 102 23 9 15 Czech-majority; 24 Magyar-, 18 German-dominant
ehnl_tabelle1_landwehr.csv k.k. Landwehr 37 16 0 11 Czech-heavy; 12 German-dominant
ehnl_tabelle1_honved.csv k.u. Honvéd 32 0 7 17 Magyar-, 7 Romanian-, 3 Slovak-dominant
ehnl_tabelle1_special.csv Kaiserjäger + Bosnian-Herceg. 8 0 0 4 Kaiserjäger (German/Italian, Tyrol); 4 b-h (93–95 % Serbo-Croat)

Every regiment carries the German recruiting town, its modern ergbez_modern, and — where it falls in the repo's geography — either a Czech okres_code/okres_cs (via districts_1910) or a Slovak sk_jaras/sk_megye (via districts_hu_1910). 55 of 179 regiments now resolve to a district (39 Czech + 16 Slovak/Subcarpathian, e.g. IR 71 Trenčín, IR 72 Bratislava, IR 34 Košice, Honvéd 15 Trenčín, 13 Bratislava). The remaining ~124 recruit in Galicia, inner Austria, Hungary, Croatia, Bosnia or Tyrol — modern names are given, but the repo has no GIS there.

Each row also carries ergbez_modern — the recruiting town in its modern standardized form (Trautenau → Trutnov, Kremsier → Kroměříž, Reichenberg → Liberec; Slovak/Galician majors curated, e.g. Preßburg → Bratislava, Krakau → Kraków). Czech-lands towns all resolve via districts_1910; towns outside the repo's Czech + Slovak geography keep the German name. The okres join uses a strict full-string matcher (an earlier substring matcher had spuriously mapped Kaschau→Aš and the Transylvanian Bistritz (Beszterce) to a Moravian okres — both removed).

Twin pairs: the recruiting district is the area unit, not the statutory city (corrected by hand, 2026-08-01). Six recruiting districts fall on a 1910 city/rural twin (Kremsier, Olmütz, Troppau, Iglau, Znaim, Brünn). Nine of the ten pairs carry the same okres_de on both members, so the German-name match scored 100 on each and the tie broke on row order — landing, by accident, on the rural member. Brünn is the one pair German distinguishes (302-4 Bruenn Umgebung vs 302-5 Bruenn), so it resolved deterministically to the statutory city. That is wrong on the Ergänzungsbezirk's own definition: a district raised a whole infantry regiment from an area, while a Statutarstadt was a single municipality (D-17). Brünn → 302-4 (Brno) in all three files (this gazetteer, IR 8, Landwehr IR 14), matching the other five. Neither member alone is the Ergänzungsbezirk — it spanned city plus ring — so a covariate merge should use the pooled census_okres_id (D-18); this fixes only which of the two members the row points at.

Match rate to the person datasets

The complete 1914 order of battle — 443 units now captured across the five Tabelle I files:

Branch Units
Infantry (incl. 5 Landesschützen) 184
Feldjäger 30
Cavalry (Dragoner / Husar / Ulan + Landwehr / Honvéd) 58
Artillery (field / mountain / fortress / heavy + Landwehr / Honvéd) 130
Technical (Sappeur / Pionier / Train / railway) 41
Total 443

Matching to the person data works at two levels.

(a) Regiment-level — parsing the legion habsburg_unit_capture / _peacetime abbreviations (p.pl. / zem.p.pl. / honv.p.pl.) and the fallen ah_unit_org + ah_unit_primary to a specific Tabelle I regiment (all 171 line-infantry regiments appear in both datasets):

Dataset Field Infantry → a specific regiment
Legionnaires habsburg_unit_capture (76 % populated) 99.2 % of infantry (75.7 k)
Legionnaires habsburg_unit_peacetime (48 %) 99.6 % (48.3 k)
Habsburg fallen ah_unit_primary (Infantry-typed) 92.4 % of infantry (78.6 k)

(b) Arm-level coverage — now that the whole order of battle is captured, most non-infantry records also fall inside a Tabelle I arm (Feldjäger, cavalry, artillery, technical), not only line infantry:

Legionnaires Habsburg fallen
total records 132,993 186,192
…in a Tabelle I arm (any branch) 83,569 (63 %) 101,892 (55 %)
…of which line infantry → a specific regiment 75,729 78,630

So the crosswalk now reaches 63 % of legionnaires and 55 % of the fallen (up from 57 % / 42 % when it was infantry-only). Why not higher? It is overwhelmingly a source-record limit, not a missing-units limit — the unmatched records split as:

unmatched because… Legionnaires Habsburg fallen
no Habsburg unit recorded at all (blank, "did not serve", labour detail) 24 % 38 %
unit recorded but not in Ehnl's table — Landsturm (dom.p.pl., mobilisation-only) 5 % ~4 %
unit recorded but unparseable / other (no regiment number, rare abbrev.) 8 % ~3 %

So the dominant reason is (a) no unit recorded (a quarter of legionnaires, over a third of the fallen — the VÚA/csol source simply has no Habsburg unit for them), not (b) units missing from the table. The whole 1914 standing army is in the table; the only genuinely-absent category is the Landsturm (~4–5 %), which by definition never existed in peacetime and so cannot appear in Ehnl's peacetime order of battle.

(The k.k. Landwehr fielded 37 numbered infantry regiments + 3 Tyrolean Landesschützen = Ehnl's "40"; the Landesschützen are in ehnl_tabelle1_other.csv.)

  • The Czech-heavy regiments are precisely the top habsburg_unit_capture values among legionnaires. Common army: IR 28 (Prag) 95 %, IR 36 (Jungbunzlau) 95 %, IR 21 (Čáslau) 87 %, IR 3 (Kremsier) 83 %, IR 11 (Pisek) 79 %. Landwehr (zem.p.pl. in the data): LIR 8 (Prag) 95 %, LIR 10 (Jungbunzlau) 95 %, LIR 12 (Čáslau) 87 %, LIR 7 (Pilsen) 60 %. The high-Czech regiments produced the most legionnaires — the desertion mechanism made visible.
  • The Honvéd's 3 Slovak-dominant regiments (Trenčín, Neusohl, Neutra) + Munkács are where Slovak legionnaires concentrate.

Note on faithfulness

Both new files carry one flagged confidence = low row transcribed as printed, not corrected: Landwehr IR 4 (Klagenfurt) reads German 79 / other 21 (the 21 sits in the verschiedene column, not Slovene — the vision-OCR corrected an error in the seed I gave it); Honvéd IR 11 (Munkács/Ungvár) reads Magyar 42 / Slovak 40 / Polish 10 — historically surprising for a Ruthenian district, but that is what the 1934 table prints.

Provenance & verification of the Tabelle I extraction

Vision re-OCR (300 DPI, half-page tiles) by a Sonnet subagent, seeded with seven ground-truth rows read independently (IR 1/3/4/14/15/16/17); those matched exactly, and IR 25–29 / 49–60 were re-verified by hand. Regiment numbers are sequential 1–102; 101 of 102 rows' percentages sum to 100. Two rows carry genuine source quirks (faithfully transcribed, confidence = low): - IR 51 (Klausenburg/Kolozsvár): Magyar 34 + Romanian 72 + other 4 = 110 — an arithmetic error in the printed 1934 table (digits confirmed at 6× zoom). - IR 26 (Gran/Esztergom): the 38 % minority is printed in the Slovene column though an Esztergom regiment should read Slovak; the original page carries a period reader's pencil arrow at that very cell. Left as printed.

Limitation

Geography. The repo backbone covers only the Czech lands + Upper Hungary (Slovakia), so Galician/Austrian/Hungarian recruiting districts cannot resolve to an okres without extending the GIS. For the Czech-legionnaire question this is the right subset; a whole-army control would need the extension. (The nationality columns, by contrast, are complete for all 179 regiments.)

Deployment & identification (from the library)

⚑ Two candidate natural experiments for front assignment. (1) The B-Staffel swing. In the 1914 mobilisation the 12-division "B-Staffel" (Second Army) was ordered to Serbia on 30 July, then railed north to Galicia when Russia mobilised — its Serbia-vs-Russia destination fixed by railway timetables and Conrad's flip-flop, not by the ethnic make-up of its regiments (Lloyd (2024), Ch. 1). Identify which regiments sat in B-Staffel and you have a plausibly-exogenous front shock. (2) The co-national-enemy structure. Slav soldiers' reluctance switched off against Italy but on against Russia/Serbia (Stevenson (2004), p.189) — so the enemy's identity (Slav vs. Italian) is a theory-grounded, partly-as-if-random treatment, and date-of-capture moderates it.

Whether a regiment's front was independent of its origin matters for identification, and the sources say it was not — through two channels: - Mechanical. The 1914 plan split the army into three echelons — A-Staffel (28 inf + 10 cav divisions → Galicia/Russia), Minimalgruppe Balkan (8 → Serbia), and the swing B-Staffel (12 divisions, "sent either north or south as the situation demanded"), which Conrad ordered to Serbia on 30 July then had to rail north to Galicia (Lloyd (2024), Ch. 1). Because a regiment's corps = its recruiting territory, origin and initial front are coupled — but the B-Staffel swing is a plausibly-exogenous Serbia-vs-Galicia shock worth exploiting. - Endogenous reliability sorting. IR 28 (Prague) "went over to the Russians" at the Dukla Pass, 3 April 1915 (Lloyd (2024), Ch. 5) and was "transferred to Linz" as unreliable (Rothenberg (1976), p.139); Slav units were shifted away from co-national enemies, and the Czechoslovak Legion itself recruited on the Russian front from POWs/deserters (Lloyd (2024), Ch. 17). So the "Russia ≈ 4 %" desertion pattern reflects opportunity (the Legion was there) as much as propensity — which is exactly why the within-person double-desertion design is needed. - Co-national-enemy structure + timing. The reluctance was specific to fighting fellow Slavs: the Poles "feared being sent against their fellows under Russian rule", while "a war against Italy evoked none of the ambivalence felt among the Habsburgs' Slav populations about fighting Russia" (Stevenson (2004), pp.53, 189). And the endogeneity grows over the war — Czech morale only settled into a two-year "equilibrium" after the front stabilised in mid-1915 (Hutečka (2019), p.76). So the enemy's identity (Slav vs Italian) is a theory-grounded treatment and date-of-capture moderates it — both usable in the design.

Qualitative context from the library

  • Hutečka (2019) — a caution on the outcome itself: the "Czech deserter/traitor" image was a post-hoc national myth embraced by the exile government "for the purposes of political propaganda" (p.15); the soldiers' actual stance was "loyalty bordering on indifference," recast as suspected disloyalty by the army command's own traditional suspicion of Czechs (p.270). The double-desertion design is the corrective to that myth.
  • Deák (1990) — "an officer's loyalty was always to his regiment" (p.34); the ranks came largely from peasants of "conservative loyalty" recruited "more or less evenly from all nationalities" (pp.22–24). Supports the regiment as the unit of loyalty/fixed effect.
  • Burgschwentner et al. (2014) — on the Russian front, soldiers "frequently tried to escape the front experience by desertion or surrender," preferring "the minor penalties … or even the dangers of a prison camp to being butchered at the front" (pp.26, 59); court-martials "had little impact" (p.61). Desertion as rational front-exit.
  • Cornwall (2000) — the enemy morale war: intelligence officers worked "interrogating prisoners or deserters" to sharpen propaganda (p.22); nationalities held a "dual allegiance to dynasty and nationality" (p.40) — i.e. the second (Legion) choice was actively cultivated.
  • McNamara (2016) — the Legion's founding combat at Zborov (July 1917), ~3,500 Czech soldiers whose stand emboldened the Czech cause and coincided with the imperial amnesty.
  • Lloyd (2024) — 750 Habsburg soldiers were executed during the war (vs 346 British, 48 German), most for desertion, with punishments extending to summary decimation of units — the coercive backdrop to the desertion decision.

Next steps

  • ✅ The complete 1914 order of battle is captured — 179 infantry regiments (_infantry / _landwehr / _honved / _special, with Czech-okres + Slovak-járás joins) plus 262 non-infantry units in ehnl_tabelle1_other.csv: Feldjäger 30, all cavalry (Dragoner/Husar/Ulan + Landwehr/Honvéd), all artillery (field/mountain/fortress/heavy), technical troops and Landesschützen — 441 units total, each with its nationality %. Cavalry and artillery were corps-recruited, so they carry no single Ergänzungsbezirk (only nationality + corps). The interactive regiment explorer on this sheet now spans all 443 (filter by branch + language). Provenance: the Honvéd cavalry/artillery and the Landwehr artillery were read by hand; the rest by the vision-OCR subagent (spot-checked by geographic sanity — Husars all Magyar, Galician artillery Ruthenian). The apparent Feldjäger gaps (battalions 3, 15, 26) are real 1914 numbering gaps, not misses; the Landwehr artillery is now cleanly split into 8 Feldkanonen- + 8 Feldhaubitzdivisionen (numbered to match their Landwehr infantry division). The order of battle is complete.
  • Join ehnl_tabelle1_infantry.csv onto the person panel via habsburg_unit_peacetime / habsburg_unit_capture (reg. no.), giving every soldier a home okres + a unit ethnic-homogeneity value; then test homogeneity → loyalty and the co-national-enemy structure above.
  • Decide whether to extend the GIS to Galicia/Austria for a whole-army control, or restrict the Habsburg comparison to Czech-lands recruits.

Explore the complete order of battle — 443 units ⇱ Interactive

Every 1914 unit from Ehnl’s Tabelle I — 179 infantry regiments plus 264 cavalry, artillery, Feldjäger and technical units. Filter by branch or dominant language, search a district or type, or click a column header to sort. Infantry regiments carry a recruiting district; cavalry and artillery were corps-recruited (no single district — “—”). “Homogeneity %” is the dominant language’s share. Try branch = Infantry + language = Czech for the regiments that produced the most legionnaires.

BranchLanguage
BranchTypeNo.Recruiting districtDominant language% CzechHomogeneity %1910 district
Infantrycommon1OpavaGerman082Opava
Infantrycommon2Kronstadt (Brassó)Magyar061—
Infantrycommon3KroměřížCzech8383Kroměříž
Infantrycommon4WienGerman095—
Infantrycommon5Szatmár-NémetiMagyar058—
Infantrycommon6Neusatz (Ujvidek)German041—
Infantrycommon7KlagenfurtGerman079—
Infantrycommon8BrnoCzech6767Brno
Infantrycommon9StryiRuthenian073—
Infantrycommon10PrzemyślRuthenian047—
Infantrycommon11PísekCzech7979Písek
Infantrycommon12KomárnoMagyar3158—
Infantrycommon13KrakówPolish082—
Infantrycommon14LinzGerman098—
Infantrycommon15TernopilRuthenian062—
Infantrycommon16BelovarCroat-Serb097—
Infantrycommon17LjubljanaSlovene086—
Infantrycommon18Hradec KrálovéCzech7575Hradec Králové
Infantrycommon19Raab (Györ)Magyar095—
Infantrycommon20Nowy SączPolish086—
Infantrycommon21ČáslavCzech8787Čáslav
Infantrycommon22SinjCroat-Serb082—
Infantrycommon23ZomborMagyar052—
Infantrycommon24KolomyjaRuthenian079—
Infantrycommon25LučenecMagyar056Losonci
Infantrycommon26Gran (Esztergom)Magyar053Esztergomi
Infantrycommon27GrazGerman094—
Infantrycommon28PrahaCzech9595Praha
Infantrycommon29Nagy BecskerekCroat-Serb044—
Infantrycommon30LvivRuthenian059—
Infantrycommon31Hermannstadt (Nagyszeben)Romanian069—
Infantrycommon32BudapestMagyar091—
Infantrycommon33AradRomanian054—
Infantrycommon34KošiceMagyar091Kassai
Infantrycommon35PlzeňCzech6060Plzeň
Infantrycommon36Mladá BoleslavCzech9595Mladá Boleslav
Infantrycommon37Großwardein (Nagy Várad)Romanian049—
Infantrycommon38KecskemétMagyar097—
Infantrycommon39Debrezin (Debreczen)Magyar092—
Infantrycommon40RzeszówPolish097—
Infantrycommon41ChernivtsiRomanian054—
Infantrycommon42TerezínGerman086—
Infantrycommon43KaránsebesRomanian078—
Infantrycommon44KaposvárMagyar088—
Infantrycommon45SanokRuthenian047—
Infantrycommon46Szegedin (Szeged)Magyar079—
Infantrycommon47MariborGerman077—
Infantrycommon48Groß-(Nagy-)KanizsaMagyar082—
Infantrycommon49Sankt PöltenGerman098—
Infantrycommon50Karlsburg (Gyulafehérvár)Romanian071—
Infantrycommon51Klausenburg (Kolozsvár)Romanian072—
Infantrycommon52Fünfkirchen (Pécs)Magyar052—
Infantrycommon53AgramCroat-Serb097—
Infantrycommon54OlomoucCzech6464Olomouc
Infantrycommon55BerezhanyRuthenian059—
Infantrycommon56WadowicePolish088—
Infantrycommon57TarnówPolish091—
Infantrycommon58Ivano-FrankivskRuthenian072—
Infantrycommon59SalzburgGerman097—
Infantrycommon60Erlau (Eger)Magyar098—
Infantrycommon61TemesvárRomanian038—
Infantrycommon62Maros-VásárhelyMagyar049—
Infantrycommon63Bistritz (Beszterce)Romanian073—
Infantrycommon64Broos (Szászváros)Romanian086—
Infantrycommon65MukačevoMagyar083Munkácsi
Infantrycommon66UžhorodSlovak046Ungvári
Infantrycommon67PrešovSlovak070Eperjesi
Infantrycommon68SzolnokMagyar098—
Infantrycommon69Stuhlweißenburg (Székesfehérvár)Magyar092—
Infantrycommon70PeterwardeinCroat-Serb079—
Infantrycommon71TrenčínSlovak085Trencséni
Infantrycommon72BratislavaSlovak051Pozsonyi
Infantrycommon73ChebGerman097Cheb
Infantrycommon74JičínCzech6363Jičín
Infantrycommon75Jindřichův HradecCzech7979Jindřichův Hradec
Infantrycommon76Ödenburg (Sopron)German054—
Infantrycommon77SambirRuthenian069—
Infantrycommon78EssegCroat-Serb084—
Infantrycommon79OtočacCroat-Serb096—
Infantrycommon80ZolochivRuthenian068—
Infantrycommon81JihlavaCzech6969Jihlava
Infantrycommon82Székely UdvarhelyMagyar088—
Infantrycommon83Steinamanger (Szombathely)Magyar055—
Infantrycommon84WienGerman097—
Infantrycommon85Máramaros SzigetRuthenian033Szigeti
Infantrycommon86Maria Theresiopel (Szabatka)Magyar076—
Infantrycommon87CeljeSlovene086—
Infantrycommon88BerounCzech7272Hořovice
Infantrycommon89Gródek-JagiellońskiRuthenian060—
Infantrycommon90JarosławPolish075—
Infantrycommon91České BudějoviceGerman4554České Budějovice
Infantrycommon92ChomutovGerman080Chomutov
Infantrycommon93ŠumperkGerman3560Šumperk
Infantrycommon94TurnovGerman2276Turnov
Infantrycommon95ChortkivRuthenian070—
Infantrycommon96CarlstadtCroat-Serb097—
Infantrycommon97TriesteSlovene045—
Infantrycommon98Vysoké MýtoCzech6868Vysoké Mýto
Infantrycommon99ZnojmoGerman3760Znojmo
Infantrycommon100TěšínPolish3337Těšín
Infantrycommon101BékéscsabaMagyar084—
Infantrycommon102BenešovCzech9191Benešov
InfantryLandwehr1WienGerman095—
InfantryLandwehr2Linz; SalzburgGerman098—
InfantryLandwehr3Graz; MariborGerman094—
InfantryLandwehr4KlagenfurtGerman079—
InfantryLandwehr5TriesteSlovene045—
InfantryLandwehr6Cheb; BerounGerman097Cheb
InfantryLandwehr7Plzeň; Beroun; PísekCzech6060Plzeň
InfantryLandwehr8Praha; BerounCzech9595Praha
InfantryLandwehr9Litoměřice; ChomutovGerman086Litoměřice
InfantryLandwehr10Mladá Boleslav; TurnovCzech9595Mladá Boleslav
InfantryLandwehr11Jičín; Hradec KrálovéCzech6363Jičín
InfantryLandwehr12Čáslav; Mladá BoleslavCzech8787Čáslav
InfantryLandwehr13Olomouc; ŠumperkCzech6464Olomouc
InfantryLandwehr14Brno; JihlavaCzech6767Brno
InfantryLandwehr15Opava; OlomoucGerman082Opava
InfantryLandwehr16Kraków; WadowicePolish082—
InfantryLandwehr17RzeszówPolish097—
InfantryLandwehr18Przemyśl; SanokRuthenian047—
InfantryLandwehr19Lviv; BerezhanyRuthenian059—
InfantryLandwehr20Ivano-Frankivsk; Berezhany; ChortkivRuthenian072—
InfantryLandwehr21Sankt Pölten; WienGerman098—
InfantryLandwehr22Chernivtsi; KolomyjaRomanian054—
InfantryLandwehr23SebenicoCroat-Serb082—
InfantryLandwehr24Wien; ZnojmoGerman097Znojmo
InfantryLandwehr25KroměřížCzech8383Kroměříž
InfantryLandwehr26Maribor; CeljeGerman077—
InfantryLandwehr27Ljubljana; TriesteSlovene086—
InfantryLandwehr28Písek; Jindřichův Hradec; BenešovCzech7979Písek
InfantryLandwehr29České Budějovice; PísekGerman4554České Budějovice
InfantryLandwehr30Vysoké Mýto; Hradec KrálovéCzech6868Vysoké Mýto
InfantryLandwehr31Těšín; WadowiceGerman3337Těšín
InfantryLandwehr32Nowy Sącz; TarnówPolish091—
InfantryLandwehr33Stryi; SambirRuthenian073—
InfantryLandwehr34Jarosław; Gródek-JagiellońskiPolish075—
InfantryLandwehr35Zolochiv; TernopilRuthenian068—
InfantryLandwehr36Kolomyja; Ivano-Frankivsk; ChortkivRuthenian070—
InfantryLandwehr37CastelnuovoCroat-Serb082—
InfantryHonvéd1BudapestMagyar091—
InfantryHonvéd2Gyula; AradRomanian054—
InfantryHonvéd3DebrecinMagyar092—
InfantryHonvéd4GroßwardeinRomanian049—
InfantryHonvéd5Szegedin; GroßbecskerekMagyar079—
InfantryHonvéd6Maria-Theresiopel; Zombor; NeusatzMagyar076—
InfantryHonvéd7WerschetzRomanian044—
InfantryHonvéd8LugosRomanian078—
InfantryHonvéd9Košice; Spišská Nová VesMagyar091Iglói
InfantryHonvéd10ErlauMagyar098—
InfantryHonvéd11Mukačevo; UžhorodMagyar042Munkácsi
InfantryHonvéd12Szatmár-Németi; MáramarosszigetMagyar058Szigeti
InfantryHonvéd13BratislavaSlovak051Pozsonyi
InfantryHonvéd14Nitra; LeviceSlovak085Lévai
InfantryHonvéd15TrenčínSlovak085Trencséni
InfantryHonvéd16Banská Bystrica; JelšavaMagyar056Besztercebányai
InfantryHonvéd17Stuhlweißenburg; KaposvárMagyar092—
InfantryHonvéd18Ödenburg; GünsGerman054—
InfantryHonvéd19Fünfkirchen; Kaposvár; FiumeMagyar052—
InfantryHonvéd20Groß-KanizsaMagyar082—
InfantryHonvéd21Klausenburg; NagyenyedRomanian062—
InfantryHonvéd22Maros-VásárhelyMagyar049—
InfantryHonvéd23Hermannstadt; DévaRomanian069—
InfantryHonvéd24Kronstadt; CzikszeredaMagyar061—
InfantryHonvéd25Agram; WarasdinCroat-Serb097—
InfantryHonvéd26Carlstadt; GospićCroat-Serb097—
InfantryHonvéd27EssegCroat-Serb084—
InfantryHonvéd28EssegCroat-Serb096—
InfantryHonvéd29Budapest; JászberényMagyar091—
InfantryHonvéd30BudapestMagyar091—
InfantryHonvéd31VeßprimMagyar092—
InfantryHonvéd32BistritzRomanian073—
Infantryspecial1Innsbruck; Bressanone; TrentoGerman058—
Infantryspecial2Innsbruck; Bressanone; TrentoGerman055—
Infantryspecial3Innsbruck; Bressanone; TrentoGerman059—
Infantryspecial4Innsbruck; Bressanone; TrentoGerman059—
Infantryspecial1SarajevoCroat-Serb094—
Infantryspecial2Banja LukaCroat-Serb093—
Infantryspecial3TuzlaCroat-Serb094—
Infantryspecial4MostarCroat-Serb095—
FeldjägerFeldjäger1nanGerman3662—
FeldjägerFeldjäger2nanCzech7474—
FeldjägerFeldjäger4nanPolish077—
FeldjägerFeldjäger5nanGerman2539—
FeldjägerFeldjäger6nanCzech6969—
FeldjägerFeldjäger7nanSlovene085—
FeldjägerFeldjäger8nanGerman078—
FeldjägerFeldjäger9nanGerman096—
FeldjägerFeldjäger10nanGerman098—
FeldjägerFeldjäger11nanGerman052—
FeldjägerFeldjäger12nanCzech6767—
FeldjägerFeldjäger13nanPolish047—
FeldjägerFeldjäger14nanRuthenian047—
FeldjägerFeldjäger16nanGerman3456—
FeldjägerFeldjäger17nanCzech6363—
FeldjägerFeldjäger18nanRuthenian059—
FeldjägerFeldjäger19nanSlovak058—
FeldjägerFeldjäger20nanSlovene048—
FeldjägerFeldjäger21nanGerman098—
FeldjägerFeldjäger22nanGerman4950—
FeldjägerFeldjäger23nanRomanian068—
FeldjägerFeldjäger24nanMagyar086—
FeldjägerFeldjäger25nanCzech7575—
FeldjägerFeldjäger27nanRomanian054—
FeldjägerFeldjäger28nanRomanian073—
FeldjägerFeldjäger29nanSlovak067—
FeldjägerFeldjäger30nanRuthenian070—
FeldjägerFeldjäger31nanCroat-Serb095—
FeldjägerFeldjäger32nanSlovak074—
FeldjägerFeldjäger1nanCroat-Serb096—
CavalryDragoner1nanCzech4848—
CavalryDragoner2nanCzech6161—
CavalryDragoner3nanGerman097—
CavalryDragoner4nanGerman099—
CavalryDragoner5nanSlovene051—
CavalryDragoner6nanCzech6161—
CavalryDragoner7nanGerman5050—
CavalryDragoner8nanCzech5858—
CavalryDragoner9nanRomanian050—
CavalryDragoner10nanCzech6262—
CavalryDragoner11nanCzech9090—
CavalryDragoner12nanCzech5050—
CavalryDragoner13nanCzech5151—
CavalryDragoner14nanCzech5959—
CavalryDragoner15nanGerman093—
CavalryHusar1nanMagyar085—
CavalryHusar2nanMagyar089—
CavalryHusar3nanMagyar068—
CavalryHusar4nanMagyar071—
CavalryHusar5nanMagyar090—
CavalryHusar6nanMagyar090—
CavalryHusar7nanMagyar098—
CavalryHusar8nanMagyar074—
CavalryHusar9nanMagyar081—
CavalryHusar10nanMagyar097—
CavalryHusar11nanMagyar096—
CavalryHusar12nanMagyar096—
CavalryHusar13nanMagyar097—
CavalryHusar14nanMagyar092—
CavalryHusar15nanMagyar091—
CavalryHusar16nanMagyar094—
CavalryUlan1nanPolish085—
CavalryUlan2nanPolish084—
CavalryUlan3nanPolish069—
CavalryUlan4nanRuthenian065—
CavalryUlan5nanCroat-Serb097—
CavalryUlan6nanPolish052—
CavalryUlan7nanRuthenian072—
CavalryUlan8nanRuthenian080—
CavalryUlan11nanCzech6565—
CavalryUlan12nanCroat-Serb081—
CavalryUlan13nanRuthenian055—
ArtilleryFeldkanonen1nanPolish049—
ArtilleryFeldkanonen2nanGerman3232—
ArtilleryFeldkanonen3nanPolish2047—
ArtilleryFeldkanonen4nanGerman083—
ArtilleryFeldkanonen5nanCzech6868—
ArtilleryFeldkanonen6nanGerman085—
ArtilleryFeldkanonen7nanSlovene064—
ArtilleryFeldkanonen8nanSlovene056—
ArtilleryFeldkanonen9nanGerman071—
ArtilleryFeldkanonen10nanMagyar072—
ArtilleryFeldkanonen11nanMagyar077—
ArtilleryFeldkanonen12nanMagyar083—
ArtilleryFeldkanonen13nanMagyar044—
ArtilleryFeldkanonen14nanSlovak062—
ArtilleryFeldkanonen15nanMagyar057—
ArtilleryFeldkanonen16nanMagyar064—
ArtilleryFeldkanonen17nanMagyar086—
ArtilleryFeldkanonen18nanMagyar065—
ArtilleryFeldkanonen19nanMagyar057—
ArtilleryFeldkanonen20nanGerman040—
ArtilleryFeldkanonen21nanRomanian044—
ArtilleryFeldkanonen22nanGerman3960—
ArtilleryFeldkanonen23nanCzech7272—
ArtilleryFeldkanonen24nanCzech7070—
ArtilleryFeldkanonen25nanCzech6262—
ArtilleryFeldkanonen26nanGerman4355—
ArtilleryFeldkanonen27nanCzech6666—
ArtilleryFeldkanonen28nanRuthenian060—
ArtilleryFeldkanonen29nanPolish078—
ArtilleryFeldkanonen30nanRuthenian066—
ArtilleryFeldkanonen31nanRuthenian069—
ArtilleryFeldkanonen32nanRuthenian055—
ArtilleryFeldkanonen33nanRuthenian043—
ArtilleryFeldkanonen34nanMagyar060—
ArtilleryFeldkanonen35nanRomanian041—
ArtilleryFeldkanonen36nanRomanian044—
ArtilleryFeldkanonen37nanCroat-Serb088—
ArtilleryFeldkanonen38nanCroat-Serb063—
ArtilleryFeldkanonen39nanCroat-Serb066—
ArtilleryFeldkanonen40nanGerman093—
ArtilleryFeldkanonen41nanGerman098—
ArtilleryFeldkanonen42nanGerman086—
ArtilleryFeldhaubitz1nanPolish044—
ArtilleryFeldhaubitz2nanGerman2967—
ArtilleryFeldhaubitz3nanGerman080—
ArtilleryFeldhaubitz4nanMagyar079—
ArtilleryFeldhaubitz5nanSlovak063—
ArtilleryFeldhaubitz6nanMagyar075—
ArtilleryFeldhaubitz7nanGerman043—
ArtilleryFeldhaubitz8nanCzech8888—
ArtilleryFeldhaubitz9nanCzech6363—
ArtilleryFeldhaubitz10nanPolish076—
ArtilleryFeldhaubitz11nanRuthenian052—
ArtilleryFeldhaubitz12nanMagyar039—
ArtilleryFeldhaubitz13nanCroat-Serb072—
ArtilleryFeldhaubitz14nanGerman097—
Artilleryreitende Artillerie1nanPolish2050—
Artilleryreitende Artillerie2nanGerman2870—
Artilleryreitende Artillerie4nanMagyar087—
Artilleryreitende Artillerie5nanSlovak070—
Artilleryreitende Artillerie6nanMagyar080—
Artilleryreitende Artillerie7nanMagyar040—
Artilleryreitende Artillerie9nanGerman3960—
Artilleryreitende Artillerie10nanPolish057—
Artilleryreitende Artillerie11nanRuthenian057—
Artilleryschwere Haubitz1nanPolish040—
Artilleryschwere Haubitz2nanGerman2868—
Artilleryschwere Haubitz3nanSlovene048—
Artilleryschwere Haubitz4nanMagyar078—
Artilleryschwere Haubitz5nanMagyar055—
Artilleryschwere Haubitz6nanMagyar072—
Artilleryschwere Haubitz7nanMagyar035—
Artilleryschwere Haubitz8nanCzech6060—
Artilleryschwere Haubitz9nanCzech5454—
Artilleryschwere Haubitz10nanRuthenian046—
Artilleryschwere Haubitz11nanRuthenian046—
Artilleryschwere Haubitz12nanMagyar041—
Artilleryschwere Haubitz13nanCroat-Serb082—
Artilleryschwere Haubitz14nanGerman095—
ArtilleryGebirgsartillerie3nanSlovene048—
ArtilleryGebirgsartillerie4nanMagyar078—
ArtilleryGebirgsartillerie6nanMagyar072—
ArtilleryGebirgsartillerie7nanMagyar035—
ArtilleryGebirgsartillerie8nanCzech6060—
ArtilleryGebirgsartillerie10nanRuthenian046—
ArtilleryGebirgsartillerie11nanRuthenian046—
ArtilleryGebirgsartillerie12nanMagyar041—
ArtilleryGebirgsartillerie13nanCroat-Serb082—
ArtilleryGebirgsartillerie14nanGerman098—
ArtilleryGebirgsartillerienannanCroat-Serb092—
ArtilleryFestungsartillerie1nanGerman2374—
ArtilleryFestungsartillerie2nanPolish2738—
ArtilleryFestungsartillerie3nanRuthenian050—
ArtilleryFestungsartillerie4nanGerman035—
ArtilleryFestungsartillerie5nanMagyar026—
ArtilleryFestungsartillerie6nanMagyar054—
ArtilleryFestungsartillerie1nanGerman094—
ArtilleryFestungsartillerie2nanRomanian036—
ArtilleryFestungsartillerie3nanGerman040—
ArtilleryFestungsartillerie4nanGerman3464—
ArtilleryFestungsartillerie5nanGerman3132—
ArtilleryFestungsartillerie6nanGerman031—
ArtilleryFestungsartillerie7nanMagyar054—
ArtilleryFestungsartillerie8nanSlovene048—
ArtilleryFestungsartillerie9nanCzech6060—
ArtilleryFestungsartillerie10nanGerman2868—
TechnicalSappeur1nanPolish2350—
TechnicalSappeur2nanGerman082—
TechnicalSappeur3nanGerman048—
TechnicalSappeur4nanMagyar074—
TechnicalSappeur5nanGerman040—
TechnicalSappeur6nanMagyar072—
TechnicalSappeur7nanMagyar053—
TechnicalSappeur8nanCzech7373—
TechnicalSappeur9nanCzech6666—
TechnicalSappeur10nanPolish050—
TechnicalSappeur11nanRuthenian048—
TechnicalSappeur12nanMagyar050—
TechnicalSappeur13nanCroat-Serb086—
TechnicalSappeur14nanGerman098—
TechnicalPionier2nanGerman098—
TechnicalPionier3nanGerman048—
TechnicalPionier4nanMagyar074—
TechnicalPionier5nanMagyar055—
TechnicalPionier7nanMagyar057—
TechnicalPionier8nanCzech5656—
TechnicalPionier9nanCzech4545—
TechnicalPionier10nanPolish050—
TechnicalPionier15nanCroat-Serb092—
TechnicalBrückennannanGerman098—
TechnicalVerkehrstruppennannanGerman2030—
TechnicalTrain1nanPolish057—
TechnicalTrain2nanGerman4248—
TechnicalTrain3nanGerman056—
TechnicalTrain4nanMagyar084—
TechnicalTrain5nanMagyar053—
TechnicalTrain6nanMagyar088—
TechnicalTrain7nanMagyar057—
TechnicalTrain8nanCzech6868—
TechnicalTrain9nanCzech6767—
TechnicalTrain10nanPolish049—
TechnicalTrain11nanRuthenian058—
TechnicalTrain12nanMagyar067—
TechnicalTrain13nanCroat-Serb068—
TechnicalTrain14nanGerman3066—
TechnicalTrain15nanCroat-Serb057—
TechnicalTrain16nanCroat-Serb052—
InfantryLandesschützenII.Brixen; TrientGerman055—
InfantryLandesschützenIII.Brixen; TrientGerman059—
CavalryLandwehr-Ulan1nanRuthenian065—
CavalryLandwehr-Ulan2nanCzech5858—
CavalryLandwehr-Ulan3nanPolish069—
InfantryLandesschützenI.Salzburg; Innsbruck; TrientGerman058—
CavalryLandwehr-Ulan4nanPolish085—
CavalryLandwehr-Ulan5nanGerman097—
CavalryLandwehr-Ulan6nanGerman3960—
InfantryLandesschützennannanGerman058—
InfantryLandesschützennannanCroat-Serb082—
CavalryHonvéd-Husar1nanMagyar091—
CavalryHonvéd-Husar2nanMagyar092—
CavalryHonvéd-Husar3nanMagyar079—
CavalryHonvéd-Husar4nanMagyar076—
CavalryHonvéd-Husar5nanMagyar091—
CavalryHonvéd-Husar6nanMagyar055—
CavalryHonvéd-Husar7nanMagyar054—
CavalryHonvéd-Husar8nanMagyar052—
CavalryHonvéd-Husar9nanMagyar049—
CavalryHonvéd-Husar10nanCroat-Serb097—
ArtilleryHonvéd-Artillerie1nanMagyar093—
ArtilleryHonvéd-Artillerie2nanRomanian069—
ArtilleryHonvéd-Artillerie3nanMagyar091—
ArtilleryHonvéd-Artillerie4nanSlovak051—
ArtilleryHonvéd-Artillerie5nanMagyar049—
ArtilleryHonvéd-Artillerie6nanCroat-Serb097—
ArtilleryHonvéd-Artillerie7nanSlovak051—
ArtilleryHonvéd-Artillerie8nanMagyar092—
ArtilleryLandwehr-Feldkanonen13nanGerman083—
ArtilleryLandwehr-Feldkanonen21nanCzech7272—
ArtilleryLandwehr-Feldkanonen22nanGerman071—
ArtilleryLandwehr-Feldkanonen26nanGerman4355—
ArtilleryLandwehr-Feldkanonen43nanRuthenian055—
ArtilleryLandwehr-Feldkanonen44nanGerman3960—
ArtilleryLandwehr-Feldkanonen45nanRuthenian060—
ArtilleryLandwehr-Feldkanonen46nanRuthenian049—
ArtilleryLandwehr-Feldhaubitz13nanGerman083—
ArtilleryLandwehr-Feldhaubitz21nanCzech7272—
ArtilleryLandwehr-Feldhaubitz22nanGerman071—
ArtilleryLandwehr-Feldhaubitz26nanGerman4355—
ArtilleryLandwehr-Feldhaubitz43nanRuthenian055—
ArtilleryLandwehr-Feldhaubitz44nanGerman3960—
ArtilleryLandwehr-Feldhaubitz45nanRuthenian060—
ArtilleryLandwehr-Feldhaubitz46nanRuthenian049—

Source provenanceHow it works

Which source grounds which mechanism.

Explain it simply

A finished number in a paper — desertion in Russia was four percent — hides a chain of sources behind it: a scraped website, a census workbook, a shapefile, a hand-written dictionary. If any link is weak, the number is weak, and by the time it reaches a table nobody can tell which link it was.

This sheet keeps the chain visible. Every mechanism the project claims is drawn back to the source that grounds it, so the reader can see at a glance which claims rest on an official census, which on a scholarly encyclopedia, and which on a judgement made here.

It is also a defence against a specific kind of self-deception: a source used for two different purposes starts to look like two independent confirmations. Drawn as a graph, that shows up immediately as one node with two arrows out of it — which is one piece of evidence, not two.

What each mechanism rests on ⇱ Interactive ↕ Hover for values

The pipeline’s ladders, mappings and validations are only as good as the sources under them. Each line runs from a source (left) to a mechanism it grounds (right). Click any box to isolate its links and open a detail panel on the right — a source lists everything it feeds; a mechanism lists everything it depends on, each entry clickable. Colour is the kind of source. Esc or click empty space clears.

Click a box to trace its provenance.
SOURCESMECHANISMS · LADDERS · MAPPINGScsol.cz legionnaire records — The ~133k primary records (scraped).csol.cz legionnaire recordsVÚA fallen-soldier database — 186k Habsburg dead (scraped) — the comparison population.VÚA fallen-soldier databaseFidler 2025 — Encyklopedie — Independent reference work: unit authority + Legion generals.Fidler 2025 — EncyklopedieDolejší 2003 (P408) — The k.u.k. officer/rank echelon order.Dolejší 2003 (P408)Purdek 2013 · Rozkaz č.15/117 — Legion sleeve-insignia rank plates.Purdek 2013 · Rozkaz č.15/117APersP-01 · STANAG 2116 — NATO OF/OR bands — the cross-army common scale.APersP-01 · STANAG 2116MPIDR / Rumpler-Seger 1910 — 1910 boundary GIS + Cisleithanian census.MPIDR / Rumpler-Seger 1910ČSÚ Historický lexikon — Czech settlement → 1910 okres crosswalk.ČSÚ Historický lexikonWikidata — Settlement coordinates + German names.WikidataGISta Hungarorum 1910 — Upper-Hungary járás GIS + census.GISta Hungarorum 1910Majo 2012 (ŠÚSR) — Slovak obec → 1910 okres/župa + mother tongue.Majo 2012 (ŠÚSR)Ehnl 1934 — Tabelle I — 1914 order of battle read from the fold-out plates at 300 DPI → each regiment's recruiting district, garrison, corps, and 11-language nationality %.Ehnl 1934 — Tabelle ISchematismus 1914 — The k.u.k. + Landwehr army lists — the ErgBez recruiting-district town gazetteer.Schematismus 1914Rothenberg 1976 — Habsburg army organisation; the recruitment≠garrison rule and the wartime replacement system.Rothenberg 1976Lloyd 2024 — Eastern Front — The 1914 three-echelon plan, the B-Staffel swing, IR 28's defection, and the Legion's POW recruiting.Lloyd 2024 — Eastern FrontDeák 1990 — Officer corps: loyalty was to the regiment; the ranks recruited across nationalities.Deák 1990Hutečka 2019 — The 'Czech deserter' myth caution and the timing of Czech morale on the Eastern Front.Hutečka 2019Exit-code outcome — The desertion outcome + competing risks.Exit-code outcomeTheatre classifier — Four-pass Russia/Italy/France imputation.Theatre classifierCross-dataset linkage — Confirmed legionnaire ↔ fallen-soldier overlaps.Cross-dataset linkageUnit identity (unit_key) — regiment × army, so numbers don't collide.Unit identity (unit_key)Unit authority + theatre check — Which units existed, where — the QA gate.Unit authority + theatre checkHabsburg rank ladder (§A) — k.u.k. ordinal ladder 1–20.Habsburg rank ladder (§A)Legion rank ladder (§B) — Legion ordinal ladder incl. generals (§B-gen).Legion rank ladder (§B)Rank reset (ALT-1 scale) — The headline regressor, on a subtractable scale.Rank reset (ALT-1 scale)Czech okres / court backbone — The 1910 district spine + point-in-polygon.Czech okres / court backboneCisleithanian census panel — Okres-level covariates (Czech lands).Cisleithanian census panelCzech settlement crosswalk — Village births → 1910 okres.Czech settlement crosswalkSettlement coordinates — Point maps + spatial standard errors.Settlement coordinatesUpper-Hungary járás backbone — The 1910 járás/town spine.Upper-Hungary járás backboneTransleithanian census panel — Járás-level covariates (Slovakia).Transleithanian census panelSlovak settlement crosswalk — Slovak village births → járás.Slovak settlement crosswalkErgänzungsbezirk crosswalk — Regiment → 1910 home district (okres/járás) + modern place names — a unit's place of origin.Ergänzungsbezirk crosswalkUnit nationality / homogeneity — Per-regiment ethnic composition + a homogeneity index (the Costa-Kahn loyalty channel).Unit nationality / homogeneityDeployment & natural experiments — How origin ties to front — the B-Staffel Serbia↔Galicia swing and the co-national-enemy structure.Deployment & natural experime…

Sources are catalogued in Data sources; the validation that added Fidler as a check is on the External validation sheet.

Architecture mapHow it works

Which script imports which.

Explain it simply

This project is about forty Python modules that call each other. That is small enough to feel manageable and quite large enough to hide a nasty surprise: you change how one function reads a date, and something four steps downstream that you had forgotten depends on it starts producing plausible, wrong answers.

So the import graph is drawn automatically from the source itself — never from a hand-kept list, which would be out of date within a week. Click a module and the map highlights everything that would feel a change to it.

The practical use is before an edit, not after. If the blast radius of a one-line change is eleven modules, that is worth knowing while the change is still an idea.

How the scripts fit together ⇱ Interactive ↕ Hover for values

Every arrow is a real import, read from the source at build time — A → B means A imports B. Two views of the same graph: the flowchart lays modules out in columns by pipeline role (the natural reading of a directional dependency graph); the network is a force-directed layout where tightly-coupled modules pull together and the hubs settle in the middle. Hover a box to isolate what it touches; click a module to open its sheet.

Hover to trace · click a box to inspect it · shift-click a second box to trace the import path between them · double-click opens the module · Esc clears
NotebooksScrapingPerson parsingGeography & censusLinkage1910 registerReference toolsOutput & support01_data_collection02_data_cleaning03_visualisation15_frozen_run16_theatre_model_passfetch_vua_courtsprogress4run_lexicon_downloadsrun_matice_downloadsrun_oeulk_downloadsscraper_amaterskediv…1scraper_csol1scraper_kramerius2scraper_kramerius72scraper_verlustlisten1scraper_vuapraha1affiliations3clean1dates3education7exit_codes6habsburg4locations2occupations9provenance2ranks3religion1schema2theater2units1vua_courts1amateur_theatre4amateur_theatre_model3census2census_hu1census_labels2districts7districts_hu3geonames1lexikon1lexikon_sk2locative1matice6prng1records_geo10records_geo_hu3ruian5spatial5vanished1wikidata1linkage3bakeoffbuild_contrast_sheetbuild_de_page_mapbuild_glyph_bankbuild_lexikon_figurebuild_mark_ledgerbuild_register_walkt…build_symbol_atlasbuild_symbol_atlas_debuild_token_reviewcheck_transcriptiondraw_gold_sampleextract_prose_pageslexikon_191046lexikon_1910_attribu…4lexikon_1910_census1lexikon_1910_courts2lexikon_1910_crosswa…2lexikon_1910_de2lexikon_1910_de_notes7lexikon_1910_de_table2lexikon_1910_editions2lexikon_1910_image4lexikon_1910_institu…22lexikon_1910_ledger2lexikon_1910_names6lexikon_1910_notes22lexikon_1910_pages13lexikon_1910_register4lexikon_1910_vision13lexikon_1910_volume6locate_glyphsmake_gold_worksheetmake_gold_worksheet_…read_transcription_mdreader_batchesreport_unlisted_abbr…rescore_passesrun_de_tablerun_e1_gemini_versionrun_e1b_second_readerrun_e3_pagesrun_frozenrun_glyph_e3run_glyph_test_arun_lexikon_1910_pil…run_r5_german_notesrun_table_armrun_verify_test_bscore_certificationscore_r5_german_notesscore_table_passessection_imagesaudit_full_run_pagesbuild_conflict_workb…build_curationbuild_czisco_bridge1build_german_click_q…build_german_click_s…build_hisco_authoritybuild_matice_read_sh…build_occupation_cur…build_page_indexbuild_sk_curationbuild_theatre_gold_p…build_unit_authoritybuild_unit_timelinecheck_atlas_partiescheck_unit_timelinecheck_unterricht_tra…czech_glosses2download_hisco_sourc…draw_education_samplegerman_candidate_dia…german_click_recallgerman_locator_trialgerman_supervised_pl…map_intensitiesmap_maticemerge_matice_readingsprep_slicesrun_amateur_theatre_…run_census_berufsste…1run_czisco_checkrun_german_detect_by…run_german_oraclerun_german_reader_bo…run_intensitiesrun_link_bridgerun_matice_pilotrun_mosaic_beruf_che…run_occcanine_check1run_unterricht_checkrun_vua_courts_pilotscore_education_goldscore_sealed_abbrevi…statutory_institutio…statutory_proseverify_locatorsbuild_docsbuild_fontsbuild_validation_wor…check_ostafin_silesiadata_checks3latex_tables2lexikon_population1run_completeness1score_sealed_retestth_style_v1133

Most depended-on: lexikon_1910 (46), lexikon_1910_notes (22), lexikon_1910_institutions (22), lexikon_1910_pages (13), lexikon_1910_vision (13) · 155 modules, 5 notebooks, 296 import edges. In the network view the number on a box is its in-degree; the hubs are the boxes everything points at.

fetch_vua_courts.pyPipeline scripts

Downloads the name-level archival inventories (*inventáře*) of three H · source: utils/scraping/fetch_vua_courts.py

Loading this sheet…

progress.pyPipeline scripts

Progress-bar helper shared by the Kramerius downloaders. · source: utils/scraping/progress.py

Loading this sheet…

run_lexicon_downloads.pyPipeline scripts

Download the two outstanding 1910 *Spezialortsrepertorium* volumes int · source: utils/scraping/run_lexicon_downloads.py

Loading this sheet…

run_matice_downloads.pyPipeline scripts

Download the digitised member lists of two Czech national societies, t · source: utils/scraping/run_matice_downloads.py

Loading this sheet…

run_oeulk_downloads.pyPipeline scripts

Download every volume of *Österreich-Ungarns letzter Krieg 1914–1918* · source: utils/scraping/run_oeulk_downloads.py

Loading this sheet…

scraper_amaterskedivadlo.pyPipeline scripts

amaterskedivadlo.cz scraper — importable module + CLI. **Feasibility · source: utils/scraping/scraper_amaterskedivadlo.py

Loading this sheet…

scraper_csol.pyPipeline scripts

csol.cz legionnaire scraper — importable module + CLI. · source: utils/scraping/scraper_csol.py

Loading this sheet…

scraper_kramerius.pyPipeline scripts

Kramerius v5 downloader — importable module + CLI. · source: utils/scraping/scraper_kramerius.py

Loading this sheet…

scraper_kramerius7.pyPipeline scripts

Kramerius v7 downloader — importable module + CLI. · source: utils/scraping/scraper_kramerius7.py

Loading this sheet…

scraper_verlustlisten.pyPipeline scripts

Verlustlisten Österreich-Ungarns 1914–1919 (DES index) scraper — impor · source: utils/scraping/scraper_verlustlisten.py

Loading this sheet…

scraper_vuapraha.pyPipeline scripts

vuapraha.cz scraper — importable module + CLI. · source: utils/scraping/scraper_vuapraha.py

Loading this sheet…

affiliations.pyPipeline scripts

Parses the free-text ``Politická příslušnost`` field into per-party bo · source: utils/parsing/affiliations.py

Loading this sheet…

clean.pyPipeline scripts

First-pass normalisation of scraped archival records, applied **before · source: utils/parsing/clean.py

Loading this sheet…

dates.pyPipeline scripts

Parses Czech date strings across four standalone date columns spanning · source: utils/parsing/dates.py

Loading this sheet…

education.pyPipeline scripts

``Vzdělání`` is **not a level**. It is an *itinerary*: a sequence of · source: utils/parsing/education.py

Loading this sheet…

exit_codes.pyPipeline scripts

Exit code harmonisation for the 'Konec v legiích' field. · source: utils/parsing/exit_codes.py

Loading this sheet…

habsburg.pyPipeline scripts

Field-specific harmonisation for the VÚA *padlí v 1. sv. válce* dump · source: utils/parsing/habsburg.py

Loading this sheet…

locations.pyPipeline scripts

Decomposes a free-text Czech place string into structured components. · source: utils/parsing/locations.py

Loading this sheet…

occupations.pyPipeline scripts

The raw field is Czech free text: 119,046 non-empty values over 8,004 · source: utils/parsing/occupations.py

Loading this sheet…

provenance.pyPipeline scripts

Parses the free-text ``Zdroj informací`` field into two separate famil · source: utils/parsing/provenance.py

Loading this sheet…

ranks.pyPipeline scripts

Collapses the free-text rank fields onto **within-system ordinal** sca · source: utils/parsing/ranks.py

Loading this sheet…

religion.pyPipeline scripts

The raw ``Vyznání`` field is a small, abbreviated Czech vocabulary: de · source: utils/parsing/religion.py

Loading this sheet…

schema.pyPipeline scripts

Single source of truth for column naming. The cleaning notebook · source: utils/parsing/schema.py

Loading this sheet…

theater.pyPipeline scripts

Four-pass imputation of the Legion theatre from the raw csol.cz unit/p · source: utils/parsing/theater.py

Loading this sheet…

units.pyPipeline scripts

Extracts a clean regiment label and unit type from · source: utils/parsing/units.py

Loading this sheet…

vua_courts.pyPipeline scripts

The Vojenský historický archiv publishes name-level inventories of thr · source: utils/parsing/vua_courts.py

Loading this sheet…

amateur_theatre.pyPipeline scripts

**Feasibility pilot.** Nothing here feeds ``data/final``. The module · source: utils/parsing/amateur_theatre.py

Loading this sheet…

amateur_theatre_model.pyPipeline scripts

The amateur-theatre model pass: the chronicle clauses the rules leave · source: utils/parsing/amateur_theatre_model.py

Loading this sheet…

census.pyPipeline scripts

Harmonises the Rumpler-Seger / MPIDR census workbooks in ``data/raw/ce · source: utils/parsing/census.py

Loading this sheet…

census_hu.pyPipeline scripts

Cisleithanian analogue: :mod:`utils.parsing.census` (okres-level Rumpl · source: utils/parsing/census_hu.py

Loading this sheet…

census_labels.pyPipeline scripts

The Cisleithanian census (:mod:`utils.parsing.census`) carries **Germa · source: utils/parsing/census_labels.py

Loading this sheet…

districts.pyPipeline scripts

Builds the **authoritative** 1910 district crosswalk for the Czech cro · source: utils/parsing/districts.py

Loading this sheet…

districts_hu.pyPipeline scripts

The Cisleithanian analogue is :mod:`utils.parsing.districts` (Czech la · source: utils/parsing/districts_hu.py

Loading this sheet…

geonames.pyPipeline scripts

The RÚIAN tables (:mod:`utils.parsing.ruian`) stop at the official čás · source: utils/parsing/geonames.py

Loading this sheet…

lexikon.pyPipeline scripts

Parses the ČSÚ *Historický lexikon obcí* alphabetical register (the so · source: utils/parsing/lexikon.py

Loading this sheet…

lexikon_sk.pyPipeline scripts

Transleithanian analogue of :mod:`utils.parsing.lexikon` (the Czech ČS · source: utils/parsing/lexikon_sk.py

Loading this sheet…

locative.pyPipeline scripts

Czech place names printed in the locative (*v Brně*, *ve Vodňanech*, * · source: utils/parsing/locative.py

Loading this sheet…

matice.pyPipeline scripts

Matice česká and Matice moravská member registers — OCR to one row per · source: utils/parsing/matice.py

Loading this sheet…

prng.pyPipeline scripts

1910 Austrian Silesia did not end at today's Czech border: the eastern · source: utils/parsing/prng.py

Loading this sheet…

records_geo.pyPipeline scripts

Maps a soldier's birth district string (``birth_okres``) to the 1910 p · source: utils/parsing/records_geo.py

Loading this sheet…

records_geo_hu.pyPipeline scripts

Cisleithanian analogue: :mod:`utils.parsing.records_geo` (birth_okres · source: utils/parsing/records_geo_hu.py

Loading this sheet…

ruian.pyPipeline scripts

There are no 1910 municipal boundaries for Bohemia and Moravia, and th · source: utils/parsing/ruian.py

Loading this sheet…

spatial.pyPipeline scripts

Assigns a **court district (soudní okres / Gerichtsbezirk)** to any po · source: utils/parsing/spatial.py

Loading this sheet…

vanished.pyPipeline scripts

RÚIAN and Geonames list what exists today. A 1910 settlement that has · source: utils/parsing/vanished.py

Loading this sheet…

wikidata.pyPipeline scripts

Fetches Czech settlements (obec Q5153359 + část obce Q15978299) with * · source: utils/parsing/wikidata.py

Loading this sheet…

linkage.pyPipeline scripts

Detects individuals appearing in **both** final datasets so the · source: utils/linkage.py

Loading this sheet…

bakeoff.pyPipeline scripts

The bake-off, as a module: the configuration and the run loop, in one · source: utils/lexikon_1910/bakeoff.py

Loading this sheet…

build_contrast_sheet.pyPipeline scripts

The contrast sheet: the look-alike symbols the readers swap, side by s · source: utils/lexikon_1910/build_contrast_sheet.py

Loading this sheet…

build_de_page_map.pyPipeline scripts

Pair every Czech page of the 1910 register with the German pages that · source: utils/lexikon_1910/build_de_page_map.py

Loading this sheet…

build_glyph_bank.pyPipeline scripts

Cut the boxed glyphs out of the gold crops: the bank test A reads. · source: utils/lexikon_1910/build_glyph_bank.py

Loading this sheet…

build_lexikon_figure.pyPipeline scripts

Cut the docs-site figure out of the 1910 register — images plus derive · source: utils/lexikon_1910/build_lexikon_figure.py

Loading this sheet…

build_mark_ledger.pyPipeline scripts

Fill the mark ledger (D-109) from what the r5 gold run left on disk, a · source: utils/lexikon_1910/build_mark_ledger.py

Loading this sheet…

build_register_walkthrough.pyPipeline scripts

The docs site's Brzice walkthrough: from the printed line to the place · source: utils/lexikon_1910/build_register_walkthrough.py

Loading this sheet…

build_symbol_atlas.pyPipeline scripts

Cut the volume's own symbol legend into a labelled atlas of pictogram · source: utils/lexikon_1910/build_symbol_atlas.py

Loading this sheet…

build_symbol_atlas_de.pyPipeline scripts

Cut the German edition's own symbol legend into a labelled atlas (D-92 · source: utils/lexikon_1910/build_symbol_atlas_de.py

Loading this sheet…

build_token_review.pyPipeline scripts

Queue the suspect tokens of the gold sheets for a person to decide, on · source: utils/lexikon_1910/build_token_review.py

Loading this sheet…

check_transcription.pyPipeline scripts

Validate a human transcription worksheet, and turn it into a reader. · source: utils/lexikon_1910/check_transcription.py

Loading this sheet…

draw_gold_sample.pyPipeline scripts

Draw the pages a person will transcribe, reproducibly. · source: utils/lexikon_1910/draw_gold_sample.py

Loading this sheet…

extract_prose_pages.pyPipeline scripts

The prose of the statutory towns' pages, from the text layer. · source: utils/lexikon_1910/extract_prose_pages.py

Loading this sheet…

lexikon_1910.pyPipeline scripts

Podrobný seznam míst / Spezialortsrepertorium 1910 — grid, blocks, inv · source: utils/lexikon_1910/lexikon_1910.py

Loading this sheet…

lexikon_1910_attributes.pyPipeline scripts

What the register prints *beside* two glyphs: a school's classes and l · source: utils/lexikon_1910/lexikon_1910_attributes.py

Loading this sheet…

lexikon_1910_census.pyPipeline scripts

Reconcile transcribed 1910 register blocks against the okres-level cen · source: utils/lexikon_1910/lexikon_1910_census.py

Loading this sheet…

lexikon_1910_courts.pyPipeline scripts

The court each section of the register belongs to: one 1910 backbone c · source: utils/lexikon_1910/lexikon_1910_courts.py

Loading this sheet…

lexikon_1910_crosswalk.pyPipeline scripts

Both volumes print the same 1910 municipalities of Bohemia, each under · source: utils/lexikon_1910/lexikon_1910_crosswalk.py

Loading this sheet…

lexikon_1910_de.pyPipeline scripts

Spezialortsrepertorium Böhmen 1910 — the German twin of the Czech volu · source: utils/lexikon_1910/lexikon_1910_de.py

Loading this sheet…

lexikon_1910_de_notes.pyPipeline scripts

The German edition's apparatus, read from the image alone (D-92). · source: utils/lexikon_1910/lexikon_1910_de_notes.py

Loading this sheet…

lexikon_1910_de_table.pyPipeline scripts

The German volumes' table, cut into blocks from the image and read by · source: utils/lexikon_1910/lexikon_1910_de_table.py

Loading this sheet…

lexikon_1910_editions.pyPipeline scripts

The two editions of the 1910 register as two measurements of one truth · source: utils/lexikon_1910/lexikon_1910_editions.py

Loading this sheet…

lexikon_1910_image.pyPipeline scripts

Page geometry of the 1910 register from the rendered page alone — no t · source: utils/lexikon_1910/lexikon_1910_image.py

Loading this sheet…

lexikon_1910_institutions.pyPipeline scripts

Decode the ``Úřady, ústavy`` footnote into an institution inventory. · source: utils/lexikon_1910/lexikon_1910_institutions.py

Loading this sheet…

lexikon_1910_ledger.pyPipeline scripts

The history of every mark of the 1910 register through the reading rou · source: utils/lexikon_1910/lexikon_1910_ledger.py

Loading this sheet…

lexikon_1910_names.pyPipeline scripts

Anchor 1910 register blocks to known municipalities, and let the volum · source: utils/lexikon_1910/lexikon_1910_names.py

Loading this sheet…

lexikon_1910_notes.pyPipeline scripts

The footnote apparatus printed below the 1910 register table. · source: utils/lexikon_1910/lexikon_1910_notes.py

Loading this sheet…

lexikon_1910_pages.pyPipeline scripts

Read a whole page of the 1910 register with a cloud model, and price t · source: utils/lexikon_1910/lexikon_1910_pages.py

Loading this sheet…

lexikon_1910_register.pyPipeline scripts

A frozen run (``output/frozen/_/``, notebook 15) settl · source: utils/lexikon_1910/lexikon_1910_register.py

Loading this sheet…

lexikon_1910_vision.pyPipeline scripts

Read the below-table apparatus from the *image*, and hand the result t · source: utils/lexikon_1910/lexikon_1910_vision.py

Loading this sheet…

lexikon_1910_volume.pyPipeline scripts

One profile per volume of the 1910 register — what differs from book t · source: utils/lexikon_1910/lexikon_1910_volume.py

Loading this sheet…

locate_glyphs.pyPipeline scripts

E3, first half: a box for every printed glyph of the apparatus (D-69, · source: utils/lexikon_1910/locate_glyphs.py

Loading this sheet…

make_gold_worksheet.pyPipeline scripts

Cut the gold-sample pages into a worksheet laid out the way the page r · source: utils/lexikon_1910/make_gold_worksheet.py

Loading this sheet…

make_gold_worksheet_de.pyPipeline scripts

Build the gold worksheets for the German edition (Spezialortsrepertori · source: utils/lexikon_1910/make_gold_worksheet_de.py

Loading this sheet…

read_transcription_md.pyPipeline scripts

Read the whole-page gold sheets, and grade the transcriber while doing · source: utils/lexikon_1910/read_transcription_md.py

Loading this sheet…

reader_batches.pyPipeline scripts

The readers, batched: queued requests in, cached replies out (D-108's · source: utils/lexikon_1910/reader_batches.py

Loading this sheet…

report_unlisted_abbreviations.pyPipeline scripts

The offices items no code in ``institution_abbreviations.csv`` reaches · source: utils/lexikon_1910/report_unlisted_abbreviations.py

Loading this sheet…

rescore_passes.pyPipeline scripts

Re-score every cached symbol pass at the analysis unit. No model call, · source: utils/lexikon_1910/rescore_passes.py

Loading this sheet…

run_de_table.pyPipeline scripts

The German volumes' table read by a model, block by block, as the Czec · source: utils/lexikon_1910/run_de_table.py

Loading this sheet…

run_e1_gemini_version.pyPipeline scripts

E1 — is the gap between the two cloud pipelines the model, or the view · source: utils/lexikon_1910/run_e1_gemini_version.py

Loading this sheet…

run_e1b_second_reader.pyPipeline scripts

E1b — which cheap reader of another family errs somewhere else than Ge · source: utils/lexikon_1910/run_e1b_second_reader.py

Loading this sheet…

run_e3_pages.pyPipeline scripts

E3 on any page: the locator and DINOv3 on the readers' saved section c · source: utils/lexikon_1910/run_e3_pages.py

Loading this sheet…

run_frozen.pyPipeline scripts

The frozen run: every stage of the reading route, for one edition and · source: utils/lexikon_1910/run_frozen.py

Loading this sheet…

run_glyph_e3.pyPipeline scripts

E3, second half: name every located glyph by its nearest neighbours on · source: utils/lexikon_1910/run_glyph_e3.py

Loading this sheet…

run_glyph_test_a.pyPipeline scripts

Test A — does an image encoder tell the fill pairs apart at all? (SYMB · source: utils/lexikon_1910/run_glyph_test_a.py

Loading this sheet…

run_lexikon_1910_pilot.pyPipeline scripts

Calibration run for the 1910 special place register — four channels, o · source: utils/lexikon_1910/run_lexikon_1910_pilot.py

Loading this sheet…

run_r5_german_notes.pyPipeline scripts

The German notes arm of ``r5``: the German edition's apparatus, read b · source: utils/lexikon_1910/run_r5_german_notes.py

Loading this sheet…

run_table_arm.pyPipeline scripts

The table arm of ``r5`` — the seventeen printed columns read by a mode · source: utils/lexikon_1910/run_table_arm.py

Loading this sheet…

run_verify_test_b.pyPipeline scripts

Test B — does one Opus verify call settle what Gemini and Haiku disput · source: utils/lexikon_1910/run_verify_test_b.py

Loading this sheet…

score_certification.pyPipeline scripts

Score the sealed certification pages — once, against metrics written d · source: utils/lexikon_1910/score_certification.py

Loading this sheet…

score_r5_german_notes.pyPipeline scripts

Score the German notes arm of ``r5`` against the German gold, and set · source: utils/lexikon_1910/score_r5_german_notes.py

Loading this sheet…

score_table_passes.pyPipeline scripts

Score the table readings against the gold tables, cell by cell. · source: utils/lexikon_1910/score_table_passes.py

Loading this sheet…

section_images.pyPipeline scripts

The readers' section crops, saved as images for every page (D-112, ste · source: utils/lexikon_1910/section_images.py

Loading this sheet…

audit_full_run_pages.pyPipeline scripts

Every German full-run page, cut offline before anything is read: does · source: utils/reference_tools/places/audit_full_run_pages.py

Loading this sheet…

build_conflict_workbook.pyPipeline scripts

The Czech→HISCO dictionary flags keys ``review`` where the coding is n · source: utils/reference_tools/occupations/build_conflict_workbook.py

Loading this sheet…

build_curation.pyPipeline scripts

Encodes the accented-Czech names for the 1910 Czech-lands districts, k · source: utils/reference_tools/places/build_curation.py

Loading this sheet…

build_czisco_bridge.pyPipeline scripts

CZ-ISCO (ČSÚ classification 80146, 2025) as a **Czech→English occupati · source: utils/reference_tools/occupations/build_czisco_bridge.py

Loading this sheet…

build_german_click_queue.pyPipeline scripts

A Token Review queue for clicking every pictogram of the German gold p · source: utils/reference_tools/places/build_german_click_queue.py

Loading this sheet…

build_german_click_strips.pyPipeline scripts

Cut the German gold's office sections into strips of a few printed lin · source: utils/reference_tools/places/build_german_click_strips.py

Loading this sheet…

build_hisco_authority.pyPipeline scripts

One row per **(hisco, status, relation, product)**, carrying the Engli · source: utils/reference_tools/occupations/build_hisco_authority.py

Loading this sheet…

build_matice_read_sheets.pyPipeline scripts

Sheets of scanned lines for re-reading the Matice residences the OCR g · source: utils/reference_tools/places/build_matice_read_sheets.py

Loading this sheet…

build_occupation_curation.pyPipeline scripts

Emits the **skeleton** of the Czech occupational dictionary: every nor · source: utils/reference_tools/occupations/build_occupation_curation.py

Loading this sheet…

build_page_index.pyPipeline scripts

Build the page index for Österreichische Statistik XCIII/1 (Unterricht · source: utils/reference_tools/education/build_page_index.py

Loading this sheet…

build_sk_curation.pyPipeline scripts

Magyar → Slovak/Rusyn name curation for the 1910 Upper-Hungary (Felvid · source: utils/reference_tools/places/build_sk_curation.py

Loading this sheet…

build_theatre_gold_page.pyPipeline scripts

The ruling page for the theatre model pass's gold (D-157). · source: utils/reference_tools/places/build_theatre_gold_page.py

Loading this sheet…

build_unit_authority.pyPipeline scripts

Derives a **factual authority table of Czechoslovak Legion units** fro · source: utils/reference_tools/units/build_unit_authority.py

Loading this sheet…

build_unit_timeline.pyPipeline scripts

Where every Czechoslovak Legion unit was, whom it answered to and who · source: utils/reference_tools/units/build_unit_timeline.py

Loading this sheet…

check_atlas_parties.pyPipeline scripts

``atlas_iv37_parties.csv`` is a **hand transcription of a photograph** · source: utils/reference_tools/parties/check_atlas_parties.py

Loading this sheet…

check_unit_timeline.pyPipeline scripts

``unit_timeline.csv`` is parsed by ``build_unit_timeline.py`` from the · source: utils/reference_tools/units/check_unit_timeline.py

Loading this sheet…

check_unterricht_transcription.pyPipeline scripts

Invariants on the transcription of Österreichische Statistik XCIII/1 ( · source: utils/reference_tools/education/check_unterricht_transcription.py

Loading this sheet…

czech_glosses.pyPipeline scripts

The authored half of the occupation pipeline, and the piece no externa · source: utils/reference_tools/occupations/czech_glosses.py

Loading this sheet…

download_hisco_sources.pyPipeline scripts

The occupation pipeline needs two external ingredients, and only one o · source: utils/reference_tools/occupations/download_hisco_sources.py

Loading this sheet…

draw_education_sample.pyPipeline scripts

Draw the `Vzdělání` strings a person will code by hand, reproducibly. · source: utils/reference_tools/education/draw_education_sample.py

Loading this sheet…

german_candidate_diagnosis.pyPipeline scripts

Which candidate-step filter removes the German glyphs the author click · source: utils/reference_tools/places/german_candidate_diagnosis.py

Loading this sheet…

german_click_recall.pyPipeline scripts

How well does the locator's box land on the glyph the author clicked? · source: utils/reference_tools/places/german_click_recall.py

Loading this sheet…

german_locator_trial.pyPipeline scripts

Does a locator trained on the German gold place German claims better t · source: utils/reference_tools/places/german_locator_trial.py

Loading this sheet…

german_supervised_placement.pyPipeline scripts

Does a glyph classifier trained on the author's clicks place German cl · source: utils/reference_tools/places/german_supervised_placement.py

Loading this sheet…

map_intensities.pyPipeline scripts

The intensities of ``run_intensities.py`` side by side, one figure per · source: utils/reference_tools/places/map_intensities.py

Loading this sheet…

map_matice.pyPipeline scripts

Maps of the Matice measure at three levels: political okres, court dis · source: utils/reference_tools/places/map_matice.py

Loading this sheet…

merge_matice_readings.pyPipeline scripts

Merge the residence readings made from ``build_matice_read_sheets.py`` · source: utils/reference_tools/places/merge_matice_readings.py

Loading this sheet…

prep_slices.pyPipeline scripts

Prepare reading slices for the qualitative pass (scratch; not part of · source: utils/reference_tools/background/prep_slices.py

Loading this sheet…

run_amateur_theatre_pilot.pyPipeline scripts

Feasibility pilot — the amateur-theatre database as a pre-1914 covaria · source: utils/reference_tools/places/run_amateur_theatre_pilot.py

Loading this sheet…

run_census_berufsstellung_check.pyPipeline scripts

is a `rolník` a farm *owner* or a farm *worker*? · source: utils/reference_tools/occupations/run_census_berufsstellung_check.py

Loading this sheet…

run_czisco_check.pyPipeline scripts

Matches each curated Czech key against the **head noun** of a CZ-ISCO · source: utils/reference_tools/occupations/run_czisco_check.py

Loading this sheet…

run_german_detect_by_naming.pyPipeline scripts

Detect by naming: let the bank find the German glyphs, instead of a lo · source: utils/reference_tools/places/run_german_detect_by_naming.py

Loading this sheet…

run_german_oracle.pyPipeline scripts

The oracle test: does the Czech-built bank name German glyphs when the · source: utils/reference_tools/places/run_german_oracle.py

Loading this sheet…

run_german_reader_boxes.pyPipeline scripts

The reader gives the box: can DINOv3 check German claims on the boxes · source: utils/reference_tools/places/run_german_reader_boxes.py

Loading this sheet…

run_intensities.pyPipeline scripts

Four intensities side by side, per 1,000, at three levels: political o · source: utils/reference_tools/places/run_intensities.py

Loading this sheet…

run_matice_pilot.pyPipeline scripts

Pilot — the Matice česká and Matice moravská member registers, read in · source: utils/reference_tools/places/run_matice_pilot.py

Loading this sheet…

run_mosaic_beruf_check.pyPipeline scripts

does the German word for a farmer mean an *owner*, one record at a tim · source: utils/reference_tools/occupations/run_mosaic_beruf_check.py

Loading this sheet…

run_occcanine_check.pyPipeline scripts

Codes the **German glosses** with OccCANINE (Dahl, Johansen & Vedel, " · source: utils/reference_tools/occupations/run_occcanine_check.py

Loading this sheet…

run_unterricht_check.pyPipeline scripts

Is the education ladder the one the Austrian state actually operated? · source: utils/reference_tools/education/run_unterricht_check.py

Loading this sheet…

run_vua_courts_pilot.pyPipeline scripts

Pilot — the VÚA military-court inventories as a Habsburg-side record o · source: utils/reference_tools/units/run_vua_courts_pilot.py

Loading this sheet…

score_education_gold.pyPipeline scripts

Score a hand-coded education worksheet against `education.py`. · source: utils/reference_tools/education/score_education_gold.py

Loading this sheet…

score_sealed_abbreviations.pyPipeline scripts

Score the text abbreviations on the sealed certification pages — a sec · source: utils/reference_tools/places/score_sealed_abbreviations.py

Loading this sheet…

statutory_institutions.pyPipeline scripts

The statutory cities' institutions, from their verified prose: stage 2 · source: utils/reference_tools/places/statutory_institutions.py

Loading this sheet…

statutory_prose.pyPipeline scripts

The statutory cities' prose, transcribed precisely: two vision readers · source: utils/reference_tools/places/statutory_prose.py

Loading this sheet…

verify_locators.pyPipeline scripts

Check every ledger entry's locator against its slice (scratch tool for · source: utils/reference_tools/background/verify_locators.py

Loading this sheet…

build_docs.pyPipeline scripts

Renders every authored reference document, the tabular crosswalks, and · source: utils/docs/build_docs.py

Loading this sheet…

build_fonts.pyPipeline scripts

Subset the Libertinus faces the docs site embeds -> utils/docs/fonts/* · source: utils/docs/build_fonts.py

Loading this sheet…

build_validation_workbook.pyPipeline scripts

Draws a **tier-stratified** sample of resolved records and writes an E · source: utils/validation/build_validation_workbook.py

Loading this sheet…

check_ostafin_silesia.pyPipeline scripts

Austrian Silesia 1910 against Ostafin et al. (2020): an independent tr · source: utils/validation/check_ostafin_silesia.py

Loading this sheet…

data_checks.pyPipeline scripts

One definition of "is the built data sound?", used in two places: · source: utils/validation/data_checks.py

Loading this sheet…

latex_tables.pyPipeline scripts

LaTeX table generation utilities. · source: utils/tables/latex_tables.py

Loading this sheet…

lexikon_population.pyPipeline scripts

The 1910 population of the ČSÚ *Historický lexikon obcí ČR 1869–2005*, · source: utils/validation/lexikon_population.py

Loading this sheet…

run_completeness.pyPipeline scripts

Whether a frozen run has every reply it asked for, before anything is · source: utils/validation/run_completeness.py

Loading this sheet…

score_sealed_retest.pyPipeline scripts

The full run's readings of the sealed certification pages, scored — a · source: utils/validation/score_sealed_retest.py

Loading this sheet…

th_style_v113.pyPipeline scripts

A personal, reusable Matplotlib configuration module built around the · source: utils/plotting/th_style_v113.py

Loading this sheet…

VariablesWhat's in the data

Every constructed variable, with formulas. · source: reference/VARIABLES.md

In short

Every variable constructed from the raw data (parses, ordinals, ratios, differences, flags), with its source field(s), the module that builds it, and the construction rule in mathematical notation where one applies. This is the ground for the paper's data-construction appendix. Companion: PIPELINE.md (what each script does), data/final/data_dictionary.csv (machine-readable column list), census/census_codebook.md (census families).

Conventions. Records are indexed i; Cisleithanian districts (politické okresy) d; Transleithanian districts (járások) j; Slovak municipalities (obce) m. Missing propagates: any function of a missing input is missing (NA) unless stated. All parsers work on a copy of the frame; raw source columns are never overwritten. "Legion" = legionnaires_clean, "Habsburg" = habsburg_clean, "both" = constructed identically in the two final person files.


1 · Identity and names

Variable Data Source Module Rule
surname, given_name both Přijmení/Příjmení, Jméno clean.clean_names peel wrapping () and quotes, strip trailing ?
name_uncertain both same clean.clean_names 1{surname or given name carried a trailing ?}
legion_id / vua_id L / H scraper record id — stable primary keys (csol.cz /legionar/{id}/; VÚA /padli-1-svetova/{id}/)

2 · Dates

Built by dates.add_date_columns; each column clipped to a plausibility window [ℓ,h] — outside ⇒ NA:

Variable Data Source Window [ℓ,h] Partial-date fallback
birth_date both Datum narození 1860-01-01 … 1905-12-31 year → birth_year_only
capture_date L Datum zajetí 1914 … 1918 month, year → capture_date_precision
enroll_date L Datum zařazení do legií 1914 … 1922 month, year → enroll_date_precision
death_date both (sparse for L) Datum úmrtí 1914 … 1923 year → death_year_only; L keeps death_date_raw
exit_date L Konec v legiích (text before the parenthesis) 1914 … 1939 month, year → exit_date_precision

Parse order per cell (first success wins): strict DD.MM.YYYY; strict DD.MM.YY with the century fix

y↦y−100iffy>1930∧ℓyr≤y−100≤hyr

(so 6.12.19 → 1919, not 2019) — and, for the three partial-date columns only, the mirror image y↦y+100 iff y<ℓyr∧ℓyr≤y+100≤hyr (1.4.1818 → 1918); regex-extract of the first full-date substring (rescues double-date cells like "23.8.1890 23.9.1891"); for the partial-date columns, M.YYYY or an impossible day (29.2.1918) → the 1st of that month; bare YYYY → January 1 of that year; first in-range YYYY anywhere in the string (rescues "1899 (1889)"). The precision column records which step succeeded:

*_date_precisioni∈{day, month, year},NA iff the date is NA.

Build of 2026-09-23: capture_date 99,021 day / 347 month / 1,068 year; enroll_date 118,869 / 930 / 1,069; exit_date 101,662 / 461 / 3,404 (D-61). Before dates 1.2.0 the partial strings were dropped and the exit date was not range-checked at all (1818–2192).

Derived (notebook 02 §1/§2):

birth_yeari=year(birth_datei),death_yeari=year(death_datei),
age_at_deathi=death_yeari−birth_yeari.

Caveat for duration models: partial dates are placed at the 1st of the month or Jan 1 — condition on birth_year_only / death_year_only / *_date_precision when day- or month-level timing matters. Notebook 03's monthly series keep day- and month-precise dates only, since a year-only date would pile into January.

exit_date (Legion) — the text of the raw exit field before its parenthesis, through the same parser (exit_codes.parse_exit_date), so a year inside the parenthesis (Sloužil v čs. armádě po r. 1918) can never be read as the exit.


3 · Exit outcome (Legion)

Built by exit_codes.add_exit_columns from Konec v legiích ("DD.MM.YYYY ( CODE )"). Let $C_i \subseteq {B, \check{C}SA, D, DM, N, NS, P, S, V, X, Z}$ be the parsed code set (exit_codes, serialised ";"-joined):

exit_ci=1{c∈Ci}for each code c,desertedi=exit_Di=1{D∈Ci}.

deserted is the primary outcome. Codes: B killed in action · ČSA continued in the post-1918 army · D deserted · DM demobilised · N missing · NS unfit · P executed · S died · V returned home · X see note · Z died of wounds. Empty Ci (no code parsed / raw missing, ≈30.8k rows) ⇒ all dummies False — for hazard models treat "no exit code" as censoring information, not as non-desertion. Extraction is token-wise with a drop list of archival markers; ? uncertainty marks are stripped (( D? ) keeps its D); a token that does not parse completely as codes is refused whole (the two ambiguous raw values MD/DMD, legion_id 113629 and 18666, carry no code by decision — plausibly DM, not certain). ⚠ Builds before 2026-07-16 carried a fabricated exit_N on every empty cell (26,386 rows — missing values leaked in as the string "\<NA>"); the true N count is ≈644. See AUDIT_2026-07-16.md A3 before comparing against old outputs.


4 · Ranks and the rank reset (headline RHS)

Three raw fields are parsed by ranks.add_rank_columns (per prefix p∈{habsburg_rank, legion_rank, last_legion_rank}): Hodnost v době zajetí (system = habsburg), Hodnost v době zařazení and Poslední hodnost v legiích (system = legion).

Within-system ordinals. Each raw string maps (de-accent, split on ,;/, first-match over specificity-ordered token rules, each token required to match at a word start) to a canonical label rip ({p}_clean) and its within-system ordinal oH:k.u.k. ladder→{1,…,20} or oL:Legion ladder→{1,…,17}, per ranks/rank_ladders.md §A/§B/§B-gen. Progressions keep both ends:

Column names below are written with the prefix p standing in for the template {p} used in the prose — i.e. p_ord denotes the column {p}_ord (habsburg_rank_ord, legion_rank_ord, last_legion_rank_ord).

p_ordi=maxko(rik),p_ord_lowi=minko(rik),

over the k ranks listed in the cell; {p}_clean is the label attaining the max. Non-ranks are dropped to NA: Habsburg podporučík/rotmistr/nadpraporčík (not k.u.k. ranks — §A note), Legion zajatec/repatriant/žák/sestra/adjutant. pracovník/občan are scored at the base rung 1 (Legion; robustness cut planned, §H#1).

Also dropped (ranks 1.4.0) are statements about a rank rather than a rank held — kandidát na X (candidate for X), požitky/počitky X (paid at X's rate), nebyl X (was not X) — and bare generál, which names the tier, not a grade. Each previously resolved to the rank it names; because drops apply per split token, pracovník / požitky poručíka still yields pracovník (1) rather than an officer grade.

The word-start requirement is what keeps a longer unknown word from inheriting a shorter rank it contains (generálmajor⧸→major, generálporučík⧸→poručík); genuine compounds (nadstrážmistr, novobranec, nadělostřelec, ostrostřelec) are listed as their own tokens. See VALIDATION_FIDLER.md §2.

The one-year-volunteer token is status, not rank:

p_jdi=1{j.d. token present},

stripped before matching. {p}_branch (arm of service implied by the rank word) and {p}_tier (Mannschaft/NCO/Aspirant/Officer, resp. Mužstvo…Štábní důstojník) are lookups on the matched label/ordinal.

The cross-system bridge (ALT 1 common scale). The two ordinals are not subtractable (Habsburg poručík has oH=10, Legion poručík oL=9 — the same OF-1 grade). The bridge is the name-anchored common grade c:canonical label→{1,…,22} (RANK_COMMON; same Czech rank word ⇒ same grade in either system), with the three subaltern grades sub-graded inside NATO OF-1 (podporučík 10 < poručík 11 < nadporučík 12; setník = kapitán = 13, …):

p_commoni=c(rip).

Rank reset (ranks.add_rank_reset; relative rank deprivation on Legion enrolment):

rank_reseti=c(rilegion enrol)−c(rihabsburg capture)

Negative ⇒ demotion on entering the Legion. NA unless both sides resolved. The enrolment rank drives the reset, not the last rank held.

Non-combat statuses (legion_noncombat). pracovník (labour-service entry track) and občan (civilian) are non-combat statuses, not grades, yet sit at common grade 1; differencing them against a real officer rank manufactures the extreme "deprivation" (colonel → "labourer" = −16) that the qualitative record does not support (labour battalions were transitional; officer degradation was a rebutted rumour — VALIDATION_FIDLER.md §3). The reset is retained but flagged:

legion_noncombati=1{rilegion enrol∈{pracovník, občan}},NA iff no Legion enrolment rank is recorded (19,270; D-62),

7,696 records (5.8%). Lead the headline reset on ¬legion_noncombat (combat ranks, ≈85.7k resolved of 91.6k); the full column is the inclusive robustness variant. Added ranks 1.4.1.

Coarse robustness variant. Each common grade maps to a NATO OF/OR band β(·) (APersP-01), indexed monotonically by

b(OR-k)=k(k=1..9),b(OF-k)=9+k(k=1..10),
rank_reset_bandi=b(β(c(rilegion)))−b(β(c(rihabsburg))).

All three subalterns share OF-1, so subaltern↔subaltern resets are 0 by construction here — the fine and band resets agree on demoted-vs-not for 99.8% of resolved rows (rank_ladders.md §G3). Cardinal magnitudes on the common scale are an analyst convention (consecutive integers); the sign is the robust object — lead with the demotion indicator 1{rank_reseti<0}.


5 · Unit identity and theatre of service

Unit (units.add_unit_columns). The raw field is label ( formation ). Let ρi be the label with the parenthetical stripped (regiment) and ai the formation mapped to a canonical army (legion_army, via ARMY_MAP, read off the last parenthetical with a trailing ? stripped; unmapped values pass through verbatim, X/? → NA). The analysable unit identity is

unit_keyi=ρi‖“ @ ”‖(ai or “unknown”),

which separates armies that reuse a regiment number: 6,058 labels → 6,609 identities. 4,347 records (3.3 %) name a non-Czechoslovak formation (French Foreign Legion, French, Serbian, Russian, British armies), and 1.pl. is majority Foreign Legion. Use unit_key for unit fixed effects, clustering and size denominators; regiment is a display label only. unit_type and regiment_plot remain functions of ρi.

Legion theater (theater.add_theater_column) ∈ {Russia, Italy, France, Serbia/Balkans, Britain/Dominions, Post-war (ČSA), Unknown} — four-pass imputation, each pass filling only rows still Unknown:

  1. keyword regex on the enrolment-unit string;
  2. regiment-mate majority vote: for regiment g(i) (unit text before the parenthetical), theateri=mode{theateri′:g(i′)=g(i), known} — inline maths must stay on one line;
  3. application-city lookup (Misto podání přihlášky — Omsk→Russia, Padula→Italy, Chicago→France…; D-60: of US applicants with a theatre-naming unit, 1,659 served in France and 26 in a British or Dominion unit);
  4. the pass-1 regex on the transfer unit (Převelen k útvaru).

Coverage ≈ 96.5%. Validated externally: on Czechoslovak-Legion records with a numbered rifle/infantry regiment, theater agrees with the Fidler (2025) unit authority on 99.63 % (71,580 of 71,846). The 266 conflicts are contradictions inside csol.cz — e.g. 21.stř.pl. ( Čs. legie v Rusku ), when the 21st–23rd existed only in France — reproduced faithfully rather than silently corrected, and surfaced by a soft QA check (VALIDATION_FIDLER.md §2.5). The comparison must be restricted to Czechoslovak-Legion records: including foreign armies drops apparent agreement to 95.4 % purely because their regiment numbers coincide with Legion ones. Note also that pass 2 keys on the regiment label, which is army-ambiguous (see above), so the vote is taken over a slightly heterogeneous group for the ≈3 % of records on a foreign army.

Habsburg theater (renamed from theatre; habsburg.add_theatre_column) — earliest-occurring front stem in Bojiště (Macedonia/Romania/France/Italy/Serbia/Russia/Rear), plus

in_captivityi=1{Bojištěi∋"zajet/zajat"}.

6 · Demographics

Variable Data Source Rule
education (label), education_ord L Vzdělání Highest institution attended on the 1869 Austrian ladder; ordinal ∈{0,…,5}: 0 No schooling (analfabet) < 1 Elementary (obecná/Volksschule) < 2 Burgher school (měšťanská/Bürgerschule) < 3 Vocational secondary (obchodní/průmyslová/odborná/hospodářská) < 4 Upper secondary (gym./reálka/obch.ak./vyšší prům./uč.ústav/kadetka) < 5 Tertiary (univerzita/technika). The string is an itinerary, so the parser tokenises it and takes the max: education_ord=maxi∈ℐ(s)rung(i) over the institutions ℐ(s) named in the cell. Where two institutions tie on the top rung (1,512 records) the winner is fixed by education.TIE_BREAK (D-51), not by which one appears first in the string. pok. is excluded from ℐ (below). Grounded in Moore 2020 + Lassnigg 2015 — Education ladder, D-08
education_apprentice L Vzdělání 1{a pokračovací (gewerbliche Fortbildungs-) school is named} — the apprentice track. It was legally compulsory schooling and selected on nothing (no credential was required to enter an apprenticeship), so it never enters education_ord; it sets only a floor of 1, since a continuation school followed the compulsory eight years. Under v1.0.0 it scored Upper secondary on 8,117 records. ⚠ A lower bound, not a rate: the 1908/09 enrolment has 5.3 continuation pupils per full-time vocational pupil in the Czech lands, the records name them at 0.86 to one, so men largely did not write the compulsory evening classes down. Do not read 6.6 % as the apprenticeship rate. See Education validation
education_track L Vzdělání branch of the highest institution: compulsory / vocational / academic / teacher / military / arts / tertiary / none
education_top, education_institutions L Vzdělání canonical key of the highest institution, and every institution named, pipe-joined in ladder order (audit column — the education analogue of occupation_match)
education_top_imputed L Vzdělání 1{education_top is the apprentice floor, not a school the record names} — 1,112 records whose only school is the continuation school. The rung is floored at Elementary and education_track reads continuation; this is the one case where education_top is not a member of education_institutions, and the QA gate asserts exactly that
education_semesters L Vzdělání Semesters stated at the top institution (technika 4 sem., 722 records). Lifted out of the string before the itinerary is parsed, so a semester count can never be read into a year column — which is what happened up to education.py 2.0.0. Not a year: never pool it with the education_years_* columns
education_years_elementary, _burgher, _continuation, _secondary L Vzdělání years stated at each rung. A number attaches backwards to the institution it follows (ob. 5), forwards when the cell opens with one (8 ob., 3mě). ⚠ Not commensurable — do not sum. Continuation-school hours varied by Land and trade (regional laws 1907–1931), which is why no education_years_total is emitted
education_complete L Vzdělání 1{years at the top rung≥its standard course}, with standards Volksschule 8, Bürgerschule 3, Gymnasium 8, Realschule 7, Handelsakademie / uč.ústav / kadetka 4. <NA> wherever no source establishes the length — the whole vocational branch. Attendance and attainment are kept apart deliberately: a bare reálka does not say whether the man finished. ⚠ Known defect, D-53 — do not use for the elementary rung without reading it. The Volksschule standard of 8 is the law's eight-year obligation across institutions; Österreichische Statistik XCIII/1 Table 34 counts elementary schools by ascending classes and finds an eight-class Volksschule 10 times in Cisleithania, twice in the Czech lands, against 1,114 five-class schools in Böhmen. Years and classes are different units and this column conflates them: 3,894 records are scored complete and 3,137 reading ob. 5 — the modal full Bohemian Volksschule — incomplete. Unfixed pending a decision; education_ord, education_top, education_track and education_years_elementary are unaffected. See Education validation
pol_* dummies L Politická příslušnost pol_ki=1{token of party k⊆normalised string}; multi-affiliation cells set several dummies. A token shadowed by a longer matched token does not fire (stát.pokr. is Č15, not also Č13/Č17 via pokr.), and "rusín" → rusyn, not russian. Genealogy from Akademický atlas plate IV.37 (D-50): every party that the register routes into Č23 Česká státoprávní demokracie → 1919 Národní demokracie folds into nar_dem — mladočeši (Č2), staročeši (Č1), realisté (Č12→Č13), and all four progressive parties (Č7, Č13, Č15, Č17), which is why there is no longer a pokr bin. Civic orgs (sokol, dtj, orel, slov_liga, volna_myslenka, odbory) are kept separate from parties
pol_sokol, pol_orel, pol_dtj L Politická příslušnost The three mass gymnastic movements, kept as dummies of their own because they proxy a mobilisation current rather than a party: Sokol Czech nationalist, Orel Catholic, DTJ social-democratic. Never folded into an aligned party — and the party alignments v1.x asserted are dropped, because among the 1,323 Sokol members who also name a party the modal choice is National Socialist (59 %), not National Democrat (18 %). The spread is the point: Sokol ran across the Czech national parties, so the dummy measures nationalist mobilisation rather than a party choice. ⚠ pol_sokol is 26.1 % of all non-empty affiliation cells and is the sole entry on 87 % of them — exclude the civic orgs from any specification that reads pol_* as party affiliation
pol_recorded L Politická příslušnost 1{the field says something} — 39,616 of 132,993 (29.8 %). Every pol_* dummy is <NA>, not False, where this is 0 (D-52): the field is blank on 70.2 % of legionnaires, and False there would assert not a member about a source that is silent. Any specification reading the dummies must condition on it
pol_atlas_codes L Politická příslušnost The IV.37 register codes the string is evidence for, pipe-joined (Č15, D2\|D6). Empty for civic orgs, nationality markers and Slovak formations — the plate is v českých zemích and covers none of them. Audit column, the affiliation analogue of occupation_match
religion L Vyznání passthrough (rename only)
religion_en L Vyznání English gloss via religion.py curated map (~40 keys, 99.5% coverage); <NA> for the unmapped tail
regiment_en, unit_key_en, last_legion_unit_en L unit labels English rendering via units.translate_unit (number + English unit_type); foreign/Other units pass through verbatim
{prefix}_rank_tier_en L rank tier English gloss of the Czech/German tier (Enlisted / NCO / Officer candidate / Officer / Staff officer / General)
occupation, occupation_interwar L Zaměstnání, Povolání 1918–1938 passthrough (raw Czech kept verbatim; the coded form is below)

Occupation — HISCO and the derived class scales

Built by occupations.py from the curated Czech dictionary (reference/occupations/occupation_hisco_curation.csv) plus the HISCO authority (hisco_authority.csv). Method, coverage and the judgement calls: OCCUPATION_CODING.md.

Variable Src From Construction
occupation_hisco L Zaměstnání 5-digit HISCO micro group. Exact match on the normalised key, then the token-boundary compound rules. 98.4 % of non-empty values; the rest <NA>
occupation_hisco_major L occupation_hisco 1-digit HISCO major group. <NA> only on the sentinels (−1 no occupation, −2 employer named instead); a QA invariant asserts every real code carries one
occupation_en L occupation_hisco English HISCO label
occupation_hisclass L HISCO + modifiers HISCLASS 1–12, plus HSN's class 13 ("unskilled, unspecified") for HISCO 99900/99920. <NA> where HISCO assigns none — students and every other sentinel (D-42), any relation value, and the four sharper codes D-41 accepted that HSN never classed (95530, 95240, 79475). 95.5 % of non-empty, down from 95.8 %
occupation_hisclass_5 L occupation_hisclass 1 elite (1–2) · 2 lower middle (3–6) · 3 skilled (7, 9) · 4 farmers (8) · 5 unskilled (10–13)
occupation_hisclass_label L occupation_hisclass readable class name
occupation_socpo L HISCO + status SOCPO social power (van de Putte & Miles 2005): 1 unskilled · 2 semi-skilled · 3 skilled · 41 middle class, farmers · 42 middle class, supervisors · 5 elite. <NA> on every HISCO sentinel (D-42) — a scheme that ranks occupations cannot rank a refusal to name one. Ranks rolník 41, middle class, where HISCLASS gives 8: the D-33 question is sharper on this axis than on HISCLASS
occupation_status L curation HISCO status modifier — 21 Master, 22 Journeyman, 23 Apprentice, 41 Student, 42 Graduate, 33 Subordinate; -9 = none. The list is 11–13, 21–24, 31–34, 41, 42, 51, 52 and a QA invariant holds emitted values to it. Populated on ~2.7 % of coded records — the source records a job title, not a labour-force status. Use occupation_career_stage
occupation_relation L curation HISCO relation modifier; -9 = none. Any value here means HISCLASS is deliberately absent
occupation_match L matcher exact (87.7 %) · dual (5.5 %) · head (3.6 %) · prefix (1.8 %) · <NA> (1.5 %). Restrict on this for a strict-coding robustness check
occupation_qualifier L normalised key The modifier attached to the trade — strojní in strojní zámečník, bankovní in bankovní úředník. 13,559 records (11.4 % of non-empty), 1,919 distinct. Found by locating the curated head noun (longest curated proper suffix, else prefix), so it survives a compound being promoted to a curated exact key. Never set on a dual — that leading run is a second occupation, and a QA invariant asserts it. Predicts within HISCO code: Russia-only desertion 3.38 % for strojní zámečník vs 6.06 % bare, 0.54 % bankovní úředník vs 1.64 % bare
occupation_career_stage L normalised key Position in the trade: apprentice · assistant · journeyman · senior · master · independent · leaseholder · owner. Whole-token scan; highest rung wins. 3,502 records (2.9 %) — this is the ceiling, not a floor (§ OCCUPATION_CODING.md). Descriptive: never feeds HISCO or HISCLASS
occupation_career_stage_ord L occupation_career_stage the same as 1–8, rising autonomy. Convenience only — it folds the guild ladder (učeň → tovaryš → mistr) and the clerical/estate ladder (praktikant → adjunkt → vrchní) into one scale and they are not commensurable, and ownership is a third axis rather than a rung. Prefer the string column
occupation_hisco_2, _en_2, _hisclass_2 L second occupation Populated where two occupations were recorded (occupation_match == "dual", 6,518 records) — e.g. rolník zámečník. Both are coded; which one to use is left to the analysis
occupation_hisclass_high L the two HISCLASS values the higher-status of the pair (numerically the lower); equals occupation_hisclass when only one occupation was recorded
occupation_interwar_hisco, _en, _hisclass, _hisclass_5, _qualifier, _career_stage(_ord), _match L Povolání 1918–1938 same machinery on the interwar field (5,080 non-empty, 84.0 % coded — a different, more institutional vocabulary). _hisclass_5 and _match were added 2026-08-02 so the field sits on the same 5-class scale as the enrolment one and takes the same exact-only robustness check

Removed 2026-08-02 (D-30): occupation_hiscam, occupation_hiscam_2, occupation_interwar_hiscam. HISCAM is non-monotonic on this sample — by Russia-only quintile it runs 4.38 / 4.31 / 4.13 / 4.71 / 2.71, so only the top quintile separates — and the within-code variation it was the only scale to carry (zámečník 60 vs machinery fitter 66) is now explicit in occupation_qualifier and in the code itself. Rebuild hisco_authority.csv if it is ever wanted back: the column is still in the authority, just not emitted.


7 · Record provenance (Legion)

provenance.add_provenance_columns parses Zdroj informací — the source csol.cz cites for the record — into two families that must not be pooled (D-44). Write zi for the normalised field and ⪯ for "is a prefix of". The two VÚA-VHA personnel-document names are stripped from the front in sequence, never matched as substrings; the residual ri is whatever survives.

Variable Data Source Rule
record_source L Zdroj informací passthrough (verbatim; 625 distinct values)
source_spis L same 1{legionářský poslužný spis⪯zi} — the VÚA-VHA service file. 127,222 (95.7%)
source_karta L same 1{osobní karta legionáře⪯zi⊖spis} — the personal card, tested after the service-file prefix is removed. 102,168 (76.8%); both documents 97,075
source_extra L same 1{ri≠∅} — a source cited beyond the two personnel documents. 5,823 (4.4%)
source_extra_ref L same ri verbatim, NA where ri=∅; 489 distinct (published okres volumes, regimental memorial books, family archives, museum fonds)

Missing stays missing. Where zi is NA (431 records) all four columns are NA, never 0 — an unrecorded source is not evidence that a document is absent, and collapsing the two would put "not stated" into the reference category of any regression using these flags.

The two families measure different things, which is why they are separate. source_spis/source_karta say what the archive holds; source_extra says that csol's editors added a citation on top — a marker of modern editorial attention, typically a local historian working an okres over ninety years later. Among Russia records the card rate is identical for deserters and the demobilised (87.11% both) while source_extra runs 1.05% against 5.47%: pooled into one variable, a flat signal and a steep one cancel.

Interpretive limit. record_source is csol's editorial attribution, not the archive's own provenance stamp, and must not be described as the latter. It is also not exogenous to record quality: conditioning on source_karta, occupation is missing on 7.96% of Russian deserters against 0.16% of the demobilised — the document is present and the occupation line inside it is blank, so these flags describe differential recording rather than controlling it away. See DECISIONS.md D-44.


8 · Habsburg-only parses (VÚA)

Variable Source Rule
cause, cause_codes, cause_<CODE>, cause_uncertain, cause_label Příčina úmrtí stem scan (declension-folding); codes {KIA, DIS, MIS, EXE, DRO, SUI, CAP}; cause = earliest-occurring stem, dummies = any-mention: cause_ci=1{c∈codesi}; cause_uncertain = ? present; every dummy NA on a blank cause (8,297, D-62)
ah_unit_primary, ah_unit_type, ah_unit_org, ah_regiment_no, ah_n_units Příslušnost k útvaru… split on ;; first unit is primary; arm from keyword list (specific arms before pěší); organisation ∈ {Common army, Landwehr (zeměbrana), Honvéd, Landsturm (domobrana)}; regiment no. = first integer; ah_n_units = count of units listed
note_legion, note_legion_ref Poznámka 1{note names the Legion explicitly} — legionář, legie, KLEG, Kancelář legií, čs. dobrovolník, rota Nazdar, Česká družina, or one of the twelve Russian rifle regiments (čs. střelecký pluk 1–12, never with a register page) — + the matched phrase; 966 records. Feeds linkage criterion (B) (D-59)
note_register_ref Poznámka the Czechoslovak regiment or battalion that keeps the death register (náhradní prapor čs. pěšího pluku 33) or the k.u.k. Seznam vozatajského pluku — provenance, not service; 10,548 records. Until 2026-09-22 these phrases set note_legion
note_declared_dead, note_dead_ref Poznámka "prohlášen za mrtvého" + file reference \d{3,}/\d{2}
note_rank, note_disease Poznámka most-specific-first rank term, matched at a word start (dělostřelec is not střelec); disease stem → English label
birth_*, home_* decomposition Místo narození, Domovem locations.parse_location: municipality + okres anchored on the first ", okres X"; *_german = first non-okres parenthetical; *_loc_uncertain = ? present; *_n_candidates = ;-alternatives + 1

9 · Place standardisation (both datasets, roles birth and home)

Cisleithanian key (records_geo.add_place_standardization); Transleithanian complement (records_geo_hu). Raw {role}_municipality / {role}_okres stay untouched as source text; everything below is derived.

District identifiers. The 1910 political-okres id encodes the MPIDR hierarchy:

okres_id=1000·REGION+DISTRICT,okres_id(court_id)=⌊court_id/1000⌋,

with court_id = MPIDR ID_1 (= census IDCourtdistr); Transleithanian unit_id = GISta kod (string id), parent megye_id.

Variable Rule
{role}_okres_id raw district string → 1910 okres, resolution order: curated alias → exact political-okres name → exact court-district name (→ parent okres) → token-set fuzzy ≥ 90; pass 2 via municipality: settlement whose okres timeline contains the raw okres (post-1910 label recovery), or unambiguous settlement name when okres is absent (foreign-land-vetoed)
{role}_okres_match provenance tier ∈ {okres, court, fuzzy, alias, settlement, muni}; strict analyses can drop the weak tiers (fuzzy, muni); NA = outside the Czech-lands 1910 frame
{role}_okres_cs/_de 1910 okres names from the backbone
{role}_municipality_cs/_de standardised modern-Czech / German settlement names (lexikon + Wikidata), keyed (name, okres_id) with land-vetoed unique-name fallback
{role}_court_id/_cs/_de soudní okres of the matched settlement — geometric assignment (point-in-polygon of the settlement's coordinates in the MPIDR court polygons)
{role}_lat/_lon/_qid WGS84 coordinates + Wikidata QID of the matched settlement (~40–50% coverage)
{role}_land positive classification: resolved okres_id ⇒ "Czech lands"; curated Upper-Hungary district list ⇒ "Slovakia"; else text markers (Austria, Galicia, Russia, Hungary, …); NA if unresolvable
{role}_jaras_id/_match/_sk/_hu Transleithanian district (only for records with {role}_okres_id = NA — a record sits in at most one 1910 frame): district-seat / county / fuzzy pass, then the obec settlement pass under refuse-over-guess rules (unambiguous obec; district string must corroborate okres_sk/župa when present; positively-foreign lands and cross-frame homonyms refused); tiers
{role}_megye_id/_sk/_hu parent county (filled even when only the county resolved — tier megye)
{role}_obec_sk matched Slovak obec (key into the obec-level mother-tongue shares of settlements_hu_1910)

Census attachment (wide files, data/final/analysis/): pure code joins — Cisleithanian on birth_okres_id=okres_id, Transleithanian on birth_jaras_id=unit_id.


10 · Linkage flags (both datasets)

linkage.find_overlap (1.3.0, D-59) — blocked fuzzy match; blocks share (birth year, phonetic surname initial). Let ϕ(·) be the phonetic key (de-accented, with the orthographic folds tsch/cz/tz/z → c, sch → s, w → v, y/ie → i, umlaut digraphs, doubled letters), and score surname and given name separately: sijsur = rapidfuzz token_set_ratio on ϕ(surname), sijgiv likewise on the given names. A candidate pair is a confirmed overlap iff

sijsur≥{85okresi=okresj90otherwise∧sijgiv≥80∧[DOBifull=DOBjfull⏟(A) high∨note_legionj∧okresi=okresj∧yeari=yearj⏟(B) medium],

where full DOB requires day+month+year on both sides not produced by the year-only fallback — and a pair whose two full DOBs disagree is refused whatever the note says. The survivors are made one-to-one: each legionnaire keeps its best VÚA record and each VÚA record its best legionnaire (confidence tier, then surname score, then given-name score), and an exact tie is refused, never broken by order. Everything weaker (name+year coincidence, district-only) is dropped, not down-tiered — precision-first, because the overlap is a control in the comparison design. Per person:

Variable Rule
in_other_dataset 1{record has a confirmed match in the other dataset}
match_id the matched vua_id ↔ legion_id (highest-confidence match if several)
match_confidence high (DOB-confirmed) / medium (note-confirmed)
dup_group probable duplicate person inside one file: an id shared by records with the same surname, given name, full DOB and birth municipality; NA otherwise. Flagged, never merged (625 csol / 281 VÚA rows)

Verified counts (build of 2026-09-23): 1,340 matches = 1,201 high + 139 medium; every VÚA record linked at most once, so in_other_dataset is 1,340 on both sides. Under the rule before 2026-09-22 — one token_sort_ratio ≥ 80 on "surname given", and a note flag that mostly recorded the register-keeping regiment — the count was 2,239 (1,776 + 463), with 120 VÚA records linked to more than one legionnaire (audit 2026-09-22 A1–A3).


11 · Census — derived shares and rates

Cisleithanian okres panel (census_okres_1910, d = okres)

All raw census columns are counts renamed to English (census_labels; crosswalk in census/census_column_labels.csv). Derived ratios (add_shares_cis; division by zero or missing ⇒ NA):

lang_x_shared=lang_xdlang_totald,x∈{german, czech, polish, ruthenian, slovene, serbocroat, italian_ladin, romanian, magyar}

(lang_czech = the census bloc Böhmisch–Mährisch–Slowakisch; lang_total = Umgangssprache total, i.e. present citizens);

rel_r_shared=rel_rdrel_totald,r∈{roman_catholic, greek_catholic, greek_orthodox, lutheran, calvinist, jewish};
literacy_rated=lit_read_writedlit_age_over10d(literate = reads and writes, population aged >10);
pop_totald=∑s∈Spop_s_inhabitantsd,S={<500, 500–2000, 2001–5000, 5001–10000, 10001–20000, >20000}

(present population summed over settlement-size classes);

illegitimacy_rated=births_livebirths_illegitimatedbirths_livebirths_totald(1911 vital statistics).

Court-level source files (934 units) are aggregated to the okres before any ratio: xd=∑c:⌊c/1000⌋=dxc (counts only).

Transleithanian járás panel (census_hu_jaras_1910, j = járás/town)

Mother-tongue shares (built in census_hu from the ETN table; base = present population):

slovak_sharej=etn_tot_anyanyelvjetn_jelenlevo_nepessegj,

and identically magyar_share, german_share, ruthen_share (numerators magyar/német/ruthén anyanyelv). Religion shares (add_shares_hu):

rel_r_sharej=rel_rjpop_presentj,r∈{roman_catholic, greek_catholic, calvinist, lutheran, orthodox, jewish}.

Slovak obec crosswalk (settlements_hu_1910, m = obec/časť; Majo 2012)

Obec-level 1910 mother-tongue shares, with the convention that an unprinted column is a true zero (the source prints nothing for zero counts):

slovak_sharem=max(nmslovak,0)pop_totalm,nmx←0 if unprinted,

likewise magyar_share, german_share, rusyn_share; sum_okm=1{∑xnmx=pop_totalm} is the row-reconciliation flag (100% pass since the columns are read on their right edge — D-20; it is a hard QA invariant).


12 · Reference-table constructed fields

Table Variable Rule
districts_1910 okres_cs, gerichtsbezirk_cs (+ _modern, *_needs_review, *_cs_source) curated 1910 Czech names (accent restoration over the MPIDR de-accented spine); provenance flagged
districts_1910 is_okres_seat 1{court's Czech name=okres name (mod. "(město)")}
districts_1910 city/rural twin naming one convention: city = X (město), rural = bare X, for all ten Statutarstadt pairs (D-17)
okres_census_crosswalk census_okres_id =okres_id off-twin; = the rural member's id for both members of a twin pair — the pooled census unit (D-18)
census_okres_1910_pooled every count column ∑ over the pair's members, min_count=1
census_okres_1910_pooled every *_share, literacy_rate, illegitimacy_rate re-derived from the pooled counts, e.g. lang_czechcity+lang_czechringlang_totalcity+lang_totalring — never the mean of the two members' shares
settlements_1910 okres_id, okres_match lexikon okres_1910 string resolved through the same okres/court/fuzzy matcher as person records
settlements_1910 okres_all pipe-joined okres timeline across all census years (the post-1910-label recovery key)
settlements_1910 wd_qid, lat, lon Wikidata match on Czech name; homonyms resolved only when the modern okres uniquely agrees
settlements_1910 de_name Wikidata German label if it genuinely differs from the Czech; else the lexikon 1910 name when German-looking; else NA (no invented translations)
settlements_1910 court_id, court_cs/_de point-in-polygon of (lat, lon) in the MPIDR court polygons (EPSG:3035 forward projection)
okres_centroids_1910 okres_lat/_lon mean of the okres's geocoded settlements
districts_hu_1910 name_sk, name_sk_alt, megye_sk, is_czechoslovak curated Slovak/Rusyn names (district_sk_curation.csv); name_sk_alt also carries the four district exonyms the Magyar→Slovak bridge cannot reach and the three adjectival district names the Majo lexicón prints (D-21); county-level Czechoslovak-successor flag
districts_hu_1910 járás/town twin structure 26 seats name both a járás and a chartered town of the same megye_id; a bare district string means the járás, the municipality promotes to the town (D-19). Unlike the Czech twins the census splits all 26, so there is no pooled unit and no census_*_id indirection
overlap_matches name_score (= the lower of the two), surname_score, given_score, date_exact, okres_match, note_legion, confidence the linkage evidence per matched pair (§10)

13 · Variables the analysis derives downstream (notebook 03)

Group-level descriptives recomputed from the final files (not stored): desertion rate of group g = ∑i∈gdesertedi/|g| with binomial/normal CIs; enrolment and desertion time series by theatre from enroll_date / exit_date restricted to deserted; rank-reset histogram and demotion shares from rank_reset / rank_reset_band. The planned estimation sample (competing-risks hazard) will additionally construct person-period spells from enroll_date to exit_date with exit-type competing events — to be documented here when built.

Data dictionaryWhat's in the data

Every final column and its source. · source: data/final/data_dictionary.csv

341 rows

datasetfinal_namesource_namedescription
legionlegion_idIDStable csol.cz record id (primary key)
legionsurnamesurnameCleaned surname
legiongiven_namegivenCleaned given name
legionname_uncertainname_uncertainTrue if the raw name carried an uncertainty mark (?)
legionbirth_datebirth_dateParsed date of birth (NaT if unresolved)
legionbirth_yearbirth_yearYear of birth (from birth_date)
legionbirth_year_onlybirth_year_onlyTrue if only a year (not full date) was available
legionbirth_municipalityObec narozeníRaw municipality of birth (obec), as recorded — see birth_municipality_cs/_de for standardised names
legionbirth_okresOkres narozeníRaw district of birth, as recorded — see birth_okres_cs/_de/_id (census key) and birth_court_* (soudní okres)
legionhome_municipalityDomovská obecRaw home municipality (domicile), as recorded — see home_municipality_cs/_de for standardised names
legionhome_okresDomovský okresRaw home district (domicile), as recorded — see home_okres_cs/_de/_id and home_court_* (soudní okres)
legionreligionVyznáníDeclared religion (Vyznání)
legionreligion_enreligion_enEnglish gloss of religion (řím.kat.→Roman Catholic, pravosl.→Orthodox, …); <NA> for the rare unmapped tail (from religion.py)
legioneducationeducationHighest school ATTENDED, on the 1869 Austrian ladder: No schooling / Elementary / Burgher school / Vocational secondary / Upper secondary / Tertiary (from education.py 2.0.0)
legioneducation_ordeduc_ordThe same ladder as integer 0–5. NB the apprentices' continuation school (pokračovací/Fortbildungsschule) does NOT raise it — it was compulsory schooling and selected on nothing; see education_apprentice
legioneducation_trackeduc_trackBranch of the highest school: compulsory / vocational / academic / teacher / military / arts / tertiary / continuation (only a continuation school recorded; the rung is the imputed Elementary floor) / none
legioneducation_apprenticeeduc_apprenticeAttended a continuation school (pokračovací š. / gewerbliche Fortbildungsschule), i.e. the apprentice track
legioneducation_topeduc_topCanonical key of the highest institution (elementary, burgher, gymnasium, realschule, teacher, university, …). Where two schools sit on the same rung the winner is fixed by education.TIE_BREAK, a written rule (D-51), not by which one the clerk wrote first
legioneducation_top_imputededuc_top_imputedTrue where education_top is the apprentice floor rather than a school the record names: only a continuation school was recorded, so the rung is floored at Elementary and education_institutions does not list it (education.py 2.1.0)
legioneducation_institutionseduc_institutionsEvery institution named in the record, pipe-joined in ladder order (audit column)
legioneducation_years_elementaryeduc_years_elementaryYears stated at the Volksschule (obecná škola)
legioneducation_years_burghereduc_years_burgherYears stated at the Bürgerschule (měšťanská škola)
legioneducation_years_continuationeduc_years_continuationYears stated at the continuation school. ⚠ Weekly hours varied by Land and trade (regional laws 1907–1931) — not commensurable with the other year columns; do not sum
legioneducation_years_secondaryeduc_years_secondaryYears stated at the highest post-compulsory institution. Semesters are NOT counted here — see education_semesters
legioneducation_semesterseduc_semestersSemesters stated at the top institution ('technika 4 sem.'). Pulled out before the itinerary is parsed so it can never be read as years; <NA> where none is stated (education.py 2.1.0)
legioneducation_completeeduc_completeFinished the top rung's standard course (Volksschule 8 years; Bürgerschule 3; Gymnasium 8; Realschule 7; Handelsakademie/uč.ústav 4). <NA> where no source establishes the course length — the whole vocational branch. Open defect D-53: for a bare Volksschule it compares the stated number against 8 years, conflating classes with years
legionoccupationZaměstnáníOccupation at enrolment (Zaměstnání)
legionoccupation_interwarPovolání v letech 1918 - 1938Occupation 1918–1938
legionoccupation_hiscooccupation_hiscoHISCO occupational code at enrolment, 5-digit micro group (from occupations.py)
legionoccupation_hisco_majoroccupation_hisco_majorHISCO major group, 1 digit (0/1 professional … 9 unskilled)
legionoccupation_enoccupation_enEnglish HISCO label of the occupation at enrolment
legionoccupation_hisclassoccupation_hisclassHISCLASS social class, 1 (higher managers) – 12 (unskilled farm workers), 13 = unskilled, unspecified
legionoccupation_hisclass_labeloccupation_hisclass_labelReadable HISCLASS name
legionoccupation_hisclass_5occupation_hisclass_5HISCLASS collapsed to 5 classes: 1 elite, 2 lower middle, 3 skilled, 4 farmers, 5 unskilled
legionoccupation_socpooccupation_socpoSOCPO social-power level: 1 unskilled, 2 semi-skilled, 3 skilled, 41 farmers, 42 supervisors, 5 elite
legionoccupation_statusoccupation_statusHISCO status modifier (21 master, 22 journeyman, 23 apprentice, 41 student, …); -9 = none. Populated on ~2.7% of coded records — csol.cz records a job title, not a labour-force status. Prefer occupation_career_stage
legionoccupation_relationoccupation_relationHISCO relation modifier (21 former/retired, …); -9 = none. Any value here means HISCLASS is deliberately absent
legionoccupation_matchoccupation_matchHow the raw string was matched: exact / dual / head / prefix (<NA> if uncoded) — see OCCUPATION_CODING.md
legionoccupation_qualifieroccupation_qualifierThe modifier attached to the trade — 'strojní' in 'strojní zámečník', 'bankovní' in 'bankovní úředník'. Recorded on ~11% of non-empty values whether or not the compound is separately coded; predicts within HISCO code (see OCCUPATION_CODING.md)
legionoccupation_career_stageoccupation_career_stagePosition in the trade read off the title: apprentice / assistant / journeyman / senior / master / independent / leaseholder / owner. <NA> where the title names none. Descriptive — never feeds HISCO or HISCLASS
legionoccupation_career_stage_ordoccupation_career_stage_ordoccupation_career_stage as 1–8 (rising autonomy). Convenience only: it folds the guild ladder and the clerical ladder into one scale, which are not commensurable — see VARIABLES.md
legionoccupation_hisco_2occupation_hisco_2HISCO code of the *second* occupation where two were recorded (occupation_match='dual')
legionoccupation_en_2occupation_en_2English HISCO label of the second occupation
legionoccupation_hisclass_2occupation_hisclass_2HISCLASS of the second occupation
legionoccupation_hisclass_highoccupation_hisclass_highThe higher-status of the two HISCLASS values (numerically the lower); equals occupation_hisclass when only one occupation was recorded
legionoccupation_interwar_hiscooccupation_interwar_hiscoHISCO code of the 1918–1938 occupation
legionoccupation_interwar_enoccupation_interwar_enEnglish HISCO label of the 1918–1938 occupation
legionoccupation_interwar_hisclassoccupation_interwar_hisclassHISCLASS of the 1918–1938 occupation
legionoccupation_interwar_hisclass_5occupation_interwar_hisclass_5HISCLASS_5 of the 1918–1938 occupation — the same 5-class scale as occupation_hisclass_5, so the two fields are comparable
legionoccupation_interwar_qualifieroccupation_interwar_qualifieroccupation_qualifier for the 1918–1938 occupation
legionoccupation_interwar_career_stageoccupation_interwar_career_stageoccupation_career_stage for the 1918–1938 occupation
legionoccupation_interwar_career_stage_ordoccupation_interwar_career_stage_ordoccupation_career_stage_ord for the 1918–1938 occupation
legionoccupation_interwar_matchoccupation_interwar_matchHow the 1918–1938 string was matched: exact / head / prefix (<NA> if uncoded) — lets the exact-only robustness check run on this field too
legionhabsburg_unit_peacetimePrezenční služba u rakousko-uherského útvaruHabsburg peacetime unit (Prezenční služba)
legionhabsburg_unit_captureRakousko-uherský útvar v době zajetíHabsburg unit at time of capture
legionhabsburg_unit_capture_2Rakousko-uherský útvar v době zajetí_2Secondary Habsburg unit at capture
legionhabsburg_rankHodnost v době zajetíHabsburg rank at capture (raw string)
legionhabsburg_rank_cleanhabsburg_rank_cleanStandardised Habsburg rank — canonical Czech label (highest if a progression)
legionhabsburg_rank_enhabsburg_rank_enEnglish gloss of the standardised Habsburg rank (German k.u.k. term in parentheses)
legionhabsburg_rank_ordhabsburg_rank_ordHabsburg rank ordinal 1–20 (k.u.k. ladder; highest reached; <NA> if unresolved)
legionhabsburg_rank_ord_lowhabsburg_rank_ord_lowLowest ordinal among listed ranks (= ord for singletons; preserves progressions)
legionhabsburg_rank_jdhabsburg_rank_jdTrue if a one-year-volunteer (jednoroční dobrovolník / Einjährig-Freiwilliger) marker was present; <NA> where the rank cell is blank
legionhabsburg_rank_branchhabsburg_rank_branchArm of service inferred from the rank term (artillery/cavalry/jäger/engineers/infantry/rifles; sparse)
legionhabsburg_rank_tierhabsburg_rank_tierCoarse rank tier (Mannschaft / NCO / Aspirant / Officer)
legionhabsburg_rank_tier_enhabsburg_rank_tier_enEnglish gloss of the Habsburg rank tier (Enlisted / NCO / Officer …)
legionhabsburg_rank_commonhabsburg_rank_commonSystem-blind ALT-1 common grade of the Habsburg capture rank (subtractable across systems; rank_ladders.md §G)
legionhabsburg_rank_bandhabsburg_rank_bandCoarse NATO OF/OR band of the Habsburg capture rank (APersP-01; robustness axis)
legioncapture_datecapture_dateParsed date of capture by Allied forces (1914–1918). A month or a year alone is kept as the first of that month / year — see capture_date_precision
legioncapture_date_precisioncapture_date_precisionHow exact capture_date is: day / month / year; <NA> where no date parsed (dates.py 1.2.0)
legioncapture_placeMisto zajetíPlace of capture
legionapplication_dateDatum podání přihlášky do legiíDate of application to the Legions
legionapplication_placeMisto podání přihlášky do legiíPlace of application to the Legions
legionenroll_dateenroll_dateParsed date of enrolment into the Legions (1914–1922). A month or a year alone is kept as the first of that month / year — see enroll_date_precision
legionenroll_date_precisionenroll_date_precisionHow exact enroll_date is: day / month / year; <NA> where no date parsed (dates.py 1.2.0)
legionlegion_unitJednotka v době zařazeníLegion unit at enrolment (raw Jednotka string)
legionregimentregimentClean regiment label, display only — shared across armies (from units.py)
legionregiment_enregiment_enEnglish rendering of the regiment label (6.stř.pl.→6th Rifle Regiment); unmapped/foreign units pass through verbatim (from units.py translate_unit)
legionlegion_armylegion_armyArmy/formation named in the unit's LAST parenthetical (CS Legion (Russia), French Foreign Legion, British Army, …); unmapped forms pass through verbatim (units.py 1.3.0)
legionunit_keyunit_keyUnit identity for analysis: 'regiment @ army'. Use instead of regiment for unit FE, clustering and size denominators
legionunit_key_enunit_key_enEnglish form of unit_key: 'regiment_en @ army'
legionunit_typeunit_typeArm / unit type (from units.py)
legionlegion_rankHodnost v době zařazeníLegion rank at enrolment (raw string)
legionlegion_rank_cleanlegion_rank_cleanStandardised Legion enrolment rank — canonical Czech label (highest if a progression)
legionlegion_rank_enlegion_rank_enEnglish gloss of the standardised Legion enrolment rank
legionlegion_rank_ordlegion_rank_ordLegion enrolment-rank ordinal 1–14 (Legion ladder §B; highest reached; <NA> if unresolved)
legionlegion_rank_ord_lowlegion_rank_ord_lowLowest ordinal among listed enrolment ranks (= ord for singletons; preserves progressions)
legionlegion_rank_jdlegion_rank_jdTrue if a one-year-volunteer (jednoroční dobrovolník) marker was present on the enrolment rank; <NA> where the rank cell is blank
legionlegion_rank_branchlegion_rank_branchArm of service inferred from the enrolment-rank term (artillery/cavalry/infantry/rifles/engineers/labour; sparse)
legionlegion_rank_tierlegion_rank_tierCoarse Legion rank tier (Mužstvo / Poddůstojník / Aspirant / Důstojník / Štábní důstojník)
legionlegion_rank_tier_enlegion_rank_tier_enEnglish gloss of the Legion rank tier (Enlisted / NCO / Officer candidate / Officer / Staff officer / General)
legionlegion_rank_commonlegion_rank_commonSystem-blind ALT-1 common grade of the Legion enrolment rank (subtractable across systems; rank_ladders.md §G)
legionlegion_rank_bandlegion_rank_bandCoarse NATO OF/OR band of the Legion enrolment rank (APersP-01; robustness axis)
legionrank_resetrank_resetRelative rank deprivation on Legion enrolment: legion_rank_common − habsburg_rank_common (fine ALT-1 sub-grade; negative = demotion). <NA> unless both ranks resolved. Headline RHS.
legionrank_reset_bandrank_reset_bandCoarse-band robustness variant of rank_reset on the NATO OF/OR index (all subalterns share OF-1, so subaltern↔subaltern = 0).
legionlegion_noncombatlegion_noncombatTrue when the Legion enrolment rank is a non-combat status (pracovník = labour-entry track / občan = civilian), not a grade; <NA> where no enrolment rank resolved. rank_reset is retained, but the headline reads it on legion_noncombat == False (D-04; VALIDATION_FIDLER §3).
legiontheatertheaterLegion: theatre of service (Russia / Italy / France / Serbia/Balkans / Britain/Dominions / Post-war (ČSA) / Unknown). Habsburg: front of death (Russia / Italy / Serbia / Macedonia / Romania / France / Rear / Unknown). Only Russia, Italy and France compare across the two
legionexit_codesexit_codes_strExit codes as ';'-joined string (D=deserted, B=KIA, …)
legionexit_Bexit_BTrue if the exit codes include B (Killed in action). False where they do not, including records with no exit code at all, which are censored
legionexit_ČSAexit_ČSATrue if the exit codes include ČSA (Served in post-1918 Czechoslovak army). False where they do not, including records with no exit code at all, which are censored
legionexit_Dexit_DTrue if the exit codes include D (Deserted). False where they do not, including records with no exit code at all, which are censored
legionexit_DMexit_DMTrue if the exit codes include DM (Demobilised). False where they do not, including records with no exit code at all, which are censored
legionexit_Nexit_NTrue if the exit codes include N (Missing). False where they do not, including records with no exit code at all, which are censored
legionexit_NSexit_NSTrue if the exit codes include NS (Unfit for service). False where they do not, including records with no exit code at all, which are censored
legionexit_Pexit_PTrue if the exit codes include P (Executed). False where they do not, including records with no exit code at all, which are censored
legionexit_Sexit_STrue if the exit codes include S (Died). False where they do not, including records with no exit code at all, which are censored
legionexit_Vexit_VTrue if the exit codes include V (Returned home). False where they do not, including records with no exit code at all, which are censored
legionexit_Xexit_XTrue if the exit codes include X (See note). False where they do not, including records with no exit code at all, which are censored
legionexit_Zexit_ZTrue if the exit codes include Z (Died of wounds). False where they do not, including records with no exit code at all, which are censored
legiondeserteddesertedTrue if exit codes include D (Legion desertion — primary outcome). False also where no exit code was recorded (30.8k records): read those as censored, not as a recorded non-desertion
legionexit_dateexit_dateDate of exit from the Legions, from the text before the parenthesis; 1914–1939, century slips fixed; a month or a year alone is kept as the first of that month / year — see exit_date_precision
legionexit_date_precisionexit_date_precisionHow exact exit_date is: day / month / year; <NA> where no date parsed (exit_codes.py 1.2.0)
legionlast_legion_unitPoslední útvar v legiíchLast Legion unit
legionlast_legion_unit_enlast_legion_unit_enEnglish rendering of the last Legion unit (from units.py translate_unit)
legionlast_legion_rankPoslední hodnost v legiíchLast Legion rank (raw string)
legionlast_legion_rank_cleanlast_legion_rank_cleanStandardised last Legion rank — canonical Czech label (highest if a progression)
legionlast_legion_rank_enlast_legion_rank_enEnglish gloss of the standardised last Legion rank
legionlast_legion_rank_ordlast_legion_rank_ordLast Legion-rank ordinal 1–14 (Legion ladder §B; highest reached; <NA> if unresolved)
legionlast_legion_rank_ord_lowlast_legion_rank_ord_lowLowest ordinal among listed last-ranks (= ord for singletons)
legionlast_legion_rank_jdlast_legion_rank_jdTrue if a one-year-volunteer marker was present on the last rank; <NA> where the rank cell is blank
legionlast_legion_rank_branchlast_legion_rank_branchArm of service inferred from the last-rank term (sparse)
legionlast_legion_rank_tierlast_legion_rank_tierCoarse Legion rank tier of the last rank (Mužstvo … Štábní důstojník)
legionlast_legion_rank_tier_enlast_legion_rank_tier_enEnglish gloss of the last Legion rank tier (Enlisted … General)
legionlast_legion_rank_commonlast_legion_rank_commonSystem-blind ALT-1 common grade of the last Legion rank (rank_ladders.md §G)
legionlast_legion_rank_bandlast_legion_rank_bandCoarse NATO OF/OR band of the last Legion rank (APersP-01; robustness axis)
legiondeath_datedeath_dateParsed date of death, 1914–1923 only (war-period mortality). For legionnaires the post-1923 deaths of veterans are NaT here — see death_date_raw
legiondeath_year_onlydeath_year_onlyTrue if only a year was available for death
legiondeath_date_rawDatum úmrtíLegionnaires: the raw Datum úmrtí string, kept because 8,034 of 12,480 recorded deaths fall after 1923 and outside death_date
legiondeath_cause_rawPříčina úmrtíRaw cause-of-death string (legionnaire, unparsed)
legiondeath_placeMísto úmrtíPlace of death
legionburial_placeMísto pohřbeníPlace of burial
legionlegion_organizationLegionářska organizaceVeteran legion organisation membership
legionnotesPoznámkaFree-text note (Poznámka)
legionrecord_sourceZdroj informacíArchival source csol.cz cites for the record (Zdroj informací) — chiefly 'legionářský poslužný spis' (service file) and/or 'osobní karta legionáře' (personal card), sometimes plus a published okres volume. Verbatim; 626 distinct values
legionsource_spissource_spisTrue if the record cites a legionářský poslužný spis (VÚA-VHA service file). <NA> where no source is recorded
legionsource_kartasource_kartaTrue if the record cites an osobní karta legionáře (VÚA-VHA personal card). <NA> where no source is recorded
legionsource_extrasource_extraTrue if a source beyond the two standard personnel documents is cited — a published okres volume, memorial book, family archive or museum fond. A marker of modern editorial attention, not of a different wartime document
legionsource_extra_refsource_extra_refThe extra citation verbatim, <NA> where there is none. ~489 distinct values
legionpol_*Politická příslušnostpol_* — political-affiliation dummies, one per canonical in affiliations.PARTY_ORDER, appended dynamically by to_english(). Boolean with <NA> wherever pol_recorded is False. Two members are NOT dummies: pol_recorded (was the field filled in at all) and pol_atlas_codes (a string audit column carrying the Akademicky atlas plate IV.37 register codes the affiliation string is evidence for) — affiliations.py 2.1.0
legionin_other_datasetutils.linkage (notebook 02 §4)True if this person was matched in the other dataset (confirmed overlap; see match_confidence)
legionmatch_idutils.linkage (notebook 02 §4)Matched id in the other dataset (vua_id ↔ legion_id)
legionmatch_confidenceutils.linkage (notebook 02 §4)Linkage evidence: high = identical full date of birth + compatible names; medium = explicit VÚA Legion note + same birth okres + birth year + compatible names (linkage.py 1.3.0; one-to-one)
legiondup_grouputils.linkage (notebook 02 §4)Probable duplicate record of the same person INSIDE this dataset: the smallest id among records sharing surname + given name + full date of birth + birth municipality; <NA> otherwise. Nothing is dropped
legionbirth_municipality_csbirth_municipality / birth_okres (derived)Standardised modern Czech birth municipality (Historický lexikon obcí)
legionbirth_municipality_debirth_municipality / birth_okres (derived)Standardised German birth municipality (1910 exonym; Wikidata/lexikon; NA where none existed)
legionbirth_okres_idbirth_municipality / birth_okres (derived)1910 political-okres id (region·1000+district) of birth — administrative unit
legionbirth_okres_matchbirth_municipality / birth_okres (derived)How birth_okres_id resolved (okres/court/fuzzy/alias/settlement/muni)
legionbirth_okres_twinbirth_municipality / birth_okres (derived)Which member of a city/rural twin pair birth sits in: city / ring / undecided (no municipality to decide); NA off-twin
legionbirth_court_snappedbirth_municipality / birth_okres (derived)True where birth_court_id was pulled back into the record's own okres by the D-35 near-miss repair (≤1 km); exclude these rows when the court layer is used as independent evidence
legionbirth_okres_praguebirth_municipality / birth_okres (derived)Greater-Prague rule (D-36): city = in okres Praha and kept; suburb = moved to the separate 1910 okres of its municipality; <NA> off Praha
legionbirth_census_okres_idbirth_municipality / birth_okres (derived)1910 census unit of birth — the census-merge key. Equals birth_okres_id except at the ten city/rural twin pairs, where both members pool into one unit (the court-level sources do not separate them)
legionbirth_okres_csbirth_municipality / birth_okres (derived)1910 political-okres Czech name of birth (MPIDR backbone)
legionbirth_okres_debirth_municipality / birth_okres (derived)1910 political-okres German name of birth (MPIDR backbone)
legionbirth_court_idbirth_municipality / birth_okres (derived)1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of birth, via settlement spatial join
legionbirth_court_csbirth_municipality / birth_okres (derived)Court-district (soudní okres) Czech name of birth
legionbirth_court_debirth_municipality / birth_okres (derived)Court-district (Gerichtsbezirk) German name of birth
legionbirth_landbirth_municipality / birth_okres (derived)Crown land/country of birth (Czech lands/Slovakia/Austria/Galicia/…) — positive district classification
legionbirth_latbirth_municipality / birth_okres (derived)Latitude of birth settlement (WGS84; Wikidata)
legionbirth_lonbirth_municipality / birth_okres (derived)Longitude of birth settlement (WGS84; Wikidata)
legionbirth_qidbirth_municipality / birth_okres (derived)Wikidata QID of birth settlement
legionbirth_jaras_idbirth_okres (derived, Transleithanian)1910 járás (district) unit id of birth — Upper-Hungary census key (GISta Hungarorum)
legionbirth_jaras_matchbirth_okres (derived, Transleithanian)How birth_jaras_id resolved (jaras/town/megye/fuzzy/settlement)
legionbirth_jaras_twinbirth_okres (derived, Transleithanian)Which member of a járás/chartered-town twin pair birth sits in: city / ring / undecided (no municipality to decide); NA off-twin
legionbirth_jaras_skbirth_okres (derived, Transleithanian)Slovak/Rusyn járás name of birth
legionbirth_jaras_hubirth_okres (derived, Transleithanian)Magyar járás/seat name of birth
legionbirth_megye_idbirth_okres (derived, Transleithanian)1910 vármegye (county) id of birth
legionbirth_megye_skbirth_okres (derived, Transleithanian)Slovak/Rusyn county name of birth
legionbirth_megye_hubirth_okres (derived, Transleithanian)Magyar county name of birth
legionbirth_obec_skbirth_okres (derived, Transleithanian)Slovak obec the settlement pass matched for birth (key to the Majo-2012 obec-level mother-tongue shares); <NA> for district-seat matches
legionhome_municipality_cshome_municipality / home_okres (derived)Standardised modern Czech home (domicile) municipality (Historický lexikon obcí)
legionhome_municipality_dehome_municipality / home_okres (derived)Standardised German home (domicile) municipality (1910 exonym; Wikidata/lexikon; NA where none existed)
legionhome_okres_idhome_municipality / home_okres (derived)1910 political-okres id (region·1000+district) of home (domicile) — administrative unit
legionhome_okres_matchhome_municipality / home_okres (derived)How home (domicile)_okres_id resolved (okres/court/fuzzy/alias/settlement/muni)
legionhome_okres_twinhome_municipality / home_okres (derived)Which member of a city/rural twin pair home (domicile) sits in: city / ring / undecided (no municipality to decide); NA off-twin
legionhome_court_snappedhome_municipality / home_okres (derived)True where home (domicile)_court_id was pulled back into the record's own okres by the D-35 near-miss repair (≤1 km); exclude these rows when the court layer is used as independent evidence
legionhome_okres_praguehome_municipality / home_okres (derived)Greater-Prague rule (D-36): city = in okres Praha and kept; suburb = moved to the separate 1910 okres of its municipality; <NA> off Praha
legionhome_census_okres_idhome_municipality / home_okres (derived)1910 census unit of home (domicile) — the census-merge key. Equals home (domicile)_okres_id except at the ten city/rural twin pairs, where both members pool into one unit (the court-level sources do not separate them)
legionhome_okres_cshome_municipality / home_okres (derived)1910 political-okres Czech name of home (domicile) (MPIDR backbone)
legionhome_okres_dehome_municipality / home_okres (derived)1910 political-okres German name of home (domicile) (MPIDR backbone)
legionhome_court_idhome_municipality / home_okres (derived)1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of home (domicile), via settlement spatial join
legionhome_court_cshome_municipality / home_okres (derived)Court-district (soudní okres) Czech name of home (domicile)
legionhome_court_dehome_municipality / home_okres (derived)Court-district (Gerichtsbezirk) German name of home (domicile)
legionhome_landhome_municipality / home_okres (derived)Crown land/country of home (domicile) (Czech lands/Slovakia/Austria/Galicia/…) — positive district classification
legionhome_lathome_municipality / home_okres (derived)Latitude of home (domicile) settlement (WGS84; Wikidata)
legionhome_lonhome_municipality / home_okres (derived)Longitude of home (domicile) settlement (WGS84; Wikidata)
legionhome_qidhome_municipality / home_okres (derived)Wikidata QID of home (domicile) settlement
legionhome_jaras_idhome_okres (derived, Transleithanian)1910 járás (district) unit id of home (domicile) — Upper-Hungary census key (GISta Hungarorum)
legionhome_jaras_matchhome_okres (derived, Transleithanian)How home (domicile)_jaras_id resolved (jaras/town/megye/fuzzy/settlement)
legionhome_jaras_twinhome_okres (derived, Transleithanian)Which member of a járás/chartered-town twin pair home (domicile) sits in: city / ring / undecided (no municipality to decide); NA off-twin
legionhome_jaras_skhome_okres (derived, Transleithanian)Slovak/Rusyn járás name of home (domicile)
legionhome_jaras_huhome_okres (derived, Transleithanian)Magyar járás/seat name of home (domicile)
legionhome_megye_idhome_okres (derived, Transleithanian)1910 vármegye (county) id of home (domicile)
legionhome_megye_skhome_okres (derived, Transleithanian)Slovak/Rusyn county name of home (domicile)
legionhome_megye_huhome_okres (derived, Transleithanian)Magyar county name of home (domicile)
legionhome_obec_skhome_okres (derived, Transleithanian)Slovak obec the settlement pass matched for home (domicile) (key to the Majo-2012 obec-level mother-tongue shares); <NA> for district-seat matches
habsburgvua_idvua_idStable VÚA record id (primary key)
habsburgsurnamesurnameCleaned surname
habsburggiven_namegivenCleaned given name
habsburgname_uncertainname_uncertainTrue if the raw name carried an uncertainty mark (?)
habsburgbirth_datebirth_dateParsed date of birth (NaT if unresolved)
habsburgbirth_yearbirth_yearYear of birth (from birth_date)
habsburgbirth_year_onlybirth_year_onlyTrue if only a year (not full date) was available
habsburgbirth_municipalitybirth_municipalityRaw municipality of birth (obec), as recorded — see birth_municipality_cs/_de for standardised names
habsburgbirth_okresbirth_okresRaw district of birth, as recorded — see birth_okres_cs/_de/_id (census key) and birth_court_* (soudní okres)
habsburgbirth_germanbirth_germanGerman exonym of birthplace, if present
habsburgbirth_landbirth_landCrown land of birth (Czech lands / Slovakia / …)
habsburgbirth_cisleithanianbirth_cisleithanianTrue if the birthplace lay in the Austrian half (Czech lands / Austria / Galicia), False for Upper Hungary and abroad, <NA> where the land is unknown. Derived from the corrected birth_land (records_geo.sync_cisleithanian), never from the raw text
habsburgbirth_loc_uncertainbirth_loc_uncertainTrue if birthplace parse was uncertain
habsburghome_municipalityhome_municipalityRaw home municipality (domicile), as recorded — see home_municipality_cs/_de for standardised names
habsburghome_okreshome_okresRaw home district (domicile), as recorded — see home_okres_cs/_de/_id and home_court_* (soudní okres)
habsburghome_landhome_landHome crown land
habsburghome_cisleithanianhome_cisleithanianAs birth_cisleithanian, for the home (domicile) place — derived from the corrected home_land
habsburgdeath_datedeath_dateParsed date of death, 1914–1923 only (war-period mortality). For legionnaires the post-1923 deaths of veterans are NaT here — see death_date_raw
habsburgdeath_yeardeath_yearYear of death (from death_date)
habsburgdeath_year_onlydeath_year_onlyTrue if only a year was available for death
habsburgage_at_deathage_at_deathdeath_year − birth_year
habsburgdeath_placeMísto úmrtíPlace of death
habsburgburial_placeMísto pohřbeníPlace of burial
habsburgcausecausePrimary cause-of-death code (KIA/DIS/MIS/EXE/DRO/SUI/CAP)
habsburgcause_labelcause_labelEnglish label for primary cause
habsburgcause_uncertaincause_uncertainTrue if the raw cause carried a ?; <NA> where no cause was recorded
habsburgcause_KIAcause_KIATrue if the cause field mentions killed in action; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0)
habsburgcause_DIScause_DISTrue if the cause field mentions death by illness or wounds; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0)
habsburgcause_MIScause_MISTrue if the cause field mentions missing; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0)
habsburgcause_EXEcause_EXETrue if the cause field mentions execution; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0)
habsburgcause_DROcause_DROTrue if the cause field mentions drowning; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0)
habsburgcause_SUIcause_SUITrue if the cause field mentions suicide; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0)
habsburgcause_CAPcause_CAPTrue if the cause field mentions capture; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0)
habsburgtheatertheatreLegion: theatre of service (Russia / Italy / France / Serbia/Balkans / Britain/Dominions / Post-war (ČSA) / Unknown). Habsburg: front of death (Russia / Italy / Serbia / Macedonia / Romania / France / Rear / Unknown). Only Russia, Italy and France compare across the two
habsburgin_captivityin_captivityTrue if the battlefield field records death in captivity (v zajetí); <NA> where Bojiště is blank
habsburgah_unit_primaryah_unit_primaryPrimary Austro-Hungarian unit (raw)
habsburgah_unit_typeah_unit_typeHabsburg arm/branch (Infantry / Artillery / …)
habsburgah_unit_orgah_unit_orgHabsburg organisation (Common army / Landwehr / Honvéd / Landsturm)
habsburgah_regiment_noah_regiment_noHabsburg regiment number
habsburgah_n_unitsah_n_unitsCount of ';'-joined Habsburg units
habsburgnote_legionnote_legionTrue if the note EXPLICITLY records Legion service (legionář / legie / KLEG / Kancelář legií / čs. dobrovolník / rota Nazdar / Česká družina / čs. střelecký pluk 1–12). False where it does not, or where there is no note. A 'čs. pěší pluk N' reference is register provenance, not service — see note_register_ref (habsburg.py 1.2.0)
habsburgnote_legion_refnote_legion_refThe Legion phrase matched in the note (e.g. 'Kancelář legií', 'KLEG', 'legionář')
habsburgnote_register_refnote_register_refThe interwar Czechoslovak army unit whose death register lists the man ('náhradní prapor čs. pěšího pluku 33'), verbatim — archival provenance, NOT Legion service
habsburgnote_declared_deadnote_declared_deadTrue if legally declared dead
habsburgnote_dead_refnote_dead_refFile reference for the death declaration
habsburgnote_ranknote_rankRank recovered from the note
habsburgnote_diseasenote_diseaseDisease detail recovered from the note
habsburgnotesPoznámkaFree-text note (Poznámka)
habsburgin_other_datasetutils.linkage (notebook 02 §4)True if this person was matched in the other dataset (confirmed overlap; see match_confidence)
habsburgmatch_idutils.linkage (notebook 02 §4)Matched id in the other dataset (vua_id ↔ legion_id)
habsburgmatch_confidenceutils.linkage (notebook 02 §4)Linkage evidence: high = identical full date of birth + compatible names; medium = explicit VÚA Legion note + same birth okres + birth year + compatible names (linkage.py 1.3.0; one-to-one)
habsburgdup_grouputils.linkage (notebook 02 §4)Probable duplicate record of the same person INSIDE this dataset: the smallest id among records sharing surname + given name + full date of birth + birth municipality; <NA> otherwise. Nothing is dropped
habsburgbirth_municipality_csbirth_municipality / birth_okres (derived)Standardised modern Czech birth municipality (Historický lexikon obcí)
habsburgbirth_municipality_debirth_municipality / birth_okres (derived)Standardised German birth municipality (1910 exonym; Wikidata/lexikon; NA where none existed)
habsburgbirth_okres_idbirth_municipality / birth_okres (derived)1910 political-okres id (region·1000+district) of birth — administrative unit
habsburgbirth_okres_matchbirth_municipality / birth_okres (derived)How birth_okres_id resolved (okres/court/fuzzy/alias/settlement/muni)
habsburgbirth_okres_twinbirth_municipality / birth_okres (derived)Which member of a city/rural twin pair birth sits in: city / ring / undecided (no municipality to decide); NA off-twin
habsburgbirth_court_snappedbirth_municipality / birth_okres (derived)True where birth_court_id was pulled back into the record's own okres by the D-35 near-miss repair (≤1 km); exclude these rows when the court layer is used as independent evidence
habsburgbirth_okres_praguebirth_municipality / birth_okres (derived)Greater-Prague rule (D-36): city = in okres Praha and kept; suburb = moved to the separate 1910 okres of its municipality; <NA> off Praha
habsburgbirth_census_okres_idbirth_municipality / birth_okres (derived)1910 census unit of birth — the census-merge key. Equals birth_okres_id except at the ten city/rural twin pairs, where both members pool into one unit (the court-level sources do not separate them)
habsburgbirth_okres_csbirth_municipality / birth_okres (derived)1910 political-okres Czech name of birth (MPIDR backbone)
habsburgbirth_okres_debirth_municipality / birth_okres (derived)1910 political-okres German name of birth (MPIDR backbone)
habsburgbirth_court_idbirth_municipality / birth_okres (derived)1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of birth, via settlement spatial join
habsburgbirth_court_csbirth_municipality / birth_okres (derived)Court-district (soudní okres) Czech name of birth
habsburgbirth_court_debirth_municipality / birth_okres (derived)Court-district (Gerichtsbezirk) German name of birth
habsburgbirth_latbirth_municipality / birth_okres (derived)Latitude of birth settlement (WGS84; Wikidata)
habsburgbirth_lonbirth_municipality / birth_okres (derived)Longitude of birth settlement (WGS84; Wikidata)
habsburgbirth_qidbirth_municipality / birth_okres (derived)Wikidata QID of birth settlement
habsburgbirth_jaras_idbirth_okres (derived, Transleithanian)1910 járás (district) unit id of birth — Upper-Hungary census key (GISta Hungarorum)
habsburgbirth_jaras_matchbirth_okres (derived, Transleithanian)How birth_jaras_id resolved (jaras/town/megye/fuzzy/settlement)
habsburgbirth_jaras_twinbirth_okres (derived, Transleithanian)Which member of a járás/chartered-town twin pair birth sits in: city / ring / undecided (no municipality to decide); NA off-twin
habsburgbirth_jaras_skbirth_okres (derived, Transleithanian)Slovak/Rusyn járás name of birth
habsburgbirth_jaras_hubirth_okres (derived, Transleithanian)Magyar járás/seat name of birth
habsburgbirth_megye_idbirth_okres (derived, Transleithanian)1910 vármegye (county) id of birth
habsburgbirth_megye_skbirth_okres (derived, Transleithanian)Slovak/Rusyn county name of birth
habsburgbirth_megye_hubirth_okres (derived, Transleithanian)Magyar county name of birth
habsburgbirth_obec_skbirth_okres (derived, Transleithanian)Slovak obec the settlement pass matched for birth (key to the Majo-2012 obec-level mother-tongue shares); <NA> for district-seat matches
habsburghome_municipality_cshome_municipality / home_okres (derived)Standardised modern Czech home (domicile) municipality (Historický lexikon obcí)
habsburghome_municipality_dehome_municipality / home_okres (derived)Standardised German home (domicile) municipality (1910 exonym; Wikidata/lexikon; NA where none existed)
habsburghome_okres_idhome_municipality / home_okres (derived)1910 political-okres id (region·1000+district) of home (domicile) — administrative unit
habsburghome_okres_matchhome_municipality / home_okres (derived)How home (domicile)_okres_id resolved (okres/court/fuzzy/alias/settlement/muni)
habsburghome_okres_twinhome_municipality / home_okres (derived)Which member of a city/rural twin pair home (domicile) sits in: city / ring / undecided (no municipality to decide); NA off-twin
habsburghome_court_snappedhome_municipality / home_okres (derived)True where home (domicile)_court_id was pulled back into the record's own okres by the D-35 near-miss repair (≤1 km); exclude these rows when the court layer is used as independent evidence
habsburghome_okres_praguehome_municipality / home_okres (derived)Greater-Prague rule (D-36): city = in okres Praha and kept; suburb = moved to the separate 1910 okres of its municipality; <NA> off Praha
habsburghome_census_okres_idhome_municipality / home_okres (derived)1910 census unit of home (domicile) — the census-merge key. Equals home (domicile)_okres_id except at the ten city/rural twin pairs, where both members pool into one unit (the court-level sources do not separate them)
habsburghome_okres_cshome_municipality / home_okres (derived)1910 political-okres Czech name of home (domicile) (MPIDR backbone)
habsburghome_okres_dehome_municipality / home_okres (derived)1910 political-okres German name of home (domicile) (MPIDR backbone)
habsburghome_court_idhome_municipality / home_okres (derived)1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of home (domicile), via settlement spatial join
habsburghome_court_cshome_municipality / home_okres (derived)Court-district (soudní okres) Czech name of home (domicile)
habsburghome_court_dehome_municipality / home_okres (derived)Court-district (Gerichtsbezirk) German name of home (domicile)
habsburghome_lathome_municipality / home_okres (derived)Latitude of home (domicile) settlement (WGS84; Wikidata)
habsburghome_lonhome_municipality / home_okres (derived)Longitude of home (domicile) settlement (WGS84; Wikidata)
habsburghome_qidhome_municipality / home_okres (derived)Wikidata QID of home (domicile) settlement
habsburghome_jaras_idhome_okres (derived, Transleithanian)1910 járás (district) unit id of home (domicile) — Upper-Hungary census key (GISta Hungarorum)
habsburghome_jaras_matchhome_okres (derived, Transleithanian)How home (domicile)_jaras_id resolved (jaras/town/megye/fuzzy/settlement)
habsburghome_jaras_twinhome_okres (derived, Transleithanian)Which member of a járás/chartered-town twin pair home (domicile) sits in: city / ring / undecided (no municipality to decide); NA off-twin
habsburghome_jaras_skhome_okres (derived, Transleithanian)Slovak/Rusyn járás name of home (domicile)
habsburghome_jaras_huhome_okres (derived, Transleithanian)Magyar járás/seat name of home (domicile)
habsburghome_megye_idhome_okres (derived, Transleithanian)1910 vármegye (county) id of home (domicile)
habsburghome_megye_skhome_okres (derived, Transleithanian)Slovak/Rusyn county name of home (domicile)
habsburghome_megye_huhome_okres (derived, Transleithanian)Magyar county name of home (domicile)
habsburghome_obec_skhome_okres (derived, Transleithanian)Slovak obec the settlement pass matched for home (domicile) (key to the Majo-2012 obec-level mother-tongue shares); <NA> for district-seat matches
settlementscs_namelexikon headwordSettlement name, modern Czech (register key; + curated extras absent from the register, e.g. Praha)
settlementsname_1910pod názvemSettlement name as used in 1910 (German exonym where the place was later renamed; else older Czech spelling; blank if unchanged)
settlementsokres_1910lexikon1910 political okres of the settlement (period name)
settlementsokres_idrecords_geo1910 okres id (= districts okres_id; join key to census)
settlementsokres_matchrecords_geoHow okres_1910 resolved: okres / court / fuzzy
settlementslanddistrictsCrown land (Böhmen / Mähren / Schlesien)
settlementsstatus_1910lexikon1910 status: obec (commune) / osada (hamlet) / cast (part-of)
settlementsparent_1910lexikonParent commune in 1910 where the settlement was dependent (osada/cast)
settlementsokres_alllexikonPipe-joined okres timeline across all census years (used to recover post-1910 okres labels)
settlementswd_qidWikidataWikidata item id (Q...) of the settlement, where geocoded
settlementslat / lonWikidata P625Coordinates (WGS84); blank where not geocoded (~40% coverage, obce-level). okres_centroids_1910.csv = okres-mean lat/lon
districtslandMPIDR LANDCrown land (Böhmen / Mähren / Schlesien)
districtsokres_codeREGION-DISTRICTPolitical-okres id (MPIDR region-district code)
districtsokres_deMPIDRPolitical okres (politický okres) seat name, German
districtsokres_cscuratedPolitical okres name, canonical 1910 Czech (census-merge key)
districtsokres_cs_moderncuratedPolitical okres name today, if the seat was renamed (else blank)
districtscourt_idMPIDR ID_1Court-district (Gerichtsbezirk) unique id
districtsmpidr_oidMPIDR OID_MPIDR polygon id (link to the .shp geometry)
districtsgerichtsbezirk_deMPIDR NAMECourt district (soudní okres / Bezirksgericht), German
districtsgerichtsbezirk_cscuratedCourt district name, canonical 1910 Czech
districtsgerichtsbezirk_cs_moderncuratedCourt district name today, if renamed (else blank)
districtsis_okres_seatderivedTrue if this court district is the political-okres seat (the court whose Czech name equals the okres name, ignoring a '(město)' marker)
crosswalkokres_iddistricts1910 political-okres id (okres_census_crosswalk.csv)
crosswalkcensus_okres_idderivedAnalytical census unit: the identity map except at the ten city/rural twin pairs, where both members point at the RURAL member's id (the pooled unit)
crosswalktwin_pooledderivedTrue if this okres is one of the 20 city/rural twin members
crosswalktwin_rolederivedcity / ring within the pair (blank off-twin)
censusokres_idregion*1000+district1910 political-okres id (= districts okres_id; join key)
censuscensus_okres_idderivedPOOLED panel only (census_okres_1910_pooled): the analytical unit, = okres_id except at the ten twin pairs where the pair is one row. Merge persons on birth_census_okres_id
censustwin_pooledderivedPOOLED panel only: True for the ten collapsed city+ring units
censusokres_cs / okres_dedistrictsPolitical okres name, 1910 Czech / German
censuslang_*Volkszählung UmgangssprachePopulation by colloquial language: lang_czech (= Böhmisch-Mährisch-Slowakisch bloc), lang_german, ...; lang_total = present citizens. Derived lang_*_share = lang_x / lang_total
censusrel_*Volkszählung ReligionPopulation by religion: rel_roman_catholic, rel_jewish, rel_lutheran, ...; rel_total. Derived rel_*_share = rel_x / rel_total
censuslit_* / disab_*Volkszählung BildungsgradLiteracy of the >10y population (lit_read_write / lit_read_only / lit_illiterate; base lit_age_over10) + blind/deaf-mute counts. Derived literacy_rate = lit_read_write / lit_age_over10
censusocc_*Volkszählung BerufOccupation by class (occ_occupation_class_a_land_forestry_*, ...) and status (economically active / workers / servants / self-employed ...)
censuspop_*Volkszählung EinwohnerLocalities / houses / households / inhabitants by settlement-size band (pop_lt500_* ... pop_gt20000_*). Derived pop_total = sum of size-class inhabitants
censusbirths_*Bewegung der Bevölkerung1911 births: live/still × legitimate/illegitimate × sex (births_livebirths_*, births_stillbirths_*). Derived illegitimacy_rate = births_livebirths_illegitimate / births_livebirths_total
censusdeaths_*Bewegung der Bevölkerung1911 deaths by age/sex/legitimacy; deaths_child_* = deaths of children under 5 by age band
censusdeath_*Bewegung der Bevölkerung — Todesursachen1911 deaths by cause × sex (death_tuberculosis_*, death_pneumonia_*, death_suicide_*, ...)
censuslivestock_*Viehzählung 1910Livestock owners by occupation: livestock_large_* / livestock_small_* / livestock_total_*
censussavings_*Statistik der Sparkassen 1910Savings-bank balance sheets (savings_balance_*, Kronen) and passbooks (savings_books_*)
censusjust_*Justizstatistik 1910-12District-court caseload (just_*) and court personnel (just_personnel_*)
censusbirth_okres_idrecords_geoPerson's birth political-okres id -> census join key (birth_okres via okres+court names, then settlement timeline / unique municipality)
censusbirth_okres_matchrecords_geoMatch tier okres/court/fuzzy/alias/settlement/muni (muni = municipality-only, weakest: land-gated + cross-frame-homonym-guarded; NA = birth outside the Czech-lands 1910 census frame)
censusbirth_lat / birth_lonwikidataBirth-settlement coordinates (WGS84) via birth_municipality -> settlement geocode (~40-50% of records; obce-level)
censusbirth_qidwikidataWikidata QID of the birth settlement

Census codebookWhat's in the data

Every census covariate, EN ← DE/HU. · source: reference/census/census_codebook.csv

568 rows

framefamilyenglish_columndescriptionsource_columnsource_headersource_file
cisVital statistics (births)births_livebirths_legitimate_mlivebirths legitimate mgeb_lebendgeborene_ehelich_mLebendgeborene > ehelich > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_legitimate_flivebirths legitimate fgeb_lebendgeborene_ehelich_wLebendgeborene > ehelich > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_legitimatelivebirths legitimategeb_lebendgeborene_ehelich_zLebendgeborene > ehelich > z.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_illegitimate_mlivebirths illegitimate mgeb_lebendgeborene_unehelich_mLebendgeborene > unehelich > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_illegitimate_flivebirths illegitimate fgeb_lebendgeborene_unehelich_wLebendgeborene > unehelich > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_illegitimatelivebirths illegitimategeb_lebendgeborene_unehelich_zLebendgeborene > unehelich > z.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_total_mlivebirths total mgeb_lebendgeborene_zusammen_mLebendgeborene > zusammen > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_total_flivebirths total fgeb_lebendgeborene_zusammen_wLebendgeborene > zusammen > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_livebirths_totallivebirths totalgeb_lebendgeborene_zusammen_zLebendgeborene > zusammen > z.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_legitimate_mstillbirths legitimate mgeb_totgeborene_ehelich_mTotgeborene > ehelich > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_legitimate_fstillbirths legitimate fgeb_totgeborene_ehelich_wTotgeborene > ehelich > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_legitimatestillbirths legitimategeb_totgeborene_ehelich_zTotgeborene > ehelich > z.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_illegitimate_mstillbirths illegitimate mgeb_totgeborene_unehelich_mTotgeborene > unehelich > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_illegitimate_fstillbirths illegitimate fgeb_totgeborene_unehelich_wTotgeborene > unehelich > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_illegitimatestillbirths illegitimategeb_totgeborene_unehelich_zTotgeborene > unehelich > z.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_total_mstillbirths total mgeb_totgeborene_zusammen_mTotgeborene > zusammen > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_total_fstillbirths total fgeb_totgeborene_zusammen_wTotgeborene > zusammen > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_stillbirths_totalstillbirths totalgeb_totgeborene_zusammen_zTotgeborene > zusammen > z.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_total_number_born_mtotal number born mgeb_gesamtzahl_der_geborenen_mGesamtzahl der Geborenen > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_total_number_born_ftotal number born fgeb_gesamtzahl_der_geborenen_wGesamtzahl der Geborenen > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_total_number_borntotal number borngeb_gesamtzahl_der_geborenen_zGesamtzahl der Geborenen > z.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_roman_catholic_mreligion roman catholic mgeb_religion_romisch_katholisch_mReligion > römisch-katholisch > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_roman_catholic_freligion roman catholic fgeb_religion_romisch_katholisch_wReligion > römisch-katholisch > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_greek_catholic_mreligion greek catholic mgeb_religion_griechisch_katholisch_mReligion > griechisch-katholisch > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_greek_catholic_freligion greek catholic fgeb_religion_griechisch_katholisch_wReligion > griechisch-katholisch > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_greek_orthodox_mreligion greek orthodox mgeb_religion_griechisch_orientalisch_mReligion > griechisch-orientalisch > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_greek_orthodox_freligion greek orthodox fgeb_religion_griechisch_orientalisch_wReligion > griechisch-orientalisch > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_evangelical_mreligion evangelical mgeb_religion_evangelisch_mReligion > evangelisch > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_evangelical_freligion evangelical fgeb_religion_evangelisch_wReligion > evangelisch > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_jewish_mreligion jewish mgeb_religion_israelitisch_mReligion > israelitisch > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_jewish_freligion jewish fgeb_religion_israelitisch_wReligion > israelitisch > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_other_mreligion other mgeb_religion_sonstige_mReligion > sonstige > m.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisVital statistics (births)births_religion_other_freligion other fgeb_religion_sonstige_wReligion > sonstige > w.Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_legitimate_mdeceased birth to 5 years legitimate mgest_gestorbene_von_der_geburt_bis_zu_5_jahren_ehelich_mGestorbene von der Geburt bis zu 5 Jahren > ehelich > m.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_legitimate_fdeceased birth to 5 years legitimate fgest_gestorbene_von_der_geburt_bis_zu_5_jahren_ehelich_wGestorbene von der Geburt bis zu 5 Jahren > ehelich > w.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_legitimate_sumdeceased birth to 5 years legitimate sumgest_gestorbene_von_der_geburt_bis_zu_5_jahren_ehelich_summeGestorbene von der Geburt bis zu 5 Jahren > ehelich > SummeBewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_illegitimate_mdeceased birth to 5 years illegitimate mgest_gestorbene_von_der_geburt_bis_zu_5_jahren_unehelich_mGestorbene von der Geburt bis zu 5 Jahren > unehelich > m.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_illegitimate_fdeceased birth to 5 years illegitimate fgest_gestorbene_von_der_geburt_bis_zu_5_jahren_unehelich_wGestorbene von der Geburt bis zu 5 Jahren > unehelich > w.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_illegitimate_sumdeceased birth to 5 years illegitimate sumgest_gestorbene_von_der_geburt_bis_zu_5_jahren_unehelich_summeGestorbene von der Geburt bis zu 5 Jahren > unehelich > SummeBewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_total_mdeceased birth to 5 years total mgest_gestorbene_von_der_geburt_bis_zu_5_jahren_zusammen_mGestorbene von der Geburt bis zu 5 Jahren > zusammen > m.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_total_fdeceased birth to 5 years total fgest_gestorbene_von_der_geburt_bis_zu_5_jahren_zusammen_wGestorbene von der Geburt bis zu 5 Jahren > zusammen > w.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_birth_to_5_years_total_sumdeceased birth to 5 years total sumgest_gestorbene_von_der_geburt_bis_zu_5_jahren_zusammen_summeGestorbene von der Geburt bis zu 5 Jahren > zusammen > SummeBewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_age_over_5_years_mdeceased age over 5 years mgest_gestorbene_im_alter_uber_5_jahre_mGestorbene im Alter über 5 Jahre > m.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_age_over_5_years_fdeceased age over 5 years fgest_gestorbene_im_alter_uber_5_jahre_wGestorbene im Alter über 5 Jahre > w.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_deceased_age_over_5_years_sumdeceased age over 5 years sumgest_gestorbene_im_alter_uber_5_jahre_summeGestorbene im Alter über 5 Jahre > SummeBewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_total_number_deceased_mtotal number deceased mgest_gesamtzahl_der_gestorbenen_mGesamtzahl der Gestorbenen > m.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_total_number_deceased_ftotal number deceased fgest_gesamtzahl_der_gestorbenen_wGesamtzahl der Gestorbenen > w.Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeathsdeaths_total_number_deceased_sumtotal number deceased sumgest_gesamtzahl_der_gestorbenen_summeGesamtzahl der Gestorbenen > SummeBewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_to_1_month_legitimate_mto 1 month legitimate mgest_kind_bis_zu_1_monat_ehelich_mBis zu 1 Monat > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_to_1_month_legitimate_fto 1 month legitimate fgest_kind_bis_zu_1_monat_ehelich_wBis zu 1 Monat > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_to_1_month_illegitimate_mto 1 month illegitimate mgest_kind_bis_zu_1_monat_unehelich_mBis zu 1 Monat > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_to_1_month_illegitimate_fto 1 month illegitimate fgest_kind_bis_zu_1_monat_unehelich_wBis zu 1 Monat > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_months_legitimate_m1 to 2 months legitimate mgest_kind_von_1_bis_zu_2_monaten_ehelich_mVon 1 bis zu 2 Monaten > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_months_legitimate_f1 to 2 months legitimate fgest_kind_von_1_bis_zu_2_monaten_ehelich_wVon 1 bis zu 2 Monaten > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_months_illegitimate_m1 to 2 months illegitimate mgest_kind_von_1_bis_zu_2_monaten_unehelich_mVon 1 bis zu 2 Monaten > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_months_illegitimate_f1 to 2 months illegitimate fgest_kind_von_1_bis_zu_2_monaten_unehelich_wVon 1 bis zu 2 Monaten > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_months_legitimate_m2 to 3 months legitimate mgest_kind_von_2_bis_zu_3_monaten_ehelich_mVon 2 bis zu 3 Monaten > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_months_legitimate_f2 to 3 months legitimate fgest_kind_von_2_bis_zu_3_monaten_ehelich_wVon 2 bis zu 3 Monaten > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_months_illegitimate_m2 to 3 months illegitimate mgest_kind_von_2_bis_zu_3_monaten_unehelich_mVon 2 bis zu 3 Monaten > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_months_illegitimate_f2 to 3 months illegitimate fgest_kind_von_2_bis_zu_3_monaten_unehelich_wVon 2 bis zu 3 Monaten > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_6_months_legitimate_m3 to 6 months legitimate mgest_kind_von_3_bis_zu_6_monaten_ehelich_mVon 3 bis zu 6 Monaten > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_6_months_legitimate_f3 to 6 months legitimate fgest_kind_von_3_bis_zu_6_monaten_ehelich_wVon 3 bis zu 6 Monaten > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_6_months_illegitimate_m3 to 6 months illegitimate mgest_kind_von_3_bis_zu_6_monaten_unehelich_mVon 3 bis zu 6 Monaten > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_6_months_illegitimate_f3 to 6 months illegitimate fgest_kind_von_3_bis_zu_6_monaten_unehelich_wVon 3 bis zu 6 Monaten > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_6_months_legitimate_mbirth to 6 months legitimate mgest_kind_von_der_geburt_bis_zu_6_monaten_ehelich_mVon der Geburt bis zu 6 Monaten > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_6_months_legitimate_fbirth to 6 months legitimate fgest_kind_von_der_geburt_bis_zu_6_monaten_ehelich_wVon der Geburt bis zu 6 Monaten > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_6_months_illegitimate_mbirth to 6 months illegitimate mgest_kind_von_der_geburt_bis_zu_6_monaten_unehelich_mVon der Geburt bis zu 6 Monaten > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_6_months_illegitimate_fbirth to 6 months illegitimate fgest_kind_von_der_geburt_bis_zu_6_monaten_unehelich_wVon der Geburt bis zu 6 Monaten > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_6_to_9_months_legitimate_m6 to 9 months legitimate mgest_kind_von_6_bis_zu_9_monaten_ehelich_mVon 6 bis zu 9 Monaten > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_6_to_9_months_legitimate_f6 to 9 months legitimate fgest_kind_von_6_bis_zu_9_monaten_ehelich_wVon 6 bis zu 9 Monaten > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_6_to_9_months_illegitimate_m6 to 9 months illegitimate mgest_kind_von_6_bis_zu_9_monaten_unehelich_mVon 6 bis zu 9 Monaten > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_6_to_9_months_illegitimate_f6 to 9 months illegitimate fgest_kind_von_6_bis_zu_9_monaten_unehelich_wVon 6 bis zu 9 Monaten > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_9_to_12_months_legitimate_m9 to 12 months legitimate mgest_kind_von_9_bis_zu_12_monaten_ehelich_mVon 9 bis zu 12 Monaten > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_9_to_12_months_legitimate_f9 to 12 months legitimate fgest_kind_von_9_bis_zu_12_monaten_ehelich_wVon 9 bis zu 12 Monaten > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_9_to_12_months_illegitimate_m9 to 12 months illegitimate mgest_kind_von_9_bis_zu_12_monaten_unehelich_mVon 9 bis zu 12 Monaten > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_9_to_12_months_illegitimate_f9 to 12 months illegitimate fgest_kind_von_9_bis_zu_12_monaten_unehelich_wVon 9 bis zu 12 Monaten > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_1_year_legitimate_mbirth to 1 year legitimate mgest_kind_von_der_geburt_bis_zu_1_jahr_ehelich_mVon der Geburt bis zu 1 Jahr > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_1_year_legitimate_fbirth to 1 year legitimate fgest_kind_von_der_geburt_bis_zu_1_jahr_ehelich_wVon der Geburt bis zu 1 Jahr > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_1_year_illegitimate_mbirth to 1 year illegitimate mgest_kind_von_der_geburt_bis_zu_1_jahr_unehelich_mVon der Geburt bis zu 1 Jahr > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_birth_to_1_year_illegitimate_fbirth to 1 year illegitimate fgest_kind_von_der_geburt_bis_zu_1_jahr_unehelich_wVon der Geburt bis zu 1 Jahr > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_years_legitimate_m1 to 2 years legitimate mgest_kind_von_1_bis_zu_2_jahren_ehelich_mVon 1 bis zu 2 Jahren > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_years_legitimate_f1 to 2 years legitimate fgest_kind_von_1_bis_zu_2_jahren_ehelich_wVon 1 bis zu 2 Jahren > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_years_illegitimate_m1 to 2 years illegitimate mgest_kind_von_1_bis_zu_2_jahren_unehelich_mVon 1 bis zu 2 Jahren > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_1_to_2_years_illegitimate_f1 to 2 years illegitimate fgest_kind_von_1_bis_zu_2_jahren_unehelich_wVon 1 bis zu 2 Jahren > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_years_legitimate_m2 to 3 years legitimate mgest_kind_von_2_bis_zu_3_jahren_ehelich_mVon 2 bis zu 3 Jahren > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_years_legitimate_f2 to 3 years legitimate fgest_kind_von_2_bis_zu_3_jahren_ehelich_wVon 2 bis zu 3 Jahren > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_years_illegitimate_m2 to 3 years illegitimate mgest_kind_von_2_bis_zu_3_jahren_unehelich_mVon 2 bis zu 3 Jahren > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_2_to_3_years_illegitimate_f2 to 3 years illegitimate fgest_kind_von_2_bis_zu_3_jahren_unehelich_wVon 2 bis zu 3 Jahren > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_4_years_legitimate_m3 to 4 years legitimate mgest_kind_von_3_bis_zu_4_jahren_ehelich_mVon 3 bis zu 4 Jahren > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_4_years_legitimate_f3 to 4 years legitimate fgest_kind_von_3_bis_zu_4_jahren_ehelich_wVon 3 bis zu 4 Jahren > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_4_years_illegitimate_m3 to 4 years illegitimate mgest_kind_von_3_bis_zu_4_jahren_unehelich_mVon 3 bis zu 4 Jahren > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_3_to_4_years_illegitimate_f3 to 4 years illegitimate fgest_kind_von_3_bis_zu_4_jahren_unehelich_wVon 3 bis zu 4 Jahren > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_4_to_5_years_legitimate_m4 to 5 years legitimate mgest_kind_von_4_bis_zu_5_jahren_ehelich_mVon 4 bis zu 5 Jahren > ehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_4_to_5_years_legitimate_f4 to 5 years legitimate fgest_kind_von_4_bis_zu_5_jahren_ehelich_wVon 4 bis zu 5 Jahren > ehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_4_to_5_years_illegitimate_m4 to 5 years illegitimate mgest_kind_von_4_bis_zu_5_jahren_unehelich_mVon 4 bis zu 5 Jahren > unehelich > m.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisDeaths under age 5deaths_child_4_to_5_years_illegitimate_f4 to 5 years illegitimate fgest_kind_von_4_bis_zu_5_jahren_unehelich_wVon 4 bis zu 5 Jahren > unehelich > w.Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx
cisCause of deathdeath_congenital_congenital_weakness_due_to_early_miscarriage_mcongenital congenital weakness due to early miscarriage mtodes_angeborene_lebensschwache_infolge_von_fruh_oder_missgeburt_mAngeborene Lebensschwäche infolge von Früh- oder Mißgeburt > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_congenital_congenital_weakness_due_to_early_miscarriage_fcongenital congenital weakness due to early miscarriage ftodes_angeborene_lebensschwache_infolge_von_fruh_oder_missgeburt_wAngeborene Lebensschwäche infolge von Früh- oder Mißgeburt > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_tuberculosis_lung_other_organs_mtuberculosis lung other organs mtodes_tuberkulose_der_lungen_oder_anderer_organe_mTuberkulose der Lungen oder anderer Organe > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_tuberculosis_lung_other_organs_ftuberculosis lung other organs ftodes_tuberkulose_der_lungen_oder_anderer_organe_wTuberkulose der Lungen oder anderer Organe > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_pneumonia_mpneumonia mtodes_lungenentzundung_mLungenentzündung > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_pneumonia_fpneumonia ftodes_lungenentzundung_wLungenentzündung > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_diphtheria_croup_diphtheria_mdiphtheria croup diphtheria mtodes_diphtherie_krupp_und_diphtherie_mDiphtherie (Krupp und Diphtherie) > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_diphtheria_croup_diphtheria_fdiphtheria croup diphtheria ftodes_diphtherie_krupp_und_diphtherie_wDiphtherie (Krupp und Diphtherie) > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_whooping_cough_mwhooping cough mtodes_keuchhusten_mKeuchhusten > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_whooping_cough_fwhooping cough ftodes_keuchhusten_wKeuchhusten > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_smallpox_msmallpox mtodes_blattern_mBlattern > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_smallpox_fsmallpox ftodes_blattern_wBlattern > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_scarlet_fever_mscarlet fever mtodes_scharlach_mScharlach > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_scarlet_fever_fscarlet fever ftodes_scharlach_wScharlach > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_measles_mmeasles mtodes_masern_mMasern > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_measles_fmeasles ftodes_masern_wMasern > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_typhus_mtyphus mtodes_flecktyphus_mFlecktyphus > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_typhus_ftyphus ftodes_flecktyphus_wFlecktyphus > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_typhoid_mtyphoid mtodes_bauchtyphus_mBauchtyphus > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_typhoid_ftyphoid ftodes_bauchtyphus_wBauchtyphus > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_dysentery_mdysentery mtodes_ruhr_mRuhr > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_dysentery_fdysentery ftodes_ruhr_wRuhr > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_cholera_mcholera mtodes_cholera_asiatica_mCholera asiatica > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_cholera_fcholera ftodes_cholera_asiatica_wCholera asiatica > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_native_cholera_infantum_children_mnative cholera infantum children mtodes_einheimischer_brechdurchfall_bei_kindern_mEinheimischer Brechdurchfall bei Kindern > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_native_cholera_infantum_children_fnative cholera infantum children ftodes_einheimischer_brechdurchfall_bei_kindern_wEinheimischer Brechdurchfall bei Kindern > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_native_cholera_infantum_except_childhood_mnative cholera infantum except childhood mtodes_einheimischer_brechdurchfall_ausser_dem_kindesalter_mEinheimischer Brechdurchfall außer dem Kindesalter > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_native_cholera_infantum_except_childhood_fnative cholera infantum except childhood ftodes_einheimischer_brechdurchfall_ausser_dem_kindesalter_wEinheimischer Brechdurchfall außer dem Kindesalter > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_puerperal_fever_mpuerperal fever mtodes_kindbettfieber_mKindbettfieber > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_puerperal_fever_fpuerperal fever ftodes_kindbettfieber_wKindbettfieber > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_wound_infections_mwound infections mtodes_wundinfektionskrankheiten_mWundinfektionskrankheiten > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_wound_infections_fwound infections ftodes_wundinfektionskrankheiten_wWundinfektionskrankheiten > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_other_infectious_diseases_mother infectious diseases mtodes_andere_infektionskrankheiten_mAndere Infektionskrankheiten > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_other_infectious_diseases_fother infectious diseases ftodes_andere_infektionskrankheiten_wAndere Infektionskrankheiten > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_communicable_animal_diseases_mcommunicable animal diseases mtodes_ubertragbare_tierkrankheiten_mÜbertragbare Tierkrankheiten > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_communicable_animal_diseases_fcommunicable animal diseases ftodes_ubertragbare_tierkrankheiten_wÜbertragbare Tierkrankheiten > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_stroke_mstroke mtodes_gehirnschlagfluss_mGehirnschlagfluß > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_stroke_fstroke ftodes_gehirnschlagfluss_wGehirnschlagfluß > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_organic_heart_disease_diseases_vascular_morganic heart disease diseases vascular mtodes_organische_herzkrankheiten_und_krankheiten_der_blutgefasse_mOrganische Herzkrankheiten und Krankheiten der Blutgefäße > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_organic_heart_disease_diseases_vascular_forganic heart disease diseases vascular ftodes_organische_herzkrankheiten_und_krankheiten_der_blutgefasse_wOrganische Herzkrankheiten und Krankheiten der Blutgefäße > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_malignant_neoplasms_mmalignant neoplasms mtodes_bosartige_neubildungen_mBösartige Neubildungen > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_malignant_neoplasms_fmalignant neoplasms ftodes_bosartige_neubildungen_wBösartige Neubildungen > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_other_natural_cause_of_death_mother natural cause of death mtodes_sonstige_naturliche_todesursachen_mSonstige natürliche Todesursachen > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_other_natural_cause_of_death_fother natural cause of death ftodes_sonstige_naturliche_todesursachen_wSonstige natürliche Todesursachen > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_accidental_injuries_maccidental injuries mtodes_zufallige_beschadigungen_mZufällige Beschädigungen > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_accidental_injuries_faccidental injuries ftodes_zufallige_beschadigungen_wZufällige Beschädigungen > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_suicide_msuicide mtodes_selbstmord_mSelbstmord > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_suicide_fsuicide ftodes_selbstmord_wSelbstmord > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_murder_manslaughter_mmurder manslaughter mtodes_mord_und_totschlag_mMord und Totschlag > m.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisCause of deathdeath_murder_manslaughter_fmurder manslaughter ftodes_mord_und_totschlag_wMord und Totschlag > w.Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx
cisJustice — district-court caseloadjust_dunning_issued_payment_ordersdunning issued payment ordersjust_bg_mahnsachen_erlassene_zahlungsbefehleMahnsachen > Erlassene ZahlungsbefehleJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_dunning_raised_accepted_objectionsdunning raised accepted objectionsjust_bg_mahnsachen_erhobene_und_angenommene_widersprucheMahnsachen > Erhobene und angenommene WidersprücheJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_order_procedure_issued_payment_orders_objections_not_raisedorder procedure issued payment orders objections not raisedjust_bg_mandatsverfahren_erlassene_zahlungsauftrage_gegen_welche_eindungen_nicht_erhoben_wurdenMandatsverfahren > erlassene Zahlungsaufträge, gegen welche Eindungen nicht erhoben wurdenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_lawsuits_prior_year_pending_carried_oversmall claims lawsuits prior year pending carried overjust_bg_iii_bagatellverfahren_klagen_vom_vorjahre_anhangig_ubernommenIII. Bagatellverfahren > Klagen > vom Vorjahre anhängig übernommenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_lawsuits_new_accruedsmall claims lawsuits new accruedjust_bg_iii_bagatellverfahren_klagen_neu_zugewachsenIII. Bagatellverfahren > Klagen > neu zugewachsenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_lawsuits_settledsmall claims lawsuits settledjust_bg_iii_bagatellverfahren_klagen_erledigtIII. Bagatellverfahren > Klagen > erledigtJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_lawsuits_pending_remainingsmall claims lawsuits pending remainingjust_bg_iii_bagatellverfahren_klagen_anhangig_verbliebenIII. Bagatellverfahren > Klagen > anhängig verbliebenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_disposition_final_judgment_grounds_default_waiver_acknowledgmentsmall claims disposition final judgment grounds default waiver acknowledgmentjust_bg_iii_bagatellverfahren_erledigungsart_durch_endurteil_auf_grund_versaumnis_verzicht_anerkenntnnisIII. Bagatellverfahren > Erledigungsart > durch Endurteil auf Grund Versäumnis, Verzicht, AnerkenntnnisJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_disposition_other_final_judgmentsmall claims disposition other final judgmentjust_bg_iii_bagatellverfahren_erledigungsart_durch_anderes_endurteilIII. Bagatellverfahren > Erledigungsart > durch anderes EndurteilJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_disposition_settlementsmall claims disposition settlementjust_bg_iii_bagatellverfahren_erledigungsart_durch_vergleichIII. Bagatellverfahren > Erledigungsart > durch VergleichJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_disposition_other_mannersmall claims disposition other mannerjust_bg_iii_bagatellverfahren_erledigungsart_auf_andere_weiseIII. Bagatellverfahren > Erledigungsart > auf andere WeiseJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_duration_proceedings_falls_8_to_10_to_1_monthsmall claims duration proceedings falls 8 to 10 to 1 monthjust_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_bis_1_monatIII. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > bis 1 MonatJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_duration_proceedings_falls_8_to_10_over_1_to_3_monthssmall claims duration proceedings falls 8 to 10 over 1 to 3 monthsjust_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_uber_1_bis_3_monateIII. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > über 1 bis 3 MonateJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_duration_proceedings_falls_8_to_10_over_3_to_6_monthssmall claims duration proceedings falls 8 to 10 over 3 to 6 monthsjust_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_uber_3_bis_6_monateIII. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > über 3 bis 6 MonateJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_small_claims_duration_proceedings_falls_8_to_10_over_6_monthssmall claims duration proceedings falls 8 to 10 over 6 monthsjust_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_uber_6_monateIII. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > über 6 MonateJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_lawsuits_prior_year_pending_carried_overother trials lawsuits prior year pending carried overjust_bg_andere_prozesse_klagen_vom_vorjahre_anhangig_ubernommenAndere Prozesse > Klagen > vom Vorjahre anhängig übernommenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_lawsuits_new_accrued_money_monetary_value_over_100_to_1000other trials lawsuits new accrued money monetary value over 100 to 1000just_bg_andere_prozesse_klagen_neu_zugewachsen_geld_oder_geldeswert_uber_100_k_bis_1000_kAndere Prozesse > Klagen > neu zugewachsen > Geld oder Geldeswert über 100 K bis 1000 KJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_lawsuits_new_accrued_possession_disputesother trials lawsuits new accrued possession disputesjust_bg_andere_prozesse_klagen_neu_zugewachsen_besitzstorungsstreitigkeitenAndere Prozesse > Klagen > neu zugewachsen > BesitzstörungsstreitigkeitenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_lawsuits_new_accrued_other_disputesother trials lawsuits new accrued other disputesjust_bg_andere_prozesse_klagen_neu_zugewachsen_andere_streitigkeitenAndere Prozesse > Klagen > neu zugewachsen > andere StreitigkeitenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_lawsuits_settledother trials lawsuits settledjust_bg_andere_prozesse_klagen_erledigtAndere Prozesse > Klagen > erledigtJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_lawsuits_pending_remainingother trials lawsuits pending remainingjust_bg_andere_prozesse_klagen_anhangig_verbliebenAndere Prozesse > Klagen > anhängig verbliebenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_disposition_final_judgment_grounds_default_waiver_acknowledgmentother trials disposition final judgment grounds default waiver acknowledgmentjust_bg_andere_prozesse_erledigungsart_durch_endurteil_auf_grund_von_versaumnis_verzicht_anerkenntnisAndere Prozesse > Erledigungsart > durch Endurteil auf Grund von Versäumnis, Verzicht, AnerkenntnisJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_disposition_other_final_judgment_final_orderother trials disposition other final judgment final orderjust_bg_andere_prozesse_erledigungsart_durch_anderes_endurteil_endbeschlussAndere Prozesse > Erledigungsart > durch anderes Endurteil (Endbeschluß)Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_disposition_settlementother trials disposition settlementjust_bg_andere_prozesse_erledigungsart_durch_vergleichAndere Prozesse > Erledigungsart > durch VergleichJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_disposition_other_mannerother trials disposition other mannerjust_bg_andere_prozesse_erledigungsart_auf_andere_weiseAndere Prozesse > Erledigungsart > auf andere WeiseJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_cases_proceedings_suspendedother trials cases proceedings suspendedjust_bg_andere_prozesse_falle_in_welchen_das_verfahren_ruhteAndere Prozesse > Fälle, in welchen das Verfahren ruhteJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_evidentiary_orders_final_examination_partiesother trials evidentiary orders final examination partiesjust_bg_andere_prozesse_beweisbeschlusse_auf_endliche_vernehmung_der_parteienAndere Prozesse > Beweisbeschlüsse auf endliche Vernehmung der ParteienJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_other_evidentiary_ordersother trials other evidentiary ordersjust_bg_andere_prozesse_andere_beweisbeschlusseAndere Prozesse > andere BeweisbeschlüsseJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_duration_proceedings_falls_to_to_1_monthother trials duration proceedings falls to 1 monthjust_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_bis_1_monatAndere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > bis 1 MonatJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_duration_proceedings_falls_to_over_1_to_3_monthsother trials duration proceedings falls to over 1 to 3 monthsjust_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_1_bis_3_monateAndere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 1 bis 3 MonateJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_duration_proceedings_falls_to_over_3_to_6_monthsother trials duration proceedings falls to over 3 to 6 monthsjust_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_3_bis_6_monateAndere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 3 bis 6 MonateJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_duration_proceedings_falls_to_over_6_months_to_1_yearother trials duration proceedings falls to over 6 months to 1 yearjust_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_6_monate_bis_1_jahrAndere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 6 Monate bis 1 JahrJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_other_trials_duration_proceedings_falls_to_over_1_yearother trials duration proceedings falls to over 1 yearjust_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_1_jahrAndere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 1 JahrJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_total_casesenforcement total casesjust_bg_zwangsvollstreckungen_gesamtfallZwangsvollstreckungen > GesamtfallJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_executions_recovery_fines_feesenforcement executions recovery fines feesjust_bg_zwangsvollstreckungen_exekutionen_zur_hereinbringung_von_geldstrafen_und_gebuhrenZwangsvollstreckungen > Exekutionen zur Hereinbringung von Geldstrafen und GebührenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_compulsory_lien_creationenforcement immovable assets compulsory lien creationjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsweise_pfandrechtsbegrundungZwangsvollstreckungen > auf unbeweghches Vermögen > durch zwangsweise PfandrechtsbegründungJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_forced_auction_prior_year_pending_carried_overenforcement immovable assets forced auction prior year pending carried overjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_vom_vorjahre_anhangig_ubernommenZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > vom Vorjahre anhängig übernommenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_forced_auction_new_accrualsenforcement immovable assets forced auction new accrualsjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_neu_zuwachsenZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > neu zuwachsenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_forced_auction_concluded_issuance_distribution_orderenforcement immovable assets forced auction concluded issuance distribution orderjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_beendet_durch_erlassung_verteilungsbeschlussesZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > beendet durch Erlassung VerteilungsbeschlussesJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_forced_auction_concluded_discontinuationenforcement immovable assets forced auction concluded discontinuationjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_beendet_durch_einstellungZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > beendet durch EinstellungJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_forced_auction_pending_remainingenforcement immovable assets forced auction pending remainingjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_anhangig_verbliebenZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > anhängig verbliebenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_forced_auction_cases_discontinuation_auction_proceedings_6_monthsenforcement immovable assets forced auction cases discontinuation auction proceedings 6 monthsjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_falle_der_einstellung_des_versteigerungsverfahrens_fur_wenigstens_6_monateZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > Fälle der Einstellung des Versteigerungsverfahrens für wenigstens 6 MonateJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_compulsory_administration_prior_year_carried_overenforcement immovable assets compulsory administration prior year carried overjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_vom_vorjahre_ubernommenZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > vom Vorjahre übernommenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_compulsory_administration_new_accruedenforcement immovable assets compulsory administration new accruedjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_neu_zugewachsenZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > neu zugewachsenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_compulsory_administration_concludedenforcement immovable assets compulsory administration concludedjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_beendetZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > beendetJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_immovable_assets_compulsory_administration_pending_remainingenforcement immovable assets compulsory administration pending remainingjust_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_anhangig_verbliebenZwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > anhängig verbliebenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_physical_mattersenforcement movable assets physical mattersjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_korperliche_sachenZwangsvollstreckungen > auf bewegliches Vermögen > körperliche SachenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_monetary_claimsenforcement movable assets monetary claimsjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_geldforderungenZwangsvollstreckungen > auf bewegliches Vermögen > GeldforderungenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_claims_surrender_performance_physical_mattersenforcement movable assets claims surrender performance physical mattersjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_anspruche_auf_herausgabe_und_leistung_korperlicher_sachenZwangsvollstreckungen > auf bewegliches Vermögen > Ansprüche auf Herausgabe und Leistung körperlicher SachenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_other_property_rights_compulsory_administration_prior_year_carried_overenforcement movable assets other property rights compulsory administration prior year carried overjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_vom_vorjahre_ubernommenZwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > vom Vorjahre übernommenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_other_property_rights_compulsory_administration_new_accruedenforcement movable assets other property rights compulsory administration new accruedjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_neu_zugewachsenZwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > neu zugewachsenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_other_property_rights_compulsory_administration_concludedenforcement movable assets other property rights compulsory administration concludedjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_beendetZwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > beendetJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_other_property_rights_compulsory_administration_pending_remainingenforcement movable assets other property rights compulsory administration pending remainingjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_anhangig_verbliebenZwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > anhängig verbliebenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_other_property_rights_other_enforcement_meansenforcement movable assets other property rights other enforcement meansjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_andere_exekutionsmittelZwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch andere ExekutionsmittelJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_conducted_seizure_physical_mattersenforcement movable assets conducted seizure physical mattersjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_vorgenommene_pfandung_korperlicher_sachenZwangsvollstreckungen > auf bewegliches Vermögen > vorgenommene Pfändung körperlicher SachenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_movable_assets_conducted_forced_salesenforcement movable assets conducted forced salesjust_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_vorgenommene_zwangsverkaufeZwangsvollstreckungen > auf bewegliches Vermögen > vorgenommene ZwangsverkäufeJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_obtainment_acts_omissionsenforcement obtainment acts omissionsjust_bg_zwangsvollstreckungen_zur_erwirkung_von_handlungen_und_unterlassungenZwangsvollstreckungen > zur Erwirkung von Handlungen und UnterlassungenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_security_monetary_claimsenforcement security monetary claimsjust_bg_zwangsvollstreckungen_zur_sicherung_von_geldforderungenZwangsvollstreckungen > zur Sicherung von GeldforderungenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_enforcement_eviction_tenancy_mattersenforcement eviction tenancy mattersjust_bg_zwangsvollstreckungen_zur_raumung_von_bestandsachenZwangsvollstreckungen > zur Räumung von BestandsachenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_prior_year_pending_carried_overprobate prior year pending carried overjust_bg_verlassenschaftsabhandlungen_vom_vorjahre_anhangig_ubernommenVerlassenschaftsabhandlungen > vom Vorjahre anhängig übernommenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_concludedprobate concludedjust_bg_verlassenschaftsabhandlungen_beendetVerlassenschaftsabhandlungen > beendetJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_pending_remaining_judicial_proceedings_accrualsprobate pending remaining judicial proceedings accrualsjust_bg_verlassenschaftsabhandlungen_anhangig_verblieben_gerichtliches_verfahren_im_zugeVerlassenschaftsabhandlungen > anhängig verblieben > gerichtliches Verfahren im ZugeJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_pending_remaining_judicial_steps_concluded_estate_award_fee_adjustment_dependentprobate pending remaining judicial steps concluded estate award fee adjustment dependentjust_bg_verlassenschaftsabhandlungen_anhangig_verblieben_die_gerichtlichen_schritte_beendet_und_die_einantwortung_nur_mehr_von_der_gebuhrenberichtigung_abhangigVerlassenschaftsabhandlungen > anhängig verblieben > die gerichtlichen Schritte beendet und die Einantwortung nur mehr von der Gebührenberichtigung abhängigJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_disposition_disposed_concerning_outgoing_assetsprobate disposition disposed concerning outgoing assetsjust_bg_verlassenschaftsabhandlungen_beendigungsart_abgetan_wegen_abgang_eines_vermogensVerlassenschaftsabhandlungen > Beendigungsart > abgetan wegen Abgang eines VermögensJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_disposition_de_jure_credits_estate_awardprobate disposition de jure credits estate awardjust_bg_verlassenschaftsabhandlungen_beendigungsart_durch_jure_crediti_einantwortungVerlassenschaftsabhandlungen > Beendigungsart > durch jure crediti EinantwortungJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_disposition_estate_award_heirprobate disposition estate award heirjust_bg_verlassenschaftsabhandlungen_beendigungsart_durch_einantwortung_an_die_erbeVerlassenschaftsabhandlungen > Beendigungsart > durch Einantwortung an die ErbeJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_disposition_handover_escheat_estate_treasuryprobate disposition handover escheat estate treasuryjust_bg_verlassenschaftsabhandlungen_beendigungsart_durch_ubergabe_des_kaduken_nachlasses_an_den_fiskusVerlassenschaftsabhandlungen > Beendigungsart > durch Übergabe des kaduken Nachlasses an den FiskusJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_disposition_other_mannerprobate disposition other mannerjust_bg_verlassenschaftsabhandlungen_beendigungsart_auf_andere_weiseVerlassenschaftsabhandlungen > Beendigungsart > auf andere WeiseJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_guardianships_prior_year_pending_carried_overguardianships prior year pending carried overjust_bg_vormundschaften_vom_vorjahre_anhangig_ubernommenVormundschaften > vom Vorjahre anhängig übernommenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_guardianships_new_accruedguardianships new accruedjust_bg_vormundschaften_neu_zugewachsenVormundschaften > neu zugewachsenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_guardianships_concludedguardianships concludedjust_bg_vormundschaften_beendetVormundschaften > beendetJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_guardianships_pending_remainingguardianships pending remainingjust_bg_vormundschaften_anhangig_verbliebenVormundschaften > anhängig verbliebenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_curatorships_prior_year_pending_carried_overcuratorships prior year pending carried overjust_bg_kuratelen_vom_vorjahre_anhangig_ubernom_menKuratelen > vom Vorjahre anhängig übernom- menJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_curatorships_new_accrued_concerning_insanity_idiocy_concerning_incapacity_rights_self_preservecuratorships new accrued concerning insanity idiocy concerning incapacity rights self preservejust_bg_kuratelen_neu_zugewachsen_wegen_wahn_blodsinns_und_wegen_unfahigkeit_die_rechte_selbst_zu_wahrenKuratelen > neu zugewachsen > wegen Wahn- Blödsinns und wegen Unfähigkeit, die Rechte selbst zu wahrenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_curatorships_new_accrued_concerning_prodigalitycuratorships new accrued concerning prodigalityjust_bg_kuratelen_neu_zugewachsen_wegen_verschwendungKuratelen > neu zugewachsen > wegen VerschwendungJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_curatorships_new_accrued_over_minors_under_paternal_violencecuratorships new accrued over minors under paternal violencejust_bg_kuratelen_neu_zugewachsen_uber_minderjahrige_unter_vaterlicher_gewaltKuratelen > neu zugewachsen > über Minderjährige unter väterlicher GewaltJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_curatorships_new_accrued_concerning_extension_paternal_violencecuratorships new accrued concerning extension paternal violencejust_bg_kuratelen_neu_zugewachsen_wegen_verlangerung_der_vaterlichen_gewaltKuratelen > neu zugewachsen > wegen Verlängerung der väterlichen GewaltJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_curatorships_concludedcuratorships concludedjust_bg_kuratelen_beendetKuratelen > beendetJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_curatorships_pending_remainingcuratorships pending remainingjust_bg_kuratelen_anhangig_verbliebenKuratelen > anhängig verbliebenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_land_registryland registryjust_bg_grundbuchssachenGrundbuchssachenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_termination_proceedings_tenancy_matters_terminationstermination proceedings tenancy matters terminationsjust_bg_kundigungsverfahren_in_bestandangelegenheiten_kundigungenKündigungsverfahren in Bestandangelegenheiten > KündigungenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_termination_proceedings_tenancy_matters_objectionstermination proceedings tenancy matters objectionsjust_bg_kundigungsverfahren_in_bestandangelegenheiten_heinwendungen_dagegenKündigungsverfahren in Bestandangelegenheiten > HEinwendungen dagegenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_matters_general_registermatters general registerjust_bg_sachen_des_allgemeinen_registersSachen des allgemeinen RegistersJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_commissioned_official_acts_outside_office_location_judicial_officialscommissioned official acts outside office location judicial officialsjust_bg_kommissionelle_amtshandlungen_ausserhalb_des_amtsortes_der_richterlichen_beamtenKommissionelle Amtshandlungen außerhalb des Amtsortes > der richterlichen BeamtenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_commissioned_official_acts_outside_office_location_clerkscommissioned official acts outside office location clerksjust_bg_kommissionelle_amtshandlungen_ausserhalb_des_amtsortes_der_kanzleibeamtenKommissionelle Amtshandlungen außerhalb des Amtsortes > der KanzleibeamtenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_summonses_conciliation_attempts_by_433_0summonses conciliation attempts by 433 0just_bg_vorladungen_zum_vergleichsversuche_nach_433_z_p_0Vorladungen zum Vergleichsversuche nach § 433 Z. P. 0.Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_new_accruedprobate new accruedjust_bg_verlassenschaftsabhandlungen_neu_zugewachsenVerlassenschaftsabhandlungen > neu zugewachsenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_probate_disposition_cededprobate disposition cededjust_bg_verlassenschaftsabhandlungen_beendigungsart_abgetretenVerlassenschaftsabhandlungen > Beendigungsart > abgetretenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_legal_aidlegal aidjust_bg_rechtshufesachenRechtshüfesachenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_legalizationslegalizationsjust_bg_legalisierungenLegalisierungenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — district-court caseloadjust_administrativeadministrativejust_bg_verwaltungssachenVerwaltungssachenJustizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx
cisJustice — court personneljust_personnel_areaareajust_pers_flachenFlachenJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_municipalitiesmunicipalitiesjust_pers_ortsgemeindenOrtsgemeindenJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_localitieslocalitiesjust_pers_ortschaftenOrtschaftenJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_civilian_populationcivilian populationjust_pers_zivilbevolkerungZivilbevolkerungJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_pop_totalpop totaljust_pers_gesamtbevolkerungGesamtbevolkerungJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_pop_total_attributable_inhabitants_1_km2pop total attributable inhabitants 1 km2just_pers_von_der_gesamtbevolkerung_entfallen_einwohner_auf_1_km2Von der Gesamtbevölkerung entfallen Einwohner auf 1 km2Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_judicial_officialsjudicial officialsjust_pers_richterliche_beamteRichterliche BeamteJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_clerksclerksjust_pers_kanzleibeamteKanzleibeamteJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_subordinate_officialssubordinate officialsjust_pers_unterbeamteUnterbeamteJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_office_servantsoffice servantsjust_pers_amtsdienerAmtsdienerJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_clerical_staffclerical staffjust_pers_kanzleihilfspersonalKanzleiHilfspersonalJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_auxiliary_servantsauxiliary servantsjust_pers_aushilfsdienerAushilfsdienerJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_advocatesadvocatesjust_pers_advokatenAdvokatenJustizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisJustice — court personneljust_personnel_notaries_established_stellennotaries established stellenjust_pers_notare_systemisierte_stellenNotare (systemisierte Stellen)Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_liabilities_deposits_crownsliabilities deposits crownsspar_bil_passiva_einlagenstand_kronenPassiva > Einlagenstand > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_liabilities_reserve_fund_crownsliabilities reserve fund crownsspar_bil_passiva_reservefonds_kronenPassiva > Reservefonds > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_liabilities_special_reserve_fund_crownsliabilities special reserve fund crownsspar_bil_passiva_spezialreservefonds_kronenPassiva > Spezialreservefonds > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_liabilities_other_crownsliabilities other crownsspar_bil_passiva_sonstige_kronenPassiva > Sonstige > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_liabilities_included_pension_fund_a_reserve_fund_b_special_reserve_fund_c_other_liabilities_crownsliabilities included pension fund a reserve fund b special reserve fund c other liabilities crownsspar_bil_passiva_es_sind_enthalten_pensionsfonds_in_a_reservedonds_b_spezialreservefonds_c_sonstiger_passiva_kronenPassiva > Es sind enthalten > Pensionsfonds > in a) Reservedonds, b) Spezialreservefonds, c) sonstiger Passiva > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_liabilities_included_endowment_advance_fund_a_reserve_fund_b_special_reserve_fund_c_other_liabilities_crownsliabilities included endowment advance fund a reserve fund b special reserve fund c other liabilities crownsspar_bil_passiva_es_sind_enthalten_dotation_der_vorschusskassa_in_a_reservedonds_b_spezialreservefonds_c_sonstiger_passiva_kronenPassiva > Es sind enthalten > Dotation der Vorschußkassa > in a) Reservedonds, b) Spezialreservefonds, c) sonstiger Passiva > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_liabilities_sum_crownsliabilities sum crownsspar_bil_passiva_summe_kronenPassiva > Summe > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_mortgage_loans_crownsassets mortgage loans crownsspar_bil_aktiva_hypothekardarlehen_kronenAktiva > Hypothekardarlehen > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_loans_municipalities_districts_real_mortgage_crownsassets loans municipalities districts real mortgage crownsspar_bil_aktiva_darlehen_an_gemeinden_bezirke_usw_gegen_real_hypothek_kronenAktiva > Darlehen an Gemeinden, Bezirke, usw. > gegen Real-hypothek > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_loans_municipalities_districts_without_crownsassets loans municipalities districts without crownsspar_bil_aktiva_darlehen_an_gemeinden_bezirke_usw_ohne_dieselbe_kronenAktiva > Darlehen an Gemeinden, Bezirke, usw. > ohne dieselbe > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_bills_on_hand_crownsassets bills on hand crownsspar_bil_aktiva_wechselvorrat_kronenAktiva > Wechselvorrat > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_advances_securities_pledges_crownsassets advances securities pledges crownsspar_bil_aktiva_vorschosse_auf_wertpapiere_und_pfander_kronenAktiva > Vorschósse auf Wertpapiere und Pfänder > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_advances_personal_credit_crownsassets advances personal credit crownsspar_bil_aktiva_vorschusse_auf_personalkredit_kronenAktiva > Vorschüsse auf Personalkredit > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_market_value_securities_crownsassets market value securities crownsspar_bil_aktiva_kurswert_der_wertpapiere_kronenAktiva > Kurswert der Wertpapiere > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_value_own_real_estate_crownsassets value own real estate crownsspar_bil_aktiva_wert_der_eigenen_realitaten_kronenAktiva > Wert der eigenen Realitäten > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_temporary_annexes_credit_institutions_crownsassets temporary annexes credit institutions crownsspar_bil_aktiva_zeitliche_anlagen_bei_kreditinstituten_etc_kronenAktiva > Zeitliche Anlagen bei Kreditinstituten etc. > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_cash_crownsassets cash crownsspar_bil_aktiva_kassabarschaft_kronenAktiva > Kassabarschaft > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_other_crownsassets other crownsspar_bil_aktiva_sonstige_kronenAktiva > Sonstige > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (balance sheets)savings_balance_assets_sum_crownsassets sum crownsspar_bil_aktiva_summe_kronenAktiva > Summe > KronenStatistik der Sparkassen Bilanzen 1910 District courts 934.xlsx
cisSavings banks (passbooks)savings_books_in_circulation_start_of_yearsavings books in circulation at the start of the yearspar_buch_zahl_der_sparkassebucher_einleger_interessenten_im_anfang_des_jahres_im_umlaufZahl der Sparkassebücher (Einleger, Interessenten) > im Anfang des Jahres im UmlaufStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisSavings banks (passbooks)savings_books_number_savings_books_depositors_stakeholders_year_new_issuednumber savings books depositors stakeholders year new issuedspar_buch_zahl_der_sparkassebucher_einleger_interessenten_im_laufe_des_jahres_neu_ausgegebeneZahl der Sparkassebücher (Einleger, Interessenten) > im Laufe des Jahres > neu ausgegebeneStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisSavings banks (passbooks)savings_books_number_savings_books_depositors_stakeholders_year_repaidnumber savings books depositors stakeholders year repaidspar_buch_zahl_der_sparkassebucher_einleger_interessenten_im_laufe_des_jahres_getilgteZahl der Sparkassebücher (Einleger, Interessenten) > im Laufe des Jahres > getilgteStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisSavings banks (passbooks)savings_books_in_circulation_end_of_yearsavings books in circulation at the end of the yearspar_buch_zahl_der_sparkassebucher_einleger_interessenten_zu_ende_des_jahres_im_umlaufZahl der Sparkassebücher (Einleger, Interessenten) > zu Ende des Jahres im UmlaufStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisSavings banks (passbooks)savings_books_partial_deposits_itemspartial deposits during the year, itemsspar_buch_ausserdem_im_laufe_des_jahres_erfolgte_teil_ein_zahlungen_in_postenAußerdem im Laufe des Jahres erfolgte > Teil-Ein- > zahlungen in PostenStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisSavings banks (passbooks)savings_books_partial_withdrawals_itemspartial withdrawals during the year, itemsspar_buch_ausserdem_im_laufe_des_jahres_erfolgte_teil_ruck_zahlungen_in_postenAußerdem im Laufe des Jahres erfolgte > Teil-Rück- > zahlungen in PostenStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisSavings banks (passbooks)savings_books_sum_deposits_itemssum of deposits, itemsspar_buch_summe_der_ein_zahlungen_in_postenSumme der > Ein- > zahlungen in PostenStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisSavings banks (passbooks)savings_books_sum_withdrawals_itemssum of withdrawals, itemsspar_buch_summe_der_ruck_zahlungen_in_postenSumme der > Rück- > zahlungen in PostenStatistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx
cisLivestock (large)livestock_large_owners_horses_occupation_members_agricultureowners horses occupation members agriculturevieh_gross_besitzer_von_pferden_berufszugehorige_landwirtschaftBesitzer von Pferden > Berufszugehörige > LandwirtschaftViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_horses_occupation_members_industryowners horses occupation members industryvieh_gross_besitzer_von_pferden_berufszugehorige_industrieBesitzer von Pferden > Berufszugehörige > IndustrieViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_horses_occupation_members_commerceowners horses occupation members commercevieh_gross_besitzer_von_pferden_berufszugehorige_handelBesitzer von Pferden > Berufszugehörige > HandelViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_horses_occupation_members_active_militaryowners horses occupation members active militaryvieh_gross_besitzer_von_pferden_berufszugehorige_aktives_militarBesitzer von Pferden > Berufszugehörige > aktives MilitärViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_horses_occupation_members_other_occupationsowners horses occupation members other occupationsvieh_gross_besitzer_von_pferden_berufszugehorige_andere_berufeBesitzer von Pferden > Berufszugehörige > andere BerufeViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_horses_legal_personsowners horses legal personsvieh_gross_besitzer_von_pferden_jurist_personenBesitzer von Pferden > jurist. PersonenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_horses_totalowners horses totalvieh_gross_besitzer_von_pferden_zusammenBesitzer von Pferden > zusammenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_solipeds_occupation_members_agricultureowners solipeds occupation members agriculturevieh_gross_besitzer_von_einhufern_berufszugehorige_landwirtschaftBesitzer von Einhufern > Berufszugehörige > LandwirtschaftViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_solipeds_occupation_members_other_occupationsowners solipeds occupation members other occupationsvieh_gross_besitzer_von_einhufern_berufszugehorige_andere_berufeBesitzer von Einhufern > Berufszugehörige > andere BerufeViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_solipeds_legal_personsowners solipeds legal personsvieh_gross_besitzer_von_einhufern_jurist_personenBesitzer von Einhufern > jurist. PersonenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_solipeds_totalowners solipeds totalvieh_gross_besitzer_von_einhufern_zusammenBesitzer von Einhufern > zusammenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_cattle_occupation_members_agricultureowners cattle occupation members agriculturevieh_gross_besitzer_von_rindern_berufszugehorige_landwirtschaftBesitzer von Rindern > Berufszugehörige > LandwirtschaftViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_cattle_occupation_members_industryowners cattle occupation members industryvieh_gross_besitzer_von_rindern_berufszugehorige_industrieBesitzer von Rindern > Berufszugehörige > IndustrieViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_cattle_occupation_members_commerceowners cattle occupation members commercevieh_gross_besitzer_von_rindern_berufszugehorige_handelBesitzer von Rindern > Berufszugehörige > HandelViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_cattle_occupation_members_other_occupationsowners cattle occupation members other occupationsvieh_gross_besitzer_von_rindern_berufszugehorige_andere_berufeBesitzer von Rindern > Berufszugehörige > andere BerufeViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_cattle_legal_personsowners cattle legal personsvieh_gross_besitzer_von_rindern_jurist_personenBesitzer von Rindern > jurist. PersonenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_owners_cattle_totalowners cattle totalvieh_gross_besitzer_von_rindern_zusammenBesitzer von Rindern > zusammenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_horses_agricultureagricultural landowners horses agriculturevieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_landwirtschaftLandwirtschaftliche Grundbesitzer mit Pferden > LandwirtschaftViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_horses_other_occupationsagricultural landowners horses other occupationsvieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_andere_berufeLandwirtschaftliche Grundbesitzer mit Pferden > andere BerufeViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_horses_legal_personsagricultural landowners horses legal personsvieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_jurist_personenLandwirtschaftliche Grundbesitzer mit Pferden > jurist. PersonenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_horses_totalagricultural landowners horses totalvieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_zusammenLandwirtschaftliche Grundbesitzer mit Pferden > zusammenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_other_solipeds_agricultureagricultural landowners other solipeds agriculturevieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_landwirtschaftLandwirtschaftliche Grundbesitzer mit anderen Einhufern > LandwirtschaftViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_other_solipeds_other_occupationsagricultural landowners other solipeds other occupationsvieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_andere_berufeLandwirtschaftliche Grundbesitzer mit anderen Einhufern > andere BerufeViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_other_solipeds_legal_personsagricultural landowners other solipeds legal personsvieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_jurist_personenLandwirtschaftliche Grundbesitzer mit anderen Einhufern > jurist. PersonenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_other_solipeds_totalagricultural landowners other solipeds totalvieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_zusammenLandwirtschaftliche Grundbesitzer mit anderen Einhufern > zusammenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_cattle_agricultureagricultural landowners cattle agriculturevieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_landwirtschaftLandwirtschaftliche Grundbesitzer mit Rindern > LandwirtschaftViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_cattle_other_occupationsagricultural landowners cattle other occupationsvieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_andere_berufeLandwirtschaftliche Grundbesitzer mit Rindern > andere BerufeViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_cattle_legal_personsagricultural landowners cattle legal personsvieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_jurist_personenLandwirtschaftliche Grundbesitzer mit Rindern > jurist. PersonenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (large)livestock_large_agricultural_landowners_cattle_totalagricultural landowners cattle totalvieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_zusammenLandwirtschaftliche Grundbesitzer mit Rindern > zusammenViehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_goats_occupation_members_agricultureowners goats occupation members agriculturevieh_klein_besitzer_von_ziegen_berufszugehorige_landwirtschaftBesitzer von Ziegen > Berufszugehörige > LandwirtschaftViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_goats_occupation_members_industryowners goats occupation members industryvieh_klein_besitzer_von_ziegen_berufszugehorige_industrieBesitzer von Ziegen > Berufszugehörige > IndustrieViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_goats_occupation_members_commerceowners goats occupation members commercevieh_klein_besitzer_von_ziegen_berufszugehorige_handelBesitzer von Ziegen > Berufszugehörige > HandelViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_goats_occupation_members_other_occupationsowners goats occupation members other occupationsvieh_klein_besitzer_von_ziegen_berufszugehorige_andere_berufeBesitzer von Ziegen > Berufszugehörige > andere BerufeViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_goats_legal_personsowners goats legal personsvieh_klein_besitzer_von_ziegen_jurist_personenBesitzer von Ziegen > jurist. PersonenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_goats_totalowners goats totalvieh_klein_besitzer_von_ziegen_zusammenBesitzer von Ziegen > zusammenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_sheep_occupation_members_agricultureowners sheep occupation members agriculturevieh_klein_besitzer_von_schafen_berufszugehorige_landwirtschaftBesitzer von Schafen > Berufszugehörige > LandwirtschaftViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_sheep_other_occupationsowners sheep other occupationsvieh_klein_besitzer_von_schafen_andere_berufeBesitzer von Schafen > andere BerufeViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_sheep_legal_personsowners sheep legal personsvieh_klein_besitzer_von_schafen_jurist_personenBesitzer von Schafen > jurist. PersonenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_sheep_totalowners sheep totalvieh_klein_besitzer_von_schafen_zusammenBesitzer von Schafen > zusammenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_pigs_occupation_members_agricultureowners pigs occupation members agriculturevieh_klein_besitzer_von_schweinen_berufszugehorige_landwirtschaftBesitzer von Schweinen > Berufszugehörige > LandwirtschaftViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_pigs_occupation_members_industryowners pigs occupation members industryvieh_klein_besitzer_von_schweinen_berufszugehorige_industrieBesitzer von Schweinen > Berufszugehörige > IndustrieViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_pigs_occupation_members_commerceowners pigs occupation members commercevieh_klein_besitzer_von_schweinen_berufszugehorige_handelBesitzer von Schweinen > Berufszugehörige > HandelViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_pigs_occupation_members_other_occupationsowners pigs occupation members other occupationsvieh_klein_besitzer_von_schweinen_berufszugehorige_andere_berufeBesitzer von Schweinen > Berufszugehörige > andere BerufeViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_pigs_legal_personsowners pigs legal personsvieh_klein_besitzer_von_schweinen_jurist_personenBesitzer von Schweinen > jurist. PersonenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_owners_pigs_totalowners pigs totalvieh_klein_besitzer_von_schweinen_zusammenBesitzer von Schweinen > zusammenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_goats_agricultureagricultural landowners holdings goats agriculturevieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_ziegen_landwirtschaftLandwirtschaftliche Grundbesitzer im Besitze von > Ziegen > LandwirtschaftViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_goats_other_occupationsagricultural landowners holdings goats other occupationsvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_ziegen_andere_berufeLandwirtschaftliche Grundbesitzer im Besitze von > Ziegen > andere BerufeViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_goats_totalagricultural landowners holdings goats totalvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_ziegen_zusammenLandwirtschaftliche Grundbesitzer im Besitze von > Ziegen > zusammenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_sheep_agricultureagricultural landowners holdings sheep agriculturevieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_landwirtschaftLandwirtschaftliche Grundbesitzer im Besitze von > Schafen > LandwirtschaftViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_sheep_other_occupationsagricultural landowners holdings sheep other occupationsvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_andere_berufeLandwirtschaftliche Grundbesitzer im Besitze von > Schafen > andere BerufeViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_sheep_legal_personsagricultural landowners holdings sheep legal personsvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_jurist_personenLandwirtschaftliche Grundbesitzer im Besitze von > Schafen > jurist. PersonenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_sheep_totalagricultural landowners holdings sheep totalvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_zusammenLandwirtschaftliche Grundbesitzer im Besitze von > Schafen > zusammenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_pigs_agricultureagricultural landowners holdings pigs agriculturevieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_landwirtschaftLandwirtschaftliche Grundbesitzer im Besitze von > Schweinen > LandwirtschaftViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_pigs_industryagricultural landowners holdings pigs industryvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_industrieLandwirtschaftliche Grundbesitzer im Besitze von > Schweinen > IndustrieViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_pigs_commerceagricultural landowners holdings pigs commercevieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_handelLandwirtschaftliche Grundbesitzer im Besitze von > Schweinen > HandelViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_pigs_other_occupationsagricultural landowners holdings pigs other occupationsvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_andere_berufeLandwirtschaftliche Grundbesitzer im Besitze von > Schweinen > andere BerufeViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_pigs_legal_personsagricultural landowners holdings pigs legal personsvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_jurist_personenLandwirtschaftliche Grundbesitzer im Besitze von > Schweinen > jurist. PersonenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (small)livestock_small_agricultural_landowners_holdings_pigs_totalagricultural landowners holdings pigs totalvieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_zusemmenLandwirtschaftliche Grundbesitzer im Besitze von > Schweinen > zusemmenViehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_total_number_livestock_ownerstotal number livestock ownersvieh_ges_gesamtzahl_der_viehbesitzerGesamtzahl der ViehbesitzerViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_held_horsesheld horsesvieh_ges_davon_besitzen_pferdeDavon besitzen > PferdeViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_held_other_solipedsheld other solipedsvieh_ges_davon_besitzen_andere_einhuferDavon besitzen > andere EinhuferViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_held_cattleheld cattlevieh_ges_davon_besitzen_rinderDavon besitzen > RinderViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_held_small_livestockheld small livestockvieh_ges_davon_besitzen_kleinviehDavon besitzen > KleinviehViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_held_beehivesheld beehivesvieh_ges_davon_besitzen_bienenDavon besitzen > BienenViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_horses_aloneunder total number owners horses alonevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_pferden_alleinUnter der Gesamtzahl sind Besitzer von > Pferden alleinViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_horses_cattle_aloneunder total number owners horses cattle alonevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_pferden_und_rindern_alleinUnter der Gesamtzahl sind Besitzer von > Pferden und Rindern alleinViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_horses_cattle_small_livestockunder total number owners horses cattle small livestockvieh_ges_unter_der_gesamtzahl_sind_besitzer_von_pferden_rindern_und_kleinviehUnter der Gesamtzahl sind Besitzer von > Pferden, Rindern und KleinviehViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_cattle_aloneunder total number owners cattle alonevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_rindern_alleinUnter der Gesamtzahl sind Besitzer von > Rindern alleinViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_cattle_small_livestock_without_horsesunder total number owners cattle small livestock without horsesvieh_ges_unter_der_gesamtzahl_sind_besitzer_von_rindern_und_kleinvieh_ohne_pferdeUnter der Gesamtzahl sind Besitzer von > Rindern und Kleinvieh ohne PferdeViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_goats_adjacent_cattleunder total number owners goats adjacent cattlevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_ziegen_neben_rindernUnter der Gesamtzahl sind Besitzer von > Ziegen neben RindernViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_goats_without_cattleunder total number owners goats without cattlevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_ziegen_ohne_rinderUnter der Gesamtzahl sind Besitzer von > Ziegen ohne RinderViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_goats_aloneunder total number owners goats alonevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_ziegen_alleinUnter der Gesamtzahl sind Besitzer von > Ziegen alleinViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_sheep_adjacent_cattleunder total number owners sheep adjacent cattlevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schafen_neben_rindernUnter der Gesamtzahl sind Besitzer von > Schafen neben RindernViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_sheep_without_cattleunder total number owners sheep without cattlevieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schafen_ohne_rinderUnter der Gesamtzahl sind Besitzer von > Schafen ohne RinderViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_pigs_cattle_without_sheep_goatsunder total number owners pigs cattle without sheep goatsvieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schweinen_mit_rindern_ohne_schafe_oder_ziegenUnter der Gesamtzahl sind Besitzer von > Schweinen mit Rindern ohne Schafe oder ZiegenViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisLivestock (total)livestock_total_under_total_number_owners_pigs_cattle_sheep_goatsunder total number owners pigs cattle sheep goatsvieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schweinen_mit_rindern_mit_schafe_oder_ziegenUnter der Gesamtzahl sind Besitzer von > Schweinen mit Rindern mit Schafe oder ZiegenViehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_permanentoccupation class a land forestry byproducts economically active permanentberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_selb_standigeBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > Selb-ständigeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_tenantsoccupation class a land forestry byproducts economically active tenantsberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_pachterBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > PächterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_employeesoccupation class a land forestry byproducts economically active employeesberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_angestellteBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > AngestellteVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_workersoccupation class a land forestry byproducts economically active workersberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_arbeiterBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > ArbeiterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_apprenticesoccupation class a land forestry byproducts economically active apprenticesberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_lehrlingeBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > LehrlingeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_day_laborersoccupation class a land forestry byproducts economically active day laborersberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_taglohnerBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > TaglöhnerVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_assisting_family_membersoccupation class a land forestry byproducts economically active assisting family membersberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_helfende_familien_mitglBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > helfende Familien-mitgl.Volkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_economically_active_totaloccupation class a land forestry byproducts economically active totalberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_zusammenBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > zusammenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_members_without_own_main_occupationoccupation class a land forestry byproducts members without own main occupationberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_angehorige_ohne_eigenen_hauptberufBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Angehörige ohne eigenen HauptberufVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_house_servants_servantsoccupation class a land forestry byproducts house servants servantsberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hausdiener_schaft_dienstbotenBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hausdiener-schaft, DienstbotenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_sum_occupationoccupation class a land forestry byproducts sum occupationberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_summe_der_berufs_zugehorigenBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Summe der Berufs-zugehörigenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_aoccupation class a land forestry byproducts secondary occupation classes aberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_aBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > AVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_boccupation class a land forestry byproducts secondary occupation classes bberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_bBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > BVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_coccupation class a land forestry byproducts secondary occupation classes cberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_cBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > CVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_doccupation class a land forestry byproducts secondary occupation classes dberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_dBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > DVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_a_land_forestry_byproducts_secondary_occupation_overalloccupation class a land forestry byproducts secondary occupation overallberuf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_uberhauptBerufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > überhauptVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_permanentoccupation class b industry trades economically active permanentberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_selb_standigeBerufsklasse B. Industrie und Gewerbe > Berufstätige > Selb-ständigeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_tenantsoccupation class b industry trades economically active tenantsberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_pachterBerufsklasse B. Industrie und Gewerbe > Berufstätige > PächterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_employeesoccupation class b industry trades economically active employeesberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_angestellteBerufsklasse B. Industrie und Gewerbe > Berufstätige > AngestellteVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_workersoccupation class b industry trades economically active workersberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_arbeiterBerufsklasse B. Industrie und Gewerbe > Berufstätige > ArbeiterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_apprenticesoccupation class b industry trades economically active apprenticesberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_lehrlingeBerufsklasse B. Industrie und Gewerbe > Berufstätige > LehrlingeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_day_laborersoccupation class b industry trades economically active day laborersberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_taglohnerBerufsklasse B. Industrie und Gewerbe > Berufstätige > TaglöhnerVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_assisting_family_membersoccupation class b industry trades economically active assisting family membersberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_helfende_familien_mitglBerufsklasse B. Industrie und Gewerbe > Berufstätige > helfende Familien-mitgl.Volkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_economically_active_totaloccupation class b industry trades economically active totalberuf_berufsklasse_b_industrie_und_gewerbe_berufstatige_zusammenBerufsklasse B. Industrie und Gewerbe > Berufstätige > zusammenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_members_without_own_main_occupationoccupation class b industry trades members without own main occupationberuf_berufsklasse_b_industrie_und_gewerbe_angehorige_ohne_eigenen_hauptberufBerufsklasse B. Industrie und Gewerbe > Angehörige ohne eigenen HauptberufVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_house_servants_servantsoccupation class b industry trades house servants servantsberuf_berufsklasse_b_industrie_und_gewerbe_hausdiener_schaft_dienstbotenBerufsklasse B. Industrie und Gewerbe > Hausdiener-schaft, DienstbotenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_sum_occupationoccupation class b industry trades sum occupationberuf_berufsklasse_b_industrie_und_gewerbe_summe_der_berufs_zugehorigenBerufsklasse B. Industrie und Gewerbe > Summe der Berufs-zugehörigenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_secondary_occupation_classes_aoccupation class b industry trades secondary occupation classes aberuf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_aBerufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > AVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_secondary_occupation_classes_boccupation class b industry trades secondary occupation classes bberuf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_bBerufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > BVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_secondary_occupation_classes_coccupation class b industry trades secondary occupation classes cberuf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_cBerufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > CVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_secondary_occupation_classes_doccupation class b industry trades secondary occupation classes dberuf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_dBerufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > DVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_b_industry_trades_secondary_occupation_overalloccupation class b industry trades secondary occupation overallberuf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_uberhauptBerufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > überhauptVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_permanentoccupation class c commerce transport including inn tavern economically active permanentberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_selb_standigeBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > Selb-ständigeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_tenantsoccupation class c commerce transport including inn tavern economically active tenantsberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_pachterBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > PächterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_employeesoccupation class c commerce transport including inn tavern economically active employeesberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_angestellteBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > AngestellteVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_workersoccupation class c commerce transport including inn tavern economically active workersberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_arbeiterBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > ArbeiterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_apprenticesoccupation class c commerce transport including inn tavern economically active apprenticesberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_lehrlingeBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > LehrlingeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_day_laborersoccupation class c commerce transport including inn tavern economically active day laborersberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_taglohnerBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > TaglöhnerVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_assisting_family_membersoccupation class c commerce transport including inn tavern economically active assisting family membersberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_helfende_familien_mitglBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > helfende Familien-mitgl.Volkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_totaloccupation class c commerce transport including inn tavern economically active totalberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_zusammenBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > zusammenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_members_without_own_main_occupationoccupation class c commerce transport including inn tavern members without own main occupationberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_angehorige_ohne_eigenen_hauptberufBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Angehörige ohne eigenen HauptberufVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_house_servants_servantsoccupation class c commerce transport including inn tavern house servants servantsberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hausdiener_schaft_dienstbotenBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hausdiener-schaft, DienstbotenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_sum_occupationoccupation class c commerce transport including inn tavern sum occupationberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_summe_der_berufs_zugehorigenBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Summe der Berufs-zugehörigenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_aoccupation class c commerce transport including inn tavern secondary occupation classes aberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_aBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > AVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_boccupation class c commerce transport including inn tavern secondary occupation classes bberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_bBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > BVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_coccupation class c commerce transport including inn tavern secondary occupation classes cberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_cBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > CVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_doccupation class c commerce transport including inn tavern secondary occupation classes dberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_dBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > DVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_overalloccupation class c commerce transport including inn tavern secondary occupation overallberuf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_uberhauptBerufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > überhauptVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_permanentoccupation class d public military service liberal occupations without occupation economically active permanentberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_selb_standigeBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > Selb-ständigeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_tenantsoccupation class d public military service liberal occupations without occupation economically active tenantsberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_pachterBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > PächterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_employeesoccupation class d public military service liberal occupations without occupation economically active employeesberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_angestellteBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > AngestellteVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_workersoccupation class d public military service liberal occupations without occupation economically active workersberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_arbeiterBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > ArbeiterVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_apprenticesoccupation class d public military service liberal occupations without occupation economically active apprenticesberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_lehrlingeBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > LehrlingeVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_day_laborersoccupation class d public military service liberal occupations without occupation economically active day laborersberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_taglohnerBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > TaglöhnerVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_assisting_family_membersoccupation class d public military service liberal occupations without occupation economically active assisting family membersberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_helfende_familien_mitglBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > helfende Familien-mitgl.Volkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_totaloccupation class d public military service liberal occupations without occupation economically active totalberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_zusammenBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > zusammenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_members_without_own_main_occupationoccupation class d public military service liberal occupations without occupation members without own main occupationberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_angehorige_ohne_eigenen_hauptberufBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Angehörige ohne eigenen HauptberufVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_house_servants_servantsoccupation class d public military service liberal occupations without occupation house servants servantsberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hausdiener_schaft_dienstbotenBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hausdiener-schaft, DienstbotenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_sum_occupationoccupation class d public military service liberal occupations without occupation sum occupationberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_summe_der_berufs_zugehorigenBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Summe der Berufs-zugehörigenVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_aoccupation class d public military service liberal occupations without occupation secondary occupation classes aberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_aBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > AVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_boccupation class d public military service liberal occupations without occupation secondary occupation classes bberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_bBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > BVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_coccupation class d public military service liberal occupations without occupation secondary occupation classes cberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_cBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > CVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_doccupation class d public military service liberal occupations without occupation secondary occupation classes dberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_dBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > DVolkszaehlung Beruf 1910 Political district 406.xlsx
cisOccupationocc_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_overalloccupation class d public military service liberal occupations without occupation secondary occupation overallberuf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_uberhauptBerufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > überhauptVolkszaehlung Beruf 1910 Political district 406.xlsx
cisLiteracy / educationlit_population_presenttotal present populationbild_die_gesamte_anwesande_bevolkerungDie gesamte anwesande BevölkerungVolkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationlit_age_0_10age years to including 10 birth year 1910 1900bild_davon_standen_im_alter_von_vollen_jahren_bis_einschlieblich_10_geburtsjahre_1910_1900Davon standen im Alter von vollen Jahren > bis einschlieblich 10 (Geburtsjahre 1910-1900)Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationlit_age_over10age years over 10 birth year before 1900bild_davon_standen_im_alter_von_vollen_jahren_uber_10_geburtsjahre_vor_1900Davon standen im Alter von vollen Jahren > über 10 (Geburtsjahre vor 1900)Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationlit_read_writeover 10 years age persons read writebild_von_den_uber_10_jahre_alten_personen_konnen_lesen_und_schreibenVon den über 10 Jahre alten Personen können > lesen und schreibenVolkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationlit_read_onlyover 10 years age persons readbild_von_den_uber_10_jahre_alten_personen_konnen_nur_lesenVon den über 10 Jahre alten Personen können > nur lesenVolkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationlit_illiterateover 10 years age persons neither read nor writebild_von_den_uber_10_jahre_alten_personen_konnen_weder_lesen_noch_schreibenVon den über 10 Jahre alten Personen können > weder lesen noch schreibenVolkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationdisab_blindblind totalbild_blind_zusblind zus.Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationdisab_deafmutedeafmute totalbild_taubstumm_zustaubstumm zus.Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisLiteracy / educationdisab_blind_deafmuteblind deafmute totalbild_blind_und_taubstumm_zusblind und taubstumm zus.Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_lt500_localitiesunder 500 inhabitants number localitiespop_unter_500_einwohner_azahl_der_ortschaftenUnter 500 Einwohner > Azahl der OrtschaftenVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_lt500_houses_inhabitedunder 500 inhabitants inhabited housespop_unter_500_einwohner_bewohnte_hauserUnter 500 Einwohner > bewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_lt500_houses_uninhabitedunder 500 inhabitants uninhabited housespop_unter_500_einwohner_unbewohnte_hauserUnter 500 Einwohner > unbewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_lt500_householdsunder 500 inhabitants householdspop_unter_500_einwohner_wohnparteienUnter 500 Einwohner > WohnparteienVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_lt500_inhabitantsunder 500 inhabitantspop_unter_500_einwohner_einwohnerUnter 500 Einwohner > EinwohnerVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_500_2000_localities500 to 2000 inhabitants number localitiespop_500_bis_2000_einwohner_azahl_der_ortschaften500 bis 2000 Einwohner > Azahl der OrtschaftenVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_500_2000_houses_inhabited500 to 2000 inhabitants inhabited housespop_500_bis_2000_einwohner_bewohnte_hauser500 bis 2000 Einwohner > bewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_500_2000_houses_uninhabited500 to 2000 inhabitants uninhabited housespop_500_bis_2000_einwohner_unbewohnte_hauser500 bis 2000 Einwohner > unbewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_500_2000_households500 to 2000 inhabitants householdspop_500_bis_2000_einwohner_wohnparteien500 bis 2000 Einwohner > WohnparteienVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_500_2000_inhabitants500 to 2000 inhabitantspop_500_bis_2000_einwohner_einwohner500 bis 2000 Einwohner > EinwohnerVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_2001_5000_localities2001 to 5000 inhabitants number localitiespop_2001_bis_5000_einwohner_azahl_der_ortschaften2001 bis 5000 Einwohner > Azahl der OrtschaftenVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_2001_5000_houses_inhabited2001 to 5000 inhabitants inhabited housespop_2001_bis_5000_einwohner_bewohnte_hauser2001 bis 5000 Einwohner > bewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_2001_5000_houses_uninhabited2001 to 5000 inhabitants uninhabited housespop_2001_bis_5000_einwohner_unbewohnte_hauser2001 bis 5000 Einwohner > unbewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_2001_5000_households2001 to 5000 inhabitants householdspop_2001_bis_5000_einwohner_wohnparteien2001 bis 5000 Einwohner > WohnparteienVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_2001_5000_inhabitants2001 to 5000 inhabitantspop_2001_bis_5000_einwohner_einwohner2001 bis 5000 Einwohner > EinwohnerVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_5001_10000_localities5001 to 10000 inhabitants number localitiespop_5001_bis_10000_einwohner_azahl_der_ortschaften5001 bis 10000 Einwohner > Azahl der OrtschaftenVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_5001_10000_houses_inhabited5001 to 10000 inhabitants inhabited housespop_5001_bis_10000_einwohner_bewohnte_hauser5001 bis 10000 Einwohner > bewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_5001_10000_houses_uninhabited5001 to 10000 inhabitants uninhabited housespop_5001_bis_10000_einwohner_unbewohnte_hauser5001 bis 10000 Einwohner > unbewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_5001_10000_households5001 to 10000 inhabitants householdspop_5001_bis_10000_einwohner_wohnparteien5001 bis 10000 Einwohner > WohnparteienVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_5001_10000_inhabitants5001 to 10000 inhabitantspop_5001_bis_10000_einwohner_einwohner5001 bis 10000 Einwohner > EinwohnerVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_10001_20000_localities10001 to 20000 inhabitants number localitiespop_10001_bis_20000_einwohner_azahl_der_ortschaften10001 bis 20000 Einwohner > Azahl der OrtschaftenVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_10001_20000_houses_inhabited10001 to 20000 inhabitants inhabited housespop_10001_bis_20000_einwohner_bewohnte_hauser10001 bis 20000 Einwohner > bewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_10001_20000_houses_uninhabited10001 to 20000 inhabitants uninhabited housespop_10001_bis_20000_einwohner_unbewohnte_hauser10001 bis 20000 Einwohner > unbewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_10001_20000_households10001 to 20000 inhabitants householdspop_10001_bis_20000_einwohner_wohnparteien10001 bis 20000 Einwohner > WohnparteienVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_10001_20000_inhabitants10001 to 20000 inhabitantspop_10001_bis_20000_einwohner_einwohner10001 bis 20000 Einwohner > EinwohnerVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_gt20000_localitiesover 20000 inhabitants number localitiespop_uber_20000_einwohner_azahl_der_ortschaftenÜber 20000 Einwohner > Azahl der OrtschaftenVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_gt20000_houses_inhabitedover 20000 inhabitants inhabited housespop_uber_20000_einwohner_bewohnte_hauserÜber 20000 Einwohner > bewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_gt20000_houses_uninhabitedover 20000 inhabitants uninhabited housespop_uber_20000_einwohner_unbewohnte_hauserÜber 20000 Einwohner > unbewohnte > HäuserVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_gt20000_householdsover 20000 inhabitants householdspop_uber_20000_einwohner_wohnparteienÜber 20000 Einwohner > WohnparteienVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisPopulation by settlement sizepop_gt20000_inhabitantsover 20000 inhabitantspop_uber_20000_einwohner_einwohnerÜber 20000 Einwohner > EinwohnerVolkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx
cisReligionrel_roman_catholicpresent population by religion roman catholicrel_romisch_katholischAnwesende Bevölkerung nach der Religion > römisch- > katholischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_greek_catholicpresent population by religion greek catholicrel_griechisch_katholischAnwesende Bevölkerung nach der Religion > griechisch- > katholischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_armenian_catholicpresent population by religion armenian catholicrel_armenisch_katholischAnwesende Bevölkerung nach der Religion > armenisch- > katholischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_old_catholicpresent population by religion age catholicrel_alt_katholischAnwesende Bevölkerung nach der Religion > alt- > katholischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_greek_orthodoxpresent population by religion greek orthodoxrel_griechisch_orientalischAnwesende Bevölkerung nach der Religion > griechisch- > orientalischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_armenian_orthodoxpresent population by religion armenian orthodoxrel_armenisch_orientalischAnwesende Bevölkerung nach der Religion > armenisch- > orientalischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_lutheranpresent population by religion evangelical augsburg confessionrel_evangelisch_augsburger_bekenntnissesAnwesende Bevölkerung nach der Religion > evangelisch > Augsburger BekenntnissesVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_calvinistpresent population by religion evangelical helvetic confessionrel_evangelisch_helvetischen_bekenntnissesAnwesende Bevölkerung nach der Religion > evangelisch > helvetischen BekenntnissesVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_moravianpresent population by religion moravianrel_herrnhuterAnwesende Bevölkerung nach der Religion > HerrnhuterVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_anglicanpresent population by religion anglicanrel_anglikanerAnwesende Bevölkerung nach der Religion > AnglikanerVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_mennonitepresent population by religion mennoniterel_mennonitenAnwesende Bevölkerung nach der Religion > MennonitenVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_unitarianpresent population by religion unitarianrel_unitarierAnwesende Bevölkerung nach der Religion > UnitarierVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_lipovanpresent population by religion lipovanrel_lippowanerAnwesende Bevölkerung nach der Religion > LippowanerVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_jewishpresent population by religion jewishrel_israelitenAnwesende Bevölkerung nach der Religion > IsraelitenVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_muslimpresent population by religion muslimrel_islamitenAnwesende Bevölkerung nach der Religion > IslamitenVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_otherpresent population by religion other confessionsrel_andere_konfessionenAnwesende Bevölkerung nach der Religion > andere KonfessionenVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_nonepresent population by religion no religionrel_konfessionsloseAnwesende Bevölkerung nach der Religion > KonfessionsloseVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisReligionrel_totalpresent population by religion total 2 18rel_zusammenAnwesende Bevölkerung nach der Religion > zusammen (Spalte 2-18)Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_germanpresent population austrian citizenship by colloquial language germanumgspr_deutschAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > deutschVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_czechpresent population austrian citizenship by colloquial language czech moravian slovakumgspr_bohmisch_mahrisch_slowakischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > böhmisch-mährisch -slowakischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_polishpresent population austrian citizenship by colloquial language polishumgspr_polnischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > polnischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_ruthenianpresent population austrian citizenship by colloquial language ruthenianumgspr_ruthenischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > ruthenischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_slovenepresent population austrian citizenship by colloquial language sloveneumgspr_slowenischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > slowenischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_serbocroatpresent population austrian citizenship by colloquial language serbian croatianumgspr_serbisch_kroatischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > serbisch-kroatischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_italian_ladinpresent population austrian citizenship by colloquial language italian ladinumgspr_italienisch_ladinischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > italienisch-ladinischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_romanianpresent population austrian citizenship by colloquial language romanianumgspr_rumanischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > rumänischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_magyarpresent population austrian citizenship by colloquial language magyarumgspr_magyarischAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > magyarischVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)lang_totalpresent population austrian citizenship by colloquial language total 20 28umgspr_zusammenAnwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > zusammen (Spalte 20-28)Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisColloquial language (ethnicity)foreign_nationals_presentpresent foreign nationalsumgspr_anwesende_staatsfremdeAnwesende StaatsfremdeVolkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx
cisDerived share/ratelang_*_shareShare of population by colloquial language (of lang_total)(derived)
cisDerived share/raterel_*_shareShare of population by religion (of rel_total)(derived)
cisDerived share/rateliteracy_rateLiterate (read+write) / population over age 10(derived)
cisDerived share/rateillegitimacy_rateIllegitimate live births / all live births(derived)
cisDerived share/ratepop_totalTotal population (sum of size-class inhabitants)(derived)
huMother tongue & religionarea_cadastral_holdsarea holdsetn_terulet_kat_holdetn_terulet_kat_holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue & religionpop_presentpresent populationetn_jelenlevo_nepessegetn_jelenlevo_nepessegGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_magyarmagyar mother tongueetn_magyar_anyanyelvetn_magyar_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_germangerman mother tongueetn_nemet_anyanyelvetn_nemet_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_slovakslovak mother tongueetn_tot_anyanyelvetn_tot_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_romanianromanian mother tongueetn_olah_anyanyelvetn_olah_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_ruthenianruthenian mother tongueetn_ruthen_anyanyelvetn_ruthen_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_croatiancroatian mother tongueetn_horvat_anyanyelvetn_horvat_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_serbianserbian mother tongueetn_szerb_anyanyelvetn_szerb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_otherother mother tongueetn_egyeb_anyanyelvetn_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_roman_catholicroman religionetn_romai_kat_vallasetn_romai_kat_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_greek_catholicgreek religionetn_gorog_kat_vallasetn_gorog_kat_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_calvinistcalvinist religionetn_reformatus_vallasetn_reformatus_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_lutheranlutheran religionetn_evangelikus_vallasetn_evangelikus_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_orthodoxorthodox religionetn_ortodox_vallasetn_ortodox_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_unitarianunitarian religionetn_unitarius_vallasetn_unitarius_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_jewishjewish religionetn_izraelita_vallasetn_izraelita_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huReligionrel_otherother religionetn_egyeb_vallasetn_egyeb_vallasGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_bulgarianbulgarian other mother tongueetn_bolgar_egyeb_anyanyelvetn_bolgar_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_bunjevacbunjevac other mother tongueetn_bunyevac_egyeb_anyanyelvetn_bunyevac_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_romaniromani other mother tongueetn_cigany_egyeb_anyanyelvetn_cigany_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_czech_moravianczech moravian other mother tongueetn_cseh_morva_egyeb_anyanyelvetn_cseh_morva_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_illyrian_dalmatianillyrian dalmatian other mother tongueetn_illir_dalmat_egyeb_anyanyelvetn_illir_dalmat_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_krashovanikrashovani other mother tongueetn_krassovan_egyeb_anyanyelvetn_krassovan_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_polishpolish other mother tongueetn_lengyel_egyeb_anyanyelvetn_lengyel_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_italianitalian other mother tongueetn_olasz_egyeb_anyanyelvetn_olasz_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_sokcisokci other mother tongueetn_sokac_egyeb_anyanyelvetn_sokac_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_sloveneslovene other mother tongueetn_szloven_egyeb_anyanyelvetn_szloven_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huMother tongue (ethnicity)lang_other_otherother mother tongueetn_egyeb_egyeb_anyanyelvetn_egyeb_egyeb_anyanyelvGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_arable_holdsarable holdsagr_a1 szántó kat holdagr_a1 szántó kat holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_arable_incomearable incomeagr_a2 szántó jövedelme, koronaagr_a2 szántó jövedelme, koronaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_meadow_holdsmeadow holdsagr_a5 ret kat holdagr_a5 ret kat holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_meadow_incomemeadow jovedagr_a6 ret jöved koronaagr_a6 ret jöved koronaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_vineyard_holdsvineyard holdsagr_a7 szőlö kat holdagr_a7 szőlö kat holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_vineyard_incomevineyard jovedagr_a8 szőlő jöved koronaagr_a8 szőlő jöved koronaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_pasture_holdspasture holdsagr_a9 legelő kat holdagr_a9 legelő kat holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_pasture_incomepasture jovedagr_a0 legel jöved koronaagr_a0 legel jöved koronaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_forest_holdsforest holdsagr_b1 erdő kat holdagr_b1 erdő kat holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_forest_incomeerdojovedagr_b2 erdőjöved koronaagr_b2 erdőjöved koronaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_settlement_productive_area_holdstelepul termo terulete holdsagr_b3 települ termő területe, kat holdagr_b3 települ termő területe, kat holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_settlement_unproductive_area_holdssettlement termeketlen terulete holdsagr_b4 telep terméketlen területe, kat holdagr_b4 telep terméketlen területe, kat holdGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_settlement_net_incomesettlement net incomeagr_b5 telep tiszta jövedelme, koronaagr_b5 telep tiszta jövedelme, koronaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_parcels_countfoldreszletek szamaagr_b6 földrészletek számaagr_b6 földrészletek számaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_holdings_countbirtokivek szamaagr_b7 birtokívek számaagr_b7 birtokívek számaGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_field22mezo22agr_b8 Mező22agr_b8 Mező22GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_field23mezo23agr_b9 Mező23agr_b9 Mező23GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_useful_pctuseful pctagr_b0 hasznos %agr_b0 hasznos %GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_income_per_area_unitjovedelem teruletegysegreagr_c1 jövedelem területegységreagr_c1 jövedelem területegységreGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_forest_income_per_area_uniterdojovedelem teruletegysegreagr_c2 erdojövedelem területegységreagr_c2 erdojövedelem területegységreGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_vineyard_income_per_area_unitvineyard jovedelem teruletegysegreagr_c3 szölö jövedelem területegységreagr_c3 szölö jövedelem területegységreGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_meadow_pasture_income_per_area_unitmeadow pasture income teruletegysegreagr_c4 rét és legelő jövedelme területegységreagr_c4 rét és legelő jövedelme területegységreGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_arable_income_per_area_unitarable income teruletegysegreagr_c5 szanto jövedelme területegysegreagr_c5 szanto jövedelme területegysegreGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_pasture_income_2pasture incomeagr_c6 legelö jovedelmeagr_c6 legelö jovedelmeGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_meadow_income_2meadow incomeagr_c7 rét jövedelmeagr_c7 rét jövedelmeGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_income_holdingjovedelem holdingagr_c8 jövedelem per birtokagr_c8 jövedelem per birtokGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_parcel_holdingparcel holdingagr_c9 parcella per birtokagr_c9 parcella per birtokGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_parcel_sizeparcellameretagr_c0 parcellaméretagr_c0 parcellaméretGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_farm_sizegazdasagmeretagr_d1 gazdaságméretagr_d1 gazdaságméretGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_per_capita_incomeper capita jovedelemagr_d2 egy lakosra juto jövedelemagr_d2 egy lakosra juto jövedelemGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_per_capita_areaper capita areaagr_d3 egy lakosra juto területagr_d3 egy lakosra juto területGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_forest_pctforest pctagr_d4 erdo%agr_d4 erdo%GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_vineyard_pctvineyard pctagr_d5 szolo%agr_d5 szolo%GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_meadow_pasture_pctmeadow pasture pctagr_d6 ret es legelo %agr_d6 ret es legelo %GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_arable_pctarable pctagr_d7 szanto%agr_d7 szanto%GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_pasture_pctpasture pctagr_d8 csak legelo%agr_d8 csak legelo%GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_meadowmeadowagr_d9 csak rétagr_d9 csak rétGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_total_areaosszes areaagr_d10 összes területagr_d10 összes területGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_present_populationpresent populationagr_jelen_nepagr_jelen_nepGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_agrarian_earnersagrarian kereso 6agr_8mezg_kereső (6)agr_8mezg_kereső (6)GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_total_agrarian_agrariantotal agrarian agrarianagr_10osz_mezgagr_10osz_mezgGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_income_popjov popagr_jöv_nepes10agr_jöv_nepes10GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_income_agrarian_earnersjov agrarian earnersagr_jöv_agrker10agr_jöv_agrker10GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_income_agr_totaljov agr osszagr_jöv_agr_összagr_jöv_agr_összGISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_holdings_agr_earnersholdings agr earnersagr_birtokiv_agr_keres10agr_birtokiv_agr_keres10GISta Hungarorum JARAS 1910 (ETN/Agrar)
huAgricultureagr_productive_area_per_agrarian_capitaproductive area per agrarian capitaagr_termoter_mezglakosagr_termoter_mezglakosGISta Hungarorum JARAS 1910 (ETN/Agrar)
huDerived share/rateslovak_share / magyar_share / german_share / ruthen_shareMother-tongue shares (of pop_present)(derived)
huDerived share/raterel_*_shareReligion shares (of pop_present)(derived)

Data sourcesWhat's in the data

Provenance and licences. · source: reference/DATA_SOURCES.md

In short

Provenance, licensing, and role of every external dataset in the thesis pipeline. Storage is split by role: data/ = machine inputs+outputs (gitignored, Dropbox-synced), reference/ = small curated/authored artifacts (tracked → GitHub), library/ = human reading (gitignored). Paths below are the in-repo skeleton locations. Derived analysis files land in data/final/ (see data_dictionary.csv).

GIS vs. census tables. Two physically distinct kinds of geographic input feed the pipeline and must not be conflated: GIS = boundary shapefiles (.shp/.dbf polygons + stable geo-IDs + the unit hierarchy), and census tables = the XLSX workbooks holding the actual demographic counts. They are joined by exact unit code, never by name. For the Czech lands the two come from different providers in different repo roots (MPIDR shapefile in reference/ vs. Rumpler-Seger census XLSX in data/raw/census/); for Upper Hungary both come from the same provider (GISta Hungarorum), split into shp/ and tables/ subfolders. The per-source rows below tag each as GIS or census XLSX.

See PLACE_MATCHING.md for how these sources are used to resolve a soldier's birthplace to its 1910 okres/járás (the matching logic, CZ + SK).

Last updated: 2026-08-01 (added §3a, the HISCO occupational-classification sources).


1. Primary person-level datasets

# Source Provider N Access Local path
1 Czechoslovak Legionnaire records Databáze legionářů, csol.cz (Československá obec legionářská + VÚA) ~132,993 Scraped (utils/scraping/scraper_csol.py) data/raw/legionnaires/legionnaires.csv
2 Fallen Habsburg soldiers (WWI) VÚA — Vojenský ústřední archiv, vuapraha.cz 186,191 Scraped 2026-06-10 (scraper_vuapraha.py) data/raw/habsburg/habsburg_fallen.csv

Legal basis for scraping: §90 zákon č. 121/2000 Sb. (scientific-research exception) + GDPR Art. 89. The VÚA bulk-export request (Mgr. Flosman, ref. MO 290537/2026-211100) is superseded by the full scrape and no longer on the critical path.


2. Geographic & census — Cisleithanian (Czech lands)

Here the GIS (#3, boundary shapefile) and the census XLSX (#4, demographic workbooks) are two separate downloads from two providers, both descending from Rumpler & Seger 2010. #3 lives under reference/ (small, curated, tracked to GitHub); #4 under data/raw/census/ (gitignored). They join on exact unit code — IDCourtdistr = court_id, IDPoldistrict = okres_id — never by name.

# Source Kind Citation / ID Role & consumer Local path
3 MPIDR Population History GIS Collection — 1910 administrative boundary shapefile (political okres + soudní okres / Gerichtsbezirk polygons, stable geo-IDs in ID_1) GIS (.shp/.dbf) MPIDR & CGG 2012, slightly modified version of a GIS-file by Rumpler & Seger 2010, Rostock (distr. via MOSAIC/IPUMS) Spine of the okres/court backbone — .dbf read by districts.py; .shp polygons drive the court-district point-in-polygon join in spatial.py reference/places/mpidr/ (Austro_Hungarian_Empire_1910_v.1.0.{shp,dbf,shx,prj,sbn,sbx})
4 1910/1911 Cisleithanian census — 18 workbooks (mother tongue + religion, occupation, education, population, births/deaths/causes, livestock, savings banks, district-court justice; okres code 406 / court 934), plus the CODE file … 961.xlsx codebook census XLSX Rumpler & Seger 2010 (see #3); Die Habsburgermonarchie 1848–1918, Bd. IX/2 Okres-level covariate panel — every *.xlsx read + merge-expanded by census.py; codebook validates unit names via districts.validate_against_code data/raw/census/
5 Historický lexikon obcí ČR 1869–2011 — settlement register (modern Czech name, 1910-era name, full okres timeline), narrative PDF PDF (not GIS) Český statistický úřad (ČSÚ) Settlement crosswalk (lexikon.py, reads the CSU_Historicky-lexikon-obci-CR_1869-2011_abecedni-prehled.pdf) → 1910-okres of each obec, post-1910 okres recovery via the okres timeline, Czech municipality names library/lexicons/ (PDF gitignored, ~7 MB)
6 Wikidata — Czech settlement coordinates + Czech/German labels + modern okres SPARQL (coords) Wikidata Query Service (WDQS); CC0 Settlement coordinates, German municipality names (wikidata.py); enables point-level maps + Kelly-2019 spatial SEs cached data/raw/wikidata/
7 Vollständiges Ortsverzeichnis 1910 (bilingual settlement gazetteer, ALTO OCR) OCR XML ONB AC03177788 Superseded / dropped — OCR corruption; parser archived (utils/parsing/_archive/gazetteer.py) data/raw/places/ortsverzeichnis/
7a Podrobný seznam míst zemí rakouských … IX. Čechy — the census's own special place register: 480 pages of table at settlement level (area, houses, population by sex, confession and language), plus a two-column apparatus naming each place's institutions and a keyed postal/railway legend 756-scan PDF + per-page ALTO + whole-volume OCR Vídeň/Wien 1916, c. k. dvorní a státní tiskárna. Národní digitální knihovna, Kramerius 5, uuid:c2d4a3a0-6fb2-11ec-bc36-005056827e51 — all pages reported public In progress, nothing in data/ derives from it yet. Read by utils/lexikon_1910/lexikon_1910*.py; 20-page pilot driven by utils/lexikon_1910/run_lexikon_1910_pilot.py. Targets: settlement-level 1910 covariates, a per-locality institutional inventory (the Czech-vs-German cooperative suffix among it), and the 106 Prague-suburb rows D-36 leaves blank. See LEXIKON_1910.md library/lexicons/NDK_Podrobny-seznam-mist-Cechy-1910_cz* (gitignored, 3.34 GB)
7b Spezialortsrepertorium der österreichischen Länder … IX. Böhmen — the German edition of #7a, composited and proofed separately from the same returns 764-page PDF + plaintext (no ALTO) Wien 1915, k.k. Hof- und Staatsdruckerei. Universitätsbibliothek Wien, urn:nbn:at:at-ubw:g-80326, ÖBV AC01116810 Second keying of #7a, read by lexikon_1910_de.py. Matched to the Czech volume by name within an okres, never by the printed running number — each edition orders a district alphabetically in its own language. Mähren (AC03014110) and Schlesien are held and unread library/lexicons/UBW_Spezialortsrepertorium-Boehmen-1910_de* (gitignored)
24 ČÚZK RÚIAN — Registr územní identifikace, adres a nemovitostí, the state file of the exchange format (VFR 3.1) 20260831_ST_UKSG.xml.zip — state → okres → obec → část obce → katastrální území → ZSJ, each with its definition point and generalised boundary (obce, KÚ; ZSJ original), valid 2026-08-31 GML/XML (S-JTSK, EPSG:5514), 94.6 MB zipped Český úřad zeměměřický a katastrální (ČÚZK); open data, CC-BY 4.0. Downloaded 2026-10-29 with the author's approval from services.cuzk.gov.cz/vfr/202608/ Today's geography — ruian.py → data/final/geo/ruian_obce (6,258, polygons) · ruian_casti_obce (15,106) · ruian_ku (13,074, polygons) · ruian_zsj (23,598) · ruian_obce_adjacency (shared boundary vertices), and the candidates of the settlements_1910 crosswalk (settlements_1910_geo, D-151). S-JTSK → WGS 84 in closed form (Křovák inverse + EPSG:1623 Helmert); checked against Wikidata, median 8 m over 5,793 points data/raw/geo/ruian/ (gitignored)
25 ČÚZK Geonames — Databáze geografických jmen ČR, the whole-state GeoPackage GEONAMES-5514-gpkg-20260930.zip — every named place on the state map series as a point: municipalities, parts, and the místní části below them (mills, gamekeepers' lodges, solitary farms; 17,534), castles, works, mountain lodges GeoPackage (S-JTSK), 36.2 MB zipped ČÚZK, maintained from ZABAGED; open data, CC-BY 4.0 (feed rights: žádné podmínky neplatí). Downloaded 2026-10-29 with approval geonames.py → data/final/geo/geonames_places (43,359 points in six settlement-like layers); the four below the část obce are candidates of the crosswalk where RÚIAN has no place of that name in scope — 693 settlements. Czech names only data/raw/geo/geonames/ (gitignored; the .gpkg is unpacked beside the zip on first read)
26 PRNG — Państwowy Rejestr Nazw Geograficznych, localities half, PRNG_MIEJSCOWOSCI_XLSX.zip (export of 2026-10-28) XLSX, WGS 84 as DMS text, 23.0 MB zipped Główny Urząd Geodezji i Kartografii (GUGiK); "bezpłatnie i do dowolnego wykorzystania" (free, any use) per the geoportal — no named licence. Downloaded 2026-10-29 with approval prng.py → data/final/geo/prng_places: the 1,241 Polish localities inside the 1910 courts of okresy Bílsko, Bílsko (město), Fryštát and Těšín — the half of Těšín Silesia no Czech register reaches. Crosswalk candidates of last resort; for the register's parts more than for settlements_1910, which holds only the curated Těšín and Bílsko rows there data/raw/geo/prng/ (gitignored)
27 Vanished and abandoned settlements of Czechia — Wikidata, with the identifiers of Zaniklé obce a objekty po roce 1945 — one SPARQL query (text in SOURCE.txt and utils/parsing/vanished.py) for every item carrying a zanikleobce.cz ID (P3003) or typed abandoned village (Q350895), abandoned hamlet (Q27654220), deserted farmyard (Q28807616) or former entity (Q15893266) in Czechia: QID, Czech and German labels, point, classes, zanikleobce.cz ID, administrative unit, inception/dissolution CSV (1.19 MB), retrieved 2026-10-29 12:57 UTC Wikidata, CC0; the zanikleobce.cz register (volunteer-run, no licence stated, robots.txt asks crawlers off its query pages) is not fetched — only its IDs, as Wikidata holds them. Approved by the author 2026-10-29 The places that are gone — vanished.py → data/final/geo/vanished_places (6,884 items, 6,111 with a point, 3,035 with a zanikleobce.cz ID, 2,210 with a German label; 2,875 settlement-like and so matchable — churches, chapels, synagogues, monuments are kept but never matched). Third tier of crosswalk candidates, after RÚIAN and Geonames: places 477 settlements, 461 of them on an item with a zanikleobce.cz ID, cited per row in coord_ref. ⚠ Wikidata's coverage of the register is partial (3,035 IDs) and its points are volunteer-placed; 74 items carry more than one point (one is sampled) data/raw/geo/wikidata_vanished/ (gitignored)

Provenance of #24–#27. Each download sits in its own folder under data/raw/geo/ beside a SOURCE.txt giving the URL (or, for #27, the query text and endpoint), the retrieval time, the byte size, the sha256 of the file, the publisher, the licence and the module that reads it. Every point in settlements_1910_geo then says where it came from twice over: coord_source names the source (wikidata · ruian_part · ruian_obec · ruian_zsj · geonames · wikidata_vanished · prng) and coord_ref cites the record (RÚIAN část obce 32247, ČÚZK Geonames mistni_cast:5082, Wikidata Q27659648 (vanished; zanikleobce.cz 673)).

Known property of #4 — the court-level (934) workbooks carry 330 of the 339 Czech-lands courts. A Statutarstadt had no Gerichtsbezirk of its own, so the city and the rural seat court around it appear as one row (Iglau/Jihlava Stadt, Olmuetz/Olomouc Stadt); the nine absent ids are exactly those shared seat courts. Aggregating to the okres therefore puts a twin pair's total on one member and leaves the other empty, which is why the mother-tongue, religion, justice and savings covariates are only defined at the pooled pair for nine of the ten pairs (Brno has genuine Stadt and Land court rows and is unaffected). The ten okres-level (406) workbooks all carry the full 158 and are unaffected throughout. Handled in census.build_census_panels; see PLACE_MATCHING.md and DECISIONS D-18.

The census counts (#4) are also available as PHAIDRA o:2114309 (Univ. Vienna); the copy actually used is the Rumpler-Seger MPIDR-companion, whose unit codes are 1:1 compatible with the boundary spine (#3) — that code-compatibility is why the two separate providers can be exact-joined.


3. Geographic & census — Transleithanian (Upper Hungary / Slovakia + Subcarpathia)

Unlike the Czech lands, here the GIS and the census XLSX are one provider, one download (GISta Hungarorum), physically split into two subfolders of data/raw/census_hu/: shp/ (GIS polygons) and tables/ (census workbooks). They join on exact unit code — kod / IDJaras1910 = unit_id — never by name.

# Source Kind Provider Role & consumer Local path
8a GISta Hungarorum — 1910 boundary shapefiles: MO_Megye (counties), MO_Jaras / MO_JarasVaros (districts + chartered towns), MO_Telepules (settlements); all UTF-8 .cpg GIS (.shp/.dbf) gistory.hu — MTA BTK Történettudományi Intézet; CC BY-NC Járás/county backbone — .dbf read by districts_hu.py data/raw/census_hu/shp/
8b GISta Hungarorum — járás-level census workbooks: …_4_1910_ETN.xlsx (mother tongue incl. tot_anyanyelv=Slovak + religion), …_2_Agrar_1910.xlsx (agriculture). The 281-col general table …_3_1910.xlsx is not loaded (low value, messy headers) census XLSX gistory.hu — MTA BTK; CC BY-NC Járás covariate panel — read by census_hu.py; joined to the #8a backbone on unit_id data/raw/census_hu/tables/

Non-commercial: GISta Hungarorum is CC BY-NC — permissible for this academic (non-commercial) research; cite the provider. Files fetched via curl --ssl-no-revoke (Windows). Magyar → Slovak: GIS/census names are Magyar (Nyitra, Pozsony); the Slovak/Rusyn forms the soldier records use come from the curated crosswalk district_sk_curation.csv (§4).

The GISta backbone (#8a) is district-level only. #9 adds the missing settlement layer — the Transleithanian counterpart of the Czech #5 lexikon:

# Source Kind Provider Role & consumer Local path
9 Historicko-demografický lexikón obcí Slovenska 1880–1910 (Juraj Majo, 2012; ISBN 978-80-8121-222-2) — every obec on today's Slovak territory with its period okres/župa and 1910 mother-tongue + religion structure PDF (tabular, 2009 pp.) Štatistický úrad SR (Statistical Office of the Slovak Republic) Obec → 1910 okres/župa crosswalk + mother-tongue shares (lexikon_sk.py, pdfplumber x-coordinate column read of the 1910 Part-I section, printed pp. 758–875); mapped to the GISta járás unit_id via the Slovak okres seat; feeds the settlement pass records_geo_hu.add_settlement_jaras so ordinary Slovak village births resolve library/lexicons/ (PDF gitignored, ~8 MB)

Okres→járás bridge (#9). The lexicon names a district in Slovak (okres Nové Zámky); GISta stores it as a Magyar adjectival jaras_hu (Érsekújvári) whose name_sk is the seat town (Šurany), a different place. lexikon_sk.py recovers the district's Slovak name from jaras_hu via the backbone's own Magyar→Slovak dictionary (the chartered-town exonym pairs, e.g. Érsekújvár↔Nové Zámky), and the few district exonyms whose base town is absent from the backbone (Fülek→Fiľakovo, Párkány→Štúrovo, Rimaszécs→Rimavská Seč, Udvard→Dvory nad Žitavou) are curated in district_sk_curation.csv, validated against Wikidata sk/hu labels. Result: 98.4% of parsed obce map to a GISta járás; the 4 residual districts are Hungarian-seated okresy outside the Felvidék backbone (Cserhát, Rajka, Tósziget-Csilizköz) or a region label (Žitný ostrov).

Page layout (#9) — three properties worth knowing before trusting a parse. All three were mis-read until 2026-08-01, and each failed quietly rather than loudly:

  1. Two okres header forms. The usual okres X, but three blocks name the district adjectivally — Hradný okres (= Vári járás, seat Tvrdošín), Pohronský okres (Garamvölgyi), Medzibodrocký okres (Bodrogközi). None of the three resolves through the Magyar→Slovak bridge (Vári reduces to Vár, no chartered town), so all three are curated as sk_alt in district_sk_curation.csv. DECISIONS D-21.
  2. A trailing chartered-town block per county, in four printed wordings — singular mesto so župnou právomocou / mesto so župnými právomocami / mesto so zriadeným magistrátom, and plural mestá so zriadeným magistrátom, which is the only header seven counties use. A town row carries no okres of its own, so a missed header leaves the row on the county's last járás — in the right county, and entirely plausible. DECISIONS D-20.
  3. Right-aligned numeric columns. The a b 1 2 … 9 header digits mark each column's left edge while the values below them align on the right, about half a column further over. Each number must therefore be bucketed by its right edge; bucketing by its centre drifts narrow values one column left and can merge two into one. Zero columns are unprinted, so text order alone is no help at all. Row sums reconcile for 100% of the 3,511 obce (settlements_hu_1910.sum_ok, asserted in utils/validation/data_checks.py).

Coverage caveat (#9): the lexicon lists only obce on present-day Slovakia, so it does not cover Subcarpathian Rus (today's Zakarpattia, Ukraine); Rusyn births there stay on the GISta frame (#8).


3a. Occupational classification (HISCO)

The raw Zaměstnání field is Czech free text (8,001 distinct strings). Turning it into a social class needs two things: an authority for the classification itself, and a dictionary from the surface string to a code. The two sources below supply the first; the second does not exist for Czech and is authored by this project (§4).

# Source Kind Provider Role & consumer Local path
11 History of Work — HISCO (April 2025 release) — the HISCO hierarchy hisco_45_full_03.csv (9 major → 76 minor → 296 unit → 1,675 micro groups, English labels + task descriptions); hisco_occupation_link_combined.csv (246k occupational title → HISCO links with lang_code, english_translation, status_id/relation_id/product_id); status.csv, relation.csv, product.csv (modifier code lists) CSV assets (+ SPARQL endpoint) IISG / Datalegend, maint. Richard Zijdeman; CC-BY-SA 4.0 Classification authority behind reference/occupations/hisco_authority.csv; the German lang_code rows of the link table are the bridge used to draft Czech codes. Fetched by utils/reference_tools/occupations/download_hisco_sources.py data/raw/hisco/
12 HSN standardised, HISCO-coded and classified occupational titles, release 2020.02 — 281,355 coded titles with HISCO, STATUS, RELATION, PRODUCT, HISCLASS, HISCLASS_5, HISCAM_U1, HISCAM_NL, SOCPO, OCC1950; plus the compound-occupation splitter HSN_DOUBLE CSV Historical Sample of the Netherlands / IISG Source of the HISCO → HISCLASS / HISCLASS_5 / HISCAM / SOCPO crosswalk, collapsed to one row per (hisco, status, relation, product) by build_hisco_authority.py. Its title strings are Dutch and are not used data/raw/hisco/

There is no Czech in either source. lang_code in #11 covers FR/NL/DE/EN/SV and others, not CS; the #12 strings are Dutch throughout. OccCANINE — the transformer that codes occupational descriptions automatically — is trained on 13 languages (en, da, se, nl, ca, fr, no, is, pt, ge, es, it, gr) and Czech is not one of them. The Czech → HISCO step is therefore authored (reference/occupations/occupation_hisco_curation.csv, §4) and everything downstream of the code is off-the-shelf. Method and coverage: OCCUPATION_CODING.md.

ShareAlike. #11 is CC-BY-SA 4.0, so the derived reference/occupations/hisco_authority.csv inherits that licence and may only be redistributed under it, with attribution. Recorded in reference/occupations/SOURCE_LICENCE.txt, which also lists the citations for HISCO, HISCLASS, HISCAM and SOCPO.

Provenance of the folder. data/raw/hisco/MANIFEST.TXT records byte size, SHA-256, source URL, retrieval date and licence for every file, written by the download script.

11 is fetched unattended; #12 sits behind a click-through and is placed by hand — the

script checks for it and prints the landing page rather than pretending to fetch it.


3b. Reference works used for validation (not inputs to the pipeline)

# Source Kind Provider Role & consumer Local path
10 Encyklopedie československých legií (Jiří Fidler, 2025; ISBN 978-80-242-9615-4) — 1,289 entries: 233 units, 230 sub-units, 280 officer biographies, 264 places, 44 sea transports, 16 dated orders of battle. Every unit entry states its theatre, formation date, parent formation, commanders and renaming e-book (EPUB) Universum, Praha (in copyright) External ground truth, not a pipeline input: unit→theatre authority behind reference/units/unit_authority.csv (built by build_unit_authority.py, facts only), the theatre-agreement QA check in utils/validation/data_checks.py, and the Legion general grades in rank_ladders.md §B-gen. Full results: reference/VALIDATION_FIDLER.md library/books/ (gitignored, ~35 MB)
13 CZ-ISCO (ČSÚ classification 80146, 2025 edition; Sdělení ČSÚ č. 399/2024 Sb.) — 1,991 codes over five levels, published in a Czech and an English file keyed on the same code. Plus ILO ISCO-08 Volume I (structure, group definitions, ISCO-08 ↔ ISCO-88 correspondence tables) CSV ×2 + PDF Český statistický úřad; ILO for the manual External ground truth, not a pipeline input: the only Czech→English occupational bridge this project has, used by build_czisco_bridge.py → czisco_authority.csv and run_czisco_check.py as a third coder on the Czech side. Never assigns a HISCO code. Results: reference/VALIDATION_OCCUPATIONS.md data/raw/czisco/
14 Vienna Database on European Family History — Numeric data file, Version 0.1, the unharmonized Austrian file — 163,400 persons / 25,000 households in 44 localities of present-day Austria (Salzburg, Upper/Lower Austria, Tyrol, Carinthia, 8 Vienna districts), 1632–1947, from census data and soul listings (libri status animarum); the 1857 Vienna census is 34 % of it. 53 variables, German variable names and value labels only. Occupation on a closed list of 521 German nominal titles (berufn) + 234 systematic (berufs) + Berufsstellung, recorded independently of an 8-way household position (alstel) SPSS .sav + fixed-width .dat + codebook .doc Built 1974–1984 at the University of Vienna with the MPI for History, Göttingen and the University of Salzburg; distributed by MOSAIC / IPUMS (unharmonized collection) External ground truth, not a pipeline input: the only individual-level source here pairing a German occupational title with a household position, used by run_mosaic_beruf_check.py as the fourth check on rolník — the one that reaches what Check 6's ecological design could not. Never assigns a HISCO code. Results: reference/VALIDATION_OCCUPATIONS.md Check 7 data/raw/mosaic_unharmonized_austria_numeric/ (gitignored, ~59 MB)

| 19 | Teaching the Empire: Education and State Loyalty in Late Habsburg Austria (Scott O. Moore, 2020; Purdue UP, Central European Studies) — ch. 1 sets out the Cisleithanian school system: compulsory ages 6–14 as Volksschule 5 + Bürgerschule 3 under the Reichsvolksschulgesetz of 14 May 1869, Gymnasium (8 yrs) and Realschule (7 yrs) entered at age ten in parallel with the Bürgerschule, the Lehrerbildungsanstalt entered from the Bürgerschule at fifteen | PDF (monograph) | Purdue University Press (in copyright) | Establishes the ladder education.py 2.0.0 encodes — the general half. Not a pipeline input and nothing in data/ derives from it, but unlike #10/#13/#14 it does not check an output, it defines the scheme. Explicit that Hungary is outside its account (p. 36), which is why the ordinal is not established for Slovak and Rusyn births. See EDUCATION_LADDER.md §1, D-08 | library/books/Moore-2020_Teaching-the-Empire_Habsburg-Schooling-and-State-Loyalty.pdf (gitignored, ~10 MB) | | 20 | 'Muddling through' and historical institutionalism — … the dualistic Austrian VET system (Lorenz Lassnigg, 2015; IHS Sociological Series WP 108, Vienna) — the vocational branch: the Fortbildungsschule as part of compulsory mass schooling under communal governance, apprenticeship regulated by the Gewerbeordnung and entered on no educational credential, the Dumreicher Staatsgewerbeschulen of the 1880s, Handelsschule vs Handelsakademie | PDF (working paper) | Institut für Höhere Studien (IHS), Vienna — free | Establishes the vocational half of the ladder — the ~17 % of Vzdělání Moore does not reach, and the source that settles pok. off the ordinal. Its quantitative series starts 1924 and covers the Republic of Austria, so it is background, not a benchmark. See EDUCATION_LADDER.md §2, D-08 | library/literature/Lassnigg-2015_Muddling-Through_Austrian-VET-System.pdf (gitignored) | | 21 | Statistik der Unterrichtsanstalten in den im Reichsrate vertretenen Königreichen und Ländern für das Jahr 1908/1909 (Österreichische Statistik, XCIII. Band, 1. Heft; k.k. Statistische Zentralkommission, Wien 1912) — the administration's own annual census of every school in Cisleithania: a 44-page Einleitung and 41 tables, by Land and by Unterrichtssprache. Table 20 is per institution (Standort, Gründungsjahr, language); Table 34 gives allgemeine Volksschulen by number of ascending classes; Table 39 gives Schulbesuchserleichterungen and truancy penalties | PDF (image-only scan, 279 pp.) | k.k. Hof- und Staatsdruckerei, Wien; scanned by the Österreichische Nationalbibliothek. Public domain by age | External ground truth, not a pipeline input: the primary source that had been missing under #19/#20, and the one EDUCATION_LADDER.md §5 nominated by name. Consumed by utils/reference_tools/education/run_unterricht_check.py (education's first external check) via the transcription unterricht_1908_curation.csv. Confirms pok. off the ordinal from the state's own four-way Gewerbeschule taxonomy, and is what caught the education_complete class-vs-year conflation. ⚠ Cisleithania by definition — it cannot reach the Hungarian ladder, so D-08 stays open. Results: VALIDATION_EDUCATION.md | library/education/OeStat-1912_Bd-XCIII-1_Statistik-der-Unterrichtsanstalten-1908-09.pdf (gitignored, ~434 MB). ⚠ Merged from 15 ÖNB download parts; Einleitung X–XI and table page 15 are absent — the gaps are rows in reference/education/unterricht_page_index.csv | | 30 | Historical dataset of administrative units with social-economic attributes for Austrian Silesia 1837–1910 (Ostafin, Kaim, Siwek & Miklar 2020, Scientific Data 7:208; GASID project, Jagiellonian University) — boundaries of ~700 communes and their parts, courts and political districts, vectorised from the Second Military Survey (1:28,800), with census attributes 1857–1910: population present (civil, military), sex, houses, Umgangssprache, religion, foreigners, area; livestock at district level. The 1910 commune figures are typed from the Spezialortsrepertorium … XI. Schlesien (1917). No institutions | shapefiles (.dbf attributes) + description PDF | Harvard Dataverse doi:10.7910/DVN/K7YPAF, CC0 1.0 | External ground truth, not a pipeline input: an independent transcription of the register's Silesian table, read by utils/validation/check_ostafin_silesia.py (D-183) against our census, the full run's table readings and the ČSÚ lexicon 1869–2005. ⚠ Its commune-name field changes within a commune, some settlements are filed as communes of their own, and its area is measured on the map | data/raw/geo/ostafin_silesia/ (1910 layers only: SA_1910_{CmP,DC,DP}; SOURCE.txt + SHA256.txt) |

Why they are listed apart: sources #1–#9 supply data the pipeline consumes; #10, #13, #14,

21 and #30 supply facts the pipeline is checked against; #19 and #20 supply the **scheme a variable

is built on** — the school ladder behind education.py, which no dataset in data/raw/ states. Nothing in data/ derives from them. Fidler was the first genuinely independent yardstick the project had — csol.cz and VÚA share the same archival lineage, so agreement between them is not evidence the parsing is right.

What CZ-ISCO can and cannot do. It is the 2025 classification, so it validates a reading — what a Czech occupational word means in English — and nothing more. The historical core of this corpus is absent from it at every level (rolník, hajný, příručí, tkadlec, mlynář, kolář, bednář, cihlář, havíř, soustružník), and it reaches about 57 % of coded records. Nor is there a path from it to HISCO: the ILO manual's correspondence tables stop at ISCO-88 while HISCO is built on ISCO-68, and chaining two lossy crosswalks across forty years of occupational change into a scheme designed for 1690–1970 data would manufacture precision the sources do not carry.

Licence. ČSÚ classifications are public administrative information, reusable with attribution (zákon č. 89/1995 Sb.; ČSÚ open-data terms). The ILO volume is © International Labour Organization 2012 and is held for reference only — nothing is extracted from it into reference/.

What the Vienna Database can and cannot do. It is the only source in this project that records an occupational title and a household position for the same individual, which is the one thing Check 6 could not do — and it is weak in three ways the write-up must keep repeating. Title and position are not independent: one clerk assigned both under a listing convention that gives the farm title to the holder, so the check corroborates what the word meant to the people writing it down rather than measuring who farmed what. It covers no Czech locality — 44 places in present-day Austria, where Alpine impartible inheritance is not Bohemian estate agriculture. And it reaches rolník only through a Landwirt ≈ Bauer synonymy: the term on the closed list is BAUER, a bare LANDWIRT is absent from it, and this project's own gloss for sedlák — the Czech word that means Bauer — covers 6 records against rolník's 21,461.

Citation and licence. MOSAIC states the required form verbatim, and it is what references.bib emits: Vienna Database on European Family History. Numeric data file, Version 0.1 [SPSS file]. Vienna: University of Vienna, 2003. The terms are that data is free to any researcher conditional on registering with IPUMS and citing it properly — there is no redistribution grant, which is one more reason the .sav stays in gitignored data/ and only derived counts reach reference/.

One discrepancy, recorded rather than smoothed. MOSAIC describes the Austrian collection as "overall 180,304 persons" while giving this file as 163,400 — which is also exactly what the .sav contains. The check uses the file, so the 163,400 figure is the one that governs; the gap is presumably material in the collection but not in this release.


3c. Habsburg army — order of battle and recruitment geography

Neither a pipeline input nor a validation yardstick: these are the sources behind a crosswalk that is built but not yet joined. They give a Habsburg unit a home district and an ethnic composition, which is what turns habsburg_unit_capture / habsburg_unit_peacetime from a string into a covariate. Method, match rates and limits: reference/units/ERGAENZUNG.md.

# Source Kind Provider Role & consumer Local path
15 Ehnl, Maximilian (1934), Die österreichisch-ungarische Landmacht nach Aufbau, Gliederung, Friedensgarnison, Einteilung und nationaler Zusammensetzung im Sommer 1914 (Ergänzungsheft 9 zu Österreich-Ungarns letzter Krieg 1914–1918). Tabelle I = the 1914 peacetime order of battle: per regiment the Inhaber, founding year, Ergänzungsbezirk (recruiting district), garrison, corps/division/brigade, and the full Umgangssprache nationality breakdown — 11 languages as % of the Grundbuchstand PDF (~394 MB) + plaintext Verlag der Militärwissenschaftlichen Mitteilungen, Wien Crosswalk source. Tabelle I re-read from the page scans at 300 DPI (the fold-out's own text layer is corrupt) into reference/units/ehnl_tabelle1_*.csv — 443 units, 179 of them infantry. Supplies both the home-district join and the per-regiment homogeneity measure the Costa–Kahn mechanism needs on the Habsburg side library/ah-army/ (gitignored)
16 Schematismus für das k.u.k. Heer und für die k.u.k. Kriegsmarine für 1914, plus the k.k. Landwehr/Gendarmerie 1914 and k.u.k. 1904 volumes and Österreich-Ungarns letzter Krieg Bd. 6 (1917) PDF + OCR text k.k. Hof- und Staatsdruckerei, Wien Crosswalk source, since superseded in part. Its ErgBezKmdo. <town> tokens produced the first recruiting-district gazetteer (ergaenzungsbezirk_1914.csv; 19 Czech-lands districts resolved to a 1910 okres). Ehnl's Tabelle I later settled the regiment⇄district pairing the OCR could not library/ah-army/ (gitignored)

What this strand does and does not yet do. The crosswalk exists and is verified; it has not been joined to the person panel. 99.2 % of infantry legionnaires and 92.4 % of infantry fallen already resolve to a specific Tabelle I regiment, and 63 % / 55 % of all records fall inside some Tabelle I arm — but the binding limit is the source records, not the table: a quarter of legionnaires and over a third of the fallen carry no Habsburg unit at all. Only the Landsturm (~4–5 %) is genuinely absent from Ehnl, and necessarily so — it had no peacetime existence.

Two limits worth carrying into any specification. Recruitment ≠ garrison — only IR 14 and 41 were strictly territorially garrisoned, so a regiment predicts a soldier's home district well and his station poorly. And homogeneity decays: wartime Marschbataillone and lowered standards eroded the regional composition Tabelle I records for the summer of 1914, so the date of capture matters. Geographically the repo reaches only the Czech lands and Upper Hungary, so 55 of 179 regiments resolve to a district; the rest recruit in Galicia, inner Austria, Hungary, Croatia, Bosnia or Tyrol. The nationality columns, by contrast, are complete for all 179.


3d. Consulted for prospective covariates — acquired, not built

Recorded so that a later reader knows these were examined and what the examination concluded. Nothing in data/ or reference/ derives from #17 or #18; #22 is a cached scrape under data/raw/theatre/ and feeds nothing in data/final/; #23 is a parsed pilot whose tables stay in data/interim/vua_courts/ and likewise feeds nothing in data/final/; #28, #29 and #31 are downloaded scans in library/associations/, read by rule into data/interim/matice/ (D-158, D-184) and likewise feeding nothing in data/final/.

# Source Kind Provider What it offers Local path
17 Akademický atlas českých dějin (Semotanová – Cajthaml – Šimůnek, eds.), 2nd supplemented ed., Praha 2016, ISBN 978-80-200-2574-6 Printed atlas, consulted from a borrowed library copy Academia / Historický ústav AV ČR Plates, not data. For this project the relevant ones are IV.45b (p. 328 — corps districts and the infantry recruiting districts as polygons with named seats, no regiment numbers), IV.46 (p. 330 — mutinies, strikes and the "green cadres", i.e. Habsburg deserters at home, the last as generalised hatching), IV.4b/c (p. 265 — every political okres named, with the Moravian enclaves in Silesia), IV.33 · IV.67–69 · IV.76b · IV.85a (national-mobilisation and Czech-vs-German institutional measures at locality level), IV.48b/d · IV.52 (okres-level 1910 covariates; the marital-fertility index is the only one not already computable from #4) Partly held since 2026-09-03. Five plates are now photographed and kept in library/atlas/ (gitignored, in copyright): IV.37 (p. 317–318 — the register of 55 political parties of the Czech lands 1861–1918, with founding years, area of operation and every merger/renaming), IV.36 (p. 315–316 — election tables, diets and Reichsrat) and IV.39 (p. 320 — national composition of the diets). IV.37 is transcribed to reference/parties/atlas_iv37_parties.csv and checked by check_atlas_parties.py; it is the authority behind affiliations.py, replacing the Wikipedia sourcing that module was built on. The other plates listed at left are photographed in the same shoot and can be brought across on request
18 Purš, Jaroslav (1958), Tábory v českých zemích 1868–1871, Československý časopis historický 6, pp. 234–266, 446–470, 661–690 Digitised journal article, 88 pp. (PDF + OCR) Kramerius 7, Knihovna AV ČR — licence reported public The source behind atlas plate IV.33. A narrative, not a catalogue — no table, no appendix: "nejméně 143" assemblies (Bohemia 102, Moravia 37, Silesia 4) named in prose, with ~79 locational anchors, 56 attendance figures in round thousands, 21 banned library/literature/purs-1958_tabory/ (gitignored)
22 Databáze českého amatérského divadla (DbČAD) — feasibility pilot, 2026-10-12; not a pipeline input Online encyclopedic database; map endpoint and all 7,368 place entries fetched 2026-10-12 (7,465 requests, every one answered 200) NIK – Národní institut pro kulturu (formerly NIPOS), Praha, 2005–2026. Grew out of Místopis českého amatérského divadla I–II (Praha: IPOS-ARTAMA, 2001–2002), which its entries cite 7,368 places with coordinates (5,033 obce, 1,644 místní části, 691 části); per place the ensembles with their years (fuzzy 191x kept as a decade) and První zpráva, the first surviving report — not a founding date. The settlement-level, dated counterpart of the atlas's amateur-theatre plate (#17). ⚠ Compiled okres by okres from different source bases (one 1949 district report dates 21 of 129 places in okres Kutná Hora), and by construction a database of Czech theatre data/raw/theatre/ (gitignored): gzipped raw pages, fetch_log.csv, SOURCE.txt, parsed places.csv (with the chronicle, bibliography and archive citations of each place kept whole as chronicle_text / bibliography_text / archive_text — the database's own prose, never to leave data/) + ensembles.csv; the database's own key to its abbreviations, both lists, as abbreviations.csv (2026-10-27, D-138). Parked 2026-10-13, see ROADMAP.md. Scraper utils/scraping/scraper_amaterskedivadlo.py; tabulation utils/parsing/amateur_theatre.py via utils/reference_tools/places/run_amateur_theatre_pilot.py
23 VÚA–VHA Praha, archival inventories of three wartime military courts (Tzv. rakouské fondy do roku 1918): Divizní soud Praha (NAD 229, archivní pomůcka č. 699, Praha 2024), Zeměbranecký divizní soud Praha (NAD 360, pomůcka č. 729, Praha 2025), Zeměbranecký divizní soud – expozitura Brno (NAD 356, pomůcka č. 704, Praha 2024), all three compiled by P. Heřmánek — pilot, 2026-10-12; not a pipeline input Born-digital text PDFs (80 · 183 · 213 pp.) Vojenský ústřední archiv – Vojenský historický archiv, Praha The only Habsburg-side record of prosecuted disloyalty seen so far. One entry per case file: original signature (Dst nn/yy), the accused by name with rank, unit and birth year (the two Landwehr courts add birthplace, okres, domicile and — Praha only — occupation), a description of the offence, what the file holds, subject terms and the paragraphs cited. Parsed by utils/parsing/vua_courts.py into one row per case file and one per accused: 2,330 files / 3,034 named accused, 899 of them soldiers; 119 files flagged desertion (subject term dezerce (delikt) or § 183 MStG; 33 · 21 · 65 by court) with 127 soldiers in them. Counts as printed by utils/reference_tools/units/run_vua_courts_pilot.py (vua_courts 0.1.0), which also runs a pilot link to the legionnaire file on name + birth year with the Habsburg unit as tie-breaker, against a shifted-birth-year placebo. ⚠ Hinterland courts — men caught at home, not men who crossed the lines; survival is partial (the 1917–18 files were heavily weeded; DG Praha is estimated by its own inventory to have lost about two thirds); most accused of the two Landwehr courts are civilians; and the Brno inventory lists places without saying which is the birthplace data/raw/vua_courts/ (gitignored): the three PDFs, fetch_log.csv, SOURCE.txt (URL, retrieval time, sha256). Fetcher utils/scraping/fetch_vua_courts.py; parsed tables and candidate pairs rebuilt into data/interim/vua_courts/ by the pilot script. Nothing in data/final/ derives from it
28 Matice česká, I. Jména p.p. zakladatelův Matice české na konci r. … (with II. Výtah z oučtů and III. Seznam spisů a map), V Praze: nákladem Musea Království českého, 1869–1891 — the issues na konci r. 1873 (printed 1874, 76 pp.) and na konci r. 1890 (printed 1891, 94 pp.) — downloaded 2026-10-29, read into one row per entry (matice, D-158) and summed to a provisional okres measure (D-161); not a pipeline input Digitised periodical, page scans + OCR + ALTO (Kramerius 7) Moravská zemská knihovna (MZK); originals in the Národní knihovna ČR (54 H 421109, 54 F 152661). Licence reported public (Volná díla) The society's own yearly list of its founders (zakladatelé, members who paid the one-off founder's contribution) — a candidate measure of committed Czech nationalism. One line per founder: name, occupation, residence, the year the person became a founder and the running membership number, e.g. "Beneš Václav, rolník ve Velkých Zamachách, r. 50, č. 1656." The 1890 issue holds three lists — living founders (entry year), deceased founders (number, year and often day of death) and corporate founders (reading clubs, gymnasia, municipalities, numbered in Roman numerals) — so it reaches back to the society's start in 1831 on its own. ⚠ The residence printed is the one at the list date, not at entry; many founders are priests, teachers and officials whose posting the church or state decided; and only these two issues are digitised in MZK library/associations/ (gitignored): one PDF + OCR TXT per issue, the _pages/ cache with per-page OCR and ALTO, SOURCE.txt (URL, retrieval time, sha256). Downloader utils/scraping/run_matice_downloads.py (drives scraper_kramerius7.py)
29 Matice moravská, member lists printed in Časopis Matice moravské (Brno: nákladem Matice moravské, 1869–) — the full rosters of roč. 15 (1891, Seznam spolkových členů, bound with the Výroční zpráva … za rok 1889, pp. IV–XIII) and roč. 33 (1909, Seznam členů Matice Moravské dle stavu ze dne 1. března 1909, pp. 9–19, with Seskupení členstva dle stavu a míst, pp. 19–20); the partial lists of 1876 (Seznam údů … až do listopadu 1876, pp. 194–199), 1877 and 1878 (Do pokladnice Matice Moravské složili …, the year's payers), the 1891 additions and the Seznam důvěrníků (local trustees, Moravia and Bohemia) — located and downloaded 2026-10-29 (1878 saved by hand, the rest 56 pages from MZK); all of them, the 1878 file from its PDF text layer, read into one row per entry (matice, D-158) and summed to a provisional okres measure (D-161), in which the 1909 roster stands for Moravia and Silesia (D-182); not a pipeline input Digitised periodical, page scans + OCR + ALTO (Kramerius 7); 1878 as a bound-volume PDF (roč. 10–12, 604 pp.) MZK (periodical uuid:b3dde880-8de5-11e5-b433-5ef3fc9bb22f); licence reported public for 1869–1915, on-site only from 1916; the journal paused 1883–1890. 1878: Internet Archive gri_33125006700724, Getty Research Institute copy The Moravian counterpart of #28, and a membership rather than a founders' list. One entry per member: name, occupation, residence ("Apetauer Jos., pekař ve Ždaře."); founding members (údové zakládající, 100 zl. once or 10 × 10 zl.) separate from yearly ones, with the amount paid in 1876–1878; corporate members (reading clubs, teachers' unions, schools) in line. Found from the obsah ročníku of every public volume, since the full-text index misses the lists (no hit for zakládající in a volume that prints it). 1893–1898 print only Seznam nových členů (newcomers); no other year of 1869–1915 names a list in its contents, though 1878 shows a contents page can omit one. ⚠ A small, educated membership — priests, teachers, officials — so a measure of the national élite, not of mass mobilisation; the residence is the one at the list date; and the 1878 binding stops before the back matter of roč. 11–12 library/associations/ (gitignored): IA_Casopis-Matice-moravske_1878-1880.pdf (saved by hand, list at printed pp. 176–180); the MZK slices as MZK_Matice-moravska_<year>_*.pdf/.txt/_pages/ with SOURCE.txt, by utils/scraping/run_matice_downloads.py v1.1.0 (jobs mm1876 … mm1909)
31 Matice česká, Zpráva o Matici České, the monthly payment lists printed in the Časopis Musea království Českého (V Praze: nákladem Musea království Českého), roč. 60–88 (1886–1914) — located and downloaded 2026-11-03 (94 slices, 186 pages), read into one row per payment and summed to persons and to Bohemia's measure (matice 0.4.0, D-184); not a pipeline input Digitised periodical, page scans + OCR text (Kramerius 7; no ALTO on this host) Národní muzeum (periodical uuid:c6e7b030-14d0-11df-9021-000d606f5dc6, 1855–1922); licence reported public The founders joining after the register stops. #28 ends with the issue of 1891; from then on the society printed no founder list, but every issue of its parent journal closes with the month's payments into the Matice: name, occupation, residence and amount, e.g. "Klein Vác., rolník v Hřivici 105 K", each month closed by its total (Úhrnem). The founder's 52 zl. 50 kr. / 105 K could be paid in instalments, so a payer recurs (21 K, or 4–5 K a month for years); until 1888 the periods are ranges (Od 1 září do konce listopadu 1885) and names are printed given name first, after it month by month and surname first. Found by full-text search of each volume and the OCR of the hits (--discover-payments, manifest reference/places/matice_payment_pages.csv), since no contents page names the section. ⚠ Many payers are pupils and students whose parents paid, and schools and libraries pay a corporate fee in the same list; the residence is the one at payment; 10 of the 240 months of 1894–1913 are not found (some printed together with the next, Červenec. Srpen.), and about one month in six differs from its printed total by more than 5 %, mostly a total the OCR garbled library/associations/ (gitignored): NM_Casopis-Musea_mcpYYYY-I_Matice-prispevky.pdf/.txt/_pages/ per slice, with SOURCE.txt, by run_matice_downloads.py v1.2.0 (--set payments)

What the examination of #23 concluded — set aside, 2026-10-12; closed for good, 2026-11-03. The pilot link was run (run_vua_courts_pilot.py, vua_courts 0.1.0; the figures below are its printed output). Of 854 soldiers with a name and birth year, 96 have a legionnaire of the same name and year — against 83 and 77 when the court birth years are shifted by −4 and +4, where no true match is possible. Of the 64 picks the Habsburg unit differs in 42 of the 47 that can be compared and the birthplace in 26 of 30; 8 are corroborated by unit or birthplace, one by both. The overlap between men tried at home and men who reached the Legion is a handful, so the source gives no person-level measure of prior disloyalty. Nor a district one: the 1,896 civilians with a birthplace come from 1,241 places, 958 of them named once, and only the Prague Landwehr court states the okres (396 of its 705 civilians). Material for single cases, not for a variable. Not pursued further.

Why #17's recruiting-district plate is worth anything, given #15. Ehnl names the recruiting town, and the crosswalk represents each district by the okres of its seat. The real Ergänzungsbezirk was roughly six okresy wide — 24 of them covered the Czech lands' 158 districts. The plate is the only thing seen so far that draws the areas, which would let the join run the other way, from birth okres → regiment, and so reach the quarter of legionnaires with no unit recorded. The districts were unions of whole political okresy, so what is actually wanted is a ~158-row lookup, not traced polygons.

Why #18 is not the first nationalism covariate to build. A tábor was held on a symbolic hill — Říp, Blaník, Zvičín, Oreb — chosen for its Hussite resonance, not where its participants lived; Purš says the crowds came "z dalekého okolí". It geocodes (hill X near village Y) but the catchment is wide and undefined, which is precisely wrong for a district-level covariate. With ~102 Bohemian events over ~100 okresy the variable is near-binary and zero-inflated, and it plausibly tracks the Czech-share and urbanisation already available from #4. The institutional measures on #17 — amateur-theatre localities, Czech záložny, Czech vs German gymnasia — are resident in their towns, so the locality is the associational base. What #18 uniquely carries is repression: banned assemblies, dispersals, prosecutions, troops billeted on villages that refused taxes.

One discrepancy, recorded rather than smoothed. Plate IV.33 totals 159 assemblies (102 / 53 / 4); Purš totals 143 (102 / 37 / 4). Bohemia and Silesia agree exactly, Moravia does not — the atlas's Moravian figure must come from its other cited source, Janák, ČMM 77 (1958). Either is usable; which one is being used has to be stated.


4. Curated crosswalks (this project — auditable data files, in repo)

File Purpose
reference/places/district_cs_curation.csv MPIDR Czech district-name curation (accent restoration on the de-accented spine; #3)
reference/places/district_sk_curation.csv Magyar→Slovak/Rusyn county + district-seat names for the GISta backbone (#8a); built by build_sk_curation.py
reference/units/unit_authority.csv Legion unit → theatre / kind / formation date / lineage (361 entries), derived from Fidler 2025 (#10) by build_unit_authority.py. Facts only — no prose from the copyrighted source is extracted or stored
reference/occupations/hisco_authority.csv HISCO micro code × modifier → English label + HISCLASS / HISCLASS_5 / HISCAM / SOCPO, built from #11 + #12 by build_hisco_authority.py. CC-BY-SA 4.0 (derivative of #11)
reference/occupations/occupation_hisco_curation.csv Czech occupational string → HISCO — authored for this thesis, the piece no external source supplies; built/refreshed by build_occupation_curation.py, consumed by utils/parsing/occupations.py
reference/units/ehnl_tabelle1_{infantry,landwehr,honved,special,other}.csv The 1914 order of battle, 443 units, from #15: per unit the Ergänzungsbezirk, garrison, corps, and the 11-language Umgangssprache shares with derived dominant_lang / dominant_share / herfindahl / pct_slav; Czech-lands units join to okres_code via districts_1910, Slovak/Subcarpathian to sk_jaras via districts_hu_1910. Built and verified; not yet joined to the person panel. See reference/units/ERGAENZUNG.md
reference/units/ergaenzungsbezirk_1914.csv Recruiting-district gazetteer from the ErgBezKmdo. tokens of #16 — 19 Czech-lands districts resolved to a 1910 okres, the rest flagged out-of-region. Largely superseded by the Tabelle I files above, kept as the independent read

5. Literature GIS/citation notes

  • MPIDR / Rumpler-Seger must be cited as in reference/places/Citation information Austro-Hungarian Empire.doc: MPIDR [Max Planck Institute for Demographic Research] and CGG [University of Rostock] 2012: MPIDR Population History GIS Collection (slightly modified version of a GIS-file by Rumpler and Seger 2010) — Rostock.
  • GISta Hungarorum — attribute gistory.hu (MTA BTK); CC BY-NC.
  • Wikidata — CC0, no attribution required (acknowledged as good practice).
  • ČÚZK RÚIAN and Geonames (#24, #25) — CC-BY 4.0; attribute as Zdroj: ČÚZK (or Source: Czech Office for Surveying, Mapping and Cadastre), naming the dataset and its date (RÚIAN VFR ST_UKSG 2026-08-31; Geonames 2026-09-30).
  • PRNG (#26) — free for any use; cite Państwowy Rejestr Nazw Geograficznych, GUGiK, with the export date (2026-10-28).
  • Wikidata vanished places (#27) — CC0. Cite each place back to the register it was imported from where it carries an ID: Zaniklé obce a objekty po roce 1945, https://www.zanikleobce.cz, object <zanikleobce_id> (the ID is in vanished_places and in coord_ref).
  • History of Work / HISCO — CC-BY-SA 4.0; attribute IISG/Datalegend and license any redistributed derivative alike. Cite the classification itself as van Leeuwen, Maas & Miles (2002); HISCLASS as van Leeuwen & Maas (2011); HISCAM as Lambert et al. (2013); SOCPO as van de Putte & Miles (2005). Full text in reference/occupations/SOURCE_LICENCE.txt.

6. Source URLs

# Source Link(s)
1 Czechoslovak Legionnaire records https://www.csol.cz/legionar/ · VÚA mirror https://www.vuapraha.cz/legionari/
2 Fallen Habsburg soldiers (WWI) https://www.vuapraha.cz/padli-1-svetova/
3 MPIDR Population History GIS Collection https://mosaic.ipums.org/historical-gis-datafiles
4 1910 Cisleithanian census Rumpler-Seger companion (MOSAIC, as #3); alt. PHAIDRA (Univ. Vienna) https://phaidra.univie.ac.at/detail/o:2114309
5 Historický lexikon obcí ČR 1869–2015: https://csu.gov.cz/produkty/historicky-lexikon-obci-1869-az-2015 · 2001 ed.: https://csu.gov.cz/produkty/historicky-lexikon-obci-ceske-republiky-2001-877ljn6lu9
6 Wikidata (WDQS) https://query.wikidata.org/
7 Vollständiges Ortsverzeichnis 1910 (AC03177788, superseded) https://digi.landesbibliothek.at/viewer/fullscreen/AC03177788/68/
8a/8b GISta Hungarorum (shapefiles + census tables) https://www.gistory.hu/g/en/gistory/otka · Hungarian historical census portal https://www.nepszamlalasok.hu/index.php?oldal=adatokev
9 Historicko-demografický lexikón obcí Slovenska 1880–1910 (Majo, ŠÚSR 2012) https://slovak.statistics.sk/wps/portal/ext/products/informationmessages (ŠÚSR publications)
10 Encyklopedie československých legií (Fidler, Universum 2025) https://www.universum.cz — ISBN 978-80-242-9615-4; validation only, see reference/VALIDATION_FIDLER.md
11 History of Work — HISCO dataset https://druid.datalegend.net/HistoryOfWork/historyOfWork-all-latest/ · SPARQL https://api.druid.datalegend.net/datasets/HistoryOfWork/historyOfWork-all-latest/sparql · assets …/assets/<asset_id> (ids in download_hisco_sources.py) · project page https://iisg.amsterdam/en/data/data-websites/history-of-work
12 HSN HISCO-coded occupational titles, release 2020.02 https://socialhistory.org/en/hsn/hsn-standardized-hisco-coded-and-classified-occupational-titles-release-201801
13 CZ-ISCO (ČSÚ klasifikace 80146) + ILO ISCO-08 Volume I classification https://csu.gov.cz/klasifikace-zamestnani-cz-isco · code lists (CS/EN) via the ČSÚ metadata system https://apl.czso.cz/iSMS/klasstr.jsp?kodcis=80146 · Sdělení ČSÚ č. 399/2024 Sb. · ILO manual https://www.ilo.org/publications/international-standard-classification-occupations-2008-isco-08-structure-group — validation only, see reference/VALIDATION_OCCUPATIONS.md
14 Vienna Database on European Family History — Austrian numeric data file (unharmonized) https://mosaic.ipums.org/mosaic-data-files — Unharmonized data sets → Austria; same portal as #3. Free on IPUMS registration + proper citation. Validation only, see reference/VALIDATION_OCCUPATIONS.md Check 7
15 Ehnl (1934), Die österreichisch-ungarische Landmacht … im Sommer 1914 Ergänzungsheft 9 zu Österreich-Ungarns letzter Krieg 1914–1918, Wien 1934 — public domain by age. Crosswalk source, see reference/units/ERGAENZUNG.md
16 k.u.k. / k.k. Schematismus 1904 · 1914 k.k. Hof- und Staatsdruckerei, Wien — public domain by age; widely mirrored (ÖNB ALEX, archive.org)
17 Akademický atlas českých dějin (Semotanová – Cajthaml – Šimůnek, eds.) Academia, Praha; 2nd supplemented ed. 2016, ISBN 978-80-200-2574-6 — in copyright, consulted from a library copy, no images held here. The atlas was built in GIS at the Historický ústav AV ČR, so vector layers behind the plates exist and are worth requesting rather than transcribing
18 Purš (1958), Tábory v českých zemích 1868–1871, ČsČH 6 https://kramerius.lib.cas.cz/view/uuid:b4391333-42c7-11e1-5298-001143e3f55c — volume pid as given; article at pp. 234–266, 446–470, 661–690. Licence reported public; fetched 2026-08-11, all 88 pages with image + OCR
19 Moore (2020), Teaching the Empire Purdue University Press, Central European Studies — ISBN 978-1-55753-874-0; in copyright, reference only. Publisher https://www.press.purdue.edu
20 Lassnigg (2015), 'Muddling through' and historical institutionalism IHS Sociological Series WP 108 — free download https://irihs.ihs.ac.at/id/eprint/3735/
21 Österreichische Statistik XCIII/1 (1912), Statistik der Unterrichtsanstalten … 1908/1909 k.k. Hof- und Staatsdruckerei, Wien — public domain by age. Scanned by the Österreichische Nationalbibliothek (https://www.onb.ac.at); downloaded 2026-09-07 in 15 parts and merged. ⚠ Einleitung X–XI and table page 15 were not fetched — re-export them from the same ÖNB viewer if the Gymnasium detail or Table 6 is ever needed
22 Databáze českého amatérského divadla (NIK, formerly NIPOS) https://www.amaterskedivadlo.cz — place entry main.php?data=geo&id={geo_id}, map endpoint ajax/map2.php. Cite as: Databáze českého amatérského divadla [online]. Praha: NIK – Národní institut pro kulturu (dříve NIPOS), 2005–2026 [cit. 2026-10-12]. All rights reserved by NIK; no robots.txt (404), pages carry robots: ALL,FOLLOW, no terms of use or citation rule published. Retrieved under the §90 research basis of §1, sequentially at ≥ 1.5 s per request; raw pages are not redistributed. Contact for the operators is on the site's Kontakt page — worth writing to before a full scrape
23 VÚA–VHA military-court inventories (DG Praha · LDG Praha · LDG – expozitura Brno) https://www.vuapraha.cz/wp-content/uploads/2024/02/Divizni-soud-Praha.pdf · https://www.vuapraha.cz/wp-content/uploads/2025/08/Zemebranecky-divizni-soud-Praha.pdf · https://www.vuapraha.cz/wp-content/uploads/2024/09/Zemebranecky-divizni-soud-expozitura-Brno.pdf — retrieved 2026-10-12 (exact time and sha256 per file in data/raw/vua_courts/SOURCE.txt, written by the fetcher from its own log). Finding aids published by the archive on its own site; robots.txt allows /wp-content/. Three requests, sequential, ≥ 3 s apart, TLS verified through the operating system's trust store, User-Agent naming the script only. Retrieved under the §90 research basis of §1; the PDFs are not redistributed
24 ČÚZK RÚIAN, VFR state file ST_UKSG listing https://services.cuzk.gov.cz/vfr/ · file https://services.cuzk.gov.cz/vfr/202608/20260831_ST_UKSG.xml.zip · contents (FAQ 14) https://cuzk.gov.cz/ruian/Poskytovani-udaju-ISUI-RUIAN-VDP/Vymenny-format-RUIAN-(VFR)/FAQ-casto-kladene-otazky-k-VFR/14-Co-obsahuje-kompletni-datova-sada-(K).aspx
25 ČÚZK Geonames (GPKG) Atom feed https://atom.cuzk.gov.cz/GEONAMES-GPKG/GEONAMES-GPKG.xml · file https://openzu.cuzk.gov.cz/opendata/GEONAMES-GPKG/epsg-5514/GEONAMES-5514-gpkg-20260930.zip
26 PRNG localities (GUGiK) https://opendata.geoportal.gov.pl/prng/PRNG_MIEJSCOWOSCI_XLSX.zip · about https://www.geoportal.gov.pl/pl/dane/panstwowy-rejestr-nazw-geograficznych-prng/
27 Wikidata vanished places + zanikleobce.cz IDs SPARQL endpoint https://query.wikidata.org/sparql (query text in data/raw/geo/wikidata_vanished/SOURCE.txt) · property P3003 https://www.wikidata.org/wiki/Property:P3003 · register https://www.zanikleobce.cz
28 Matice česká founder register, issues 1873 · 1890 (MZK) 1873: https://www.digitalniknihovna.cz/mzk/view/uuid:08f95d10-1773-11eb-bd18-005056827e51 · 1890: https://www.digitalniknihovna.cz/mzk/view/uuid:c86bfec0-021d-11eb-8183-005056827e52 · API host https://api.kramerius.mzk.cz/search/api/client/v7.0/ (no robots.txt, 404 on 2026-10-29). Licence reported public; fetched 2026-10-29 sequentially, 3 s between requests, one attempt per request, no sign-in or credentials
29 Matice moravská member lists (Časopis Matice moravské, MZK + Internet Archive) periodical https://www.digitalniknihovna.cz/mzk/periodical/uuid:b3dde880-8de5-11e5-b433-5ef3fc9bb22f · 1891 roster https://www.digitalniknihovna.cz/mzk/view/uuid:7ff8af90-9f04-11e8-ba56-5ef3fc9bb22f · 1909 roster https://www.digitalniknihovna.cz/mzk/view/uuid:ddbb8cc0-998b-11e8-a1e1-005056827e51 · the slices' pids in run_matice_downloads.py · 1878 https://archive.org/details/gri_33125006700724. Licence reported public (MZK, 1869–1915); same host and politeness as #28
30 Ostafin et al. 2020, Austrian Silesia 1837–1910 https://doi.org/10.7910/DVN/K7YPAF · paper https://doi.org/10.1038/s41597-020-0546-z
31 Matice česká monthly payment lists (Časopis Musea království Českého, Národní muzeum) periodical https://kramerius.nm.cz/periodical/uuid:c6e7b030-14d0-11df-9021-000d606f5dc6 · every slice's issue pid and page positions in reference/places/matice_payment_pages.csv · API host https://kramerius.nm.cz/search/api/client/v7.0/ (no robots.txt: the address answers the viewer's page, 2026-11-03). Licence reported public; fetched 2026-11-03 sequentially, 3 s between requests, up to 3 attempts per request (the host drops a TLS handshake now and then), no sign-in or credentials

Derived final datasets produced

data/final/ (all built by notebooks/02_data_cleaning.ipynb): legionnaires_clean · habsburg_clean (person-level, standardised) · districts_1910 · census_okres_1910 · settlements_1910 (Cisleithanian) · districts_hu_1910 · census_hu_jaras_1910 · settlements_hu_1910 (Transleithanian obec crosswalk + 1910 mother-tongue shares, Majo 2012) · {legionnaires,habsburg}_census_1910 · {legionnaires,habsburg}_census_hu_1910 (wide, person × covariates) · overlap_matches · data_dictionary.csv.

ReferencesWhat's in the data

Literature cited, in APA — hover any citation to preview.

References

Every work cited anywhere on this site — 46 entries, in APA style. This list is the master: reference/references.bib is generated from it at build time, so the two cannot disagree and neither can omit a work the other has. Inline mentions elsewhere — Author (year) — are clickable and preview the full reference on hover.

Literature

Bednařík, F. (1927). V boj! Obrázková kronika československého revolučního hnutí v Italii 1915–1918 [Into battle! A pictorial chronicle of the Czechoslovak revolutionary movement in Italy, 1915–1918]. Za svobodu.
Boháč, J. (1938). Kronika československé legie ve Francii: Kniha prvá — Rota Nazdar 1914–1916 [Chronicle of the Czechoslovak legion in France: Book one — the Nazdar Company, 1914–1916]. Naše záloha.
Burgschwentner, J., Egger, M., & Barth-Scalmani, G. (Eds.). (2014). Other fronts, other wars? First World War studies on the eve of the centennial. Brill.
Cornwall, M. (2000). The undermining of Austria-Hungary: The battle for hearts and minds. Macmillan.
Costa, D. L., & Kahn, M. E. (2003). Cowards and heroes: Group loyalty in the American Civil War. The Quarterly Journal of Economics, 118(2), 519–548. source ↗
Deák, I. (1990). Beyond nationalism: A social and political history of the Habsburg officer corps, 1848–1918. Oxford University Press.
Dippel, C., & Heblich, S. (2021). Leadership in social movements: Evidence from the "Forty-Eighters" in the Civil War. American Economic Review, 111(2), 472–505. source ↗
Dolejší, J., Fučík, J., Havel, P., & Pernes, J. (2003). Pod císařským praporem: Historie habsburské armády 1526–1918 [Under the imperial banner: A history of the Habsburg army, 1526–1918]. Elka Press.
Fan, X., Richardson, G., Xu, Z., & Zhao, S. (2026). Reason for treason (NBER Working Paper No. 35069). National Bureau of Economic Research. source ↗
Ferrara, A., Dippel, C., & Heblich, S. (2025). Frontline leadership: Evidence from American Civil War captains (NBER Working Paper No. 34057). National Bureau of Economic Research. source ↗
Fidler, J. (2025). Encyklopedie československých legií [Encyclopedia of the Czechoslovak Legions]. Universum. source ↗
Hutečka, J. (2019). Men under fire: Motivation, morale, and masculinity among Czech soldiers in the Great War, 1914–1918. Berghahn Books.
Kelly, M. (2019). The standard errors of persistence (CEPR Discussion Paper No. 13783). Centre for Economic Policy Research.
Klub přátel pplk. Karla Vašátka. (2009). Hodnostní označení čs. legií na Rusi 1918–1920 [Rank insignia of the Czechoslovak legions in Russia, 1918–1920]. http://www.karelvasatko.cz/uniformy-ruske-legie/hodnostni-oznaceni-rlvseobecne source ↗
Lassnigg, L. (2015). ‘Muddling through’ and historical institutionalism: Explanatory devices for the long term development of the dualistic Austrian VET system (IHS Sociological Series No. 108). Institut für Höhere Studien. source ↗
Lloyd, N. (2024). The Eastern Front: A history of the First World War. Viking.
McNamara, K. J. (2016). Dreams of a great small nation: The mutinous army that threatened a revolution, destroyed an empire, founded a republic, and remade the map of Europe. PublicAffairs.
Moore, S. O. (2020). Teaching the empire: Education and state loyalty in late Habsburg Austria. Purdue University Press. source ↗
Ottinger, S., & Voigtländer, N. (2025). History's masters: The effect of European monarchs on state performance. Econometrica, 93(1), 95–128. source ↗
Purdek, I. (2013). Československá vojenská symbolika v rokoch 1914–1939 [Czechoslovak military symbolism, 1914–1939]. Vojenský historický ústav.
Rest, S., Ortner, M. C., & Ilming, T. (2002). Des Kaisers Rock im 1. Weltkrieg: Uniformierung und Ausrüstung der österreichisch-ungarischen Armee von 1914 bis 1918 [The emperor's coat in the First World War]. Verlag Militaria.
Rothenberg, G. E. (1976). The Army of Francis Joseph. Purdue University Press.
Rozenas, A., Talibova, R., & Zhukov, Y. M. (2024). Fighting for tyranny: State repression and combat motivation. American Economic Journal: Applied Economics, 16(3), 44–75. source ↗
Stevenson, D. (2004). Cataclysm: The First World War as political tragedy. Basic Books.
Vaněk, O., Holeček, V., & Medek, R. (Eds.). (1925–1929). Za svobodu: Obrázková kronika československého revolučního hnutí na Rusi 1914–1920 [For freedom: A pictorial chronicle of the Czechoslovak revolutionary movement in Russia, 1914–1920] (Vols. 1–4). Památník odboje.

Statutes & standards

The interwar Czechoslovak statutes behind the rank framework, and the NATO code standard the common scale is expressed in. Full treatment on the Rank recognition 1918–38 sheet.

Vládní nařízení č. 141/1927 Sb. z. a n., jímž se vydávají nové branné předpisy [Government decree No. 141/1927 Coll., issuing new defence regulations]. (1927). Sbírka zákonů a nařízení, částka 64. source ↗
Zákon č. 194/1920 Sb. z. a n., jímž se upravuje přijímání vojenských gážistů bývalé rakousko-uherské, rakouské a uherské branné moci do branné moci republiky Československé [Act No. 194/1920 Coll., on the admission of former Austro-Hungarian military officials into the Czechoslovak armed forces]. (1920). Sbírka zákonů a nařízení, částka 41. source ↗
Zákon č. 195/1920 Sb. z. a n., o úpravě služebních požitků československého vojska [Act No. 195/1920 Coll., on the regulation of the service emoluments of the Czechoslovak army]. (1920). Sbírka zákonů a nařízení, částka 41. source ↗
Zákon č. 462/1919 Sb. z. a n., o propůjčování míst legionářům [Act No. 462/1919 Coll., on the allocation of posts to legionnaires]. (1919). Sbírka zákonů a nařízení, částka 93. source ↗
Ministerstvo obrany České republiky. (n.d.). Vojenské hodnosti [Military ranks]. Retrieved 2026, from https://doarmady.mo.gov.cz/o-armade/poznejte-armadu/vojenske-hodnosti source ↗
NATO Standardization Office. (2022). APersP-01: NATO codes for grades of military personnel (Edition A, Version 3). North Atlantic Treaty Organization.

Data & reference sources

Vollständiges Ortsverzeichnis 1910 [Complete gazetteer of settlements, 1910] [Digitised gazetteer, ALTO OCR; shelfmark AC03177788]. Österreichische Nationalbibliothek. https://digi.landesbibliothek.at/viewer/fullscreen/AC03177788/68/ source ↗
Ehnl, M. (1934). Die österreichisch-ungarische Landmacht nach Aufbau, Gliederung, Friedensgarnison, Einteilung und nationaler Zusammensetzung im Sommer 1914 (Ergänzungsheft 9 zu Österreich-Ungarns letzter Krieg 1914–1918). Verlag der Militärwissenschaftlichen Mitteilungen.
MPIDR & CGG. (2012). MPIDR population history GIS collection: Austro-Hungarian Empire 1910 [Data set, based on Rumpler & Seger 2010]. Max Planck Institute for Demographic Research & University of Rostock. source ↗
MTA BTK Történettudományi Intézet. (n.d.). GISta Hungarorum: Historical GIS and 1910 census of the Kingdom of Hungary [Data set]. Retrieved 2026, from https://www.gistory.hu/g/en/gistory/otka source ↗
Majo, J. (2012). Historicko-demografický lexikón obcí Slovenska 1880–1910 [Historical-demographic lexicon of Slovak municipalities, 1880–1910]. Štatistický úrad Slovenskej republiky.
Národní institut pro kulturu. (2005–2026). Databáze českého amatérského divadla [Database of Czech amateur theatre] [Data set]. Retrieved October 12, 2026, from https://www.amaterskedivadlo.cz source ↗
Ostafin, K., Kaim, D., Siwek, T., & Miklar, A. (2020). Historical dataset of administrative units with social-economic attributes for Austrian Silesia 1837–1910. Scientific Data, 7, 208. https://doi.org/10.1038/s41597-020-0546-z [Data set: Harvard Dataverse, https://doi.org/10.7910/DVN/K7YPAF] source ↗
Rumpler, H., & Seger, M. (2010). Die Habsburgermonarchie 1848–1918: Bd. IX/2. Soziale Strukturen [1910 Cisleithanian census, companion to the MPIDR GIS]. Verlag der Österreichischen Akademie der Wissenschaften. source ↗
Vienna Database on European Family History. (2003). Numeric data file (Version 0.1) [SPSS file]. University of Vienna. Distributed by MOSAIC, from https://mosaic.ipums.org/mosaic-data-files source ↗
Vojenský ústřední archiv. (n.d.). Padlí v 1. světové válce [Fallen in World War I] [Data set]. Retrieved 2026, from https://www.vuapraha.cz/padli-1-svetova/ source ↗
Wikidata contributors. (n.d.). Wikidata [Data set]. Wikimedia Foundation. Retrieved 2026, from https://query.wikidata.org/ source ↗
k.u.k. Kriegsministerium. (1914). Schematismus für das k.u.k. Heer und für die k.u.k. Kriegsmarine für 1914. k.k. Hof- und Staatsdruckerei.
Československá obec legionářská & Vojenský ústřední archiv. (n.d.). Databáze legionářů [Database of legionnaires] [Data set]. Retrieved 2026, from https://www.csol.cz/legionar/ source ↗
Český statistický úřad. (2015). Historický lexikon obcí České republiky 1869–2011 [Historical lexicon of municipalities of the Czech Republic, 1869–2011] [Data set]. https://csu.gov.cz/produkty/historicky-lexikon-obci-1869-az-2015 source ↗

Full provenance, licences and access notes for each dataset are on the Data sources sheet; dataset citations here follow APA data-set style (retrieval year for the live databases).

Symbol usageWhat's in the data

Every public function and who calls it.

Where every public symbol is used

Each public function/class in utils/, and every notebook or module that calls it. Found by searching for name( across the notebooks and utils/, so it cannot see dynamic dispatch, and a few deliberately generic names are excluded. Treat it as a strong hint, not proof.

No call site found 97

Public, but never called from a notebook or another module. Either superseded, or a standalone/interactive helper — worth a look before it quietly rots. (This is how wikidata.add_birth_coords was found to be superseded by records_geo.add_settlement_attrs.)

ModuleSymbolKind
utils/lexikon_1910/bakeoff.pyselect_modelsfunction
utils/lexikon_1910/bakeoff.pyrunnablefunction
utils/lexikon_1910/bakeoff.pyrun_matrixfunction
utils/lexikon_1910/bakeoff.pymeasure_encodersfunction
utils/lexikon_1910/bakeoff.pyprobefunction
utils/lexikon_1910/bakeoff.pyrate_limit_eventsfunction
utils/lexikon_1910/bakeoff.pylexikon_1910_rate_limit_reportfunction
utils/lexikon_1910/build_lexikon_figure.pyZoneclass
utils/lexikon_1910/draw_gold_sample.pyhas_apparatusfunction
utils/lexikon_1910/lexikon_1910.pycrop_headerfunction
utils/lexikon_1910/lexikon_1910_census.pyReconciliationclass
utils/lexikon_1910/lexikon_1910_de.pyDeGridclass
utils/lexikon_1910/lexikon_1910_de.pymatch_blocksfunction
utils/lexikon_1910/lexikon_1910_image.pyHeadingclass
utils/lexikon_1910/lexikon_1910_image.pylexikon_1910_image_reportfunction
utils/lexikon_1910/lexikon_1910_institutions.pyComponentclass
utils/lexikon_1910/lexikon_1910_institutions.pycompare_inventoriesfunction
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_inventory_agreement_reportfunction
utils/lexikon_1910/lexikon_1910_institutions.pysymbol_confusionfunction
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_symbol_error_reportfunction
utils/lexikon_1910/lexikon_1910_institutions.pycompare_passesfunction
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_agreement_reportfunction
utils/lexikon_1910/lexikon_1910_notes.pyImageCutclass
utils/lexikon_1910/lexikon_1910_notes.pyLegendEntryclass
utils/lexikon_1910/lexikon_1910_notes.pyreferenced_keysfunction
utils/lexikon_1910/lexikon_1910_notes.pylexikon_1910_parts_reportfunction
utils/lexikon_1910/lexikon_1910_pages.pysweep_variantfunction
utils/lexikon_1910/lexikon_1910_pages.pyRunConfigclass
utils/lexikon_1910/lexikon_1910_pages.pyconfig_reportfunction
utils/lexikon_1910/lexikon_1910_pages.pyestimate_reportfunction
utils/lexikon_1910/lexikon_1910_pages.pyPageRequestclass
utils/lexikon_1910/lexikon_1910_pages.pyPageReaderclass
utils/lexikon_1910/lexikon_1910_pages.pyGooglePageReaderclass
utils/lexikon_1910/lexikon_1910_pages.pyAnthropicPageReaderclass
utils/lexikon_1910/lexikon_1910_pages.pyde_pages_for_okresfunction
utils/lexikon_1910/lexikon_1910_pages.pyokres_name_defunction
utils/lexikon_1910/lexikon_1910_pages.pybuild_requestsfunction
utils/lexikon_1910/lexikon_1910_pages.pyrun_syncfunction
utils/lexikon_1910/lexikon_1910_pages.pyrun_batchfunction
utils/lexikon_1910/lexikon_1910_pages.pyadopt_batchfunction
utils/lexikon_1910/lexikon_1910_pages.pyblocks_to_rereadfunction
utils/lexikon_1910/lexikon_1910_pages.pymonitor_reportfunction
utils/lexikon_1910/lexikon_1910_pages.pyassemble_claimsfunction
utils/lexikon_1910/lexikon_1910_pages.pywrite_claimsfunction
utils/lexikon_1910/lexikon_1910_pages.pyvotesfunction
utils/lexikon_1910/lexikon_1910_pages.pyvote_reportfunction
utils/lexikon_1910/lexikon_1910_pages.pylexikon_1910_pages_reportfunction
utils/lexikon_1910/lexikon_1910_pages.pypreflightfunction
utils/lexikon_1910/lexikon_1910_volume.pyIdentityclass
utils/lexikon_1910/lexikon_1910_volume.pyHeaderGroupclass
utils/lexikon_1910/lexikon_1910_volume.pyVolumeProfileclass
utils/lexikon_1910/lexikon_1910_volume.pylexikon_1910_volume_reportfunction
utils/lexikon_1910/locate_glyphs.pyLinesclass
utils/lexikon_1910/locate_glyphs.pyGlyphModelclass
utils/lexikon_1910/locate_glyphs.pyModifiersclass
utils/lexikon_1910/run_frozen.pyread_livefunction
utils/lexikon_1910/run_frozen.pybatch_finishfunction
utils/lexikon_1910/run_lexikon_1910_pilot.pyOllamaReaderclass
utils/lexikon_1910/run_r5_german_notes.pyPageCutclass
utils/parsing/amateur_theatre.pyokres_tokensfunction
utils/parsing/amateur_theatre.pyactive_decade_listfunction
utils/parsing/clean.pystrip_place_markersfunction
utils/parsing/districts.pyattach_gazetteer_namesfunction
utils/parsing/districts.pywrite_curation_templatefunction
utils/parsing/districts_hu.pywrite_sk_curation_templatefunction
utils/parsing/education.pyclean_educationfunction
utils/parsing/education.pyladder_reportfunction
utils/parsing/exit_codes.pylabel_exit_codesfunction
utils/parsing/exit_codes.pyparse_exit_datefunction
utils/parsing/habsburg.pyparse_causefunction
utils/parsing/habsburg.pyextract_theatrefunction
utils/parsing/habsburg.pyparse_ah_unitsfunction
utils/parsing/lexikon.pysettlement_slugfunction
utils/parsing/locations.pyparse_locationfunction
utils/parsing/locative.pynoun_locativesfunction
utils/parsing/locative.pyadjective_locativesfunction
utils/parsing/locative.pynoun_genitivesfunction
utils/parsing/locative.pyadjective_genitivesfunction
utils/parsing/ranks.pyenglish_labelfunction
utils/parsing/ranks.pyresolve_ordinalsfunction
utils/parsing/ranks.pybranch_offunction
utils/parsing/ranks.pycommon_gradefunction
utils/parsing/religion.pytranslate_religionfunction
utils/parsing/units.pyextract_regimentfunction
utils/parsing/units.pyextract_armyfunction
utils/parsing/units.pytranslate_unitfunction
utils/parsing/wikidata.pyadd_birth_coordsfunction
utils/plotting/th_style_v113.pyannotate_barsfunction
utils/plotting/th_style_v113.pydespinefunction
utils/plotting/th_style_v113.pyadd_vspansfunction
utils/reference_tools/occupations/build_czisco_bridge.pyhead_nounsfunction
utils/reference_tools/places/audit_full_run_pages.pyaudit_pagefunction
utils/reference_tools/places/map_intensities.pyBasemapclass
utils/reference_tools/units/build_unit_authority.pyheadword_numberfunction
utils/scraping/progress.pyhas_tqdmfunction
utils/scraping/scraper_csol.pyScrapeSummaryclass
utils/tables/latex_tables.pyregression_tablefunction

Full symbol index

1,832 rows

ModuleSymbolKindCalled by
utils/docs/build_docs.pyix_headfunction(internal use only)
utils/docs/build_docs.pyfavicon_hreffunction(internal use only)
utils/docs/build_docs.pymd_to_htmlfunction(internal use only)
utils/docs/build_docs.pybuild_stampfunction(internal use only)
utils/docs/build_docs.pycollect_scriptsfunction(internal use only)
utils/docs/build_docs.pygraph_sheetfunction(internal use only)
utils/docs/build_docs.pyusage_sheetfunction(internal use only)
utils/docs/build_docs.pychangelog_sheetfunction(internal use only)
utils/docs/build_docs.pymodule_indexfunction(internal use only)
utils/docs/build_docs.pydf_tablefunction(internal use only)
utils/docs/build_docs.pycertification_powerfunction(internal use only)
utils/docs/build_docs.pyrank_reset_histfunction(internal use only)
utils/docs/build_docs.pyrank_reset_areasfunction(internal use only)
utils/docs/build_docs.pyrender_bibfunction(internal use only)
utils/docs/build_docs.pyreferences_sheetfunction(internal use only)
utils/docs/build_docs.pywaterfall_blockfunction(internal use only)
utils/docs/build_docs.pywaterfall_block_habsburgfunction(internal use only)
utils/docs/build_docs.pyprovenance_sheetfunction(internal use only)
utils/docs/build_docs.pyplace_flowfunction(internal use only)
utils/docs/build_docs.pyeducation_figuresfunction(internal use only)
utils/docs/build_docs.pytheatre_figuresfunction(internal use only)
utils/docs/build_docs.pymatice_figuresfunction(internal use only)
utils/docs/build_docs.pymatice_checkfunction(internal use only)
utils/docs/build_docs.pyintensity_figuresfunction(internal use only)
utils/docs/build_docs.pylexikon_pagefunction(internal use only)
utils/docs/build_docs.pyesc_vfunction(internal use only)
utils/docs/build_docs.pyroute_flowfunction(internal use only)
utils/docs/build_docs.pyoccupation_flowfunction(internal use only)
utils/docs/build_docs.pyreading_mapfunction(internal use only)
utils/docs/build_docs.pyranks_bridgefunction(internal use only)
utils/docs/build_docs.pyunits_sheetfunction(internal use only)
utils/docs/build_docs.pyoverview_factsfunction(internal use only)
utils/docs/build_docs.pyoverview_sheetfunction(internal use only)
utils/docs/build_docs.pylive_sheetsfunction(internal use only)
utils/docs/build_docs.pyworking_treefunction(internal use only)
utils/docs/build_docs.pymainfunction(internal use only)
utils/docs/build_fonts.pymainfunction(internal use only)
utils/lexikon_1910/bakeoff.pyfind_rootfunction(internal use only)
utils/lexikon_1910/bakeoff.pyhost_slugfunction(internal use only)
utils/lexikon_1910/bakeoff.pyensure_dirsfunctionutils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/bakeoff.pygold_pagesfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/bakeoff.pyaccuracy_pagesfunction(internal use only)
utils/lexikon_1910/bakeoff.pyollama_showfunction(internal use only)
utils/lexikon_1910/bakeoff.pyunloadfunction(internal use only)
utils/lexikon_1910/bakeoff.pydaemon_upfunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/bakeoff.pypulled_modelsfunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/bakeoff.pyselect_modelsfunction— no call site found —
utils/lexikon_1910/bakeoff.pyrunnablefunction— no call site found —
utils/lexikon_1910/bakeoff.pyload_encodersfunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/bakeoff.pylabel_forfunctionutils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/bakeoff.pyresolution_forfunction(internal use only)
utils/lexikon_1910/bakeoff.pyflags_forfunctionutils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/bakeoff.pyappliesfunction(internal use only)
utils/lexikon_1910/bakeoff.pyn_pagesfunction(internal use only)
utils/lexikon_1910/bakeoff.pyplanfunctiontests/test_e1b_candidates.py, tests/test_r5_german_notes.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/bakeoff.pyload_timingsfunctionutils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/bakeoff.pyestimatefunctiontests/test_r5_german_notes.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/bakeoff.pyshow_planfunction(internal use only)
utils/lexikon_1910/bakeoff.pyfile_stampfunction(internal use only)
utils/lexikon_1910/bakeoff.pyTeeclass(internal use only)
utils/lexikon_1910/bakeoff.pyrun_pilotfunction01_data_collection.ipynb, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_table_arm.py, utils/scraping/scraper_verlustlisten.py
utils/lexikon_1910/bakeoff.pyrun_matrixfunction— no call site found —
utils/lexikon_1910/bakeoff.pyfind_pass_filefunctionutils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_verify_test_b.py
utils/lexikon_1910/bakeoff.pyload_passfunctionutils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_verify_test_b.py
utils/lexikon_1910/bakeoff.pypasses_on_diskfunctionutils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/bakeoff.pyencoder_cropfunction(internal use only)
utils/lexikon_1910/bakeoff.pyprompt_tokensfunctionutils/lexikon_1910/lexikon_1910_pages.py
utils/lexikon_1910/bakeoff.pymeasure_encodersfunction— no call site found —
utils/lexikon_1910/bakeoff.pyprobefunction— no call site found —
utils/lexikon_1910/bakeoff.pyrate_limit_eventsfunction— no call site found —
utils/lexikon_1910/bakeoff.pyvolume_cropsfunction(internal use only)
utils/lexikon_1910/bakeoff.pylexikon_1910_rate_limit_reportfunction— no call site found —
utils/lexikon_1910/bakeoff.pyde_atlasfunctiontests/test_german_plates.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/bakeoff.pyde_gold_pagesfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/bakeoff.pyde_labelfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/bakeoff.pyde_passes_on_diskfunctionutils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/bakeoff.pysummaryfunctionutils/docs/build_docs.py
utils/lexikon_1910/build_contrast_sheet.pyroles_offunction(internal use only)
utils/lexikon_1910/build_contrast_sheet.pyswapsfunctiontests/test_contrast_arm.py
utils/lexikon_1910/build_contrast_sheet.pypick_pairsfunctiontests/test_contrast_arm.py
utils/lexikon_1910/build_contrast_sheet.pyconfirmed_boxesfunction(internal use only)
utils/lexikon_1910/build_contrast_sheet.pycandidate_cutsfunction(internal use only)
utils/lexikon_1910/build_contrast_sheet.pychoosefunctiontests/test_contrast_arm.py
utils/lexikon_1910/build_contrast_sheet.pyink_sharefunctionutils/lexikon_1910/build_symbol_atlas.py
utils/lexikon_1910/build_contrast_sheet.pychoose_weightedfunctiontests/test_contrast_arm.py
utils/lexikon_1910/build_contrast_sheet.pyrenderfunctionutils/docs/build_docs.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/make_gold_worksheet_de.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/build_contrast_sheet.pybuildfunction(internal use only)
utils/lexikon_1910/build_contrast_sheet.pymainfunction(internal use only)
utils/lexikon_1910/build_de_page_map.pynormalisefunctiontests/test_court_key.py, tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/lexikon_1910_names.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py, utils/parsing/matice.py, utils/reference_tools/places/build_german_click_queue.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/build_de_page_map.pyparse_headfunction(internal use only)
utils/lexikon_1910/build_de_page_map.pycourt_crosswalkfunction(internal use only)
utils/lexikon_1910/build_de_page_map.pyde_page_wordsfunction(internal use only)
utils/lexikon_1910/build_de_page_map.pyde_page_courtsfunction(internal use only)
utils/lexikon_1910/build_de_page_map.pyload_de_volumefunction(internal use only)
utils/lexikon_1910/build_de_page_map.pycz_page_namesfunction(internal use only)
utils/lexikon_1910/build_de_page_map.pymatch_pagefunction(internal use only)
utils/lexikon_1910/build_de_page_map.pybuildfunction(internal use only)
utils/lexikon_1910/build_de_page_map.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/build_de_page_map.pymainfunction(internal use only)
utils/lexikon_1910/build_glyph_bank.pyline_heightfunctionutils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/german_candidate_diagnosis.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/build_glyph_bank.pyscan_line_heightfunctionutils/lexikon_1910/locate_glyphs.py
utils/lexikon_1910/build_glyph_bank.pyto_sourcefunction(internal use only)
utils/lexikon_1910/build_glyph_bank.pytight_boxfunctionutils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/build_glyph_bank.pycutfunctiontests/test_contrast_arm.py, tests/test_glyph_e3.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/amateur_theatre.py, utils/parsing/ruian.py
utils/lexikon_1910/build_glyph_bank.pycontact_sheetsfunction(internal use only)
utils/lexikon_1910/build_glyph_bank.pymainfunction(internal use only)
utils/lexikon_1910/build_lexikon_figure.pyZoneclass— no call site found —
utils/lexikon_1910/build_lexikon_figure.pyBoxerclass(internal use only)
utils/lexikon_1910/build_lexikon_figure.pyczech_pagefunction(internal use only)
utils/lexikon_1910/build_lexikon_figure.pygerman_pagefunction(internal use only)
utils/lexikon_1910/build_lexikon_figure.pypilot_countsfunction(internal use only)
utils/lexikon_1910/build_lexikon_figure.pyglyphsfunctionutils/reference_tools/places/run_german_oracle.py
utils/lexikon_1910/build_lexikon_figure.pybuildfunction(internal use only)
utils/lexikon_1910/build_lexikon_figure.pymainfunction(internal use only)
utils/lexikon_1910/build_mark_ledger.pypathsfunctionutils/lexikon_1910/lexikon_1910_pages.py
utils/lexikon_1910/build_mark_ledger.pyconfigurefunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/build_mark_ledger.pysection_offunction(internal use only)
utils/lexikon_1910/build_mark_ledger.pyreader_eventsfunctiontests/test_route_fixes.py
utils/lexikon_1910/build_mark_ledger.pyorder_eventsfunction(internal use only)
utils/lexikon_1910/build_mark_ledger.pyopus_eventsfunctiontests/test_abbreviations.py, tests/test_opus_cards.py, tests/test_route_fixes.py
utils/lexikon_1910/build_mark_ledger.pyauthor_eventsfunction(internal use only)
utils/lexikon_1910/build_mark_ledger.pyunplaced_goldfunction(internal use only)
utils/lexikon_1910/build_mark_ledger.pybuildfunction(internal use only)
utils/lexikon_1910/build_mark_ledger.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/build_mark_ledger.pytrailsfunction(internal use only)
utils/lexikon_1910/build_mark_ledger.pymainfunction(internal use only)
utils/lexikon_1910/build_register_walkthrough.pybuildfunction(internal use only)
utils/lexikon_1910/build_register_walkthrough.pymainfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas.pycluster_linesfunctionutils/lexikon_1910/build_symbol_atlas_de.py
utils/lexikon_1910/build_symbol_atlas.pycolumns_offunction(internal use only)
utils/lexikon_1910/build_symbol_atlas.pyload_targetsfunctionutils/lexikon_1910/build_symbol_atlas_de.py
utils/lexikon_1910/build_symbol_atlas.pymatch_glossfunctionutils/lexikon_1910/build_symbol_atlas_de.py
utils/lexikon_1910/build_symbol_atlas.pybuildfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas.pyink_sharefunctionutils/lexikon_1910/build_contrast_sheet.py
utils/lexikon_1910/build_symbol_atlas.pymainfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pyload_targetsfunctionutils/lexikon_1910/build_symbol_atlas.py
utils/lexikon_1910/build_symbol_atlas_de.pyplate_wordsfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pydark_maskfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pyplate_inkfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pydrop_rulesfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pyink_columnsfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pyplate_entriesfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pybody_rowsfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pybuildfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pyborrowfunction(internal use only)
utils/lexikon_1910/build_symbol_atlas_de.pycomposefunctiontests/test_glyph_e3.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/build_symbol_atlas_de.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/build_symbol_atlas_de.pymainfunction(internal use only)
utils/lexikon_1910/build_token_review.pysuspects_in_sheetfunction(internal use only)
utils/lexikon_1910/build_token_review.pycandidatesfunctionutils/reference_tools/occupations/build_conflict_workbook.py, utils/reference_tools/occupations/run_czisco_check.py, utils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/build_token_review.pytext_linesfunctionutils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/run_verify_test_b.py, utils/reference_tools/places/build_german_click_strips.py
utils/lexikon_1910/build_token_review.pymunicipality_bandsfunctionutils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/build_german_click_queue.py
utils/lexikon_1910/build_token_review.pychallengesfunction(internal use only)
utils/lexikon_1910/build_token_review.pyheadingsfunctionutils/docs/build_docs.py
utils/lexikon_1910/build_token_review.pybox_itemsfunction(internal use only)
utils/lexikon_1910/build_token_review.pybuildfunction(internal use only)
utils/lexikon_1910/build_token_review.pywrite_heading_rulingsfunction(internal use only)
utils/lexikon_1910/build_token_review.pyapplyfunction02_data_cleaning.ipynb, tests/test_amateur_theatre.py, utils/docs/build_docs.py, utils/parsing/affiliations.py, utils/parsing/amateur_theatre.py, utils/parsing/amateur_theatre_model.py, utils/parsing/census_labels.py, utils/parsing/dates.py, utils/parsing/districts.py, utils/parsing/education.py, utils/parsing/exit_codes.py, utils/parsing/ranks.py, utils/parsing/theater.py, utils/parsing/units.py, utils/reference_tools/occupations/build_conflict_workbook.py, utils/reference_tools/places/run_intensities.py, utils/validation/build_validation_workbook.py
utils/lexikon_1910/build_token_review.pymainfunction(internal use only)
utils/lexikon_1910/check_transcription.pysplit_entriesfunctionutils/parsing/vua_courts.py
utils/lexikon_1910/check_transcription.pyparsefunction(internal use only)
utils/lexikon_1910/check_transcription.pymainfunction(internal use only)
utils/lexikon_1910/draw_gold_sample.pydrawfunctionutils/docs/build_docs.py, utils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/map_intensities.py
utils/lexikon_1910/draw_gold_sample.pyhas_apparatusfunction— no call site found —
utils/lexikon_1910/draw_gold_sample.pydraw_volumefunctiontests/test_ms_sample.py
utils/lexikon_1910/draw_gold_sample.pymainfunction(internal use only)
utils/lexikon_1910/extract_prose_pages.pytable_footfunctionutils/lexikon_1910/lexikon_1910_notes.py
utils/lexikon_1910/extract_prose_pages.pylinesfunctionutils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/extract_prose_pages.pyparagraphsfunction(internal use only)
utils/lexikon_1910/extract_prose_pages.pyvision_readfunction(internal use only)
utils/lexikon_1910/extract_prose_pages.pymainfunction(internal use only)
utils/lexikon_1910/lexikon_1910.pysummary_labelfunctiontests/test_de_table.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/lexikon_1910_de_table.py
utils/lexikon_1910/lexikon_1910.pyInvariantclassutils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/read_transcription_md.py
utils/lexikon_1910/lexikon_1910.pyparse_int_cellfunctionutils/lexikon_1910/lexikon_1910_census.py, utils/lexikon_1910/lexikon_1910_de.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pyAltoPageclasstests/test_lexikon_1910_table_foot.py
utils/lexikon_1910/lexikon_1910.pyread_altofunctionutils/lexikon_1910/bakeoff.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/extract_prose_pages.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910.pycolumn_boundariesfunction(internal use only)
utils/lexikon_1910/lexikon_1910.pycolumn_templatefunctionutils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pyfit_columnsfunctionutils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pywrite_grid_templatefunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pyread_grid_templatefunctionutils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/make_gold_worksheet.py
utils/lexikon_1910/lexikon_1910.pybody_bandfunctiontests/test_lexikon_1910_table_foot.py, tests/test_lexikon_1910_table_top.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_pages.py
utils/lexikon_1910/lexikon_1910.pytable_bottomfunctiontests/test_lexikon_1910_table_foot.py
utils/lexikon_1910/lexikon_1910.pyheader_footfunctiontests/test_lexikon_1910_table_top.py
utils/lexikon_1910/lexikon_1910.pytable_topfunctiontests/test_lexikon_1910_table_top.py
utils/lexikon_1910/lexikon_1910.pyRowclasstests/test_lexikon_1910_pilot_checks.py, tests/test_score_table_passes.py, utils/lexikon_1910/lexikon_1910_de.py, utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pyextract_rowsfunctiontests/test_lexikon_1910_table_foot.py, tests/test_lexikon_1910_table_top.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pyBlockclasstests/test_lexikon_1910_pilot_checks.py, tests/test_score_table_passes.py, utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/read_transcription_md.py
utils/lexikon_1910/lexikon_1910.pysegment_blocksfunctionutils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pycarry_blockfunctiontests/test_lexikon_1910_pilot_checks.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pysummary_rowsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pygrade_rowfunctionutils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/read_transcription_md.py
utils/lexikon_1910/lexikon_1910.pygrade_block_sumfunctiontests/test_lexikon_1910_pilot_checks.py, utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pygrade_sequencefunction(internal use only)
utils/lexikon_1910/lexikon_1910.pygrade_pagefunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pyrender_pagefunctionutils/lexikon_1910/bakeoff.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/extract_prose_pages.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910.pyrunning_headfunctiontests/test_lexikon_1910_table_top.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pycrop_blockfunctionutils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/lexikon_1910.pyscale_to_widthfunctionutils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_notes.py
utils/lexikon_1910/lexikon_1910.pytile_blockfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910.pycrop_headerfunction— no call site found —
utils/lexikon_1910/lexikon_1910.pylexikon_1910_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_attributes.pyromanfunctiontests/test_lexikon_1910_attributes.py
utils/lexikon_1910/lexikon_1910_attributes.pyschoolsfunctiontests/test_lexikon_1910_attributes.py
utils/lexikon_1910/lexikon_1910_attributes.pycooperativesfunctiontests/test_lexikon_1910_attributes.py
utils/lexikon_1910/lexikon_1910_attributes.pyattribute_recordsfunctiontests/test_lexikon_1910_attributes.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_attributes.pyentry_partsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_attributes.pyscore_attributesfunctiontests/test_lexikon_1910_attributes.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_attributes.pylexikon_1910_attributes_reportfunctiontests/test_lexikon_1910_attributes.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_census.pyis_summary_rowfunction(internal use only)
utils/lexikon_1910/lexikon_1910_census.pyReconciliationclass— no call site found —
utils/lexikon_1910/lexikon_1910_census.pyregister_totalsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_census.pycensus_targetsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_census.pyhouse_candidatesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_census.pyreconcilefunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_census.pydiagnosefunction(internal use only)
utils/lexikon_1910/lexikon_1910_census.pylexikon_1910_census_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_courts.pyslugfunctiontests/test_court_key.py
utils/lexikon_1910/lexikon_1910_courts.pyslug_variantsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_courts.pyload_backbonefunctiontests/test_court_key.py, tests/test_prefreeze_route.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_courts.pycourt_formsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_courts.pycourt_idfunctiontests/test_route_fixes.py
utils/lexikon_1910/lexikon_1910_courts.pynearfunctiontests/test_court_key.py, tests/test_lexikon_1910_image.py, utils/parsing/ruian.py
utils/lexikon_1910/lexikon_1910_courts.pyhead_courtsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_courts.pyload_aliasesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_courts.pyCourtKeyclasstests/test_court_key.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/score_certification.py
utils/lexikon_1910/lexikon_1910_courts.pylexikon_1910_courts_reportfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_crosswalk.pygerman_keyfunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_crosswalk.pypair_courtfunctiontests/test_register_build.py, utils/lexikon_1910/build_register_walkthrough.py
utils/lexikon_1910/lexikon_1910_crosswalk.pypair_partsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_crosswalk.pyorder_holdoutfunctiontests/test_register_build.py, utils/lexikon_1910/build_register_walkthrough.py
utils/lexikon_1910/lexikon_1910_crosswalk.pycrosswalk_editionsfunctiontests/test_register_build.py, utils/lexikon_1910/lexikon_1910_register.py
utils/lexikon_1910/lexikon_1910_crosswalk.pycrosswalk_reportfunctionutils/lexikon_1910/lexikon_1910_register.py, utils/parsing/ruian.py
utils/lexikon_1910/lexikon_1910_de.pynormalise_namefunctiontests/test_amateur_theatre.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/parsing/amateur_theatre.py
utils/lexikon_1910/lexikon_1910_de.pybuild_page_indexfunctionutils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de.pyokres_pagesfunctionutils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de.pyDeGridclass— no call site found —
utils/lexikon_1910/lexikon_1910_de.pyfit_gridfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de.pyextract_rows_defunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de.pyis_headingfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de.pymatch_blocksfunction— no call site found —
utils/lexikon_1910/lexikon_1910_de.pycompare_cellsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de.pylexikon_1910_de_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de_notes.pyheader_system_defunctiontests/test_de_table.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pybuild_system_defunctiontests/test_german_plates.py, tests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pynative_widthfunction(internal use only)
utils/lexikon_1910/lexikon_1910_de_notes.pyrender_nativefunctiontests/test_de_table.py, utils/lexikon_1910/draw_gold_sample.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/audit_full_run_pages.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910_de_notes.pystrip_heightfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pyempty_notesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_de_notes.pycut_pagefunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pylast_heading_cropfunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pyheader_cropfunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pyheader_checkfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pyfoldfunctiontests/test_ostafin_silesia.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_certification.py, utils/validation/check_ostafin_silesia.py, utils/validation/lexikon_population.py
utils/lexikon_1910/lexikon_1910_de_notes.pyexpand_court_abbreviationsfunctiontests/test_route_fixes.py, utils/lexikon_1910/lexikon_1910_courts.py
utils/lexikon_1910/lexikon_1910_de_notes.pyname_formsfunctiontests/test_r5_german_notes.py, tests/test_route_fixes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_de_notes.pyload_court_crosswalkfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pycourt_namefunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_notes.pyrelabel_sectionsfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_table.pyPrintedRowclasstests/test_cache_guard.py, tests/test_de_table.py
utils/lexikon_1910/lexikon_1910_de_table.pyTableBlockclasstests/test_de_table.py
utils/lexikon_1910/lexikon_1910_de_table.pycolumn_indexfunctiontests/test_lexikon_1910_volume.py, utils/lexikon_1910/lexikon_1910_volume.py
utils/lexikon_1910/lexikon_1910_de_table.pydetectfunctiontests/test_lexikon_1910_image.py, tests/test_lexikon_1910_pilot_geometry.py, utils/lexikon_1910/draw_gold_sample.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_image.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_de_table.pytable_inkfunction(internal use only)
utils/lexikon_1910/lexikon_1910_de_table.pyprinted_rowsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_de_table.pycut_blocksfunctiontests/test_cache_guard.py, tests/test_de_table.py
utils/lexikon_1910/lexikon_1910_de_table.pypage_blocksfunctiontests/test_de_table.py, utils/lexikon_1910/run_de_table.py, utils/reference_tools/places/audit_full_run_pages.py
utils/lexikon_1910/lexikon_1910_de_table.pycrop_blockfunctionutils/lexikon_1910/lexikon_1910.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/lexikon_1910_de_table.pybuild_systemfunctiontests/test_contrast_arm.py, tests/test_de_table.py, tests/test_e1b_candidates.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_de_table.pyrow_schemafunctiontests/test_de_table.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/lexikon_1910_de_table.pygrade_blockfunctiontests/test_de_table.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/lexikon_1910_de_table.pysummary_labelfunctiontests/test_de_table.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/lexikon_1910.py
utils/lexikon_1910/lexikon_1910_de_table.pycourt_labelsfunctiontests/test_de_table.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/lexikon_1910_de_table.pytable_rowsfunctiontests/test_score_table_passes.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/validation/check_ostafin_silesia.py
utils/lexikon_1910/lexikon_1910_de_table.pylexikon_1910_de_table_reportfunctionutils/lexikon_1910/run_de_table.py
utils/lexikon_1910/lexikon_1910_editions.pyname_formsfunctiontests/test_r5_german_notes.py, tests/test_route_fixes.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_editions.pypair_unitsfunctiontests/test_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_editions.pypair_by_formsfunctiontests/test_cz_anchor.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_editions.pyunit_claimsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_editions.pytruth_itemsfunctiontests/test_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_editions.pyjoint_errorsfunctiontests/test_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_editions.pycompare_pairfunction(internal use only)
utils/lexikon_1910/lexikon_1910_editions.pykeep_codesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_editions.pycompare_editionsfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py, tests/test_r5_german_notes.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_editions.pylexikon_1910_editions_reportfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_image.pyRuleclasstests/test_lexikon_1910_image.py
utils/lexikon_1910/lexikon_1910_image.pyLineclasstests/test_cz_anchor.py
utils/lexikon_1910/lexikon_1910_image.pyHeadingclass— no call site found —
utils/lexikon_1910/lexikon_1910_image.pySegmentclasstests/test_r5_german_notes.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_image.pyPageGeometryclasstests/test_lexikon_1910_image.py
utils/lexikon_1910/lexikon_1910_image.pyto_greyfunctionutils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_notes.py
utils/lexikon_1910/lexikon_1910_image.pyink_thresholdfunctionutils/lexikon_1910/lexikon_1910_de_table.py
utils/lexikon_1910/lexikon_1910_image.pysheet_boxfunction(internal use only)
utils/lexikon_1910/lexikon_1910_image.pyvertical_rulesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_image.pyhorizontal_rulesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_image.pyrule_maskfunctiontests/test_lexikon_1910_image.py, utils/lexikon_1910/lexikon_1910_notes.py
utils/lexikon_1910/lexikon_1910_image.pyink_bandsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_image.pylines_infunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_image.pyfind_headingsfunctiontests/test_cz_anchor.py
utils/lexikon_1910/lexikon_1910_image.pyblank_cutsfunctiontests/test_lexikon_1910_image.py, tests/test_lexikon_1910_pilot_geometry.py, utils/lexikon_1910/lexikon_1910_notes.py
utils/lexikon_1910/lexikon_1910_image.pydetectfunctiontests/test_lexikon_1910_image.py, tests/test_lexikon_1910_pilot_geometry.py, utils/lexikon_1910/draw_gold_sample.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_image.pyis_edgefunction(internal use only)
utils/lexikon_1910/lexikon_1910_image.pysegmentsfunctiontests/test_lexikon_1910_image.py
utils/lexikon_1910/lexikon_1910_image.pycropsfunction15_frozen_run.ipynb, tests/test_lexikon_1910_image.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_image.pyuncoveredfunctiontests/test_lexikon_1910_image.py
utils/lexikon_1910/lexikon_1910_image.pyzone_offunction(internal use only)
utils/lexikon_1910/lexikon_1910_image.pylexikon_1910_image_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pyAbbreviationclasstests/test_lexikon_1910_order.py, tests/test_pooled_score.py
utils/lexikon_1910/lexikon_1910_institutions.pyload_abbreviationsfunctiontests/test_abbreviations.py, tests/test_contrast_arm.py, tests/test_de_table.py, tests/test_e1b_candidates.py, tests/test_german_plates.py, tests/test_lookalike_rules.py, tests/test_prefreeze_route.py, tests/test_r5_german_notes.py, tests/test_religion_categories.py, tests/test_repeated_marks.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/check_transcription.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_r5_german_notes.py, utils/lexikon_1910/score_table_passes.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/lexikon_1910_institutions.pyload_religion_categoriesfunctiontests/test_religion_categories.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/score_certification.py
utils/lexikon_1910/lexikon_1910_institutions.pyEntryclasstests/test_abbreviations.py, tests/test_german_plates.py, tests/test_lexikon_1910_order.py, tests/test_lookalike_rules.py, tests/test_prefreeze_route.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_institutions.pyparse_entriesfunctiontests/test_lexikon_1910_parts.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/report_unlisted_abbreviations.py
utils/lexikon_1910/lexikon_1910_institutions.pycooperative_colon_rulefunction(internal use only)
utils/lexikon_1910/lexikon_1910_institutions.pybracketed_glyph_rulefunction(internal use only)
utils/lexikon_1910/lexikon_1910_institutions.pydecode_entryfunctiontests/test_abbreviations.py, tests/test_german_plates.py, tests/test_lexikon_1910_order.py, tests/test_lookalike_rules.py, tests/test_prefreeze_route.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_institutions.pydecode_sectionsfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_institutions.pyComponentclass— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pyload_componentsfunctiontests/test_abbreviations.py, utils/lexikon_1910/report_unlisted_abbreviations.py
utils/lexikon_1910/lexikon_1910_institutions.pyexpand_abbreviatedfunctiontests/test_abbreviations.py
utils/lexikon_1910/lexikon_1910_institutions.pysplit_itemsfunctiontests/test_abbreviations.py
utils/lexikon_1910/lexikon_1910_institutions.pyunlisted_itemsfunctiontests/test_abbreviations.py, utils/lexikon_1910/report_unlisted_abbreviations.py
utils/lexikon_1910/lexikon_1910_institutions.pyunlisted_reportfunction15_frozen_run.ipynb, tests/test_abbreviations.py, utils/lexikon_1910/report_unlisted_abbreviations.py
utils/lexikon_1910/lexikon_1910_institutions.pyjoin_symbolsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_join_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_institutions.pyplace_indexfunctionutils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/reference_tools/places/run_matice_pilot.py
utils/lexikon_1910/lexikon_1910_institutions.pyattach_placesfunctionutils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_places_joined_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_institutions.pybuild_inventoryfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_inventory_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_institutions.pycompare_inventoriesfunction— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_inventory_agreement_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pydescribe_referencefunctionutils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_institutions.pyscore_against_referencefunctiontests/test_lexikon_1910_scoring.py, tests/test_r5_german_notes.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_accuracy_reportfunctiontests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pysymbol_confusionfunction— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_symbol_error_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pysymbol_ratesfunctiontests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_symbol_rates_reportfunctiontests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pypart_attributionfunctiontests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_part_attribution_reportfunctiontests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pyclustered_intervalfunctiontests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_clustered_interval_reportfunctiontests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pysecond_readerfunctiontests/test_lexikon_1910_second_reader.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_second_reader_reportfunctiontests/test_lexikon_1910_second_reader.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_runsfunctiontests/test_lexikon_1910_order.py, utils/lexikon_1910/read_transcription_md.py
utils/lexikon_1910/lexikon_1910_institutions.pylearn_orderfunctiontests/test_lexikon_1910_order.py, tests/test_repeated_marks.py
utils/lexikon_1910/lexikon_1910_institutions.pyout_of_orderfunctiontests/test_lexikon_1910_order.py, tests/test_unit_timeline.py, utils/reference_tools/units/build_unit_timeline.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_restartfunctiontests/test_lexikon_1910_order.py
utils/lexikon_1910/lexikon_1910_institutions.pyrepeatable_marksfunctiontests/test_repeated_marks.py
utils/lexikon_1910/lexikon_1910_institutions.pyrepeated_marksfunctiontests/test_repeated_marks.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_allowsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_institutions.pyorder_supportsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_institutions.pyrepeat_candidatesfunctiontests/test_repeated_marks.py
utils/lexikon_1910/lexikon_1910_institutions.pycheck_orderfunctiontests/test_lexikon_1910_order.py, tests/test_repeated_marks.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_institutions.pyposition_swapsfunctiontests/test_lookalike_rules.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_summaryfunctiontests/test_lexikon_1910_order.py, tests/test_repeated_marks.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_reference_flagsfunctiontests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_joinsfunctiontests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_sheet_rowsfunctionutils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_scoresfunctiontests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pyorder_disputesfunctiontests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_order_reportfunctiontests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_order_disputes_reportfunctiontests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/lexikon_1910_institutions.pycompare_passesfunction— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pydisagreementsfunctionutils/lexikon_1910/lexikon_1910_editions.py, utils/reference_tools/occupations/run_occcanine_check.py
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_agreement_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_institutions.pylexikon_1910_institutions_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_ledger.pyEventclasstests/test_abbreviations.py, tests/test_cz_anchor.py, tests/test_frozen_run.py, tests/test_lexikon_1910_ledger.py, tests/test_lookalike_rules.py, tests/test_pooled_score.py, tests/test_prefreeze_route.py, tests/test_route_fixes.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_ledger.pybox_markfunctiontests/test_cz_anchor.py, tests/test_route_fixes.py, utils/lexikon_1910/build_mark_ledger.py
utils/lexikon_1910/lexikon_1910_ledger.pyunplaced_markfunctiontests/test_abbreviations.py, tests/test_cz_anchor.py, tests/test_lookalike_rules.py, tests/test_route_fixes.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_ledger.pywrite_ledgerfunctiontests/test_lexikon_1910_ledger.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_ledger.pyread_ledgerfunctiontests/test_lexikon_1910_ledger.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_frozen.py, utils/scraping/scraper_verlustlisten.py
utils/lexikon_1910/lexikon_1910_ledger.pyby_markfunctionutils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_ledger.pymajorityfunctionutils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/score_r5_german_notes.py, utils/parsing/amateur_theatre.py
utils/lexikon_1910/lexikon_1910_ledger.pyflaggedfunctiontests/test_verify_test_b.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/vua_courts.py
utils/lexikon_1910/lexikon_1910_ledger.pyfinal_valuefunctiontests/test_lexikon_1910_ledger.py
utils/lexikon_1910/lexikon_1910_ledger.pywith_finalfunctiontests/test_abbreviations.py, tests/test_cz_anchor.py, tests/test_frozen_run.py, tests/test_lexikon_1910_ledger.py, tests/test_lookalike_rules.py, tests/test_route_fixes.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_ledger.pyis_claim_markfunction(internal use only)
utils/lexikon_1910/lexikon_1910_ledger.pyclaim_rulingsfunctiontests/test_abbreviations.py, tests/test_cz_anchor.py, tests/test_route_fixes.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_ledger.pyrunning_valuesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_ledger.pystep_changesfunctiontests/test_lexikon_1910_ledger.py, utils/lexikon_1910/build_mark_ledger.py
utils/lexikon_1910/lexikon_1910_ledger.pytrailfunctionutils/lexikon_1910/build_mark_ledger.py
utils/lexikon_1910/lexikon_1910_names.pyfold_lookalikesfunctiontests/test_lexikon_1910_running_head.py, utils/lexikon_1910/lexikon_1910_courts.py
utils/lexikon_1910/lexikon_1910_names.pyshape_keyfunction(internal use only)
utils/lexikon_1910/lexikon_1910_names.pyhead_politicalsfunctiontests/test_lexikon_1910_running_head.py, utils/lexikon_1910/lexikon_1910_courts.py
utils/lexikon_1910/lexikon_1910_names.pynormalisefunctiontests/test_court_key.py, tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py, utils/parsing/matice.py, utils/reference_tools/places/build_german_click_queue.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910_names.pyparse_name_cellfunctionutils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/lexikon_1910_crosswalk.py
utils/lexikon_1910/lexikon_1910_names.pyObecclassutils/lexikon_1910/lexikon_1910_crosswalk.py
utils/lexikon_1910/lexikon_1910_names.pyAuthorityclasstests/test_court_key.py, tests/test_lexikon_1910_running_head.py
utils/lexikon_1910/lexikon_1910_names.pyassign_okresyfunctiontests/test_lexikon_1910_running_head.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_names.pybuild_authorityfunctionutils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/reference_tools/occupations/build_hisco_authority.py
utils/lexikon_1910/lexikon_1910_names.pyadd_register_okresyfunctiontests/test_lexikon_1910_running_head.py
utils/lexikon_1910/lexikon_1910_names.pyAnchorclass(internal use only)
utils/lexikon_1910/lexikon_1910_names.pyanchor_blockfunctionutils/lexikon_1910/lexikon_1910_register.py
utils/lexikon_1910/lexikon_1910_names.pyanchor_blocksfunctionutils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_names.pyharvest_printed_namesfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_names.pypair_by_obecfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_names.pyanchor_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pylegend_key_marksfunctionutils/lexikon_1910/read_transcription_md.py
utils/lexikon_1910/lexikon_1910_notes.pynormalisefunctiontests/test_court_key.py, tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/lexikon_1910_names.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py, utils/parsing/matice.py, utils/reference_tools/places/build_german_click_queue.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910_notes.pyfind_headingfunctiontests/test_lexikon_1910_apparatus_coverage.py, tests/test_lexikon_1910_headings.py, tests/test_lexikon_1910_volume.py, tests/test_r5_german_notes.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_notes.pySectionclasstests/test_lexikon_1910_parts.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/run_e3_pages.py, utils/reference_tools/places/german_locator_trial.py
utils/lexikon_1910/lexikon_1910_notes.pyNotesclasstests/test_lexikon_1910_pilot_geometry.py, tests/test_lexikon_1910_segment_foot.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_notes.pydivider_rulefunctiontests/test_lexikon_1910_apparatus_coverage.py, tests/test_lexikon_1910_table_foot.py
utils/lexikon_1910/lexikon_1910_notes.pyfallback_dividerfunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pytable_footfunctionutils/lexikon_1910/extract_prose_pages.py
utils/lexikon_1910/lexikon_1910_notes.pyapparatus_bandfunctionutils/lexikon_1910/lexikon_1910.py
utils/lexikon_1910/lexikon_1910_notes.pyextract_notesfunctiontests/test_lexikon_1910_apparatus_coverage.py, tests/test_lexikon_1910_table_foot.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pynotes_boxesfunctiontests/test_lexikon_1910_segment_foot.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pyline_cutsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pycrop_notesfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pySegmentclasstests/test_r5_german_notes.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_image.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pytrailing_sectionfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pysection_segmentsfunctiontests/test_lexikon_1910_apparatus_coverage.py, tests/test_lexikon_1910_pilot_geometry.py, tests/test_lexikon_1910_segment_foot.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pycrop_segmentsfunctionutils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_notes.pyImageCutclass— no call site found —
utils/lexikon_1910/lexikon_1910_notes.pypage_inkfunctiontests/test_lexikon_1910_pilot_geometry.py
utils/lexikon_1910/lexikon_1910_notes.pytext_headingsfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_notes.pyimage_section_segmentsfunctiontests/test_lexikon_1910_pilot_geometry.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pytrailing_from_segmentsfunctiontests/test_lexikon_1910_pilot_geometry.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_notes.pycollect_sectionsfunctionutils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pyLegendEntryclass— no call site found —
utils/lexikon_1910/lexikon_1910_notes.pypostal_legendfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_notes.pyparse_referencefunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pyparse_distancefunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pymatch_districtfunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pyresolve_placesfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pylexikon_1910_places_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pykeys_by_districtfunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pyparts_by_districtfunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pyaltitudesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_notes.pyreferenced_keysfunction— no call site found —
utils/lexikon_1910/lexikon_1910_notes.pycourt_offunctionutils/lexikon_1910/score_certification.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/lexikon_1910_notes.pyentry_chunksfunctionutils/lexikon_1910/lexikon_1910_institutions.py
utils/lexikon_1910/lexikon_1910_notes.pysplit_partsfunctiontests/test_lexikon_1910_parts_score.py, utils/lexikon_1910/read_transcription_md.py
utils/lexikon_1910/lexikon_1910_notes.pyparts_recordsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_notes.pylexikon_1910_parts_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_notes.pyscore_partsfunctiontests/test_lexikon_1910_parts_score.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_notes.pylexikon_1910_parts_score_reportfunctiontests/test_lexikon_1910_parts_score.py, utils/lexikon_1910/rescore_passes.py
utils/lexikon_1910/lexikon_1910_notes.pyparts_checkfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_notes.pylexikon_1910_notes_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_pages.pyImageRuleclass(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyModelSpecclass(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyload_credentialsfunction15_frozen_run.ipynb, utils/lexikon_1910/extract_prose_pages.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910_pages.pysave_credentialsfunction15_frozen_run.ipynb
utils/lexikon_1910/lexikon_1910_pages.pymaskfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pycredentials_reportfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pycheck_credentialsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pysweep_variantfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyprompt_tokensfunctionutils/lexikon_1910/bakeoff.py
utils/lexikon_1910/lexikon_1910_pages.pyRunConfigclass— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyconfig_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyTileclass(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyplan_tilesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pytile_tokensfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pycut_tilesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyrule_cutsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pypage_bandsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyplan_bandsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyto_gemini_schemafunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pybuild_page_systemfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyfewshot_from_goldfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyload_fewshotfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyUsageclass(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyappend_ledgerfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyread_ledgerfunctiontests/test_lexikon_1910_ledger.py, utils/lexikon_1910/lexikon_1910_ledger.py, utils/lexikon_1910/run_frozen.py, utils/scraping/scraper_verlustlisten.py
utils/lexikon_1910/lexikon_1910_pages.pypage_sizefunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyestimatefunctiontests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910_pages.pyprojectfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyestimate_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyPageRequestclass— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyJobHandleclass(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyPageReaderclass— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyencode_pngfunctionutils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_pages.pyGooglePageReaderclass— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyAnthropicPageReaderclass— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pybuild_readerfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_pages.pycustom_idfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyrenderfunctionutils/docs/build_docs.py, utils/lexikon_1910/build_contrast_sheet.py, utils/lexikon_1910/make_gold_worksheet_de.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910_pages.pydetect_dividerfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyapparatus_dividerfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyload_de_page_mapfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyde_pages_for_czfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyde_pages_for_okresfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyokres_name_defunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyalto_pagefunctionutils/lexikon_1910/build_de_page_map.py
utils/lexikon_1910/lexikon_1910_pages.pybuild_requestsfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pychunk_by_sizefunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pypendingfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyrun_syncfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyrun_batchfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyknown_job_idsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pybatches_reportfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyadopt_batchfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pycancel_batchfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyload_jobsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pycollect_jobsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyclaims_from_payloadfunctionutils/lexikon_1910/run_e1_gemini_version.py
utils/lexikon_1910/lexikon_1910_pages.pynormalise_symbolfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pygrade_page_tablefunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyblocks_to_rereadfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pymonitor_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyplace_indexfunctionutils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/reference_tools/places/run_matice_pilot.py
utils/lexikon_1910/lexikon_1910_pages.pyattach_placesfunctionutils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_pages.pyload_resultsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_pages.pyassemble_claimsfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pywrite_claimsfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyvotesfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyroutefunctionutils/docs/build_docs.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/score_certification.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/lexikon_1910_pages.pyvote_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pylexikon_1910_pages_reportfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pypreflightfunction— no call site found —
utils/lexikon_1910/lexikon_1910_pages.pyload_abbreviationsfunctiontests/test_abbreviations.py, tests/test_contrast_arm.py, tests/test_de_table.py, tests/test_e1b_candidates.py, tests/test_german_plates.py, tests/test_lookalike_rules.py, tests/test_prefreeze_route.py, tests/test_r5_german_notes.py, tests/test_religion_categories.py, tests/test_repeated_marks.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/check_transcription.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_r5_german_notes.py, utils/lexikon_1910/score_table_passes.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/lexikon_1910_register.pyRunInputclasstests/test_de_table.py, tests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pyparse_run_namefunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pyname_source_pathsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pyload_runfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pyrecord_extra_pathsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pycourt_keyfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/lexikon_1910_register.pyclean_displayfunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pycell_numberfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pyrow_figuresfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pywalking_hoursfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pykeyed_referencesfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pylegend_itemsfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pyplace_referencesfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pyregister_unitsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pyload_authorityfunctionutils/lexikon_1910/build_register_walkthrough.py
utils/lexikon_1910/lexikon_1910_register.pyanchor_placesfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pysettled_byfunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pybuild_institutionsfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pybuild_cooperativesfunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pyspread_namesfunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pyspread_columnsfunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pymunicipality_ownerfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pybuild_municipalitiesfunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pyschool_typefunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pyschool_rungfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pyschool_columnsfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pybuild_runfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pybuild_statutoryfunctiontests/test_register_figures.py
utils/lexikon_1910/lexikon_1910_register.pybuild_registerfunction15_frozen_run.ipynb, tests/test_register_build.py, utils/lexikon_1910/build_register_walkthrough.py
utils/lexikon_1910/lexikon_1910_register.pycrosswalk_stalefunctiontests/test_register_build.py
utils/lexikon_1910/lexikon_1910_register.pyanchor_summaryfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pyregister_reportfunction15_frozen_run.ipynb
utils/lexikon_1910/lexikon_1910_register.pyokres_checkfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pybirthplace_joinfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pyregister_checksfunction15_frozen_run.ipynb
utils/lexikon_1910/lexikon_1910_register.pyrun_foldersfunction15_frozen_run.ipynb
utils/lexikon_1910/lexikon_1910_register.pymeasure_anchoringfunction15_frozen_run.ipynb
utils/lexikon_1910/lexikon_1910_register.pyundocumented_columnsfunctionutils/validation/data_checks.py
utils/lexikon_1910/lexikon_1910_register.pybuild_blockfunction(internal use only)
utils/lexikon_1910/lexikon_1910_register.pywrite_build_blockfunction15_frozen_run.ipynb
utils/lexikon_1910/lexikon_1910_vision.pycanonicalfunctionutils/lexikon_1910/build_token_review.py, utils/lexikon_1910/check_transcription.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/read_transcription_md.py
utils/lexikon_1910/lexikon_1910_vision.pypictogramsfunctiontests/test_german_plates.py, tests/test_r5_german_notes.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_verify_test_b.py
utils/lexikon_1910/lexikon_1910_vision.pyvocabularyfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/check_transcription.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_r5_german_notes.py, utils/lexikon_1910/score_table_passes.py, utils/reference_tools/places/statutory_institutions.py
utils/lexikon_1910/lexikon_1910_vision.pybuild_systemfunctiontests/test_contrast_arm.py, tests/test_de_table.py, tests/test_e1b_candidates.py, utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pylines_from_textfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pyguard_tokensfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_vision.pydrop_repeatsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_vision.pysymbols_checkfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pysymbols_check_sectionsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pycontent_checkfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pynotes_from_regionsfunctionutils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pysections_from_segmentsfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_vision.pycompare_notesfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pycompare_sectionsfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_vision.pylexikon_1910_vision_reportfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/lexikon_1910_volume.pyColumnclasstests/test_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_volume.pyIdentityclass— no call site found —
utils/lexikon_1910/lexikon_1910_volume.pyHeaderGroupclass— no call site found —
utils/lexikon_1910/lexikon_1910_volume.pyVolumeProfileclass— no call site found —
utils/lexikon_1910/lexikon_1910_volume.pynormalisefunctiontests/test_court_key.py, tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/lexikon_1910_names.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py, utils/parsing/matice.py, utils/reference_tools/places/build_german_click_queue.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/lexikon_1910_volume.pyparse_profilefunction(internal use only)
utils/lexikon_1910/lexikon_1910_volume.pyload_profilefunctiontests/test_de_table.py, tests/test_lexikon_1910_volume.py, tests/test_r5_german_notes.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/draw_gold_sample.py, utils/lexikon_1910/make_gold_worksheet_de.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/lexikon_1910_volume.pyprofilesfunctiontests/test_lexikon_1910_volume.py
utils/lexikon_1910/lexikon_1910_volume.pyczech_driftfunctiontests/test_lexikon_1910_volume.py
utils/lexikon_1910/lexikon_1910_volume.pylexikon_1910_volume_reportfunction— no call site found —
utils/lexikon_1910/locate_glyphs.pyPartsclasstests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pycompositesfunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pyparts_offunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pybase_offunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/locate_glyphs.pycomposefunctiontests/test_glyph_e3.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pyClaimclasstests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/german_locator_trial.py
utils/lexikon_1910/locate_glyphs.pySectionclasstests/test_lexikon_1910_parts.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/run_e3_pages.py, utils/reference_tools/places/german_locator_trial.py
utils/lexikon_1910/locate_glyphs.pygold_sheetsfunctiontests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pyclaims_in_answerfunctiontests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pysection_districtfunctiontests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pyread_sectionsfunctiontests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pyCandidateclasstests/test_glyph_e3.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/locate_glyphs.pyink_maskfunctionutils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/locate_glyphs.pybitmapfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyshape_featuresfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pybaselinesfunctiontests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pybracket_kindfunctiontests/test_glyph_e3.py, utils/reference_tools/places/german_candidate_diagnosis.py
utils/lexikon_1910/locate_glyphs.pystacked_piecesfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyLinesclass— no call site found —
utils/lexikon_1910/locate_glyphs.pyline_componentsfunctionutils/reference_tools/places/german_candidate_diagnosis.py
utils/lexikon_1910/locate_glyphs.pycandidates_on_imagefunctiontests/test_glyph_e3.py, utils/reference_tools/places/german_candidate_diagnosis.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/locate_glyphs.pysection_candidatesfunctionutils/lexikon_1910/run_e3_pages.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py, utils/reference_tools/places/run_german_detect_by_naming.py
utils/lexikon_1910/locate_glyphs.pytag_entriesfunctiontests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pyheuristic_glyphnessfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyfeature_matrixfunctionutils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/locate_glyphs.pyGlyphModelclass— no call site found —
utils/lexikon_1910/locate_glyphs.pyfit_glyph_modelfunctionutils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/locate_glyphs.pyalignfunctiontests/test_glyph_e3.py, tests/test_score_table_passes.py, tests/test_statutory_prose.py, utils/lexikon_1910/score_table_passes.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/locate_glyphs.pyentry_maskfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyplacefunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/parsing/amateur_theatre_model.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py, utils/reference_tools/places/map_intensities.py, utils/scraping/scraper_amaterskedivadlo.py
utils/lexikon_1910/locate_glyphs.pyload_sectionsfunctionutils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/locate_glyphs.pyplate_templatesfunctionutils/reference_tools/places/german_locator_trial.py
utils/lexikon_1910/locate_glyphs.pyLocatorclasstests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pylearn_templatesfunctiontests/test_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pytrain_locatorfunctionutils/lexikon_1910/run_e3_pages.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/locate_glyphs.pylocate_allfunctionutils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/locate_glyphs.pyModifiersclass— no call site found —
utils/lexikon_1910/locate_glyphs.pyletter_shapedfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyis_speckfunctionutils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/locate_glyphs.pyline_neighboursfunctionutils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/run_german_oracle.py
utils/lexikon_1910/locate_glyphs.pymodifiersfunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/run_german_oracle.py
utils/lexikon_1910/locate_glyphs.pymodifier_rowsfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pymodifier_textfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyplace_blindfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyline_startsfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pykey_leftfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pytext_columnfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pydetector_featuresfunctionutils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/locate_glyphs.pyeligiblefunctionutils/lexikon_1910/run_e3_pages.py, utils/parsing/vua_courts.py
utils/lexikon_1910/locate_glyphs.pyfit_detectorfunctionutils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/locate_glyphs.pybest_thresholdfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pysection_text_columnsfunctionutils/lexikon_1910/run_e3_pages.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/locate_glyphs.pyDetectionclassutils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/locate_glyphs.pydetect_marksfunctionutils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/locate_glyphs.pyinsidefunction16_theatre_model_pass.ipynb, utils/reference_tools/places/german_candidate_diagnosis.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_supervised_placement.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py
utils/lexikon_1910/locate_glyphs.pymatch_clicksfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pyfalse_boxesfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pysummarisefunctionutils/scraping/run_lexicon_downloads.py, utils/scraping/run_oeulk_downloads.py
utils/lexikon_1910/locate_glyphs.pybox_stabilityfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pystability_textfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pydrawfunctionutils/docs/build_docs.py, utils/lexikon_1910/draw_gold_sample.py, utils/reference_tools/places/map_intensities.py
utils/lexikon_1910/locate_glyphs.pywrite_boxesfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pywrite_candidatesfunction(internal use only)
utils/lexikon_1910/locate_glyphs.pymainfunction(internal use only)
utils/lexikon_1910/make_gold_worksheet.pybuildfunction(internal use only)
utils/lexikon_1910/make_gold_worksheet.pymainfunction(internal use only)
utils/lexikon_1910/make_gold_worksheet_de.pyload_manifestfunction(internal use only)
utils/lexikon_1910/make_gold_worksheet_de.pyrenderfunctionutils/docs/build_docs.py, utils/lexikon_1910/build_contrast_sheet.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/make_gold_worksheet_de.pysheet_textfunction(internal use only)
utils/lexikon_1910/make_gold_worksheet_de.pybuildfunction(internal use only)
utils/lexikon_1910/make_gold_worksheet_de.pymainfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyfront_matterfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/read_transcription_md.pyread_officesfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyread_office_keysfunctiontests/test_sealed_scorer.py
utils/lexikon_1910/read_transcription_md.pyread_partsfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyread_tablefunction(internal use only)
utils/lexikon_1910/read_transcription_md.pygrade_tablefunctiontests/test_lexikon_1910_block_carry.py
utils/lexikon_1910/read_transcription_md.pygrade_partsfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyread_legendfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyread_notesfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyread_attributesfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyread_worked_examplesfunctiontests/test_repeated_marks.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/read_transcription_md.pyread_order_referencefunctiontests/test_repeated_marks.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/read_transcription_md.pyread_sheetfunctiontests/test_german_plates.py, tests/test_ms_sample.py, tests/test_sealed_scorer.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_r5_german_notes.py, utils/lexikon_1910/score_table_passes.py
utils/lexikon_1910/read_transcription_md.pygrade_sheetsfunction(internal use only)
utils/lexikon_1910/read_transcription_md.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/read_transcription_md.pywritefunctiontests/test_lookalike_rules.py, tests/test_pooled_score.py, tests/test_r5_german_notes.py, tests/test_reader_batches_gemini.py, tests/test_sealed_scorer.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/occupations/download_hisco_sources.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/scraping/progress.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_vuapraha.py, utils/tables/latex_tables.py
utils/lexikon_1910/read_transcription_md.pymainfunction(internal use only)
utils/lexikon_1910/reader_batches.pyread_queuefunctiontests/test_batch_wait.py, tests/test_reader_batches.py, tests/test_reader_batches_gemini.py
utils/lexikon_1910/reader_batches.pyindex_queuefunctiontests/test_reader_batches_gemini.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/reader_batches.pysubmitfunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_opus_cards.py, tests/test_reader_batches_gemini.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/amateur_theatre_model.py, utils/scraping/run_lexicon_downloads.py
utils/lexikon_1910/reader_batches.pyto_nativefunctiontests/test_reader_batches_gemini.py
utils/lexikon_1910/reader_batches.pycheck_translationfunctiontests/test_reader_batches_gemini.py
utils/lexikon_1910/reader_batches.pycollectfunction16_theatre_model_pass.ipynb, tests/test_batch_wait.py, tests/test_opus_cards.py, tests/test_reader_batches_gemini.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/amateur_theatre_model.py
utils/lexikon_1910/reader_batches.pygemini_equivalence_submitfunction15_frozen_run.ipynb, tests/test_reader_batches_gemini.py
utils/lexikon_1910/reader_batches.pygemini_equivalence_collectfunction15_frozen_run.ipynb, tests/test_reader_batches_gemini.py
utils/lexikon_1910/reader_batches.pyline_agreementfunctiontests/test_reader_batches_gemini.py
utils/lexikon_1910/reader_batches.pycell_agreementfunctiontests/test_reader_batches_gemini.py
utils/lexikon_1910/report_unlisted_abbreviations.pyreading_entriesfunction(internal use only)
utils/lexikon_1910/report_unlisted_abbreviations.pyunlistedfunction15_frozen_run.ipynb
utils/lexikon_1910/report_unlisted_abbreviations.pymainfunction(internal use only)
utils/lexikon_1910/rescore_passes.pyout_pathsfunction(internal use only)
utils/lexikon_1910/rescore_passes.pywrite_csvfunctionutils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/rescore_passes.pygold_linefunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/rescore_passes.pysidecarfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/rescore_passes.pyscore_sidecarsfunction(internal use only)
utils/lexikon_1910/rescore_passes.pyfamily_offunctiontests/test_e1b_candidates.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/rescore_passes.pyorder_framesfunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/rescore_passes.pydispute_meansfunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/rescore_passes.pyorder_ledgerfunction(internal use only)
utils/lexikon_1910/rescore_passes.pyrescorefunction(internal use only)
utils/lexikon_1910/rescore_passes.pymainfunction(internal use only)
utils/lexikon_1910/run_de_table.pyArmclasstests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_de_table.pymake_readerfunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_de_table.pycache_keyfunctionutils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_de_table.pycutfunctiontests/test_contrast_arm.py, tests/test_glyph_e3.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/amateur_theatre.py, utils/parsing/ruian.py
utils/lexikon_1910/run_de_table.pyread_blocksfunctiontests/test_cache_guard.py, tests/test_de_table.py
utils/lexikon_1910/run_de_table.pykeep_carriedfunctiontests/test_de_table.py
utils/lexikon_1910/run_de_table.pyassemblefunctiontests/test_de_table.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_de_table.pyfrozen_passfunctionutils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_de_table.pyestimatefunctiontests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/run_de_table.pypilot_image_capfunction(internal use only)
utils/lexikon_1910/run_de_table.pysealed_scansfunctiontests/test_de_table.py
utils/lexikon_1910/run_de_table.pypage_specfunctiontests/test_de_table.py
utils/lexikon_1910/run_de_table.pymainfunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pylabelfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py, utils/parsing/schema.py, utils/reference_tools/places/build_matice_read_sheets.py, utils/validation/data_checks.py
utils/lexikon_1910/run_e1_gemini_version.pyplanfunctiontests/test_e1b_candidates.py, tests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/run_e1_gemini_version.pyflagsfunction15_frozen_run.ipynb, tests/test_e1b_candidates.py, tests/test_vua_courts.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_table_arm.py, utils/reference_tools/places/audit_full_run_pages.py
utils/lexikon_1910/run_e1_gemini_version.pyseed_first_repeatfunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pyquota_openfunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_e1_gemini_version.pywait_for_quotafunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pycache_usagefunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/run_e1_gemini_version.pyinput_tokensfunctiontests/test_e1b_candidates.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/run_e1_gemini_version.pyrunfunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pyload_csvfunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pyv2_claimsfunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pyper_entryfunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pypooledfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/lexikon_1910_institutions.py
utils/lexikon_1910/run_e1_gemini_version.pymajorityfunctionutils/lexikon_1910/lexikon_1910_ledger.py, utils/lexikon_1910/score_r5_german_notes.py, utils/parsing/amateur_theatre.py
utils/lexikon_1910/run_e1_gemini_version.pyglyph_errorsfunction(internal use only)
utils/lexikon_1910/run_e1_gemini_version.pyscorefunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_sealed_scorer.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_verify_test_b.py, utils/lexikon_1910/score_certification.py, utils/parsing/amateur_theatre_model.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/run_e1_gemini_version.pymainfunction(internal use only)
utils/lexikon_1910/run_e1b_second_reader.pyCandidateclasstests/test_glyph_e3.py, utils/lexikon_1910/locate_glyphs.py
utils/lexikon_1910/run_e1b_second_reader.pylabelfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py, utils/parsing/schema.py, utils/reference_tools/places/build_matice_read_sheets.py, utils/validation/data_checks.py
utils/lexikon_1910/run_e1b_second_reader.pyplanfunctiontests/test_e1b_candidates.py, tests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/run_e1b_second_reader.pyclaude_thinkingfunctiontests/test_e1b_candidates.py
utils/lexikon_1910/run_e1b_second_reader.pyclaude_flagsfunction(internal use only)
utils/lexikon_1910/run_e1b_second_reader.pyflagsfunction15_frozen_run.ipynb, tests/test_e1b_candidates.py, tests/test_vua_courts.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_table_arm.py, utils/reference_tools/places/audit_full_run_pages.py
utils/lexikon_1910/run_e1b_second_reader.pyestimatefunctiontests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/run_e1b_second_reader.pylistedfunction(internal use only)
utils/lexikon_1910/run_e1b_second_reader.pypingfunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_e1b_second_reader.pyreadyfunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_e1b_second_reader.pyrunfunction(internal use only)
utils/lexikon_1910/run_e1b_second_reader.pytokens_offunction(internal use only)
utils/lexikon_1910/run_e1b_second_reader.pyfamily_offunctiontests/test_e1b_candidates.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/run_e1b_second_reader.pypairsfunctionutils/lexikon_1910/build_contrast_sheet.py, utils/parsing/vua_courts.py
utils/lexikon_1910/run_e1b_second_reader.pyby_readerfunction(internal use only)
utils/lexikon_1910/run_e1b_second_reader.pyorder_pairsfunction(internal use only)
utils/lexikon_1910/run_e1b_second_reader.pysummary_tablefunction03_visualisation.ipynb, utils/tables/latex_tables.py
utils/lexikon_1910/run_e1b_second_reader.pyscorefunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_sealed_scorer.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_verify_test_b.py, utils/lexikon_1910/score_certification.py, utils/parsing/amateur_theatre_model.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/run_e1b_second_reader.pymainfunction(internal use only)
utils/lexikon_1910/run_e3_pages.pyprinted_munisfunction(internal use only)
utils/lexikon_1910/run_e3_pages.pysections_from_indexfunction(internal use only)
utils/lexikon_1910/run_e3_pages.pyfull_locatorfunction(internal use only)
utils/lexikon_1910/run_e3_pages.pyfull_detectorfunction(internal use only)
utils/lexikon_1910/run_e3_pages.pyNamerclassutils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/run_e3_pages.pyrunfunction(internal use only)
utils/lexikon_1910/run_e3_pages.pyrehearsefunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_e3_pages.pymainfunction(internal use only)
utils/lexikon_1910/run_frozen.pyNotReadyclass(internal use only)
utils/lexikon_1910/run_frozen.pyeditions_forfunction15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_lookalike_rules.py
utils/lexikon_1910/run_frozen.pyguardfunctiontests/test_frozen_run.py, tests/test_ms_sample.py, utils/reference_tools/occupations/czech_glosses.py
utils/lexikon_1910/run_frozen.pyfinished_certificationfunctiontests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyrefuse_finishedfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pynot_wiredfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyout_dirfunction15_frozen_run.ipynb, tests/test_freeze_check.py, tests/test_frozen_run.py, tests/test_lookalike_rules.py, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pypages_forfunction15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_ms_sample.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/reference_tools/places/audit_full_run_pages.py
utils/lexikon_1910/run_frozen.pypage_listfunction15_frozen_run.ipynb, tests/test_frozen_run.py, utils/reference_tools/places/audit_full_run_pages.py
utils/lexikon_1910/run_frozen.pylabelsfunction15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_lexikon_1910_pilot_geometry.py, tests/test_lookalike_rules.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/report_unlisted_abbreviations.py, utils/validation/check_ostafin_silesia.py
utils/lexikon_1910/run_frozen.pyset_run_asidefunction15_frozen_run.ipynb, tests/test_lookalike_rules.py
utils/lexikon_1910/run_frozen.pynotes_readersfunction15_frozen_run.ipynb, tests/test_frozen_run.py
utils/lexikon_1910/run_frozen.pyactorsfunction(internal use only)
utils/lexikon_1910/run_frozen.pyread_planfunction15_frozen_run.ipynb, tests/test_frozen_run.py
utils/lexikon_1910/run_frozen.pyqueue_rootfunction15_frozen_run.ipynb, tests/test_batch_wait.py
utils/lexikon_1910/run_frozen.pyset_reads_asidefunction15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pycheck_is_currentfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyread_livefunction— no call site found —
utils/lexikon_1910/run_frozen.pybatch_preparefunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pybatch_submitfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pybatch_collectfunction(internal use only)
utils/lexikon_1910/run_frozen.pybatch_finishfunction— no call site found —
utils/lexikon_1910/run_frozen.pybatch_collect_allfunction15_frozen_run.ipynb, tests/test_batch_wait.py
utils/lexikon_1910/run_frozen.pywait_forfunction15_frozen_run.ipynb, tests/test_batch_wait.py
utils/lexikon_1910/run_frozen.pyfreezefunction15_frozen_run.ipynb, tests/test_prefreeze_route.py, utils/lexikon_1910/score_certification.py
utils/lexikon_1910/run_frozen.pygold_manifest_pathfunctiontests/test_freeze_check.py
utils/lexikon_1910/run_frozen.pyfreeze_differencesfunctiontests/test_freeze_check.py, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pycompare_freezefunction15_frozen_run.ipynb, tests/test_freeze_check.py
utils/lexikon_1910/run_frozen.pycut_sectionsfunctiontests/test_section_audit.py
utils/lexikon_1910/run_frozen.pysections_missing_officesfunctiontests/test_section_audit.py
utils/lexikon_1910/run_frozen.pyverdicts_pathfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyflags_pathfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pygate_pathfunction(internal use only)
utils/lexikon_1910/run_frozen.pyrun_e3function15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pygold_rowsfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pygold_truthfunction(internal use only)
utils/lexikon_1910/run_frozen.pydino_ratesfunctiontests/test_frozen_run.py
utils/lexikon_1910/run_frozen.pye3_gatefunction15_frozen_run.ipynb, tests/test_frozen_run.py
utils/lexikon_1910/run_frozen.pyorder_flagsfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pypoint_tbfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyrouted_claimsfunctiontests/test_frozen_run.py
utils/lexikon_1910/run_frozen.pytype_claimsfunction(internal use only)
utils/lexikon_1910/run_frozen.pyrouted_typesfunction(internal use only)
utils/lexikon_1910/run_frozen.pyfinal_typesfunctiontests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pybuild_itemsfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyrepeat_claimsfunctiontests/test_repeated_marks.py
utils/lexikon_1910/run_frozen.pyopus_submitfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyopus_collectfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyopus_unansweredfunction15_frozen_run.ipynb, tests/test_batch_wait.py
utils/lexikon_1910/run_frozen.pyopus_estimatefunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyledger_pathfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pybuild_ledgerfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyoffice_claimsfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyis_institutionfunction(internal use only)
utils/lexikon_1910/run_frozen.pyfinal_claimsfunctiontests/test_abbreviations.py, tests/test_cz_anchor.py, tests/test_frozen_run.py, tests/test_lookalike_rules.py, tests/test_route_fixes.py
utils/lexikon_1910/run_frozen.pyposition_swaps_offunctiontests/test_lookalike_rules.py
utils/lexikon_1910/run_frozen.pyswap_countsfunctiontests/test_lookalike_rules.py
utils/lexikon_1910/run_frozen.pyclaim_countsfunctiontests/test_prefreeze_route.py, tests/test_route_fixes.py
utils/lexikon_1910/run_frozen.pymajority_countsfunction(internal use only)
utils/lexikon_1910/run_frozen.pycount_forfunctiontests/test_route_fixes.py
utils/lexikon_1910/run_frozen.pyrepeat_lessfunctiontests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyfinal_inventoryfunctiontests/test_lookalike_rules.py, tests/test_prefreeze_route.py, tests/test_route_fixes.py, tests/test_section_audit.py
utils/lexikon_1910/run_frozen.pyabbreviation_tablefunctiontests/test_abbreviations.py, tests/test_german_plates.py, tests/test_prefreeze_route.py, utils/lexikon_1910/score_certification.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/run_frozen.pyprinted_readingfunction(internal use only)
utils/lexikon_1910/run_frozen.pydecoded_rowsfunction(internal use only)
utils/lexikon_1910/run_frozen.pyabbreviation_claimsfunctiontests/test_abbreviations.py, tests/test_german_plates.py, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pylanguage_countsfunctiontests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyentries_readfunction(internal use only)
utils/lexikon_1910/run_frozen.pyrouted_abbreviationsfunctiontests/test_abbreviations.py
utils/lexikon_1910/run_frozen.pyabbreviation_questionfunctiontests/test_lookalike_rules.py
utils/lexikon_1910/run_frozen.pyabbreviation_rulingsfunctiontests/test_abbreviations.py
utils/lexikon_1910/run_frozen.pyabbreviation_rowsfunctiontests/test_abbreviations.py
utils/lexikon_1910/run_frozen.pyfinal_abbreviationsfunctiontests/test_abbreviations.py
utils/lexikon_1910/run_frozen.pygold_abbreviationsfunctiontests/test_abbreviations.py
utils/lexikon_1910/run_frozen.pysheet_abbreviationsfunctionutils/lexikon_1910/score_certification.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/run_frozen.pyscore_tablefunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyscore_abbreviationsfunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pycourts_pathfunction(internal use only)
utils/lexikon_1910/run_frozen.pycourt_keyfunctionutils/lexikon_1910/lexikon_1910_register.py
utils/lexikon_1910/run_frozen.pyedition_unitsfunctiontests/test_court_key.py
utils/lexikon_1910/run_frozen.pyedition_formsfunction(internal use only)
utils/lexikon_1910/run_frozen.pyanchor_claimsfunctiontests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pywithout_anchorfunctiontests/test_pooled_score.py, utils/lexikon_1910/score_certification.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/run_frozen.pyanchored_keysfunctiontests/test_cz_anchor.py
utils/lexikon_1910/run_frozen.pyrepeated_marksfunctiontests/test_repeated_marks.py, utils/lexikon_1910/lexikon_1910_institutions.py
utils/lexikon_1910/run_frozen.pyshared_symbolsfunction(internal use only)
utils/lexikon_1910/run_frozen.pycompare_editionsfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py, tests/test_r5_german_notes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/run_frozen.pyindependence_testfunction15_frozen_run.ipynb, tests/test_route_fixes.py
utils/lexikon_1910/run_frozen.pyscore_claimsfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pysealed_pagesfunctiontests/test_de_table.py, tests/test_ms_sample.py, tests/test_route_fixes.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/run_frozen.pyin_sample_pagesfunctiontests/test_ms_sample.py, tests/test_route_fixes.py
utils/lexikon_1910/run_frozen.pyvalidation_samplefunction15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_prefreeze_route.py, tests/test_route_fixes.py
utils/lexikon_1910/run_frozen.pyreview_queuefunction15_frozen_run.ipynb
utils/lexikon_1910/run_frozen.pyapply_reviewfunction15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyscore_validationfunction15_frozen_run.ipynb, tests/test_prefreeze_route.py
utils/lexikon_1910/run_frozen.pyrehearsefunctionutils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/run_glyph_e3.pyGlyphclasstests/test_glyph_e3.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/run_glyph_e3.pyglyph_atfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyplaced_glyphsfunctionutils/lexikon_1910/run_e3_pages.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/run_glyph_e3.pycutfunctiontests/test_contrast_arm.py, tests/test_glyph_e3.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/amateur_theatre.py, utils/parsing/ruian.py
utils/lexikon_1910/run_glyph_e3.pycrop_whitefunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pycut_allfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyembedfunctionutils/lexikon_1910/run_e3_pages.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/run_glyph_e3.pyunitfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/run_frozen.py, utils/parsing/lexikon_sk.py, utils/parsing/schema.py, utils/validation/data_checks.py
utils/lexikon_1910/run_glyph_e3.pyknn_votefunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pyclassifyfunctiontests/test_glyph_e3.py, utils/reference_tools/education/draw_education_sample.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/run_glyph_e3.pypage_thresholdsfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pywith_abstainfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pybank_holdsfunctiontests/test_glyph_e3.py, utils/reference_tools/places/run_german_oracle.py
utils/lexikon_1910/run_glyph_e3.pyevaluatefunctionutils/lexikon_1910/run_glyph_test_a.py
utils/lexikon_1910/run_glyph_e3.pytype_rowsfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pysummarise_runfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyname_basesfunctionutils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/run_glyph_e3.pyletter_labelfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pylettered_formsfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyname_lettersfunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/run_glyph_e3.pycompose_namesfunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/run_glyph_e3.pycomposed_rowsfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyletter_rowsfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyreader_passesfunctionutils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_glyph_e3.pyread_reader_claimsfunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/run_glyph_e3.pyreader_sectionsfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyioufunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pygold_atfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pydispute_rowsfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pyscore_passfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pyunclaimed_marksfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pyfile_underfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pyverdict_offunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pysection_glyphsfunctionutils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/run_glyph_e3.pyverdict_rowsfunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py
utils/lexikon_1910/run_glyph_e3.pyscore_unclaimedfunctiontests/test_glyph_e3.py
utils/lexikon_1910/run_glyph_e3.pyscore_readersfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pywrite_csvfunctionutils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/run_glyph_e3.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/run_glyph_e3.pycomposed_reportfunction(internal use only)
utils/lexikon_1910/run_glyph_e3.pymainfunction(internal use only)
utils/lexikon_1910/run_glyph_test_a.pysquarefunctionutils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/run_glyph_test_a.pypixel_featuresfunctionutils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/run_glyph_test_a.pypixel_valuesfunction(internal use only)
utils/lexikon_1910/run_glyph_test_a.pyencoder_featuresfunctionutils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/run_glyph_test_a.pyleave_page_outfunction(internal use only)
utils/lexikon_1910/run_glyph_test_a.pyevaluatefunctionutils/lexikon_1910/run_glyph_e3.py
utils/lexikon_1910/run_glyph_test_a.pymainfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyencode_pngfunctionutils/lexikon_1910/bakeoff.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pykey_image_digestfunctiontests/test_cache_guard.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pykey_image_cachefunctiontests/test_cache_guard.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pyTruncatedclass(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyRateLimitedclass(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyClaudeReaderclasstests/test_e1b_candidates.py, tests/test_reader_batches.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pyOpenAICompatibleReaderclasstests/test_reader_batches.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pyOllamaReaderclass— no call site found —
utils/lexikon_1910/run_lexikon_1910_pilot.pybuild_readerfunctionutils/lexikon_1910/lexikon_1910_pages.py
utils/lexikon_1910/run_lexikon_1910_pilot.pyblock_partsfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pybuild_notes_readerfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyparse_pricefunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pytable_rowsfunctiontests/test_score_table_passes.py, utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/run_de_table.py, utils/validation/check_ostafin_silesia.py
utils/lexikon_1910/run_lexikon_1910_pilot.pyrows_to_blockfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pycompare_channelsfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyparse_pagesfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pywrite_jsonfunctionutils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pywrite_csvfunctionutils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pyprobe_readerfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyheading_readerfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyprinted_line_countfunctiontests/test_lexikon_1910_pilot_checks.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_lexikon_1910_pilot.pycrop_auditfunctiontests/test_lexikon_1910_pilot_checks.py
utils/lexikon_1910/run_lexikon_1910_pilot.pyimage_cut_reportfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pylegend_passfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pyapparatus_passfunction(internal use only)
utils/lexikon_1910/run_lexikon_1910_pilot.pymainfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyArmclasstests/test_r5_german_notes.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/run_r5_german_notes.pylabelfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_table_arm.py, utils/parsing/schema.py, utils/reference_tools/places/build_matice_read_sheets.py, utils/validation/data_checks.py
utils/lexikon_1910/run_r5_german_notes.pyis_donefunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyplanfunctiontests/test_e1b_candidates.py, tests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/run_r5_german_notes.pygold_pagesfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_table_arm.py
utils/lexikon_1910/run_r5_german_notes.pymake_readerfunctionutils/lexikon_1910/run_de_table.py
utils/lexikon_1910/run_r5_german_notes.pysystemsfunctiontests/test_de_table.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/lexikon_1910/run_r5_german_notes.pycache_keyfunctionutils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/lexikon_1910/run_r5_german_notes.pycached_line_readerfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyPageCutclass— no call site found —
utils/lexikon_1910/run_r5_german_notes.pycarry_intofunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pysave_sectionsfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pycut_pagesfunctionutils/reference_tools/places/audit_full_run_pages.py
utils/lexikon_1910/run_r5_german_notes.pyjobs_offunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyread_cropsfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyclean_linesfunctiontests/test_r5_german_notes.py
utils/lexikon_1910/run_r5_german_notes.pylegend_rowsfunctiontests/test_r5_german_notes.py
utils/lexikon_1910/run_r5_german_notes.pyaudit_rowsfunctiontests/test_r5_german_notes.py
utils/lexikon_1910/run_r5_german_notes.pyassemblefunctiontests/test_de_table.py, utils/lexikon_1910/run_de_table.py
utils/lexikon_1910/run_r5_german_notes.pyrun_passfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyfrozen_passfunctionutils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_r5_german_notes.pyheader_rows_offunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyheading_rowsfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pycostfunctionutils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_verify_test_b.py
utils/lexikon_1910/run_r5_german_notes.pyrecord_timingfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyreport_passfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyparkfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pyimage_tokensfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_verify_test_b.py
utils/lexikon_1910/run_r5_german_notes.pyestimatefunctiontests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/run_r5_german_notes.pydry_runfunctionutils/lexikon_1910/run_verify_test_b.py, utils/scraping/run_matice_downloads.py
utils/lexikon_1910/run_r5_german_notes.pyreadyfunctionutils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/run_r5_german_notes.pyrunfunction(internal use only)
utils/lexikon_1910/run_r5_german_notes.pymainfunction(internal use only)
utils/lexikon_1910/run_table_arm.pylabelfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/parsing/schema.py, utils/reference_tools/places/build_matice_read_sheets.py, utils/validation/data_checks.py
utils/lexikon_1910/run_table_arm.pyrows_filefunction(internal use only)
utils/lexikon_1910/run_table_arm.pyread_by_modelfunction(internal use only)
utils/lexikon_1910/run_table_arm.pyplanfunctiontests/test_e1b_candidates.py, tests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_table_arm.pyflagsfunction15_frozen_run.ipynb, tests/test_e1b_candidates.py, tests/test_vua_courts.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/reference_tools/places/audit_full_run_pages.py
utils/lexikon_1910/run_table_arm.pyrunfunction(internal use only)
utils/lexikon_1910/run_table_arm.pymainfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pyset_armfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pypass_rowsfunctionutils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyclaim_setsfunctiontests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pydisputesfunctiontests/test_verify_test_b.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyflaggedfunctiontests/test_verify_test_b.py, utils/lexikon_1910/lexikon_1910_ledger.py, utils/parsing/vua_courts.py
utils/lexikon_1910/run_verify_test_b.pyunit_offunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyunit_idfunctiontests/test_abbreviations.py, tests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pyplace_namesfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyload_partsfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pywhose_optionsfunctiontests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pyload_basesfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyconfusionsfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pypartnersfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyoptions_forfunctiontests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pyload_geometryfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyimage_pathfunctiontests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pygold_image_pathfunctiontests/test_opus_cards.py
utils/lexikon_1910/run_verify_test_b.pyband_offunctionutils/parsing/ranks.py
utils/lexikon_1910/run_verify_test_b.pykey_startsfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pyentry_bandsfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pymarks_offunctiontests/test_abbreviations.py
utils/lexikon_1910/run_verify_test_b.pyreference_cardfunctiontests/test_opus_cards.py
utils/lexikon_1910/run_verify_test_b.pyimage_digestfunctiontests/test_opus_cards.py, tests/test_prefreeze_route.py
utils/lexikon_1910/run_verify_test_b.pysaved_image_digestfunctiontests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pybuild_itemfunctiontests/test_opus_cards.py, tests/test_prefreeze_route.py, tests/test_route_fixes.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyschema_forfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pysystem_textfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyparams_forfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyimage_tokensfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/run_verify_test_b.pybuild_allfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pydry_runfunctionutils/lexikon_1910/run_r5_german_notes.py, utils/scraping/run_matice_downloads.py
utils/lexikon_1910/run_verify_test_b.pywrite_itemsfunctiontests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pysent_pathfunctiontests/test_opus_cards.py
utils/lexikon_1910/run_verify_test_b.pyrecord_sentfunctiontests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/run_verify_test_b.pyunmatched_pathfunctiontests/test_opus_cards.py
utils/lexikon_1910/run_verify_test_b.pysubmitfunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_opus_cards.py, tests/test_reader_batches_gemini.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/parsing/amateur_theatre_model.py, utils/scraping/run_lexicon_downloads.py
utils/lexikon_1910/run_verify_test_b.pycollectfunction16_theatre_model_pass.ipynb, tests/test_batch_wait.py, tests/test_opus_cards.py, tests/test_reader_batches_gemini.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/parsing/amateur_theatre_model.py
utils/lexikon_1910/run_verify_test_b.pyclaim_verdictsfunctiontests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pymeasuresfunctiontests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pyflagged_measuresfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pyrecoveredfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pywhose_rulingsfunctiontests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pygain_intervalfunction(internal use only)
utils/lexikon_1910/run_verify_test_b.pybillfunctiontests/test_verify_test_b.py
utils/lexikon_1910/run_verify_test_b.pyscorefunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_sealed_scorer.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/parsing/amateur_theatre_model.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/run_verify_test_b.pymainfunction(internal use only)
utils/lexikon_1910/score_certification.pysealed_pathsfunctiontests/test_ms_sample.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pysheet_statusfunctionutils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pysealed_sheetsfunctionutils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/score_certification.pysealed_rowsfunctiontests/test_german_plates.py
utils/lexikon_1910/score_certification.pyprinted_entriesfunctiontests/test_sealed_scorer.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/score_certification.pycourt_offunctionutils/lexikon_1910/lexikon_1910_notes.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/score_certification.pyclaims_offunctiontests/test_abbreviations.py
utils/lexikon_1910/score_certification.pyroute_claimsfunction(internal use only)
utils/lexikon_1910/score_certification.pyprffunction(internal use only)
utils/lexikon_1910/score_certification.pyscore_setsfunctiontests/test_religion_categories.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/score_certification.pypage_clustered_intervalfunctionutils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/score_certification.pyentry_exactfunction(internal use only)
utils/lexikon_1910/score_certification.pycount_accuracyfunctionutils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/score_certification.pyper_symbolfunctiontests/test_sealed_scorer.py, utils/reference_tools/places/score_sealed_abbreviations.py
utils/lexikon_1910/score_certification.pyauthor_dependentfunctiontests/test_religion_categories.py
utils/lexikon_1910/score_certification.pyfreeze_recordfunctionutils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pyfreeze_refusalfunction(internal use only)
utils/lexikon_1910/score_certification.pywithout_anchorfunctiontests/test_pooled_score.py, utils/lexikon_1910/run_frozen.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pyscorefunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_sealed_scorer.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/amateur_theatre_model.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py
utils/lexikon_1910/score_certification.pygold_cooperativesfunctiontests/test_pooled_score.py
utils/lexikon_1910/score_certification.pyroute_cooperativesfunctiontests/test_pooled_score.py
utils/lexikon_1910/score_certification.pysidesfunctiontests/test_sealed_scorer.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pymergefunction02_data_cleaning.ipynb, tests/test_pooled_score.py, utils/lexikon_1910/lexikon_1910_register.py, utils/parsing/amateur_theatre.py, utils/parsing/amateur_theatre_model.py, utils/parsing/census_hu.py, utils/parsing/districts.py, utils/parsing/matice.py, utils/parsing/vua_courts.py, utils/reference_tools/education/score_education_gold.py, utils/reference_tools/occupations/build_czisco_bridge.py, utils/reference_tools/occupations/build_hisco_authority.py, utils/reference_tools/occupations/build_occupation_curation.py, utils/reference_tools/occupations/run_census_berufsstellung_check.py, utils/reference_tools/places/merge_matice_readings.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/reference_tools/units/build_unit_timeline.py, utils/scraping/scraper_amaterskedivadlo.py, utils/validation/check_ostafin_silesia.py
utils/lexikon_1910/score_certification.pyby_categoryfunctiontests/test_religion_categories.py, tests/test_sealed_scorer.py, utils/reference_tools/places/statutory_institutions.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pywaiting_marksfunctionutils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pymeasurefunctiontests/test_religion_categories.py, tests/test_sealed_scorer.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pyscore_pooledfunctiontests/test_sealed_scorer.py
utils/lexikon_1910/score_certification.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/score_certification.pymainfunction(internal use only)
utils/lexikon_1910/score_r5_german_notes.pyfamily_offunctiontests/test_e1b_candidates.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py
utils/lexikon_1910/score_r5_german_notes.pyabbreviationsfunction(internal use only)
utils/lexikon_1910/score_r5_german_notes.pysheet_pathsfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/score_r5_german_notes.pyas_writtenfunctiontests/test_r5_german_notes.py
utils/lexikon_1910/score_r5_german_notes.pyread_goldfunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_german_plates.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/score_certification.py, utils/parsing/amateur_theatre_model.py
utils/lexikon_1910/score_r5_german_notes.pyledger_namesfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/score_r5_german_notes.pyarm_summaryfunction(internal use only)
utils/lexikon_1910/score_r5_german_notes.pyside_filesfunction(internal use only)
utils/lexikon_1910/score_r5_german_notes.pylegend_agreementfunction(internal use only)
utils/lexikon_1910/score_r5_german_notes.pyside_linesfunction(internal use only)
utils/lexikon_1910/score_r5_german_notes.pyscore_germanfunction(internal use only)
utils/lexikon_1910/score_r5_german_notes.pymatched_cz_scansfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/score_r5_german_notes.pycomparefunctiontests/test_statutory_institutions.py, utils/reference_tools/places/statutory_institutions.py
utils/lexikon_1910/score_r5_german_notes.pywrite_csvfunctionutils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py
utils/lexikon_1910/score_r5_german_notes.pymainfunction(internal use only)
utils/lexikon_1910/score_table_passes.pynorm_namefunctiontests/test_score_table_passes.py
utils/lexikon_1910/score_table_passes.pynorm_name_strictfunctiontests/test_score_table_passes.py
utils/lexikon_1910/score_table_passes.pynorm_numberfunctiontests/test_score_table_passes.py
utils/lexikon_1910/score_table_passes.pynorm_textfunctiontests/test_score_table_passes.py
utils/lexikon_1910/score_table_passes.pynormfunctiontests/test_glyph_e3.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_glyph_test_a.py, utils/parsing/locative.py, utils/reference_tools/background/verify_locators.py
utils/lexikon_1910/score_table_passes.pyload_goldfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/score_table_passes.pyload_rowsfunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/score_table_passes.pylabel_offunctionutils/lexikon_1910/run_frozen.py
utils/lexikon_1910/score_table_passes.pypair_scorefunction(internal use only)
utils/lexikon_1910/score_table_passes.pyalignfunctiontests/test_glyph_e3.py, tests/test_score_table_passes.py, tests/test_statutory_prose.py, utils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/statutory_prose.py
utils/lexikon_1910/score_table_passes.pyreadingsfunction16_theatre_model_pass.ipynb
utils/lexikon_1910/score_table_passes.pyscore_readingfunctiontests/test_score_table_passes.py
utils/lexikon_1910/score_table_passes.pycrossfunction(internal use only)
utils/lexikon_1910/score_table_passes.pyfmtfunctionutils/docs/build_docs.py, utils/validation/build_validation_workbook.py
utils/lexikon_1910/score_table_passes.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/lexikon_1910/score_table_passes.pypass_filesfunction(internal use only)
utils/lexikon_1910/score_table_passes.pymainfunction(internal use only)
utils/lexikon_1910/section_images.pyname_forfunction(internal use only)
utils/lexikon_1910/section_images.pysavefunctiontests/test_abbreviations.py, tests/test_cache_guard.py, tests/test_cz_anchor.py, tests/test_frozen_run.py, tests/test_opus_cards.py, tests/test_prefreeze_route.py, tests/test_route_fixes.py, utils/lexikon_1910/build_contrast_sheet.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/extract_prose_pages.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/make_gold_worksheet_de.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py, utils/reference_tools/occupations/build_conflict_workbook.py, utils/reference_tools/places/build_german_click_queue.py, utils/reference_tools/places/build_german_click_strips.py, utils/reference_tools/places/build_matice_read_sheets.py, utils/reference_tools/places/statutory_prose.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/validation/build_validation_workbook.py
utils/lexikon_1910/section_images.pyread_indexfunctiontests/test_frozen_run.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py
utils/lexikon_1910/section_images.pyindex_from_namesfunctionutils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py
utils/linkage.pyphonetic_keyfunctionutils/parsing/vua_courts.py
utils/linkage.pynames_compatiblefunctionutils/parsing/vua_courts.py, utils/validation/data_checks.py
utils/linkage.pyfind_overlapfunction02_data_cleaning.ipynb
utils/linkage.pyadd_overlap_flagsfunction02_data_cleaning.ipynb
utils/linkage.pyadd_duplicate_groupsfunction02_data_cleaning.ipynb
utils/linkage.pylinkage_selftestfunctionutils/validation/data_checks.py
utils/linkage.pylinkage_reportfunction02_data_cleaning.ipynb
utils/parsing/affiliations.pyparse_affiliationfunction(internal use only)
utils/parsing/affiliations.pyatlas_codesfunction(internal use only)
utils/parsing/affiliations.pyaffiliation_selftestfunctionutils/validation/data_checks.py
utils/parsing/affiliations.pyadd_affiliation_columnsfunction02_data_cleaning.ipynb
utils/parsing/affiliations.pyaffiliation_reportfunction02_data_cleaning.ipynb
utils/parsing/amateur_theatre.pynormalise_namefunctiontests/test_amateur_theatre.py, utils/lexikon_1910/lexikon_1910_de.py, utils/lexikon_1910/lexikon_1910_pages.py
utils/parsing/amateur_theatre.pycity_part_namefunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pystrip_diacriticsfunction(internal use only)
utils/parsing/amateur_theatre.pyhaversine_kmfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyokres_tokensfunction— no call site found —
utils/parsing/amateur_theatre.pysettlement_framefunctiontests/test_amateur_theatre.py, utils/docs/build_docs.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyjoin_places_to_settlementsfunctiontests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pystarts_beforefunctiontests/test_amateur_theatre.py, utils/scraping/scraper_amaterskedivadlo.py
utils/parsing/amateur_theatre.pyfirst_record_periodfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyplace_datesfunction(internal use only)
utils/parsing/amateur_theatre.pyclassify_associationfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyassociation_countsfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyyear_spanfunctionutils/parsing/amateur_theatre_model.py
utils/parsing/amateur_theatre.pyinfer_centuryfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyparse_leading_datefunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyattests_beforefunctiontests/test_amateur_theatre.py, utils/parsing/amateur_theatre_model.py
utils/parsing/amateur_theatre.pychronicle_line_kindfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyread_chroniclefunctiontests/test_amateur_theatre_model.py, utils/parsing/amateur_theatre_model.py
utils/parsing/amateur_theatre.pychronicle_linesfunctiontests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pychronicle_eventsfunction(internal use only)
utils/parsing/amateur_theatre.pychronicle_place_summaryfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pycentury_inference_checkfunction(internal use only)
utils/parsing/amateur_theatre.pychronicle_reportfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyis_reportfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pyclause_cutsfunction(internal use only)
utils/parsing/amateur_theatre.pysplit_clausesfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pychronicle_clausesfunctiontests/test_amateur_theatre.py, tests/test_amateur_theatre_model.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyevent_cuesfunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pycue_tablefunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyclause_reportfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pycode_clausefunctiontests/test_amateur_theatre.py
utils/parsing/amateur_theatre.pycode_clausesfunctiontests/test_amateur_theatre.py, tests/test_amateur_theatre_model.py, utils/parsing/amateur_theatre_model.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyactive_decade_listfunction— no call site found —
utils/parsing/amateur_theatre.pyensemble_chronicle_clausesfunctiontests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyassign_companiesfunction(internal use only)
utils/parsing/amateur_theatre.pyevidence_tablefunctiontests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyyear_boundsfunctiontests/test_amateur_theatre.py, utils/docs/build_docs.py
utils/parsing/amateur_theatre.pyplace_year_panelfunctiontests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyarea_year_tablesfunctiontests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pypanel_reportfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pyensemble_page_summaryfunctiontests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pycoverage_tablesfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre.pycountry_tablesfunction(internal use only)
utils/parsing/amateur_theatre.pycoverage_reportfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre_model.pyclause_keysfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyload_clausesfunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pyselect_openfunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyopen_countsfunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pyglossary_patternsfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyglossaryfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyadd_contextfunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pyclause_messagefunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pybuild_requestfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyrequest_keyfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pybuild_queuefunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pyestimate_costfunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pyusage_costfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyread_statefunction(internal use only)
utils/parsing/amateur_theatre_model.pysubmitfunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_opus_cards.py, tests/test_reader_batches_gemini.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py, utils/scraping/run_lexicon_downloads.py
utils/parsing/amateur_theatre_model.pyparse_replyfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pycollectfunction16_theatre_model_pass.ipynb, tests/test_batch_wait.py, tests/test_opus_cards.py, tests/test_reader_batches_gemini.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_verify_test_b.py
utils/parsing/amateur_theatre_model.pyreading_problemsfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyreadings_tablefunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pycost_reportfunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pyapply_readingsfunctiontests/test_amateur_theatre_model.py, utils/reference_tools/places/run_amateur_theatre_pilot.py, utils/reference_tools/places/run_matice_pilot.py
utils/parsing/amateur_theatre_model.pyload_readingsfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/parsing/amateur_theatre_model.pywrite_readingsfunction16_theatre_model_pass.ipynb
utils/parsing/amateur_theatre_model.pydraw_gold_samplefunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pywrite_gold_sheetfunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyfill_gold_sheetfunctiontests/test_amateur_theatre_model.py
utils/parsing/amateur_theatre_model.pyread_goldfunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_german_plates.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_r5_german_notes.py
utils/parsing/amateur_theatre_model.pyscorefunction16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py, tests/test_sealed_scorer.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_verify_test_b.py, utils/lexikon_1910/score_certification.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/german_supervised_placement.py
utils/parsing/census.pyload_census_filefunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/census.pybuild_census_panelfunction(internal use only)
utils/parsing/census.pyokres_census_crosswalkfunction02_data_cleaning.ipynb
utils/parsing/census.pypool_twin_okresyfunction(internal use only)
utils/parsing/census.pyblank_twin_membersfunction(internal use only)
utils/parsing/census.pybuild_census_panelsfunction02_data_cleaning.ipynb
utils/parsing/census.pyadd_census_okres_idfunction02_data_cleaning.ipynb
utils/parsing/census.pycensus_reportfunction02_data_cleaning.ipynb
utils/parsing/census_hu.pybuild_census_hufunction02_data_cleaning.ipynb
utils/parsing/census_hu.pycensus_hu_reportfunction02_data_cleaning.ipynb
utils/parsing/census_labels.pyenglish_rename_mapfunctionutils/parsing/census.py
utils/parsing/census_labels.pyadd_shares_cisfunctionutils/parsing/census.py
utils/parsing/census_labels.pyadd_shares_hufunction(internal use only)
utils/parsing/census_labels.pyto_english_censusfunctionutils/parsing/census.py, utils/parsing/census_hu.py
utils/parsing/clean.pynormalize_missingfunction02_data_cleaning.ipynb
utils/parsing/clean.pyclean_namesfunction02_data_cleaning.ipynb
utils/parsing/clean.pystrip_place_markersfunction— no call site found —
utils/parsing/clean.pyclean_placesfunction02_data_cleaning.ipynb
utils/parsing/clean.pyclean_dataframefunction02_data_cleaning.ipynb
utils/parsing/clean.pyclean_reportfunction02_data_cleaning.ipynb
utils/parsing/dates.pyparse_datefunctionutils/parsing/exit_codes.py
utils/parsing/dates.pyadd_date_columnsfunction02_data_cleaning.ipynb, utils/parsing/habsburg.py
utils/parsing/dates.pydate_reportfunction02_data_cleaning.ipynb, utils/parsing/habsburg.py
utils/parsing/districts.pyread_dbffunctionutils/docs/build_docs.py, utils/parsing/districts_hu.py, utils/parsing/spatial.py, utils/reference_tools/places/map_matice.py, utils/validation/check_ostafin_silesia.py
utils/parsing/districts.pyparse_mpidrfunctionutils/reference_tools/places/build_curation.py
utils/parsing/districts.pyattach_gazetteer_namesfunction— no call site found —
utils/parsing/districts.pybuild_district_backbonefunction02_data_cleaning.ipynb
utils/parsing/districts.pywrite_curation_templatefunction— no call site found —
utils/parsing/districts.pydistricts_reportfunction02_data_cleaning.ipynb
utils/parsing/districts.pyvalidate_against_codefunction02_data_cleaning.ipynb
utils/parsing/districts_hu.pybuild_jaras_backbonefunction02_data_cleaning.ipynb, utils/reference_tools/places/build_sk_curation.py, utils/validation/build_validation_workbook.py
utils/parsing/districts_hu.pywrite_sk_curation_templatefunction— no call site found —
utils/parsing/districts_hu.pyjaras_reportfunction02_data_cleaning.ipynb, utils/parsing/records_geo_hu.py
utils/parsing/education.pyparse_educationfunctionutils/reference_tools/education/draw_education_sample.py, utils/reference_tools/education/score_education_gold.py
utils/parsing/education.pyclean_educationfunction— no call site found —
utils/parsing/education.pyadd_education_columnfunction02_data_cleaning.ipynb
utils/parsing/education.pyeducation_selftestfunctionutils/validation/data_checks.py
utils/parsing/education.pyladder_reportfunction— no call site found —
utils/parsing/education.pyeducation_reportfunction02_data_cleaning.ipynb
utils/parsing/exit_codes.pyparse_exit_codesfunctionutils/validation/data_checks.py
utils/parsing/exit_codes.pylabel_exit_codesfunction— no call site found —
utils/parsing/exit_codes.pyadd_exit_columnsfunction02_data_cleaning.ipynb, utils/tables/latex_tables.py
utils/parsing/exit_codes.pyparse_exit_datefunction— no call site found —
utils/parsing/exit_codes.pyexit_code_reportfunction02_data_cleaning.ipynb
utils/parsing/geonames.pygpkg_pointfunctiontests/test_ruian.py
utils/parsing/geonames.pyread_geonamesfunctionutils/parsing/ruian.py
utils/parsing/geonames.pygeonames_reportfunctionutils/parsing/ruian.py
utils/parsing/habsburg.pyparse_causefunction— no call site found —
utils/parsing/habsburg.pyadd_cause_columnsfunction(internal use only)
utils/parsing/habsburg.pycause_reportfunction(internal use only)
utils/parsing/habsburg.pyextract_theatrefunction— no call site found —
utils/parsing/habsburg.pyadd_theatre_columnfunction(internal use only)
utils/parsing/habsburg.pytheatre_reportfunction(internal use only)
utils/parsing/habsburg.pyparse_ah_unitsfunction— no call site found —
utils/parsing/habsburg.pyadd_ah_unit_columnsfunction(internal use only)
utils/parsing/habsburg.pyah_unit_reportfunction03_visualisation.ipynb
utils/parsing/habsburg.pyhabsburg_selftestfunctionutils/validation/data_checks.py
utils/parsing/habsburg.pyadd_note_columnsfunction(internal use only)
utils/parsing/habsburg.pynote_reportfunction03_visualisation.ipynb
utils/parsing/habsburg.pyadd_habsburg_columnsfunction02_data_cleaning.ipynb
utils/parsing/habsburg.pyhabsburg_reportfunction02_data_cleaning.ipynb
utils/parsing/lexikon.pysettlement_slugfunction— no call site found —
utils/parsing/lexikon.pyadd_settlement_idfunctiontests/test_register_build.py
utils/parsing/lexikon.pyparse_lexikonfunction(internal use only)
utils/parsing/lexikon.pybuild_settlement_crosswalkfunction02_data_cleaning.ipynb
utils/parsing/lexikon.pylexikon_reportfunction02_data_cleaning.ipynb
utils/parsing/lexikon_sk.pyparse_lexikon_skfunction(internal use only)
utils/parsing/lexikon_sk.pybuild_crosswalk_skfunction02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py
utils/parsing/lexikon_sk.pylexikon_sk_reportfunction02_data_cleaning.ipynb
utils/parsing/locations.pyparse_locationfunction— no call site found —
utils/parsing/locations.pyadd_location_columnsfunctionutils/parsing/habsburg.py
utils/parsing/locations.pylocation_reportfunction03_visualisation.ipynb, utils/parsing/habsburg.py
utils/parsing/locative.pynormfunctiontests/test_glyph_e3.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_glyph_test_a.py, utils/lexikon_1910/score_table_passes.py, utils/reference_tools/background/verify_locators.py
utils/parsing/locative.pynoun_locativesfunction— no call site found —
utils/parsing/locative.pyadjective_locativesfunction— no call site found —
utils/parsing/locative.pynoun_genitivesfunction— no call site found —
utils/parsing/locative.pyadjective_genitivesfunction— no call site found —
utils/parsing/locative.pylocative_keysfunctiontests/test_locative.py
utils/parsing/locative.pyczech_spellingsfunctiontests/test_locative.py
utils/parsing/locative.pyshort_namefunction(internal use only)
utils/parsing/locative.pybuild_indexfunctiontests/test_locative.py, utils/reference_tools/places/run_matice_pilot.py
utils/parsing/locative.pysplit_residencefunctiontests/test_locative.py, utils/parsing/matice.py
utils/parsing/locative.pyfuzzy_keyfunctiontests/test_locative.py
utils/parsing/locative.pyqualifier_pointfunctiontests/test_locative.py
utils/parsing/locative.pynear_candidatesfunction(internal use only)
utils/parsing/locative.pyfuzzy_nearfunction(internal use only)
utils/parsing/locative.pymatch_residencefunctiontests/test_locative.py
utils/parsing/locative.pydecidefunctiontests/test_locative.py
utils/parsing/locative.pylast_word_indexfunctiontests/test_locative.py
utils/parsing/locative.pymatch_allfunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pynormalisefunctiontests/test_court_key.py, tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/lexikon_1910_names.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py, utils/reference_tools/places/build_german_click_queue.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/places/statutory_prose.py
utils/parsing/matice.pyalto_linesfunction(internal use only)
utils/parsing/matice.pyread_pdf_linesfunction(internal use only)
utils/parsing/matice.pyread_linesfunction(internal use only)
utils/parsing/matice.pyassign_sectionsfunction(internal use only)
utils/parsing/matice.pycollapse_spacingfunctiontests/test_matice.py
utils/parsing/matice.pysplit_residencefunctiontests/test_locative.py, utils/parsing/locative.py
utils/parsing/matice.pyis_corporatefunction(internal use only)
utils/parsing/matice.pyparse_personfunctiontests/test_matice.py
utils/parsing/matice.pysurname_offunctiontests/test_matice.py
utils/parsing/matice.pyroman_to_intfunctiontests/test_matice.py
utils/parsing/matice.pyint_to_romanfunction(internal use only)
utils/parsing/matice.pymc_completefunction(internal use only)
utils/parsing/matice.pyassemble_mcfunction(internal use only)
utils/parsing/matice.pyparse_mcfunctiontests/test_matice.py
utils/parsing/matice.pyresolve_dittofunctiontests/test_matice.py
utils/parsing/matice.pyjoin_linesfunction(internal use only)
utils/parsing/matice.pysplit_run_onfunctiontests/test_matice.py
utils/parsing/matice.pysplit_by_placefunctiontests/test_matice.py
utils/parsing/matice.pycolumn_marginsfunctiontests/test_matice.py
utils/parsing/matice.pyassemble_linesfunction(internal use only)
utils/parsing/matice.pypaid_year_segmentsfunctiontests/test_matice.py
utils/parsing/matice.pyparse_sourcefunction(internal use only)
utils/parsing/matice.pyparse_allfunctionutils/parsing/vua_courts.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/units/run_vua_courts_pilot.py
utils/parsing/matice.pytotals_reportfunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pymc_number_orderfunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pyentry_year_from_numberfunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pyoccupation_classfunctiontests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pymember_groupfunctiontests/test_matice.py
utils/parsing/matice.pymc_foundersfunctiontests/test_matice.py
utils/parsing/matice.pyfounder_keyfunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pyeb_ratefunctiontests/test_matice.py, utils/reference_tools/places/run_intensities.py
utils/parsing/matice.pymeasure_groupsfunctionutils/reference_tools/places/map_matice.py, utils/reference_tools/places/run_intensities.py
utils/parsing/matice.pyokres_measurefunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pyown_society_zfunctiontests/test_matice.py
utils/parsing/matice.pyown_society_czechfunctiontests/test_matice.py
utils/parsing/matice.pyadd_own_societyfunctiontests/test_matice.py
utils/parsing/matice.pycensus_court_poolfunctionutils/docs/build_docs.py, utils/reference_tools/places/map_intensities.py, utils/reference_tools/places/map_matice.py, utils/reference_tools/places/run_intensities.py
utils/parsing/matice.pycourt_measurefunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pymonth_headingfunctiontests/test_matice.py
utils/parsing/matice.pypayment_amountfunctiontests/test_matice.py
utils/parsing/matice.pyparse_payerfunctiontests/test_matice.py
utils/parsing/matice.pyread_text_linesfunction(internal use only)
utils/parsing/matice.pyslice_linesfunction(internal use only)
utils/parsing/matice.pymonth_blocksfunctiontests/test_matice.py
utils/parsing/matice.pyblock_paymentsfunctiontests/test_matice.py
utils/parsing/matice.pyparse_paymentsfunctionutils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pyblock_name_orderfunctiontests/test_matice.py
utils/parsing/matice.pymonth_checkfunction(internal use only)
utils/parsing/matice.pypayer_keyfunctiontests/test_matice.py
utils/parsing/matice.pyborrow_placesfunctiontests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pypayment_personsfunctiontests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py
utils/parsing/matice.pyjoinersfunctiontests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py
utils/parsing/occupations.pyload_hisco_authorityfunctionutils/reference_tools/occupations/build_conflict_workbook.py, utils/validation/data_checks.py
utils/parsing/occupations.pyload_occupation_curationfunction(internal use only)
utils/parsing/occupations.pysplit_occupationsfunction(internal use only)
utils/parsing/occupations.pyextract_qualifierfunction(internal use only)
utils/parsing/occupations.pyextract_career_stagefunction(internal use only)
utils/parsing/occupations.pycode_occupationfunctionutils/docs/build_docs.py, utils/validation/data_checks.py
utils/parsing/occupations.pyadd_occupation_columnsfunction02_data_cleaning.ipynb
utils/parsing/occupations.pymatcher_selftestfunctionutils/validation/data_checks.py
utils/parsing/occupations.pyoccupation_reportfunction02_data_cleaning.ipynb
utils/parsing/prng.pyparse_dmsfunctiontests/test_ruian.py
utils/parsing/prng.pyread_prngfunctionutils/parsing/ruian.py
utils/parsing/prng.pysilesia_okres_idsfunctionutils/parsing/ruian.py
utils/parsing/prng.pyadd_court_1910functionutils/parsing/ruian.py
utils/parsing/prng.pyprng_reportfunctionutils/parsing/ruian.py
utils/parsing/provenance.pyadd_provenance_columnsfunction02_data_cleaning.ipynb
utils/parsing/provenance.pyprovenance_reportfunction02_data_cleaning.ipynb
utils/parsing/provenance.pyprovenance_selftestfunctionutils/validation/data_checks.py
utils/parsing/ranks.pyclean_rankfunctionutils/validation/data_checks.py
utils/parsing/ranks.pyenglish_labelfunction— no call site found —
utils/parsing/ranks.pyresolve_ordinalsfunction— no call site found —
utils/parsing/ranks.pybranch_offunction— no call site found —
utils/parsing/ranks.pycommon_gradefunction— no call site found —
utils/parsing/ranks.pyband_offunctionutils/lexikon_1910/run_verify_test_b.py
utils/parsing/ranks.pyadd_rank_columnsfunction02_data_cleaning.ipynb
utils/parsing/ranks.pyadd_rank_resetfunction02_data_cleaning.ipynb
utils/parsing/ranks.pyrank_reportfunction02_data_cleaning.ipynb
utils/parsing/records_geo.pysettlement_name_setfunction02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py
utils/parsing/records_geo.pybuild_okres_lookupfunction(internal use only)
utils/parsing/records_geo.pyadd_birth_okres_idfunctionutils/parsing/lexikon.py
utils/parsing/records_geo.pyadd_birth_settlement_okresfunction(internal use only)
utils/parsing/records_geo.pybuild_twin_pairsfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/parsing/census.py, utils/validation/data_checks.py
utils/parsing/records_geo.pyresolve_twin_pairsfunction(internal use only)
utils/parsing/records_geo.pystrip_prahafunction(internal use only)
utils/parsing/records_geo.pyload_prague_suburbsfunctionutils/validation/data_checks.py
utils/parsing/records_geo.pyresolve_prague_suburbsfunction02_data_cleaning.ipynb
utils/parsing/records_geo.pyprague_suburb_reportfunction02_data_cleaning.ipynb
utils/parsing/records_geo.pytwin_pair_reportfunction02_data_cleaning.ipynb
utils/parsing/records_geo.pybirth_okres_reportfunction02_data_cleaning.ipynb
utils/parsing/records_geo.pysync_cisleithanianfunctionutils/parsing/records_geo_hu.py
utils/parsing/records_geo.pysettlement_homonymsfunctiontests/test_place_matching_precision.py
utils/parsing/records_geo.pyadd_settlement_attrsfunctiontests/test_place_matching_precision.py
utils/parsing/records_geo.pyadd_okres_namesfunction(internal use only)
utils/parsing/records_geo.pyadd_landfunction(internal use only)
utils/parsing/records_geo.pyadd_place_standardizationfunction02_data_cleaning.ipynb
utils/parsing/records_geo_hu.pyadd_jaras_idfunction02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py
utils/parsing/records_geo_hu.pyadd_settlement_jarasfunction02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py
utils/parsing/records_geo_hu.pybuild_jaras_twin_pairsfunction02_data_cleaning.ipynb, utils/validation/data_checks.py
utils/parsing/records_geo_hu.pyresolve_jaras_twinsfunction02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py
utils/parsing/records_geo_hu.pyset_land_from_jarasfunction02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py
utils/parsing/records_geo_hu.pyrecords_geo_hu_selftestfunctionutils/validation/data_checks.py
utils/parsing/records_geo_hu.pyjaras_twin_reportfunction02_data_cleaning.ipynb
utils/parsing/records_geo_hu.pyjaras_reportfunction02_data_cleaning.ipynb, utils/parsing/districts_hu.py
utils/parsing/religion.pytranslate_religionfunction— no call site found —
utils/parsing/religion.pyadd_religion_enfunction02_data_cleaning.ipynb
utils/parsing/religion.pyreligion_reportfunction02_data_cleaning.ipynb
utils/parsing/ruian.pykrovak_inversefunctiontests/test_ruian.py
utils/parsing/ruian.pykrovak_forwardfunctiontests/test_ruian.py
utils/parsing/ruian.pysjtsk_to_wgs84functiontests/test_ruian.py, utils/parsing/geonames.py
utils/parsing/ruian.pywgs84_to_sjtskfunctionutils/parsing/vanished.py
utils/parsing/ruian.pyread_vfrfunction(internal use only)
utils/parsing/ruian.pywkb_multipolygonfunction(internal use only)
utils/parsing/ruian.pywrite_geoparquetfunctiontests/test_ruian.py
utils/parsing/ruian.pyread_geoparquet_polygonsfunctiontests/test_ruian.py
utils/parsing/ruian.pybuild_tablesfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py, utils/scraping/scraper_amaterskedivadlo.py
utils/parsing/ruian.pyobce_adjacencyfunctiontests/test_ruian.py
utils/parsing/ruian.pywrite_tablesfunction(internal use only)
utils/parsing/ruian.pyruian_reportfunction(internal use only)
utils/parsing/ruian.pylocate_courtsfunctionutils/parsing/prng.py, utils/parsing/vanished.py
utils/parsing/ruian.pylocate_obcefunctionutils/parsing/vanished.py
utils/parsing/ruian.pydistance_to_okresfunction(internal use only)
utils/parsing/ruian.pyname_keyfunctiontests/test_ruian.py
utils/parsing/ruian.pymodern_okres_keyfunctiontests/test_ruian.py
utils/parsing/ruian.pycandidate_placesfunction(internal use only)
utils/parsing/ruian.pyhalf_keyfunctiontests/test_ruian.py
utils/parsing/ruian.pyprefix_keyfunctiontests/test_ruian.py
utils/parsing/ruian.pyregister_courtsfunctiontests/test_ruian.py
utils/parsing/ruian.pybuild_crosswalkfunctiontests/test_ruian.py
utils/parsing/ruian.pycoord_refsfunction(internal use only)
utils/parsing/ruian.pycrosswalk_reportfunctionutils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_register.py
utils/parsing/ruian.pybuild_current_geographyfunction02_data_cleaning.ipynb
utils/parsing/schema.pyto_englishfunction02_data_cleaning.ipynb
utils/parsing/schema.pyschema_reportfunction02_data_cleaning.ipynb
utils/parsing/schema.pywrite_data_dictionaryfunction02_data_cleaning.ipynb
utils/parsing/spatial.pylaea_forwardfunctiontests/test_ruian.py, utils/docs/build_docs.py, utils/parsing/ruian.py, utils/reference_tools/places/map_intensities.py, utils/reference_tools/places/map_matice.py
utils/parsing/spatial.pyread_polygonsfunctionutils/docs/build_docs.py, utils/reference_tools/places/map_matice.py
utils/parsing/spatial.pyCourtLocatorclass02_data_cleaning.ipynb
utils/parsing/spatial.pyadd_court_idsfunction(internal use only)
utils/parsing/spatial.pyadd_settlement_courtsfunction02_data_cleaning.ipynb
utils/parsing/spatial.pysettlement_court_reportfunction02_data_cleaning.ipynb
utils/parsing/theater.pyextract_theaterfunction(internal use only)
utils/parsing/theater.pytheater_selftestfunctionutils/validation/data_checks.py
utils/parsing/theater.pyadd_theater_columnfunction02_data_cleaning.ipynb
utils/parsing/theater.pytheater_reportfunction02_data_cleaning.ipynb
utils/parsing/units.pyextract_regimentfunction— no call site found —
utils/parsing/units.pyextract_armyfunction— no call site found —
utils/parsing/units.pymake_unit_keyfunction(internal use only)
utils/parsing/units.pyextract_unit_typefunction(internal use only)
utils/parsing/units.pytranslate_unitfunction— no call site found —
utils/parsing/units.pyadd_unit_columnsfunction02_data_cleaning.ipynb
utils/parsing/units.pyunit_reportfunction02_data_cleaning.ipynb
utils/parsing/vanished.pyfetch_vanishedfunctionutils/parsing/ruian.py
utils/parsing/vanished.pybuild_vanishedfunctionutils/parsing/ruian.py
utils/parsing/vanished.pyvanished_reportfunctionutils/parsing/ruian.py
utils/parsing/vua_courts.pyread_inventory_linesfunction(internal use only)
utils/parsing/vua_courts.pysplit_entriesfunctionutils/lexikon_1910/check_transcription.py
utils/parsing/vua_courts.pyparse_paragraphsfunctiontests/test_vua_courts.py
utils/parsing/vua_courts.pyparse_entryfunctiontests/test_unit_timeline.py, utils/reference_tools/units/build_unit_timeline.py
utils/parsing/vua_courts.pyparse_attributesfunctiontests/test_vua_courts.py
utils/parsing/vua_courts.pyparse_namefunctiontests/test_vua_courts.py
utils/parsing/vua_courts.pysplit_accusedfunctiontests/test_vua_courts.py
utils/parsing/vua_courts.pyparse_linesfunctiontests/test_vua_courts.py
utils/parsing/vua_courts.pyparse_inventoryfunction(internal use only)
utils/parsing/vua_courts.pyparse_allfunctionutils/parsing/matice.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/units/run_vua_courts_pilot.py
utils/parsing/vua_courts.pycourts_reportfunctiontests/test_vua_courts.py, utils/reference_tools/units/run_vua_courts_pilot.py
utils/parsing/vua_courts.pyhabsburg_unit_keyfunction(internal use only)
utils/parsing/vua_courts.pypilot_linkfunctiontests/test_vua_courts.py, utils/reference_tools/units/run_vua_courts_pilot.py
utils/parsing/vua_courts.pypilot_link_reportfunctiontests/test_vua_courts.py, utils/reference_tools/units/run_vua_courts_pilot.py
utils/parsing/wikidata.pyfetch_cz_settlementsfunction02_data_cleaning.ipynb
utils/parsing/wikidata.pyadd_settlement_coordsfunction02_data_cleaning.ipynb
utils/parsing/wikidata.pyadd_settlement_de_namefunction02_data_cleaning.ipynb
utils/parsing/wikidata.pyadd_birth_coordsfunction— no call site found —
utils/parsing/wikidata.pyokres_centroidsfunction02_data_cleaning.ipynb
utils/parsing/wikidata.pywikidata_reportfunction02_data_cleaning.ipynb
utils/plotting/th_style_v113.pyget_palettefunction03_visualisation.ipynb
utils/plotting/th_style_v113.pyset_yaxis_thousandsfunction03_visualisation.ipynb
utils/plotting/th_style_v113.pyset_xaxis_thousandsfunction03_visualisation.ipynb
utils/plotting/th_style_v113.pyannotate_barsfunction— no call site found —
utils/plotting/th_style_v113.pydespinefunction— no call site found —
utils/plotting/th_style_v113.pyadd_vspansfunction— no call site found —
utils/plotting/th_style_v113.pyadd_vlinesfunction03_visualisation.ipynb
utils/plotting/th_style_v113.pystyle_legendfunction03_visualisation.ipynb
utils/plotting/th_style_v113.pyset_academic_stylefunction03_visualisation.ipynb, utils/reference_tools/places/map_intensities.py, utils/reference_tools/places/map_matice.py
utils/plotting/th_style_v113.pysave_for_latexfunction03_visualisation.ipynb, utils/reference_tools/places/map_intensities.py, utils/reference_tools/places/map_matice.py
utils/reference_tools/background/prep_slices.pyepub_pagesfunction(internal use only)
utils/reference_tools/background/prep_slices.pypdf_pagesfunction(internal use only)
utils/reference_tools/background/prep_slices.pydiary_pagesfunction(internal use only)
utils/reference_tools/background/prep_slices.pywrite_slicesfunction(internal use only)
utils/reference_tools/background/verify_locators.pynormfunctiontests/test_glyph_e3.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_glyph_test_a.py, utils/lexikon_1910/score_table_passes.py, utils/parsing/locative.py
utils/reference_tools/background/verify_locators.pypagesfunction15_frozen_run.ipynb, tests/test_scraper_kramerius.py, utils/lexikon_1910/lexikon_1910_census.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/score_table_passes.py, utils/reference_tools/places/run_amateur_theatre_pilot.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_verlustlisten.py
utils/reference_tools/education/build_page_index.pyrowsfunctiontests/test_lexikon_1910_second_reader.py, tests/test_r5_german_notes.py, tests/test_route_fixes.py, tests/test_unit_timeline.py, tests/test_verify_test_b.py, utils/lexikon_1910/lexikon_1910.py, utils/reference_tools/occupations/run_link_bridge.py
utils/reference_tools/education/build_page_index.pymainfunction(internal use only)
utils/reference_tools/education/check_unterricht_transcription.pyResultclass(internal use only)
utils/reference_tools/education/check_unterricht_transcription.pycheck_tablefunction(internal use only)
utils/reference_tools/education/check_unterricht_transcription.pymainfunction(internal use only)
utils/reference_tools/education/draw_education_sample.pyclassifyfunctiontests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/reference_tools/education/draw_education_sample.pysystematicfunction(internal use only)
utils/reference_tools/education/draw_education_sample.pymainfunction(internal use only)
utils/reference_tools/education/run_unterricht_check.pyrun_taxonomyfunction(internal use only)
utils/reference_tools/education/run_unterricht_check.pyrun_continuationfunction(internal use only)
utils/reference_tools/education/run_unterricht_check.pyrun_klassenfunction(internal use only)
utils/reference_tools/education/run_unterricht_check.pyrun_compositionfunctionutils/docs/build_docs.py, utils/reference_tools/occupations/run_census_berufsstellung_check.py
utils/reference_tools/education/run_unterricht_check.pyrun_languagefunction(internal use only)
utils/reference_tools/education/run_unterricht_check.pymainfunction(internal use only)
utils/reference_tools/education/score_education_gold.pyhuman_rungfunction(internal use only)
utils/reference_tools/education/score_education_gold.pymainfunction(internal use only)
utils/reference_tools/occupations/build_conflict_workbook.pycandidatesfunctionutils/lexikon_1910/build_token_review.py, utils/reference_tools/occupations/run_czisco_check.py, utils/reference_tools/places/german_supervised_placement.py
utils/reference_tools/occupations/build_conflict_workbook.pybuild_conflictsfunction(internal use only)
utils/reference_tools/occupations/build_conflict_workbook.pybuild_czisco_onlyfunction(internal use only)
utils/reference_tools/occupations/build_conflict_workbook.pybuild_decisionsfunction(internal use only)
utils/reference_tools/occupations/build_conflict_workbook.pyverify_xlsxfunction(internal use only)
utils/reference_tools/occupations/build_conflict_workbook.pyload_curationfunction(internal use only)
utils/reference_tools/occupations/build_conflict_workbook.pymainfunction(internal use only)
utils/reference_tools/occupations/build_czisco_bridge.pyhead_nounsfunction— no call site found —
utils/reference_tools/occupations/build_czisco_bridge.pymatches_headfunctionutils/reference_tools/occupations/run_czisco_check.py
utils/reference_tools/occupations/build_czisco_bridge.pybuildfunction(internal use only)
utils/reference_tools/occupations/build_czisco_bridge.pybridge_reportfunctionutils/reference_tools/occupations/run_link_bridge.py
utils/reference_tools/occupations/build_czisco_bridge.pymainfunction(internal use only)
utils/reference_tools/occupations/build_hisco_authority.pyload_hierarchyfunction(internal use only)
utils/reference_tools/occupations/build_hisco_authority.pyload_code_listsfunction(internal use only)
utils/reference_tools/occupations/build_hisco_authority.pyload_hsn_scalesfunction(internal use only)
utils/reference_tools/occupations/build_hisco_authority.pybuild_authorityfunctionutils/lexikon_1910/lexikon_1910_names.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/reference_tools/occupations/build_hisco_authority.pyauthority_reportfunction(internal use only)
utils/reference_tools/occupations/build_hisco_authority.pymainfunction(internal use only)
utils/reference_tools/occupations/build_occupation_curation.pykey_frequenciesfunction(internal use only)
utils/reference_tools/occupations/build_occupation_curation.pybuildfunction(internal use only)
utils/reference_tools/occupations/build_occupation_curation.pycuration_reportfunction(internal use only)
utils/reference_tools/occupations/build_occupation_curation.pymainfunction(internal use only)
utils/reference_tools/occupations/czech_glosses.pyduplicate_keysfunctionutils/reference_tools/occupations/run_link_bridge.py
utils/reference_tools/occupations/download_hisco_sources.pysha256functionutils/lexikon_1910/extract_prose_pages.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/amateur_theatre_model.py, utils/parsing/vanished.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/places/statutory_prose.py, utils/scraping/fetch_vua_courts.py, utils/scraping/run_matice_downloads.py
utils/reference_tools/occupations/download_hisco_sources.pyfetchfunctiontests/test_scraper_verlustlisten.py, utils/docs/build_docs.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_verlustlisten.py
utils/reference_tools/occupations/download_hisco_sources.pydownload_druidfunction(internal use only)
utils/reference_tools/occupations/download_hisco_sources.pycheck_hsnfunction(internal use only)
utils/reference_tools/occupations/download_hisco_sources.pywrite_manifestfunctionutils/scraping/run_matice_downloads.py
utils/reference_tools/occupations/download_hisco_sources.pymainfunction(internal use only)
utils/reference_tools/occupations/run_census_berufsstellung_check.pyrunfunction(internal use only)
utils/reference_tools/occupations/run_census_berufsstellung_check.pyrun_compositionfunctionutils/docs/build_docs.py, utils/reference_tools/education/run_unterricht_check.py
utils/reference_tools/occupations/run_czisco_check.pyload_bridgefunction(internal use only)
utils/reference_tools/occupations/run_czisco_check.pybest_matchfunction(internal use only)
utils/reference_tools/occupations/run_czisco_check.pycheckfunction15_frozen_run.ipynb, tests/test_court_key.py, tests/test_statutory_institutions.py, utils/docs/build_docs.py, utils/lexikon_1910/draw_gold_sample.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/reference_tools/occupations/run_occcanine_check.py, utils/reference_tools/places/statutory_institutions.py
utils/reference_tools/occupations/run_czisco_check.pytail_candidatesfunction(internal use only)
utils/reference_tools/occupations/run_czisco_check.pymainfunction(internal use only)
utils/reference_tools/occupations/run_link_bridge.pyload_authority_codesfunction(internal use only)
utils/reference_tools/occupations/run_link_bridge.pyload_german_indexfunction(internal use only)
utils/reference_tools/occupations/run_link_bridge.pyload_label_indexfunction(internal use only)
utils/reference_tools/occupations/run_link_bridge.pybridgefunctionutils/docs/build_docs.py
utils/reference_tools/occupations/run_link_bridge.pyapply_adjudicationfunction(internal use only)
utils/reference_tools/occupations/run_link_bridge.pybridge_reportfunctionutils/reference_tools/occupations/build_czisco_bridge.py
utils/reference_tools/occupations/run_link_bridge.pymainfunction(internal use only)
utils/reference_tools/occupations/run_mosaic_beruf_check.pyrun_vocabularyfunction(internal use only)
utils/reference_tools/occupations/run_mosaic_beruf_check.pyrun_positionfunction(internal use only)
utils/reference_tools/occupations/run_mosaic_beruf_check.pyrun_age_bandfunction(internal use only)
utils/reference_tools/occupations/run_mosaic_beruf_check.pyrun_keysfunction(internal use only)
utils/reference_tools/occupations/run_mosaic_beruf_check.pyrun_stabilityfunction(internal use only)
utils/reference_tools/occupations/run_occcanine_check.pyagreementfunctionutils/docs/build_docs.py
utils/reference_tools/occupations/run_occcanine_check.pyload_modelfunction(internal use only)
utils/reference_tools/occupations/run_occcanine_check.pycheckfunction15_frozen_run.ipynb, tests/test_court_key.py, tests/test_statutory_institutions.py, utils/docs/build_docs.py, utils/lexikon_1910/draw_gold_sample.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/reference_tools/occupations/run_czisco_check.py, utils/reference_tools/places/statutory_institutions.py
utils/reference_tools/occupations/run_occcanine_check.pymainfunction(internal use only)
utils/reference_tools/parties/check_atlas_parties.pyloadfunctionutils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/reference_tools/units/check_unit_timeline.py
utils/reference_tools/parties/check_atlas_parties.pyrun_checksfunctionutils/docs/build_docs.py, utils/validation/data_checks.py
utils/reference_tools/parties/check_atlas_parties.pymainfunction(internal use only)
utils/reference_tools/places/audit_full_run_pages.pyaudit_pagefunction— no call site found —
utils/reference_tools/places/audit_full_run_pages.pyflagsfunction15_frozen_run.ipynb, tests/test_e1b_candidates.py, tests/test_vua_courts.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_table_arm.py
utils/reference_tools/places/audit_full_run_pages.pymainfunction(internal use only)
utils/reference_tools/places/build_curation.pymainfunction(internal use only)
utils/reference_tools/places/build_german_click_queue.pyentries_offunction(internal use only)
utils/reference_tools/places/build_german_click_queue.pybuildfunction(internal use only)
utils/reference_tools/places/build_german_click_queue.pymainfunction(internal use only)
utils/reference_tools/places/build_german_click_strips.pypage_tokensfunction(internal use only)
utils/reference_tools/places/build_german_click_strips.pymainfunction(internal use only)
utils/reference_tools/places/build_matice_read_sheets.pyalto_boxesfunction(internal use only)
utils/reference_tools/places/build_matice_read_sheets.pycrop_entryfunction(internal use only)
utils/reference_tools/places/build_matice_read_sheets.pylabelfunction02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/build_glyph_bank.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py, utils/parsing/schema.py, utils/validation/data_checks.py
utils/reference_tools/places/build_matice_read_sheets.pybuild_sheetsfunction(internal use only)
utils/reference_tools/places/build_matice_read_sheets.pymainfunction(internal use only)
utils/reference_tools/places/build_sk_curation.pymainfunction(internal use only)
utils/reference_tools/places/build_theatre_gold_page.pypage_clausesfunction(internal use only)
utils/reference_tools/places/build_theatre_gold_page.pybuildfunction(internal use only)
utils/reference_tools/places/german_candidate_diagnosis.pyfilter_offunction(internal use only)
utils/reference_tools/places/german_candidate_diagnosis.pymainfunction(internal use only)
utils/reference_tools/places/german_click_recall.pyload_clicksfunctionutils/reference_tools/places/german_supervised_placement.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/reference_tools/places/german_click_recall.pyinsidefunction16_theatre_model_pass.ipynb, utils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/german_candidate_diagnosis.py, utils/reference_tools/places/german_supervised_placement.py, utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_oracle.py
utils/reference_tools/places/german_click_recall.pycandidate_recallfunctionutils/reference_tools/places/german_supervised_placement.py
utils/reference_tools/places/german_click_recall.pyplacement_accuracyfunctionutils/reference_tools/places/german_supervised_placement.py
utils/reference_tools/places/german_click_recall.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/reference_tools/places/german_click_recall.pymainfunction(internal use only)
utils/reference_tools/places/german_locator_trial.pygerman_sectionsfunctionutils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/german_supervised_placement.py, utils/reference_tools/places/run_german_detect_by_naming.py
utils/reference_tools/places/german_locator_trial.pysafe_plate_templatesfunctionutils/reference_tools/places/german_click_recall.py
utils/reference_tools/places/german_locator_trial.pyplaced_sharefunction(internal use only)
utils/reference_tools/places/german_locator_trial.pymainfunction(internal use only)
utils/reference_tools/places/german_supervised_placement.pylabel_candidatesfunction(internal use only)
utils/reference_tools/places/german_supervised_placement.pyplace_allfunction(internal use only)
utils/reference_tools/places/german_supervised_placement.pymainfunction(internal use only)
utils/reference_tools/places/map_intensities.pyBasemapclass— no call site found —
utils/reference_tools/places/map_intensities.pycourt_valuesfunctionutils/reference_tools/places/map_matice.py
utils/reference_tools/places/map_intensities.pydistrict_figurefunction(internal use only)
utils/reference_tools/places/map_intensities.pymunicipal_figurefunction(internal use only)
utils/reference_tools/places/map_intensities.pymainfunction(internal use only)
utils/reference_tools/places/map_matice.pycourt_shapesfunctionutils/reference_tools/places/map_intensities.py
utils/reference_tools/places/map_matice.pyborder_segmentsfunctionutils/reference_tools/places/map_intensities.py
utils/reference_tools/places/map_matice.pybase_mapfunction(internal use only)
utils/reference_tools/places/map_matice.pyokres_valuesfunction(internal use only)
utils/reference_tools/places/map_matice.pycourt_valuesfunctionutils/reference_tools/places/map_intensities.py
utils/reference_tools/places/map_matice.pysettlement_countsfunctionutils/docs/build_docs.py
utils/reference_tools/places/map_matice.pymunicipal_mapfunction(internal use only)
utils/reference_tools/places/map_matice.pymainfunction(internal use only)
utils/reference_tools/places/merge_matice_readings.pymainfunction(internal use only)
utils/reference_tools/places/run_amateur_theatre_pilot.pyrunfunction(internal use only)
utils/reference_tools/places/run_amateur_theatre_pilot.pymainfunction(internal use only)
utils/reference_tools/places/run_german_detect_by_naming.pycandidate_glyphsfunction(internal use only)
utils/reference_tools/places/run_german_detect_by_naming.pythreshold_forfunction(internal use only)
utils/reference_tools/places/run_german_detect_by_naming.pyscore_pagefunction(internal use only)
utils/reference_tools/places/run_german_detect_by_naming.pymainfunction(internal use only)
utils/reference_tools/places/run_german_oracle.pyread_clicksfunctionutils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/reference_tools/places/run_german_oracle.pyclick_glyphsfunctionutils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/reference_tools/places/run_german_oracle.pyintervalfunction(internal use only)
utils/reference_tools/places/run_german_oracle.pymeasurefunctiontests/test_religion_categories.py, tests/test_sealed_scorer.py, utils/lexikon_1910/score_certification.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/score_sealed_retest.py
utils/reference_tools/places/run_german_oracle.pyper_tokenfunction(internal use only)
utils/reference_tools/places/run_german_oracle.pyleave_one_page_outfunction(internal use only)
utils/reference_tools/places/run_german_oracle.pymainfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pysection_imagesfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pyclicked_pagesfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pyreadfunction02_data_cleaning.ipynb, 15_frozen_run.ipynb, tests/test_amateur_theatre.py, tests/test_reader_batches_gemini.py, tests/test_scraper_amaterskedivadlo.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/parsing/prng.py, utils/reference_tools/background/prep_slices.py, utils/reference_tools/occupations/download_hisco_sources.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/units/build_unit_authority.py, utils/scraping/run_matice_downloads.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/lexikon_population.py
utils/reference_tools/places/run_german_reader_boxes.pyreader_marksfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pyclassify_marksfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pybox_reportfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pysnap_boxesfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pynegative_glyphsfunction(internal use only)
utils/reference_tools/places/run_german_reader_boxes.pyjudgefunctionutils/reference_tools/places/statutory_institutions.py, utils/scraping/run_matice_downloads.py
utils/reference_tools/places/run_german_reader_boxes.pymainfunction(internal use only)
utils/reference_tools/places/run_intensities.pyload_legionfunction(internal use only)
utils/reference_tools/places/run_intensities.pytheatre_meanfunction(internal use only)
utils/reference_tools/places/run_intensities.pyadd_ratesfunction(internal use only)
utils/reference_tools/places/run_intensities.pyown_maticefunction(internal use only)
utils/reference_tools/places/run_intensities.pyokres_tablefunction(internal use only)
utils/reference_tools/places/run_intensities.pycourt_okresfunction(internal use only)
utils/reference_tools/places/run_intensities.pycourt_tablefunction(internal use only)
utils/reference_tools/places/run_intensities.pylegion_settlementsfunction(internal use only)
utils/reference_tools/places/run_intensities.pysettlement_tablefunction(internal use only)
utils/reference_tools/places/run_intensities.pymainfunction(internal use only)
utils/reference_tools/places/run_matice_pilot.pyplace_indexfunctionutils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/reference_tools/places/run_matice_pilot.pyadd_placesfunction(internal use only)
utils/reference_tools/places/run_matice_pilot.pyapply_readingsfunctiontests/test_amateur_theatre_model.py, utils/parsing/amateur_theatre_model.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/reference_tools/places/run_matice_pilot.pyadd_occupationsfunction(internal use only)
utils/reference_tools/places/run_matice_pilot.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/reference_tools/places/run_matice_pilot.pytheatre_ratefunction(internal use only)
utils/reference_tools/places/run_matice_pilot.pytheatre_checkfunctionutils/docs/build_docs.py
utils/reference_tools/places/run_matice_pilot.pymainfunction(internal use only)
utils/reference_tools/places/run_matice_pilot.pypayment_reportfunction(internal use only)
utils/reference_tools/places/run_matice_pilot.pyregister_checkfunctionutils/validation/check_ostafin_silesia.py
utils/reference_tools/places/run_matice_pilot.pycourt_levelfunction(internal use only)
utils/reference_tools/places/score_sealed_abbreviations.pyupper_boundfunctiontests/test_sealed_abbreviations.py
utils/reference_tools/places/score_sealed_abbreviations.pyparse_language_countsfunctiontests/test_sealed_abbreviations.py
utils/reference_tools/places/score_sealed_abbreviations.pyroute_sidefunction(internal use only)
utils/reference_tools/places/score_sealed_abbreviations.pygold_sidefunction(internal use only)
utils/reference_tools/places/score_sealed_abbreviations.pysidesfunctiontests/test_sealed_scorer.py, utils/lexikon_1910/score_certification.py, utils/validation/score_sealed_retest.py
utils/reference_tools/places/score_sealed_abbreviations.pymergefunction02_data_cleaning.ipynb, tests/test_pooled_score.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/score_certification.py, utils/parsing/amateur_theatre.py, utils/parsing/amateur_theatre_model.py, utils/parsing/census_hu.py, utils/parsing/districts.py, utils/parsing/matice.py, utils/parsing/vua_courts.py, utils/reference_tools/education/score_education_gold.py, utils/reference_tools/occupations/build_czisco_bridge.py, utils/reference_tools/occupations/build_hisco_authority.py, utils/reference_tools/occupations/build_occupation_curation.py, utils/reference_tools/occupations/run_census_berufsstellung_check.py, utils/reference_tools/places/merge_matice_readings.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/units/build_unit_timeline.py, utils/scraping/scraper_amaterskedivadlo.py, utils/validation/check_ostafin_silesia.py
utils/reference_tools/places/score_sealed_abbreviations.pyblockfunctiontests/test_de_table.py
utils/reference_tools/places/score_sealed_abbreviations.pymeasurefunctiontests/test_religion_categories.py, tests/test_sealed_scorer.py, utils/lexikon_1910/score_certification.py, utils/reference_tools/places/run_german_oracle.py, utils/validation/score_sealed_retest.py
utils/reference_tools/places/score_sealed_abbreviations.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/reference_tools/places/score_sealed_abbreviations.pywritefunctiontests/test_lookalike_rules.py, tests/test_pooled_score.py, tests/test_r5_german_notes.py, tests/test_reader_batches_gemini.py, tests/test_sealed_scorer.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/reference_tools/occupations/download_hisco_sources.py, utils/scraping/progress.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_vuapraha.py, utils/tables/latex_tables.py
utils/reference_tools/places/score_sealed_abbreviations.pymainfunction(internal use only)
utils/reference_tools/places/statutory_institutions.pyvocabularyfunctiontests/test_r5_german_notes.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/check_transcription.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_r5_german_notes.py, utils/lexikon_1910/score_table_passes.py
utils/reference_tools/places/statutory_institutions.pyedition_textfunction(internal use only)
utils/reference_tools/places/statutory_institutions.pypromptfunction(internal use only)
utils/reference_tools/places/statutory_institutions.pyschemafunction02_data_cleaning.ipynb
utils/reference_tools/places/statutory_institutions.pyreadfunction02_data_cleaning.ipynb, 15_frozen_run.ipynb, tests/test_amateur_theatre.py, tests/test_reader_batches_gemini.py, tests/test_scraper_amaterskedivadlo.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/parsing/prng.py, utils/reference_tools/background/prep_slices.py, utils/reference_tools/occupations/download_hisco_sources.py, utils/reference_tools/places/run_german_reader_boxes.py, utils/reference_tools/units/build_unit_authority.py, utils/scraping/run_matice_downloads.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/lexikon_population.py
utils/reference_tools/places/statutory_institutions.pycheckfunction15_frozen_run.ipynb, tests/test_court_key.py, tests/test_statutory_institutions.py, utils/docs/build_docs.py, utils/lexikon_1910/draw_gold_sample.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/reference_tools/occupations/run_czisco_check.py, utils/reference_tools/occupations/run_occcanine_check.py
utils/reference_tools/places/statutory_institutions.pycanonical_codefunctiontests/test_statutory_institutions.py
utils/reference_tools/places/statutory_institutions.pyexpansion_codesfunctiontests/test_statutory_institutions.py
utils/reference_tools/places/statutory_institutions.pyspan_groupsfunctiontests/test_statutory_institutions.py
utils/reference_tools/places/statutory_institutions.pycomparefunctiontests/test_statutory_institutions.py, utils/lexikon_1910/score_r5_german_notes.py
utils/reference_tools/places/statutory_institutions.pyjudge_promptfunction(internal use only)
utils/reference_tools/places/statutory_institutions.pyjudge_schemafunction(internal use only)
utils/reference_tools/places/statutory_institutions.pyjudgefunctionutils/reference_tools/places/run_german_reader_boxes.py, utils/scraping/run_matice_downloads.py
utils/reference_tools/places/statutory_institutions.pyadjudicatedfunctiontests/test_statutory_institutions.py
utils/reference_tools/places/statutory_institutions.pyload_rulingsfunctiontests/test_statutory_institutions.py, utils/reference_tools/places/statutory_prose.py
utils/reference_tools/places/statutory_institutions.pyapply_rulingsfunctiontests/test_statutory_institutions.py
utils/reference_tools/places/statutory_institutions.pymark_totalsfunctiontests/test_statutory_institutions.py
utils/reference_tools/places/statutory_institutions.pyby_categoryfunctiontests/test_religion_categories.py, tests/test_sealed_scorer.py, utils/lexikon_1910/score_certification.py, utils/validation/score_sealed_retest.py
utils/reference_tools/places/statutory_institutions.pymainfunction(internal use only)
utils/reference_tools/places/statutory_prose.pyrenderfunctionutils/docs/build_docs.py, utils/lexikon_1910/build_contrast_sheet.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/make_gold_worksheet_de.py
utils/reference_tools/places/statutory_prose.pytext_layerfunction(internal use only)
utils/reference_tools/places/statutory_prose.pyink_maskfunctionutils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py
utils/reference_tools/places/statutory_prose.pyline_bandsfunction(internal use only)
utils/reference_tools/places/statutory_prose.pyprint_columnsfunction(internal use only)
utils/reference_tools/places/statutory_prose.pystripsfunctionutils/reference_tools/places/build_german_click_strips.py
utils/reference_tools/places/statutory_prose.pyread_stripfunction(internal use only)
utils/reference_tools/places/statutory_prose.pyestimatefunctiontests/test_r5_german_notes.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_r5_german_notes.py
utils/reference_tools/places/statutory_prose.pyclean_replyfunctiontests/test_statutory_prose.py
utils/reference_tools/places/statutory_prose.pynormalisefunctiontests/test_court_key.py, tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/build_symbol_atlas.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/lexikon_1910_editions.py, utils/lexikon_1910/lexikon_1910_names.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_r5_german_notes.py, utils/parsing/matice.py, utils/reference_tools/places/build_german_click_queue.py, utils/reference_tools/places/german_locator_trial.py, utils/reference_tools/places/statutory_institutions.py
utils/reference_tools/places/statutory_prose.pytokensfunctiontests/test_statutory_prose.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/reference_tools/places/statutory_prose.pyload_rulingsfunctiontests/test_statutory_institutions.py, utils/reference_tools/places/statutory_institutions.py
utils/reference_tools/places/statutory_prose.pyalignfunctiontests/test_glyph_e3.py, tests/test_score_table_passes.py, tests/test_statutory_prose.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/score_table_passes.py
utils/reference_tools/places/statutory_prose.pydisagreement_kindfunction(internal use only)
utils/reference_tools/places/statutory_prose.pydetokenisefunctiontests/test_statutory_prose.py
utils/reference_tools/places/statutory_prose.pymainfunction(internal use only)
utils/reference_tools/units/build_unit_authority.pyread_entriesfunctionutils/reference_tools/units/build_unit_timeline.py
utils/reference_tools/units/build_unit_authority.pyheadword_numberfunction— no call site found —
utils/reference_tools/units/build_unit_authority.pybuildfunction(internal use only)
utils/reference_tools/units/build_unit_authority.pymainfunction(internal use only)
utils/reference_tools/units/build_unit_timeline.pysentencesfunctiontests/test_unit_timeline.py
utils/reference_tools/units/build_unit_timeline.pysplit_phasesfunctiontests/test_unit_timeline.py
utils/reference_tools/units/build_unit_timeline.pyparse_statesfunctiontests/test_unit_timeline.py
utils/reference_tools/units/build_unit_timeline.pyparse_periodfunction(internal use only)
utils/reference_tools/units/build_unit_timeline.pyparse_commandersfunction(internal use only)
utils/reference_tools/units/build_unit_timeline.pyparse_parentsfunctiontests/test_unit_timeline.py
utils/reference_tools/units/build_unit_timeline.pyparse_lifecyclefunction(internal use only)
utils/reference_tools/units/build_unit_timeline.pyparse_transportfunction(internal use only)
utils/reference_tools/units/build_unit_timeline.pyparse_entryfunctiontests/test_unit_timeline.py, utils/parsing/vua_courts.py
utils/reference_tools/units/build_unit_timeline.pyout_of_orderfunctiontests/test_lexikon_1910_order.py, tests/test_unit_timeline.py, utils/lexikon_1910/lexikon_1910_institutions.py
utils/reference_tools/units/build_unit_timeline.pyflag_suspectfunction(internal use only)
utils/reference_tools/units/build_unit_timeline.pybuildfunction(internal use only)
utils/reference_tools/units/build_unit_timeline.pymainfunction(internal use only)
utils/reference_tools/units/check_unit_timeline.pyloadfunctionutils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/lexikon_1910_volume.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/reference_tools/parties/check_atlas_parties.py
utils/reference_tools/units/check_unit_timeline.pychecksfunction(internal use only)
utils/reference_tools/units/check_unit_timeline.pymainfunction(internal use only)
utils/reference_tools/units/run_vua_courts_pilot.pyrunfunction(internal use only)
utils/scraping/fetch_vua_courts.pywrite_source_notefunctionutils/scraping/run_matice_downloads.py, utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/fetch_vua_courts.pyfetch_inventoriesfunction(internal use only)
utils/scraping/progress.pyhas_tqdmfunction— no call site found —
utils/scraping/progress.pypage_barfunctionutils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/progress.pyredirect_loggingfunctionutils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/run_lexicon_downloads.pyrun_jobfunctionutils/scraping/run_matice_downloads.py
utils/scraping/run_lexicon_downloads.pysummarisefunctionutils/lexikon_1910/locate_glyphs.py, utils/scraping/run_oeulk_downloads.py
utils/scraping/run_lexicon_downloads.pymainfunction(internal use only)
utils/scraping/run_matice_downloads.pypayment_jobsfunction(internal use only)
utils/scraping/run_matice_downloads.pyall_jobsfunction(internal use only)
utils/scraping/run_matice_downloads.pyrobots_disallowedfunction(internal use only)
utils/scraping/run_matice_downloads.pyresolve_slicefunction(internal use only)
utils/scraping/run_matice_downloads.pycheck_positionsfunction(internal use only)
utils/scraping/run_matice_downloads.pysolrfunction(internal use only)
utils/scraping/run_matice_downloads.pypayment_entriesfunction(internal use only)
utils/scraping/run_matice_downloads.pylist_runsfunctiontests/test_matice.py
utils/scraping/run_matice_downloads.pyissue_labelfunctiontests/test_matice.py
utils/scraping/run_matice_downloads.pydiscover_paymentsfunction(internal use only)
utils/scraping/run_matice_downloads.pydiscover_issuefunction(internal use only)
utils/scraping/run_matice_downloads.pywrite_manifestfunctionutils/reference_tools/occupations/download_hisco_sources.py
utils/scraping/run_matice_downloads.pyassemble_slicefunction(internal use only)
utils/scraping/run_matice_downloads.pyrun_jobfunctionutils/scraping/run_lexicon_downloads.py
utils/scraping/run_matice_downloads.pywrite_source_notefunctionutils/scraping/fetch_vua_courts.py, utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/run_matice_downloads.pydry_runfunctionutils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py
utils/scraping/run_matice_downloads.pymainfunction(internal use only)
utils/scraping/run_oeulk_downloads.pyvolume_keyfunctiontests/test_scraper_kramerius.py
utils/scraping/run_oeulk_downloads.pylist_volumesfunction(internal use only)
utils/scraping/run_oeulk_downloads.pypdf_page_countfunction(internal use only)
utils/scraping/run_oeulk_downloads.pypages_on_diskfunction(internal use only)
utils/scraping/run_oeulk_downloads.pyrun_volumefunction(internal use only)
utils/scraping/run_oeulk_downloads.pysummarisefunctionutils/lexikon_1910/locate_glyphs.py, utils/scraping/run_lexicon_downloads.py
utils/scraping/run_oeulk_downloads.pymainfunction(internal use only)
utils/scraping/scraper_amaterskedivadlo.pyHistoryViewErrorclass(internal use only)
utils/scraping/scraper_amaterskedivadlo.pyBlockedErrorclassutils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_amaterskedivadlo.pyServerUnhealthyErrorclassutils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_amaterskedivadlo.pyRequestBudgetExceededclassutils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_amaterskedivadlo.pyparse_map_xmlfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyparse_year_tokenfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyparse_spanfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyparse_ensemble_listfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyparse_first_recordfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyparse_place_pagefunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyparse_place_ensemblesfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyparse_activity_decadesfunctiontests/test_amateur_theatre.py
utils/scraping/scraper_amaterskedivadlo.pyparse_ensemble_pagefunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyview_statefunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyis_history_viewfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyTheatreClientclasstests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pywrite_source_notefunctionutils/scraping/fetch_vua_courts.py, utils/scraping/run_matice_downloads.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_amaterskedivadlo.pyfetch_placesfunction(internal use only)
utils/scraping/scraper_amaterskedivadlo.pyplaces_tablefunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyselect_geo_idsfunction(internal use only)
utils/scraping/scraper_amaterskedivadlo.pyscrape_placesfunction(internal use only)
utils/scraping/scraper_amaterskedivadlo.pybuild_tablesfunctionutils/parsing/ruian.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/scraping/scraper_amaterskedivadlo.pycompare_sourcesfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/scraping/scraper_amaterskedivadlo.pyestimate_full_runfunctionutils/reference_tools/places/run_amateur_theatre_pilot.py
utils/scraping/scraper_amaterskedivadlo.pyselect_ensemble_idsfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyscrape_ensemblesfunction(internal use only)
utils/scraping/scraper_amaterskedivadlo.pybuild_ensemble_pagesfunctiontests/test_scraper_amaterskedivadlo.py, utils/reference_tools/places/run_amateur_theatre_pilot.py
utils/scraping/scraper_amaterskedivadlo.pyparse_abbreviation_pagefunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pyfetch_abbreviationsfunctiontests/test_scraper_amaterskedivadlo.py
utils/scraping/scraper_amaterskedivadlo.pybuild_parserfunctionutils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_amaterskedivadlo.pymainfunction(internal use only)
utils/scraping/scraper_csol.pyScrapeResultclass(internal use only)
utils/scraping/scraper_csol.pyScrapeSummaryclass— no call site found —
utils/scraping/scraper_csol.pyparse_recordfunctiontests/test_scraper_verlustlisten.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pymake_sessionfunction01_data_collection.ipynb, tests/test_scraper_verlustlisten.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pyfetch_with_retriesfunctionutils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pyfetch_onefunction(internal use only)
utils/scraping/scraper_csol.pyautodetect_max_idfunction01_data_collection.ipynb, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pyCSVWriterclassutils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pywrite_xlsx_from_csvfunction(internal use only)
utils/scraping/scraper_csol.pyiter_scrapefunctionutils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pyscrape_rangefunction01_data_collection.ipynb, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pytest_idfunction01_data_collection.ipynb, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pyvalidate_outputfunction01_data_collection.ipynb, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pybuild_parserfunctionutils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_csol.pymainfunction(internal use only)
utils/scraping/scraper_kramerius.pyKrameriusClientclassutils/scraping/run_oeulk_downloads.py
utils/scraping/scraper_kramerius.pyparse_pidfunctionutils/scraping/scraper_kramerius7.py
utils/scraping/scraper_kramerius.pyparse_hostfunctionutils/scraping/run_matice_downloads.py, utils/scraping/scraper_kramerius7.py
utils/scraping/scraper_kramerius.pysafe_filenamefunctionutils/scraping/scraper_kramerius7.py
utils/scraping/scraper_kramerius.pydownload_documentfunctionutils/scraping/run_lexicon_downloads.py, utils/scraping/run_matice_downloads.py, utils/scraping/run_oeulk_downloads.py, utils/scraping/scraper_kramerius7.py
utils/scraping/scraper_kramerius.pymainfunction(internal use only)
utils/scraping/scraper_kramerius7.pyKramerius7Clientclassutils/scraping/run_matice_downloads.py
utils/scraping/scraper_kramerius7.pyparse_pidfunctionutils/scraping/scraper_kramerius.py
utils/scraping/scraper_kramerius7.pyparse_hostfunctionutils/scraping/run_matice_downloads.py, utils/scraping/scraper_kramerius.py
utils/scraping/scraper_kramerius7.pysafe_filenamefunctionutils/scraping/scraper_kramerius.py
utils/scraping/scraper_kramerius7.pydownload_documentfunctionutils/scraping/run_lexicon_downloads.py, utils/scraping/run_matice_downloads.py, utils/scraping/run_oeulk_downloads.py, utils/scraping/scraper_kramerius.py
utils/scraping/scraper_kramerius7.pymainfunction(internal use only)
utils/scraping/scraper_verlustlisten.pyBlockedErrorclassutils/scraping/scraper_amaterskedivadlo.py
utils/scraping/scraper_verlustlisten.pyServerUnhealthyErrorclassutils/scraping/scraper_amaterskedivadlo.py
utils/scraping/scraper_verlustlisten.pyRequestBudgetExceededclassutils/scraping/scraper_amaterskedivadlo.py
utils/scraping/scraper_verlustlisten.pyCanaryErrorclass(internal use only)
utils/scraping/scraper_verlustlisten.pyproject_offunctiontests/test_scraper_verlustlisten.py
utils/scraping/scraper_verlustlisten.pyparse_recordfunctiontests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_verlustlisten.pymake_sessionfunction01_data_collection.ipynb, tests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_verlustlisten.pyDESClientclasstests/test_scraper_verlustlisten.py
utils/scraping/scraper_verlustlisten.pyread_ledgerfunctiontests/test_lexikon_1910_ledger.py, utils/lexikon_1910/lexikon_1910_ledger.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_frozen.py
utils/scraping/scraper_verlustlisten.pywrite_source_notefunctionutils/scraping/fetch_vua_courts.py, utils/scraping/run_matice_downloads.py, utils/scraping/scraper_amaterskedivadlo.py
utils/scraping/scraper_verlustlisten.pywalk_idsfunctiontests/test_scraper_verlustlisten.py
utils/scraping/scraper_verlustlisten.pycheck_canaryfunction01_data_collection.ipynb
utils/scraping/scraper_verlustlisten.pyscrape_rangefunction01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_verlustlisten.pypilot_idsfunctiontests/test_scraper_verlustlisten.py
utils/scraping/scraper_verlustlisten.pyrun_pilotfunction01_data_collection.ipynb, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_table_arm.py
utils/scraping/scraper_verlustlisten.pypilot_reportfunction01_data_collection.ipynb
utils/scraping/scraper_verlustlisten.pytest_idfunction01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_verlustlisten.pybuild_parserfunctionutils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py
utils/scraping/scraper_verlustlisten.pymainfunction(internal use only)
utils/scraping/scraper_vuapraha.pyDBclass(internal use only)
utils/scraping/scraper_vuapraha.pymake_sessionfunction01_data_collection.ipynb, tests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_vuapraha.pyfetch_with_retriesfunctionutils/scraping/scraper_csol.py
utils/scraping/scraper_vuapraha.pyparse_recordfunctiontests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_vuapraha.pyCSVWriterclassutils/scraping/scraper_csol.py
utils/scraping/scraper_vuapraha.pyautodetect_max_idfunction01_data_collection.ipynb, utils/scraping/scraper_csol.py
utils/scraping/scraper_vuapraha.pyiter_scrapefunctionutils/scraping/scraper_csol.py
utils/scraping/scraper_vuapraha.pyscrape_rangefunction01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_vuapraha.pytest_idfunction01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_vuapraha.pyvalidate_outputfunction01_data_collection.ipynb, utils/scraping/scraper_csol.py
utils/scraping/scraper_vuapraha.pybuild_parserfunctionutils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py
utils/scraping/scraper_vuapraha.pymainfunction(internal use only)
utils/tables/latex_tables.pyRawLaTeXclass(internal use only)
utils/tables/latex_tables.pyseries_tablefunction(internal use only)
utils/tables/latex_tables.pysummary_tablefunction03_visualisation.ipynb, utils/lexikon_1910/run_e1b_second_reader.py
utils/tables/latex_tables.pyregression_tablefunction— no call site found —
utils/tables/latex_tables.pyexit_code_tablefunction03_visualisation.ipynb
utils/tables/latex_tables.pymissing_tablefunction01_data_collection.ipynb
utils/tables/latex_tables.pydf_to_latexfunction03_visualisation.ipynb
utils/validation/build_validation_workbook.pybuild_place_sheetfunction(internal use only)
utils/validation/build_validation_workbook.pybuild_rank_sheetfunction(internal use only)
utils/validation/build_validation_workbook.pymainfunction(internal use only)
utils/validation/check_ostafin_silesia.pykeyfunction02_data_cleaning.ipynb, tests/test_ostafin_silesia.py, tests/test_vua_courts.py, utils/docs/build_docs.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/lexikon_1910_notes.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/score_certification.py, utils/parsing/schema.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/reference_tools/units/check_unit_timeline.py, utils/scraping/run_oeulk_downloads.py, utils/scraping/scraper_amaterskedivadlo.py, utils/validation/data_checks.py
utils/validation/check_ostafin_silesia.pygerman_namefunctiontests/test_ostafin_silesia.py
utils/validation/check_ostafin_silesia.pydigitsfunctiontests/test_ostafin_silesia.py
utils/validation/check_ostafin_silesia.pyostafinfunction(internal use only)
utils/validation/check_ostafin_silesia.pycommunesfunctiontests/test_ostafin_silesia.py
utils/validation/check_ostafin_silesia.pycensus_checkfunction(internal use only)
utils/validation/check_ostafin_silesia.pytable_rowsfunctiontests/test_score_table_passes.py, utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py
utils/validation/check_ostafin_silesia.pyjoin_registerfunctiontests/test_ostafin_silesia.py
utils/validation/check_ostafin_silesia.pyregister_checkfunctionutils/reference_tools/places/run_matice_pilot.py
utils/validation/check_ostafin_silesia.pylexicon_candidatesfunctiontests/test_ostafin_silesia.py
utils/validation/check_ostafin_silesia.pyjoin_lexiconfunction(internal use only)
utils/validation/check_ostafin_silesia.pycausefunctionutils/parsing/amateur_theatre_model.py
utils/validation/check_ostafin_silesia.pylexicon_checkfunction(internal use only)
utils/validation/check_ostafin_silesia.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/data_checks.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/validation/check_ostafin_silesia.pymainfunction(internal use only)
utils/validation/data_checks.pyDataCheckErrorclass(internal use only)
utils/validation/data_checks.pyCheckclass02_data_cleaning.ipynb
utils/validation/data_checks.pyrun_checksfunctionutils/docs/build_docs.py, utils/reference_tools/parties/check_atlas_parties.py
utils/validation/data_checks.pyplace_checksfunction15_frozen_run.ipynb
utils/validation/data_checks.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/run_completeness.py, utils/validation/score_sealed_retest.py
utils/validation/data_checks.pyassert_data_okfunction02_data_cleaning.ipynb
utils/validation/lexikon_population.pydecodefunctiontests/test_ostafin_silesia.py, tests/test_reader_batches_gemini.py, utils/docs/build_docs.py, utils/lexikon_1910/bakeoff.py, utils/lexikon_1910/build_token_review.py, utils/lexikon_1910/extract_prose_pages.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/districts.py, utils/parsing/lexikon.py, utils/parsing/matice.py, utils/parsing/ruian.py, utils/reference_tools/background/prep_slices.py, utils/reference_tools/places/statutory_prose.py, utils/reference_tools/units/build_unit_authority.py, utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/validation/check_ostafin_silesia.py
utils/validation/lexikon_population.pyfoldfunctiontests/test_ostafin_silesia.py, utils/lexikon_1910/lexikon_1910_courts.py, utils/lexikon_1910/lexikon_1910_de_notes.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/score_certification.py, utils/validation/check_ostafin_silesia.py
utils/validation/lexikon_population.pytouches_1910functiontests/test_ostafin_silesia.py
utils/validation/lexikon_population.pydata_rowsfunction(internal use only)
utils/validation/lexikon_population.pynote_rowsfunction(internal use only)
utils/validation/lexikon_population.pyreadfunction02_data_cleaning.ipynb, 15_frozen_run.ipynb, tests/test_amateur_theatre.py, tests/test_reader_batches_gemini.py, tests/test_scraper_amaterskedivadlo.py, utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/lexikon_1910_vision.py, utils/lexikon_1910/reader_batches.py, utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py, utils/parsing/prng.py, utils/reference_tools/background/prep_slices.py, utils/reference_tools/occupations/download_hisco_sources.py, utils/reference_tools/places/run_german_reader_boxes.py, utils/reference_tools/places/statutory_institutions.py, utils/reference_tools/units/build_unit_authority.py, utils/scraping/run_matice_downloads.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py
utils/validation/run_completeness.pyqueue_labelfunctiontests/test_run_completeness.py
utils/validation/run_completeness.pymissing_repliesfunctiontests/test_run_completeness.py
utils/validation/run_completeness.pyread_gapsfunction15_frozen_run.ipynb, tests/test_run_completeness.py
utils/validation/run_completeness.pyopus_gapsfunction15_frozen_run.ipynb, tests/test_run_completeness.py
utils/validation/run_completeness.pyunacceptedfunction15_frozen_run.ipynb, tests/test_run_completeness.py
utils/validation/run_completeness.pywrite_recordfunction15_frozen_run.ipynb, tests/test_run_completeness.py
utils/validation/run_completeness.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/score_sealed_retest.py
utils/validation/score_sealed_retest.pyscore_runfunction(internal use only)
utils/validation/score_sealed_retest.pybetweenfunction03_visualisation.ipynb, utils/docs/build_docs.py, utils/parsing/matice.py, utils/reference_tools/occupations/build_hisco_authority.py, utils/reference_tools/occupations/run_mosaic_beruf_check.py, utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_intensities.py, utils/reference_tools/places/run_matice_pilot.py, utils/validation/data_checks.py
utils/validation/score_sealed_retest.pyreportfunction02_data_cleaning.ipynb, 03_visualisation.ipynb, 15_frozen_run.ipynb, tests/test_religion_categories.py, tests/test_run_completeness.py, tests/test_sealed_scorer.py, utils/docs/build_docs.py, utils/lexikon_1910/build_de_page_map.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/read_transcription_md.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_glyph_e3.py, utils/lexikon_1910/score_certification.py, utils/lexikon_1910/score_table_passes.py, utils/linkage.py, utils/parsing/clean.py, utils/parsing/dates.py, utils/parsing/exit_codes.py, utils/parsing/habsburg.py, utils/reference_tools/places/german_click_recall.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/check_ostafin_silesia.py, utils/validation/data_checks.py, utils/validation/run_completeness.py
utils/validation/score_sealed_retest.pyretestfunction(internal use only)
utils/validation/score_sealed_retest.pymainfunction(internal use only)

Party registerWhat's in the data

55 parties of the Czech lands 1861–1918, with every merger and renaming (Akademický atlas, plate IV.37). · source: reference/parties/atlas_iv37_parties.csv

55 rows

codeblocname_csname_enfoundedcechymoravaslezskonote_csrefs
Č1czechNárodní stranaNational Party (Old Czechs)18611861.01861.0staročeši, 1918 pokračovali v Č 23Č23
Č2czechNárodní strana svobodomyslnáNational Liberal Party (Young Czechs)18741874.0mladočeši, 1918 transformace v Č 23Č23
Č3czechLidová stranaPeople's Party (Moravia)18911891.01909 po sjednocení s Moravskou str. pokrokovou – Č 17Č17
Č4czechKřesťanskosociální stranaChristian-Social Party1894/981894.01899.01911.0v Čechách 1906 nahrazena Č 14Č14
Č5czechKatolická strana národníCatholic National Party18961897.01896.0v Čechách 1906 nahrazena Č 14Č14
Č6czechStrana pokrokových socialistůParty of Progressive Socialists18961896.0zanikla 1897
Č7czechČeská strana radikálně pokrokováCzech Radical Progressive Party18971897.01908 sjednocení s Č 10 v Č 15Č10;Č15
Č8czechČeská strana národně sociálníCzech National Social Party18971897.01898.01898.01918 po spojení s D7 přejmenována na Č 24D7;Č24
Č9czechAgrární stranaAgrarian Party18991899.01904.01912.0
Č10czechČeská strana státoprávníCzech State-Right Party18991899.01908 sjednocení s Č 7 v Č 15Č7;Č15
Č11czechKřesťansko-sociální strana lidováChristian-Social People's Party18991899.01906 nahrazena Č 14, znovu 1912–1913Č14
Č12czechČeská strana lidováCzech People's Party (Realists)19001900.0realisté, 1905 transformace v Č 13Č13
Č13czechČeská strana pokrokováCzech Progressive Party19051905.01907.01918 spoluzakladatelka Č 23Č23
Č14czechStrana katolického liduParty of the Catholic People19061906.01910 přejmenována na Č 18Č18
Č15czechČeská strana státoprávně pokrokováCzech State-Right Progressive Party19081908.01918 spoluzakladatelka Č 23Č23
Č16czechStrana živnostnictva a obchodnictva českoslovanskéhoCzecho-Slavonic Tradesmen's and Merchants' Party19081909.01908.01909.01905 první pokusy
Č17czechLidová strana pokrokováPeople's Progressive Party (Moravia)19091909.01918 spoluzakladatelka Č 23Č23
Č18czechČeská strana křesťansko-sociálníCzech Christian-Social Party19101910.01916 sloučení v Č 22Č22
Č19czechKatolicko-národní strana konzervativníCatholic-National Conservative Party19111911.0
Č20czechKonzervativní strana lidováConservative People's Party19121912.0zanikla 1914
Č21czechMoravská strana lidováMoravian People's Party19131913.0tzv. Bulínova, zanikla 1914
Č22czechSpojená strana katolickáUnited Catholic Party19161916.0sjednocení katol. stran v Čechách
Č23czechČeská státoprávní demokracieCzech State-Right Democracy19181918.01918.01918.01919 přejmenována na Národní demokracii
Č24czechČeská strana socialistickáCzech Socialist Party19181918.01918.01918.0pokračovatelka Č 8Č8
Č25czechMoravsko-slezská strana lidováMoravian-Silesian People's Party19181918.01918.0sjednocení kat. stran Moravy a Slezska
N1germanNěmecká liberální stranaGerman Liberal Party18611861.01861.01861.0ústavověrná, 1896 nahrazena N5N5
N2germanNěmecká nacionální stranaGerman National Party1881.01890.01896 přejmenována na N4N4
N3germanRakouská křesťansko-sociální stranaAustrian Christian-Social Party18911905.0
N4germanNěmecká lidová stranaGerman People's Party18961896.0pokračovatelka N 2N2
N5germanNěmecká pokroková stranaGerman Progressive Party18961896.01896.01896.0nástupkyně N 1N1
N6germanVšeněmecká stranaPan-German Party19001901.01900.01901.0
N7germanVolná všeněmecká stranaFree Pan-German Party19031903.0secese z N 6, 1906 přejmenována na N8N6;N8
N8germanNěmecká radikální stranaGerman Radical Party19061907.01906.01906.0nástupkyně N7N7
N9germanNěmecká agrární stranaGerman Agrarian Party19051905.01914.01906.0
N10germanNěmecká dělnická stranaGerman Workers' Party19041904.01906.01907.01918 přejmenována na N 11N11
N11germanNěmecká nacionálně socialistická dělnická stranaGerman National Socialist Workers' Party19181918.01918.01918.0nástupkyně N10N10
P1polishPolská národní stranaPolish National Party18611861.0honorační
P2polishSvaz slezských katolíkůUnion of Silesian Catholics18831883.0
P3polishPolská lidová společnostPolish People's Society18841884.0evangelická, 1906 přejmenována na P 5P5
P4polishRadikálně národní stranaRadical National Party18971897.01906 přejmenována na P 6, odnož haličské PLSP6
P5polishPolská národní stranaPolish National Party19061906.01911 sloučení v P 7P7
P6polishPolská lidová stranaPolish People's Party19061906.0
P7polishPolské národní sjednoceníPolish National Union19111911.0
P8polishSlezská lidová stranaSilesian People's Party19091909.0šlonzácká, proněmecká
D1workersRakouská dělnická stranaAustrian Workers' Party18741874.01874.01874.0
D2workersČeskoslovanská sociálně demokratická strana dělnickáCzecho-Slavonic Social Democratic Workers' Party18781878.01878.01878.0do 1910/11 autonomní součást celorakouské strany
D3workersRakouská sociálně demokratická strana dělnickáAustrian Social Democratic Workers' Party18881889.01889.01889.0obnovena
D4workersPolská sociálně demokratická strana Haliče a SlezskaPolish Social Democratic Party of Galicia and Silesia18991899.0
D5workersPolská sociálně demokratická strana Slezska a MoravyPolish Social Democratic Party of Silesia and Moravia19061906.01906.0podléhala haličské straně
D6workersČeská sociálně demokratická dělnická stranaCzech Social Democratic Workers' Party19111911.01911.01911.0centralistická
D7workersFederace českých anarchokomunistůFederation of Czech Anarcho-Communists1914
V1landownerStrana ústavověrného velkostatkuParty of the Constitutionally Loyal Great Estate18611861.01861.01861.0
V2landownerStrana konzervativního velkostatkuParty of the Conservative Great Estate18611861.01861.01861.0
V3landownerStřední strana moravského velkostatkuCentre Party of the Moravian Great Estate18641864.0obnovena 1884
V4landownerStrana národních velkostatkářůParty of National Great Landowners18941894.0zanikla 1897

Unit authorityWhat's in the data

Legion units → theatre, dates, lineage (Fidler 2025). · source: reference/units/unit_authority.csv

361 rows

unit_csnumbervarianttheatrekindformeddisbandedn_commanders
DRUHÝ ČS. LODNÍ TRANSPORT DO FRANCIE2.0Francetransport1
PRVNÍ ČS. LODNÍ TRANSPORT DO FRANCIE1.0Francetransport1
TŘETÍ ČS. LODNÍ TRANSPORT DO FRANCIE3.0Francetransport1
ČTVRTÝ ČS. LODNÍ TRANSPORT DO FRANCIE4.0Francetransport1
DVACÁTÝ DRUHÝ ČS. STŘELECKÝ PLUK22.0Franceunit1918-05-201
DVACÁTÝ PRVNÍ ČS. STŘELECKÝ PLUK21.0Franceunit1918-01-191
DVACÁTÝ TŘETÍ ČS. STŘELECKÝ PLUK23.0Franceunit1918-12-031
DVACÁTÝ ČTVRTÝ ČS. STŘELECKÝ PRAPOR24.0Franceunit1919-01-181
PRVNÍ ČS. STŘELECKÁ BRIGÁDA [II]1.0IIFranceunit1918-06-091
PÁTÁ ČS. STŘELECKÁ DIVIZE5.0Franceunit1918-12-030
VELITELSTVÍ PĚCHOTY 5. ČS. DIVIZEFranceunit1918-12-031
VÝCVIKOVÝ TÁBOR ČS. DOBROVOLNÍKŮFranceunit1918-04-081
ČS. NÁHRADNÍ TĚLESOFranceunit1918-06-101
ČS. STŘELECKÝ PRAPOR V COGNACUFranceunit1917-11-141
ČS. ZVLÁŠTNÍ PRAPORFranceunit1918-11-141
VOJENSKÝ SOUD ČS. SBORUItalyinstitution1918-07-152
ZÁLOŽNÍ NEMOCNICE ČS. VOJSKAItalyinstitution1918-04-140
ČS. POLNÍ NEMOCNICEItalyinstitution1918-11-170
ČS. POLNÍ POŠTA [II]IIItalyinstitution1918-11-170
DRUHÁ NÁHRADNÍ ROTA ČS. VOJSKA2.0Italyunit1918-04-140
DRUHÁ ČS. ZVLÁŠTNÍ BRIGÁDA2.0Italyunit1918-04-261
DRUHÝ ČS. PRACOVNÍ PRAPOR2.0Italyunit1918-02-151
DRUHÝ ČS. ZVLÁŠTNÍ PLUK2.0Italyunit1918-04-221
DVANÁCTÁ ČS. STŘELECKÁ BRIGÁDA12.0Italyunit1918-07-111
JEDENÁCTÁ ČS. STŘELECKÁ BRIGÁDA11.0Italyunit1918-07-111
OSMÝ ČS. DOPLŇOVACÍ PRAPOR8.0Italyunit1918-05-200
OSMÝ ČS. PRACOVNÍ PRAPOR8.0Italyunit1918-03-150
PRVNÍ NÁHRADNÍ ROTA ČS. VOJSKA1.0Italyunit1918-04-140
PRVNÍ ČS. PRACOVNÍ PRAPOR [I]1.0IItalyunit1918-02-151
PRVNÍ ČS. ZVLÁŠTNÍ BRIGÁDA1.0Italyunit1918-04-261
PRVNÍ ČS. ZVLÁŠTNÍ DIVIZE1.0Italyunit1918-04-112
PRVNÍ ČS. ZVLÁŠTNÍ PLUK1.0Italyunit1918-04-251
PÁTÝ ČS. PRACOVNÍ PRAPOR5.0Italyunit1918-03-011
SEDMÁ ČS. STŘELECKÁ DIVIZE7.0Italyunit1918-11-172
SEDMÝ ČS. AUTOMOBILNÍ ODDÍL7.0Italyunit1918-11-170
SEDMÝ ČS. DOPLŇOVACÍ PRAPOR7.0Italyunit1918-05-200
SEDMÝ ČS. DĚLOSTŘELECKÝ PLUK7.0Italyunit1918-12-081
SEDMÝ ČS. PRACOVNÍ PRAPOR7.0Italyunit1918-03-150
SEDMÝ ČS. ZDRAVOTNICKÝ ODDÍL7.0Italyunit1918-11-170
SEDMÝ ČS. ZÁSOBOVACÍ ODDÍL7.0Italyunit1918-11-250
SKUPINA KULOMETNÝCH ROT 1. ČS. DIVIZEItalyunit1918-05-151
SKUPINA KULOMETNÝCH ROT 6. ČS. DIVIZE [II]IIItalyunit1918-11-170
SKUPINA KULOMETNÝCH ROT 6. ČS. DIVIZE [I]IItalyunit1918-07-111
SKUPINA KULOMETNÝCH ROT 7. ČS. DIVIZEItalyunit1918-11-171
TELEGRAFNÍ ROTA 6. ČS. DIVIZEItalyunit1918-11-170
TELEGRAFNÍ ROTA 7. ČS. DIVIZEItalyunit1918-11-170
TELEGRAFNÍ ROTA ČS. SBORUItalyunit1918-11-170
TŘETÍ ČS. PRACOVNÍ PRAPOR3.0Italyunit1918-02-151
TŘETÍ ČS. ZVLÁŠTNÍ PLUK3.0Italyunit1918-04-261
TŘICÁTÝ DEVÁTÝ ČS. STŘELECKÝ PLUK39.0Italyunit1918-09-251
TŘICÁTÝ DRUHÝ ČS. STŘELECKÝ PLUK32.0Italyunit1918-06-031
TŘICÁTÝ PRVNÍ ČS. STŘELECKÝ PLUK31.0Italyunit1918-06-031
TŘICÁTÝ PÁTÝ ČS. STŘELECKÝ PLUK35.0Italyunit1918-09-131
TŘICÁTÝ TŘETÍ ČS. STŘELECKÝ PLUK33.0Italyunit1918-06-031
TŘICÁTÝ ČTVRTÝ ČS. STŘELECKÝ PLUK34.0Italyunit1918-06-031
VELITELSTVÍ DĚLOSTŘELECTVA ČS. SBORUItalyunit1918-11-171
VELITELSTVÍ PĚCHOTY 6. ČS. DIVIZEItalyunit1918-11-171
VELITELSTVÍ PĚCHOTY 7. ČS. DIVIZEItalyunit1918-11-171
VELITELSTVÍ ZDRAVOTNICTVA ČS. SBORUItalyunit1918-04-161
ČS. ARMÁDNÍ SBOR [II]IIItalyunit1918-11-172
ČS. NÁRODNÍ STRÁŽItalyunit1918-08-090
ČS. SKUPINA JEZDECKÝCH ESKADRONItalyunit1918-11-171
ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍLItalyunit1918-12-101
ČS. VOZATAJSKÝ ODDÍLItalyunit1918-06-031
ČS. ZDRAVOTNICKÝ ODDÍLItalyunit1918-05-170
ČS. ZVLÁŠTNÍ DOBROVOLNICKÝ SBORItalyunit1918-04-112
ČS. ZÁSOBOVACÍ ODDÍLItalyunit1918-05-210
ČTVRTÝ ČS. PRACOVNÍ PRAPOR4.0Italyunit1918-02-251
ČTVRTÝ ČS. ZVLÁŠTNÍ PLUK4.0Italyunit1918-04-231
ŠESTÁ ČS. STŘELECKÁ DIVIZE [II]6.0IIItalyunit1918-11-172
ŠESTÁ ČS. STŘELECKÁ DIVIZE [I]6.0IItalyunit1918-07-112
ŠESTÝ ČS. AUTOMOBILNÍ ODDÍL6.0Italyunit1918-11-170
ŠESTÝ ČS. DĚLOSTŘELECKÝ PLUK [II]6.0IIItalyunit1918-12-101
ŠESTÝ ČS. DĚLOSTŘELECKÝ PLUK [I]6.0IItalyunit1918-11-121
ŠESTÝ ČS. PRACOVNÍ PRAPOR6.0Italyunit1918-03-011
ŠESTÝ ČS. ZDRAVOTNICKÝ ODDÍL6.0Italyunit1918-11-170
ŠESTÝ ČS. ZÁSOBOVACÍ ODDÍL6.0Italyunit1918-11-250
ŽELEZNIČNÍ ROTA ČS. SBORUItalyunit1918-12-010
ŽENIJNÍ ROTA 6. ČS. DIVIZEItalyunit1918-11-170
ŽENIJNÍ ROTA 7. ČS. DIVIZEItalyunit1918-11-170
AUTOMOBILNÍ PARK ČS. VOJSKARussiainstitution1919-08-261
DOČASNÝ POLNÍ SOUD ČS. VOJSKA VE VLADIVOSTOKURussiainstitution1918-09-102
DRUHÁ ČS. VOJENSKÁ NEMOCNICE2.0Russiainstitution1919-03-161
DRUHÁ ČS. VOJENSKÁ NEMOCNICE V JEKATĚRINBURGU2.0Russiainstitution1918-10-081
DRUHÁ ČS. ZVĚROLÉKAŘSKÁ NEMOCNICE2.0Russiainstitution1918-11-051
DRUHÝ POLNÍ SOUD ČS. VOJSKA2.0Russiainstitution1918-06-201
DRUHÝ ČS. DIVIZNÍ LAZARET [II]2.0IIRussiainstitution1919-02-091
DRUHÝ ČS. DIVIZNÍ LAZARET [I]2.0IRussiainstitution1917-11-151
DRUHÝ ČS. SANITNÍ VLAK [II]2.0IIRussiainstitution1918-05-291
DRUHÝ ČS. SANITNÍ VLAK [I]2.0IRussiainstitution1918-02-171
DĚLOSTŘELECKÝ SKLAD ČS. SBORURussiainstitution1918-08-051
DĚLOSTŘELECKÝ SKLAD ČS. VOJSKARussiainstitution1919-02-071
EPIDEMIOLOGICKO-BAKTERIOLOGICKÝ VÝZKUMNÝ ÚSTAV ČS. VOJSKARussiainstitution1918-12-201
HLAVNÍ LÉKÁRNICKÝ SKLAD ČS. SBORURussiainstitution1918-08-051
HLAVNÍ LÉKÁRNICKÝ SKLAD ČS. VOJSKARussiainstitution1919-03-041
INTENDANTSTVO 1. ČS. DIVIZE [II]IIRussiainstitution1919-02-011
INTENDANTSTVO 1. ČS. DIVIZE [I]IRussiainstitution1917-09-040
INTENDANTSTVO 2. ČS. DIVIZE [II]IIRussiainstitution1919-02-011
INTENDANTSTVO 2. ČS. DIVIZE [I]IRussiainstitution1917-10-040
INTENDANTSTVO 3. ČS. DIVIZERussiainstitution1919-02-011
INTENDANTSTVO ČS. BRIGÁDYRussiainstitution1917-06-200
INTENDANTSTVO ČS. SBORURussiainstitution1918-04-231
INTENDANTSTVO ČS. VELITELSTVÍ DÁLNÉHO VÝCHODURussiainstitution1919-03-121
KOMISE PRO VÝSTAVBU ČS. VOJENSKÝCH JEDNOTEKRussiainstitution1917-04-021
POLNÍ SOUD ČS. VOJSKA V IRKUTSKURussiainstitution1918-07-281
POLNÍ SOUD ČS. VOJSKA V NOVONIKOLAJEVSKURussiainstitution1918-12-032
POLNÍ SOUD ČS. VOJSKA V ČELJABINSKURussiainstitution1918-06-192
PRVNÍ POLNÍ SOUD ČS. VOJSKA1.0Russiainstitution1918-06-201
PRVNÍ ČS. DIVIZNÍ LAZARET [II]1.0IIRussiainstitution1919-02-091
PRVNÍ ČS. DIVIZNÍ LAZARET [I]1.0IRussiainstitution1917-11-151
PRVNÍ ČS. SANITNÍ VLAK [II]1.0IIRussiainstitution1918-12-051
PRVNÍ ČS. SANITNÍ VLAK [I]1.0IRussiainstitution1918-04-071
PRVNÍ ČS. VOJENSKÁ NEMOCNICE1.0Russiainstitution1919-04-011
PRVNÍ ČS. VOJENSKÁ NEMOCNICE V JEKATĚRINBURGU1.0Russiainstitution1918-10-081
PRVNÍ ČS. ZVĚROLÉKAŘSKÁ NEMOCNICE1.0Russiainstitution1918-11-051
PÁTÁ ČS. VOJENSKÁ NEMOCNICE5.0Russiainstitution1919-06-291
PÁTÝ ČS. NEMOCNIČNÍ VLAK5.0Russiainstitution1919-04-261
PÁTÝ ČS. SANITNÍ VLAK5.0Russiainstitution1918-07-051
SBOROVÝ SOUD ČS. VOJSKARussiainstitution1919-01-102
SEDMÁ ČS. VOJENSKÁ NEMOCNICE7.0Russiainstitution1919-01-151
SOUD ČS. VOJENSKÝCH ČÁSTÍ DÁLNÉHO VÝCHODURussiainstitution1919-03-242
TECHNICKÉ SKLADY ČS. VOJSKARussiainstitution1919-07-151
TÝLOVÉ INTENDANTSTVO ČS. VOJSKARussiainstitution1919-01-201
TŘETÍ ČS. DIVIZNÍ LAZARET3.0Russiainstitution1919-02-091
TŘETÍ ČS. SANITNÍ VLAK [II]3.0IIRussiainstitution1918-10-121
TŘETÍ ČS. SANITNÍ VLAK [I]3.0IRussiainstitution1918-03-261
TŘETÍ ČS. VOJENSKÁ NEMOCNICE3.0Russiainstitution1919-03-161
TŘETÍ ČS. ZVĚROLÉKAŘSKÁ NEMOCNICE3.0Russiainstitution1919-02-011
VOJENSKÝ SOUD 1. ČS. DIVIZERussiainstitution1919-03-152
VOJENSKÝ SOUD 2. ČS. DIVIZERussiainstitution1919-03-152
VOJENSKÝ SOUD 3. ČS. DIVIZERussiainstitution1919-03-152
ZÁLOŽNÍ AUTOMOBILNÍ PARK ČS. VOJSKARussiainstitution1919-01-011
ÚŘAD AUTOMOBILNÍ SLUŽBY ČS. VOJSKARussiainstitution1919-01-011
ÚŘAD ZDRAVOTNICKÉ SLUŽBY 1. ČS. DIVIZE [II]IIRussiainstitution1918-07-071
ÚŘAD ZDRAVOTNICKÉ SLUŽBY 1. ČS. DIVIZE [I]IRussiainstitution1917-11-151
ÚŘAD ZDRAVOTNICKÉ SLUŽBY 2. ČS. DIVIZE [II]IIRussiainstitution1918-10-071
ÚŘAD ZDRAVOTNICKÉ SLUŽBY 2. ČS. DIVIZE [I]IRussiainstitution1917-11-151
ÚŘAD ZDRAVOTNICKÉ SLUŽBY 3. ČS. DIVIZERussiainstitution1919-02-011
ÚŘAD ZDRAVOTNICKÉ SLUŽBY VÝCHODNÍHO ODDÍLU ČS. SBORURussiainstitution1918-05-161
ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. NÁHRADNÍCH JEDNOTEKRussiainstitution1917-11-151
ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. SBORURussiainstitution1917-11-151
ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. SBORU V POLIRussiainstitution1918-11-271
ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. SBORU V TÝLURussiainstitution1918-11-271
ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. VELITELSTVÍ DÁLNÉHO VÝCHODURussiainstitution1919-05-301
ÚŘAD ZDRAVOTNICKÉ SLUŽBY ŠTÁBU ČS. VOJSKARussiainstitution1919-03-041
ČS. POLNÍ POŠTA [I]IRussiainstitution1918-09-091
ČS. POSÁDKOVÁ NEMOCNICE VE VLADIVOSTOKURussiainstitution1920-08-051
ČS. VOJENSKÁ NEMOCNICE V JEKATĚRINBURGURussiainstitution1918-08-271
ČS. VOJENSKÁ NEMOCNICE V OMSKURussiainstitution1918-08-201
ČS. VOJENSKÁ NEMOCNICE V PETROPAVLOVSKURussiainstitution1918-08-071
ČS. VOJENSKÁ NEMOCNICE V TOMSKURussiainstitution1918-11-011
ČS. VOJENSKÁ NEMOCNICE V ČELJABINSKURussiainstitution1918-07-281
ČS. VOJENSKÁ NEMOCNICE VE ZLATOUSTURussiainstitution1918-10-191
ČS. VOJENSKÁ SPOŘITELNARussiainstitution1919-01-081
ČS. ZDRAVOTNICKÝ INSPEKTORÁT DÁLNÉHO VÝCHODURussiainstitution1918-11-271
ČTVRTÁ ČS. VOJENSKÁ NEMOCNICE [II]4.0IIRussiainstitution1920-03-161
ČTVRTÁ ČS. VOJENSKÁ NEMOCNICE [I]4.0IRussiainstitution1919-04-091
ČTVRTÝ ČS. NEMOCNIČNÍ VLAK4.0Russiainstitution1919-01-201
ČTVRTÝ ČS. SANITNÍ VLAK4.0Russiainstitution1918-05-261
ŠESTÁ ČS. VOJENSKÁ NEMOCNICE6.0Russiainstitution1919-01-091
DESÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI10.0Russiatransport1
DEVATENÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTIRussiatransport1
DEVÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI9.0Russiatransport1
DRUHÝ ČS. LODNÍ TRANSPORT DO VLASTI2.0Russiatransport1
DRUHÝ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI2.0Russiatransport1920-07-101
DVACÁTÝ DEVÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI29.0Russiatransport1
DVACÁTÝ DRUHÝ ČS. LODNÍ TRANSPORT DO VLASTI22.0Russiatransport1
DVACÁTÝ OSMÝ ČS. LODNÍ TRANSPORT DO VLASTI28.0Russiatransport1
DVACÁTÝ PRVNÍ ČS. LODNÍ TRANSPORT DO VLASTI21.0Russiatransport1
DVACÁTÝ PÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI25.0Russiatransport1
DVACÁTÝ SEDMÝ ČS. LODNÍ TRANSPORT DO VLASTI27.0Russiatransport1
DVACÁTÝ TŘETÍ ČS. LODNÍ TRANSPORT DO VLASTI23.0Russiatransport1
DVACÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI20.0Russiatransport1
DVACÁTÝ ČTVRTÝ ČS. LODNÍ TRANSPORT DO VLASTI24.0Russiatransport1
DVACÁTÝ ŠESTÝ ČS. LODNÍ TRANSPORT DO VLASTI26.0Russiatransport1
DVANÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI12.0Russiatransport1
JEDENÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI11.0Russiatransport1
OSMNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTIRussiatransport1
OSMÝ ČS. LODNÍ TRANSPORT DO VLASTI8.0Russiatransport1
PATNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTIRussiatransport1
PRVNÍ ČS. LODNÍ TRANSPORT DO VLASTI1.0Russiatransport1
PRVNÍ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI1.0Russiatransport1920-07-061
PÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI5.0Russiatransport1
SEDMNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTIRussiatransport1
SEDMÝ ČS. LODNÍ TRANSPORT DO VLASTI7.0Russiatransport1
TŘETÍ ČS. LODNÍ TRANSPORT DO VLASTI3.0Russiatransport1
TŘETÍ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI3.0Russiatransport1920-07-131
TŘICÁTÝ DRUHÝ ČS. LODNÍ TRANSPORT DO VLASTI32.0Russiatransport1
TŘICÁTÝ PRVNÍ ČS. LODNÍ TRANSPORT DO VLASTI31.0Russiatransport1
TŘICÁTÝ PÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI35.0Russiatransport1
TŘICÁTÝ TŘETÍ ČS. LODNÍ TRANSPORT DO VLASTI33.0Russiatransport1
TŘICÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI30.0Russiatransport1
TŘICÁTÝ ČTVRTÝ ČS. LODNÍ TRANSPORT DO VLASTI34.0Russiatransport1
TŘICÁTÝ ŠESTÝ ČS. LODNÍ TRANSPORT DO VLASTI36.0Russiatransport1
TŘINÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTIRussiatransport1
ČTRNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTIRussiatransport1
ČTVRTÝ ČS. LODNÍ TRANSPORT DO VLASTI4.0Russiatransport1
ČTVRTÝ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI4.0Russiatransport1920-07-171
ŠESTNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTIRussiatransport1
ŠESTÝ ČS. LODNÍ TRANSPORT DO VLASTI6.0Russiatransport1
AUTOMOBILNÍ ODDÍL 1. ČS. DIVIZERussiaunit1919-02-150
AUTOMOBILNÍ ODDÍL 2. ČS. DIVIZERussiaunit1919-02-150
AUTOMOBILNÍ ODDÍL 3. ČS. DIVIZERussiaunit1919-02-150
DESÁTÝ ČS. PĚŠÍ PLUK10.0Russiaunit1918-08-201
DESÁTÝ ČS. STŘELECKÝ PLUK [II]10.0IIRussiaunit1918-10-071
DESÁTÝ ČS. STŘELECKÝ PLUK [I]10.0IRussiaunit1918-07-121
DEVÁTÝ ČS. PĚŠÍ PLUK9.0Russiaunit1918-08-201
DEVÁTÝ ČS. STŘELECKÝ PLUK [II]9.0IIRussiaunit1918-10-071
DEVÁTÝ ČS. STŘELECKÝ PLUK [I]9.0IRussiaunit1918-08-151
DOPRAVNÍ ODDĚLENÍ ČS. VOJSKARussiaunit1919-02-011
DRUHÁ ČS. DĚLOSTŘELECKÁ BRIGÁDA2.0Russiaunit1917-11-261
DRUHÁ ČS. SAMOSTATNÁ TECHNICKÁ ROTA [II]2.0IIRussiaunit1918-12-121
DRUHÁ ČS. SAMOSTATNÁ TECHNICKÁ ROTA [I]2.0IRussiaunit1917-09-011
DRUHÁ ČS. STŘELECKÁ BRIGÁDA2.0Russiaunit1917-09-121
DRUHÁ ČS. STŘELECKÁ DIVIZE [III]2.0IIIRussiaunit1918-10-072
DRUHÁ ČS. STŘELECKÁ DIVIZE [II]2.0IIRussiaunit1918-09-032
DRUHÁ ČS. STŘELECKÁ DIVIZE [I]2.0IRussiaunit1917-10-042
DRUHÁ ČS. TECHNICKÁ ROTA2.0Russiaunit1918-09-020
DRUHÝ ODBOR ŠTÁBU ČS. VOJSKA2.0Russiaunit1919-02-011
DRUHÝ ČS. JEZDECKÝ PLUK2.0Russiaunit1918-10-041
DRUHÝ ČS. LEHKÝ DĚLOSTŘELECKÝ PLUK2.0Russiaunit1919-04-251
DRUHÝ ČS. MUNIČNÍ ODDÍL2.0Russiaunit1919-04-241
DRUHÝ ČS. NÁHRADNÍ PLUK2.0Russiaunit1917-11-291
DRUHÝ ČS. POCHODOVÝ PRAPOR2.0Russiaunit1918-11-201
DRUHÝ ČS. PĚŠÍ PLUK2.0Russiaunit1918-08-201
DRUHÝ ČS. SAMOSTATNÝ DĚLOSTŘELECKÝ ODDÍL2.0Russiaunit1917-09-201
DRUHÝ ČS. SAMOSTATNÝ JEZDECKÝ ODDÍL2.0Russiaunit1918-07-091
DRUHÝ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ MUNIČNÍ ODDÍL2.0Russiaunit1917-12-180
DRUHÝ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ ODDÍL2.0Russiaunit1917-12-181
DRUHÝ ČS. SAMOSTATNÝ MUNIČNÍ ODDÍL2.0Russiaunit1917-09-201
DRUHÝ ČS. STŘELECKÝ PLUK [III]2.0IIIRussiaunit1918-10-071
DRUHÝ ČS. STŘELECKÝ PLUK [II]2.0IIRussiaunit1917-07-301
DRUHÝ ČS. STŘELECKÝ PLUK [I]2.0IRussiaunit1916-05-191
DRUHÝ ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍL2.0Russiaunit1919-04-251
DVANÁCTÝ ČS. PĚŠÍ PLUK12.0Russiaunit1918-08-201
DVANÁCTÝ ČS. STŘELECKÝ PLUK [III]12.0IIIRussiaunit1919-02-091
DVANÁCTÝ ČS. STŘELECKÝ PLUK [II]12.0IIRussiaunit1918-10-071
DVANÁCTÝ ČS. STŘELECKÝ PLUK [I]12.0IRussiaunit1918-07-151
INSPEKTORÁT DĚLOSTŘELECTVA ČS. SBORURussiaunit1917-12-011
INSPEKTORÁT NÁHRADNÍCH JEDNOTEK ČS. SBORURussiaunit1917-12-152
JEDENÁCTÝ ČS. PĚŠÍ PLUK11.0Russiaunit1918-08-201
JEDENÁCTÝ ČS. STŘELECKÝ PLUK [II]11.0IIRussiaunit1918-12-181
JEDENÁCTÝ ČS. STŘELECKÝ PLUK [I]11.0IRussiaunit1918-07-151
JEKATĚRINBURSKÁ SKUPINA ČS. VOJSKARussiaunit1918-07-071
LETECKÝ ODDÍL 1. ČS. DIVIZERussiaunit1918-08-141
LETECKÝ ODDÍL 2. ČS. DIVIZERussiaunit1918-09-261
LETECKÝ ODDÍL ČS. VOJSKARussiaunit1919-06-021
LIKVIDAČNÍ ODBOR ŠTÁBU ČS. VOJSKARussiaunit1919-11-011
NÁHRADNÍ PRAPOR 1. ČS. DIVIZERussiaunit1918-09-300
NÁHRADNÍ PRAPOR 1. ČS. PĚŠÍHO PLUKURussiaunit1917-05-301
NÁHRADNÍ PRAPOR 2. ČS. PĚŠÍHO PLUKU.Russiaunit1917-06-191
NÁHRADNÍ PRAPOR 3. ČS. PĚŠÍHO PLUKURussiaunit1917-06-251
NÁHRADNÍ PRAPOR 4. ČS. PĚŠÍHO PLUKURussiaunit1917-06-261
NÁHRADNÍ PRAPOR 5. ČS. PĚŠÍHO PLUKURussiaunit1917-07-131
NÁHRADNÍ PRAPOR 6. ČS. PĚŠÍHO PLUKURussiaunit1917-07-131
NÁHRADNÍ PRAPOR 7. ČS. PĚŠÍHO PLUKURussiaunit1917-07-131
NÁHRADNÍ PRAPOR 8. ČS. PĚŠÍHO PLUKURussiaunit1917-07-131
OSMÝ ČS. PĚŠÍ PLUK [II]8.0IIRussiaunit1918-08-201
OSMÝ ČS. PĚŠÍ PLUK [I]8.0IRussiaunit1917-07-211
OSMÝ ČS. STŘELECKÝ PLUK [II]8.0IIRussiaunit1918-10-071
OSMÝ ČS. STŘELECKÝ PLUK [I]8.0IRussiaunit1917-08-151
PENZENSKÁ SKUPINA ČS. VOJSKARussiaunit1918-05-291
PRVNÍ ODBOR ŠTÁBU ČS. VOJSKA1.0Russiaunit1919-02-011
PRVNÍ ČS. DĚLOSTŘELECKÁ BRIGÁDA1.0Russiaunit1917-11-051
PRVNÍ ČS. JEZDECKÝ PLUK1.0Russiaunit1918-07-011
PRVNÍ ČS. LEHKÝ DĚLOSTŘELECKÝ PLUK1.0Russiaunit1919-04-251
PRVNÍ ČS. MUNIČNÍ ODDÍL1.0Russiaunit1919-04-241
PRVNÍ ČS. NÁHRADNÍ PLUK1.0Russiaunit1917-10-291
PRVNÍ ČS. NÁHRADNÍ STŘELECKÝ PLUK1.0Russiaunit1918-09-071
PRVNÍ ČS. POCHODOVÝ PRAPOR1.0Russiaunit1918-09-181
PRVNÍ ČS. PRACOVNÍ PRAPOR [II]1.0IIRussiaunit1919-03-041
PRVNÍ ČS. PĚŠÍ PLUK1.0Russiaunit1918-08-201
PRVNÍ ČS. SAMOSTATNÁ TECHNICKÁ ROTA1.0Russiaunit1917-10-291
PRVNÍ ČS. SAMOSTATNÝ DĚLOSTŘELECKÝ ODDÍL1.0Russiaunit1917-09-051
PRVNÍ ČS. SAMOSTATNÝ JEZDECKÝ ODDÍL1.0Russiaunit1918-07-031
PRVNÍ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ MUNIČNÍ ODDÍL1.0Russiaunit1917-12-180
PRVNÍ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ ODDÍL1.0Russiaunit1917-12-181
PRVNÍ ČS. SAMOSTATNÝ MUNIČNÍ ODDÍL1.0Russiaunit1917-10-101
PRVNÍ ČS. SAMOSTATNÝ ÚDERNÝ PRAPOR1.0Russiaunit1918-10-171
PRVNÍ ČS. STŘELECKÁ BRIGÁDA [I]1.0IRussiaunit1917-09-121
PRVNÍ ČS. STŘELECKÁ DIVIZE [II]1.0IIRussiaunit1918-07-072
PRVNÍ ČS. STŘELECKÁ DIVIZE [I]1.0IRussiaunit1917-09-052
PRVNÍ ČS. STŘELECKÝ PLUK [III]1.0IIIRussiaunit1918-10-071
PRVNÍ ČS. STŘELECKÝ PLUK [II]1.0IIRussiaunit1917-07-301
PRVNÍ ČS. STŘELECKÝ PLUK [I]1.0IRussiaunit1916-05-191
PRVNÍ ČS. TECHNICKÁ ROTA1.0Russiaunit1918-09-030
PRVNÍ ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍL1.0Russiaunit1919-04-251
PÁTÝ ODBOR ŠTÁBU ČS. VOJSKA5.0Russiaunit1919-03-041
PÁTÝ ČS. PĚŠÍ PLUK [II]5.0IIRussiaunit1918-08-201
PÁTÝ ČS. PĚŠÍ PLUK [I]5.0IRussiaunit1917-07-211
PÁTÝ ČS. STŘELECKÝ PLUK [II]5.0IIRussiaunit1918-10-071
PÁTÝ ČS. STŘELECKÝ PLUK [I]5.0IRussiaunit1917-08-151
RUSKO-ČESKÝ STŘELECKÝ PLUKRussiaunit1918-08-221
SEDMÝ ODBOR ŠTÁBU ČS. VOJSKA7.0Russiaunit1919-02-011
SEDMÝ ČS. PĚŠÍ PLUK [I]7.0IRussiaunit1917-07-211
SEDMÝ ČS. STŘELECKÝ PLUK [II]7.0IIRussiaunit1918-10-071
SEDMÝ ČS. STŘELECKÝ PLUK [I]7.0IRussiaunit1917-08-151
SEVEROZÁPADNÍ SKUPINA ČS. VOJSKARussiaunit1918-06-101
SIBIŘSKÁ SKUPINA ČS. VOJSKARussiaunit1918-05-251
SLOUČENÝ PRAPOR 1. ČS. STŘELECKÉHO PLUKURussiaunit1917-07-241
SLOUČENÝ PRAPOR 2. ČS. STŘELECKÉHO PLUKURussiaunit1917-07-231
SLOUČENÝ PRAPOR 3. ČS. STŘELECKÉHO PLUKURussiaunit1917-07-231
STRÁŽNÍ ROTA ŠTÁBU ČS. VOJSKARussiaunit1919-02-010
TŘETÍ ČS. LEHKÝ DĚLOSTŘELECKÝ PLUK3.0Russiaunit1919-04-251
TŘETÍ ČS. MUNIČNÍ ODDÍL3.0Russiaunit1919-04-251
TŘETÍ ČS. PĚŠÍ PLUK3.0Russiaunit1918-08-201
TŘETÍ ČS. SAMOSTATNÁ TECHNICKÁ ROTA3.0Russiaunit1918-10-301
TŘETÍ ČS. STŘELECKÁ BRIGÁDA3.0Russiaunit1917-10-041
TŘETÍ ČS. STŘELECKÁ DIVIZE3.0Russiaunit1919-02-012
TŘETÍ ČS. STŘELECKÝ PLUK [III]3.0IIIRussiaunit1918-10-071
TŘETÍ ČS. STŘELECKÝ PLUK [II]3.0IIRussiaunit1917-07-301
TŘETÍ ČS. STŘELECKÝ PLUK [I]3.0IRussiaunit1917-04-091
TŘETÍ ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍL3.0Russiaunit1919-04-251
URALSKÁ SKUPINA ČS. VOJSKARussiaunit1918-07-251
VELITELSTVÍ DĚLOSTŘELECTVA 1. ČS. DIVIZERussiaunit1919-04-241
VELITELSTVÍ DĚLOSTŘELECTVA 2. ČS. DIVIZERussiaunit1919-04-241
VELITELSTVÍ DĚLOSTŘELECTVA 3. ČS. DIVIZERussiaunit1919-04-251
VLADIVOSTOCKÁ SKUPINA ČS. VOJSKARussiaunit1918-06-291
VOZATAJSKÁ ROTA ŠTÁBU ČS. VOJSKARussiaunit1919-02-010
VÝCHODNÍ ODDÍL ČS. SBORU [II]IIRussiaunit1918-09-011
VÝCHODNÍ ODDÍL ČS. SBORU [I]IRussiaunit1918-05-161
VÝCHODNÍ SKUPINA ČS. VOJSKARussiaunit1918-06-101
ZÁPADNÍ SKUPINA ČS. VOJSKARussiaunit1918-06-101
ČELJABINSKÁ SKUPINA ČS. VOJSKARussiaunit1918-05-251
ČESKÁ DRUŽINARussiaunit1914-08-281
ČS. ARMÁDNÍ SBOR [I]IRussiaunit1917-11-053
ČS. AUTOMOBILNÍ ODDÍLRussiaunit1918-06-301
ČS. AUTOMOBILNÍ ROTARussiaunit1918-09-260
ČS. AVIATICKÝ A AUTOMOBILNÍ ODDÍLRussiaunit1918-02-171
ČS. KURGANSKÝ POCHODOVÝ PRAPORRussiaunit1918-06-200
ČS. LODNÍ ODDÍLRussiaunit1919-11-150
ČS. NÁHRADNÍ BRIGÁDARussiaunit1917-10-281
ČS. NÁHRADNÍ DĚLOSTŘELECKÝ ODDÍLRussiaunit1917-08-031
ČS. NÁHRADNÍ ODDÍLRussiaunit1916-05-261
ČS. NÁHRADNÍ PRAPOR [II]IIRussiaunit1918-06-211
ČS. NÁHRADNÍ PRAPOR [I]IRussiaunit1916-11-301
ČS. NÁMOŘNÍ ODDÍLRussiaunit1920-03-150
ČS. ODDÍL OBRNĚNÝCH AUTOMOBILŮRussiaunit1919-08-200
ČS. SAMOSTATNÁ DOPRAVNÍ ROTARussiaunit1919-04-151
ČS. SAMOSTATNÁ RADIOTELEGRAFNÍ ROTARussiaunit1919-02-010
ČS. SAMOSTATNÁ STROJÍRENSKÁ ROTARussiaunit1919-05-040
ČS. SAMOSTATNÁ TELEGRAFNÍ ROTARussiaunit1918-01-070
ČS. SAMOSTATNÝ ÚDERNÝ PRAPORRussiaunit1917-12-211
ČS. STRÁŽNÍ PRAPORRussiaunit1919-05-101
ČS. STŘELECKÁ BRIGÁDA.Russiaunit1916-05-191
ČS. STŘELECKÝ PLUKRussiaunit1916-02-021
ČS. TECHNICKÝ PRAPORRussiaunit1918-07-031
ČS. VELITELSTVÍ DÁLNÉHO VÝCHODURussiaunit1918-09-262
ČS. VOJENSKÝ TÁBOR V BORISPOLURussiaunit1917-05-301
ČS. VOJENSKÝ TÁBOR VE VALCARTIERURussiaunit1920-06-131
ČS. VOJSKO NA RUSIRussiaunit1919-02-011
ČTVRTÁ ČS. STŘELECKÁ BRIGÁDA4.0Russiaunit1917-10-041
ČTVRTÝ ODBOR ŠTÁBU ČS. VOJSKA4.0Russiaunit1919-02-012
ČTVRTÝ ČS. PĚŠÍ PLUK4.0Russiaunit1918-08-201
ČTVRTÝ ČS. STŘELECKÝ PLUK [II]4.0IIRussiaunit1918-10-071
ČTVRTÝ ČS. STŘELECKÝ PLUK [I]4.0IRussiaunit1917-09-051
ŠESTÝ ODBOR ŠTÁBU ČS. VOJSKA6.0Russiaunit1919-02-011
ŠESTÝ ČS. PĚŠÍ PLUK [II]6.0IIRussiaunit1918-08-201
ŠESTÝ ČS. PĚŠÍ PLUK [I]6.0IRussiaunit1917-07-211
ŠESTÝ ČS. STŘELECKÝ PLUK [II]6.0IIRussiaunit1918-10-071
ŠESTÝ ČS. STŘELECKÝ PLUK [I]6.0IRussiaunit1917-08-151

Commits & versionsHistory & decisions

Machine record: commits, module versions, working-tree state.

What this sheet can and cannot tell you. The tables below are read from git and the working tree at build time, so they are always true — but they are only as informative as the history itself. This repository has a shallow, lumpy history: most modules were introduced in a single commit and never revised in git, so a per-file log shows one line. This sheet is the machine half of the record and deliberately says nothing about why: the calls are listed in Decisions, the narrative is on What's new, and each module's own docstring records its design choices. If you want this sheet to become genuinely useful, commit in meaningful units with messages that say why.

Built without git history

This page was built on a machine that holds the project's files but not its repository, so the working-tree and commit tables that normally stand here could not be read. That says nothing about the state of the repository: the work described on What's new may be uncommitted. The module versions below are read from the source files and are current.

Module versions

Every module carries a __version__. Bumps are not individually logged in git (the history predates that discipline), so treat this as the current state — the audits record what changed in the 2026-07-16/17 and 2026-09-22 passes.

155 rows

ModuleVersionRoleUncommitted
utils/scraping/fetch_vua_courts.py—Scraping
utils/scraping/progress.py—Scraping
utils/scraping/run_lexicon_downloads.py—Scraping
utils/scraping/run_matice_downloads.py—Scraping
utils/scraping/run_oeulk_downloads.py—Scraping
utils/scraping/scraper_amaterskedivadlo.py—Scraping
utils/scraping/scraper_csol.py—Scraping
utils/scraping/scraper_kramerius.py—Scraping
utils/scraping/scraper_kramerius7.py—Scraping
utils/scraping/scraper_verlustlisten.py—Scraping
utils/scraping/scraper_vuapraha.py—Scraping
utils/parsing/affiliations.py—Person parsing
utils/parsing/clean.py—Person parsing
utils/parsing/dates.py—Person parsing
utils/parsing/education.py—Person parsing
utils/parsing/exit_codes.py—Person parsing
utils/parsing/habsburg.py—Person parsing
utils/parsing/locations.py—Person parsing
utils/parsing/occupations.py—Person parsing
utils/parsing/provenance.py—Person parsing
utils/parsing/ranks.py—Person parsing
utils/parsing/religion.py—Person parsing
utils/parsing/schema.py—Person parsing
utils/parsing/theater.py—Person parsing
utils/parsing/units.py—Person parsing
utils/parsing/vua_courts.py—Person parsing
utils/parsing/amateur_theatre.py—Geography & census
utils/parsing/amateur_theatre_model.py—Geography & census
utils/parsing/census.py—Geography & census
utils/parsing/census_hu.py—Geography & census
utils/parsing/census_labels.py—Geography & census
utils/parsing/districts.py—Geography & census
utils/parsing/districts_hu.py—Geography & census
utils/parsing/geonames.py—Geography & census
utils/parsing/lexikon.py—Geography & census
utils/parsing/lexikon_sk.py—Geography & census
utils/parsing/locative.py—Geography & census
utils/parsing/matice.py—Geography & census
utils/parsing/prng.py—Geography & census
utils/parsing/records_geo.py—Geography & census
utils/parsing/records_geo_hu.py—Geography & census
utils/parsing/ruian.py—Geography & census
utils/parsing/spatial.py—Geography & census
utils/parsing/vanished.py—Geography & census
utils/parsing/wikidata.py—Geography & census
utils/linkage.py—Linkage
utils/lexikon_1910/bakeoff.py—1910 register
utils/lexikon_1910/build_contrast_sheet.py—1910 register
utils/lexikon_1910/build_de_page_map.py—1910 register
utils/lexikon_1910/build_glyph_bank.py—1910 register
utils/lexikon_1910/build_lexikon_figure.py—1910 register
utils/lexikon_1910/build_mark_ledger.py—1910 register
utils/lexikon_1910/build_register_walkthrough.py—1910 register
utils/lexikon_1910/build_symbol_atlas.py—1910 register
utils/lexikon_1910/build_symbol_atlas_de.py—1910 register
utils/lexikon_1910/build_token_review.py—1910 register
utils/lexikon_1910/check_transcription.py—1910 register
utils/lexikon_1910/draw_gold_sample.py—1910 register
utils/lexikon_1910/extract_prose_pages.py—1910 register
utils/lexikon_1910/lexikon_1910.py—1910 register
utils/lexikon_1910/lexikon_1910_attributes.py—1910 register
utils/lexikon_1910/lexikon_1910_census.py—1910 register
utils/lexikon_1910/lexikon_1910_courts.py—1910 register
utils/lexikon_1910/lexikon_1910_crosswalk.py—1910 register
utils/lexikon_1910/lexikon_1910_de.py—1910 register
utils/lexikon_1910/lexikon_1910_de_notes.py—1910 register
utils/lexikon_1910/lexikon_1910_de_table.py—1910 register
utils/lexikon_1910/lexikon_1910_editions.py—1910 register
utils/lexikon_1910/lexikon_1910_image.py—1910 register
utils/lexikon_1910/lexikon_1910_institutions.py—1910 register
utils/lexikon_1910/lexikon_1910_ledger.py—1910 register
utils/lexikon_1910/lexikon_1910_names.py—1910 register
utils/lexikon_1910/lexikon_1910_notes.py—1910 register
utils/lexikon_1910/lexikon_1910_pages.py—1910 register
utils/lexikon_1910/lexikon_1910_register.py—1910 register
utils/lexikon_1910/lexikon_1910_vision.py—1910 register
utils/lexikon_1910/lexikon_1910_volume.py—1910 register
utils/lexikon_1910/locate_glyphs.py—1910 register
utils/lexikon_1910/make_gold_worksheet.py—1910 register
utils/lexikon_1910/make_gold_worksheet_de.py—1910 register
utils/lexikon_1910/read_transcription_md.py—1910 register
utils/lexikon_1910/reader_batches.py—1910 register
utils/lexikon_1910/report_unlisted_abbreviations.py—1910 register
utils/lexikon_1910/rescore_passes.py—1910 register
utils/lexikon_1910/run_de_table.py—1910 register
utils/lexikon_1910/run_e1_gemini_version.py—1910 register
utils/lexikon_1910/run_e1b_second_reader.py—1910 register
utils/lexikon_1910/run_e3_pages.py—1910 register
utils/lexikon_1910/run_frozen.py—1910 register
utils/lexikon_1910/run_glyph_e3.py—1910 register
utils/lexikon_1910/run_glyph_test_a.py—1910 register
utils/lexikon_1910/run_lexikon_1910_pilot.py—1910 register
utils/lexikon_1910/run_r5_german_notes.py—1910 register
utils/lexikon_1910/run_table_arm.py—1910 register
utils/lexikon_1910/run_verify_test_b.py—1910 register
utils/lexikon_1910/score_certification.py—1910 register
utils/lexikon_1910/score_r5_german_notes.py—1910 register
utils/lexikon_1910/score_table_passes.py—1910 register
utils/lexikon_1910/section_images.py—1910 register
utils/reference_tools/places/audit_full_run_pages.py—Reference tools
utils/reference_tools/occupations/build_conflict_workbook.py—Reference tools
utils/reference_tools/places/build_curation.py—Reference tools
utils/reference_tools/occupations/build_czisco_bridge.py—Reference tools
utils/reference_tools/places/build_german_click_queue.py—Reference tools
utils/reference_tools/places/build_german_click_strips.py—Reference tools
utils/reference_tools/occupations/build_hisco_authority.py—Reference tools
utils/reference_tools/places/build_matice_read_sheets.py—Reference tools
utils/reference_tools/occupations/build_occupation_curation.py—Reference tools
utils/reference_tools/education/build_page_index.py—Reference tools
utils/reference_tools/places/build_sk_curation.py—Reference tools
utils/reference_tools/places/build_theatre_gold_page.py—Reference tools
utils/reference_tools/units/build_unit_authority.py—Reference tools
utils/reference_tools/units/build_unit_timeline.py—Reference tools
utils/reference_tools/parties/check_atlas_parties.py—Reference tools
utils/reference_tools/units/check_unit_timeline.py—Reference tools
utils/reference_tools/education/check_unterricht_transcription.py—Reference tools
utils/reference_tools/occupations/czech_glosses.py—Reference tools
utils/reference_tools/occupations/download_hisco_sources.py—Reference tools
utils/reference_tools/education/draw_education_sample.py—Reference tools
utils/reference_tools/places/german_candidate_diagnosis.py—Reference tools
utils/reference_tools/places/german_click_recall.py—Reference tools
utils/reference_tools/places/german_locator_trial.py—Reference tools
utils/reference_tools/places/german_supervised_placement.py—Reference tools
utils/reference_tools/places/map_intensities.py—Reference tools
utils/reference_tools/places/map_matice.py—Reference tools
utils/reference_tools/places/merge_matice_readings.py—Reference tools
utils/reference_tools/background/prep_slices.py—Reference tools
utils/reference_tools/places/run_amateur_theatre_pilot.py—Reference tools
utils/reference_tools/occupations/run_census_berufsstellung_check.py—Reference tools
utils/reference_tools/occupations/run_czisco_check.py—Reference tools
utils/reference_tools/places/run_german_detect_by_naming.py—Reference tools
utils/reference_tools/places/run_german_oracle.py—Reference tools
utils/reference_tools/places/run_german_reader_boxes.py—Reference tools
utils/reference_tools/places/run_intensities.py—Reference tools
utils/reference_tools/occupations/run_link_bridge.py—Reference tools
utils/reference_tools/places/run_matice_pilot.py—Reference tools
utils/reference_tools/occupations/run_mosaic_beruf_check.py0.1Reference tools
utils/reference_tools/occupations/run_occcanine_check.py—Reference tools
utils/reference_tools/education/run_unterricht_check.py—Reference tools
utils/reference_tools/units/run_vua_courts_pilot.py—Reference tools
utils/reference_tools/education/score_education_gold.py—Reference tools
utils/reference_tools/places/score_sealed_abbreviations.py—Reference tools
utils/reference_tools/places/statutory_institutions.py—Reference tools
utils/reference_tools/places/statutory_prose.py—Reference tools
utils/reference_tools/background/verify_locators.py—Reference tools
utils/docs/build_docs.py3Output & supportyes
utils/docs/build_fonts.py—Output & support
utils/validation/build_validation_workbook.py—Output & support
utils/validation/check_ostafin_silesia.py—Output & support
utils/validation/data_checks.py—Output & support
utils/tables/latex_tables.py—Output & support
utils/validation/lexikon_population.py—Output & support
utils/validation/run_completeness.py—Output & support
utils/validation/score_sealed_retest.py—Output & support
utils/plotting/th_style_v113.py—Output & support

DecisionsHistory & decisions

Register of every judgement call, with a link to the argument. · source: reference/DECISIONS.md

In short

Every judgement call the project has taken that a reader could reasonably have expected to go the other way, in one list, newest source first within each theme.

Explain it simply

Building a dataset means making hundreds of choices that could defensibly have gone the other way, and the danger is not making them badly — it is making them invisibly. Six months later nobody, including the author, can tell which numbers are measurements and which are consequences of a forgotten decision.

So every such choice gets a line here: what was decided, why, and whether it is settled, provisional (in force, but the alternative would change results) or open. The argument itself is not repeated — it stays in whichever document already makes it, and this is the index.

The provisional ones are the interesting ones. They are the places where the paper's results depend on a call that a reasonable person could reject, stated in advance rather than discovered by a referee.

This file is an index, not an argument. Each row states what was decided and why in one line; the evidence, the alternatives tested and the numbers stay where they already live — the audit, the external validation, the rank ladders, the place-matching note. That is the same rule the rest of this documentation runs on: nothing is written twice, so nothing can drift. Follow the link in the last column for the reasoning.

Status. Settled — decided, implemented, and not expected to reopen. Provisional — in force, but the alternative is still live and would change results. Open — acknowledged and deliberately not yet decided.

Dates are the day the decision was recorded in its source document. A month-only date means the decision predates dated record-keeping; the commit log (Commits & versions) has the day if it ever matters.

# Date Decision Why Status Full reasoning
D-01 2026-06 Cross-dataset overlap keeps only confirmed pairs — exact DOB + name, or a VÚA legion note + birth okres + birth year + name. Everything weaker is dropped. Amended 2026-09-22 by D-59: the name test and the note criterion were both weaker than this row claimed. A false link corrupts both frames; lost recall is recoverable, lost precision is not. Settled Variables — match_confidence; Pipeline — linkage; Audits A1–A3
D-02 2026-07 unit_key = "regiment @ army" replaces bare regiment for unit fixed effects, clustering and size denominators. The unit parenthetical names an army, not a theatre, and each army numbers its own regiments. Settled Units & armies
D-03 2026-07 Theatre imputation stopped after four passes at ~96.5% coverage. Further passes bought coverage only by guessing. Settled Pipeline — theater
D-04 2026-07-20 pracovník and občan are flagged legion_noncombat rather than dropped; the headline runs on combat ranks, the full column as robustness. They are non-combat statuses, not grades — differencing them against an officer rank manufactures demotions. Settled Rank ladders §H; External validation §3
D-05 2026-07 rank_reset is a difference on the ALT-1 name-anchored common scale (1–22), not on either army's own ordinal. The same Czech word must score the same grade in both armies; within-system ordinals are not subtractable. Settled Rank ladders §G
D-06 2026-07-16 Bare-"Praha" births resolve to okres Praha, admitted at the weakest muni tier so they stay filterable. Base rates, not names: the prior is ~1:2,250, and a name classifier would expel the capital's migrants. Settled Audit §D3; Place matching
D-07 2026-07-16 nadlékař (Oberarzt) is left unscored on the Habsburg ladder. A medical-officer title with no honest rung on a combat ladder. Settled Rank ladders §A1b; Audit §B2
D-08 2026-07-16 Education classification is deferred, not patched. Known-imperfect, and no current result depends on it. 2026-09-02: the primary source is now in hand — Moore (2020) Teaching the Empire gives the Cisleithanian ladder (Volksschule 5 + Bürgerschule 3 = the compulsory eight; Gymnasium/Realschule forking at age 10; Lehrerbildungsanstalt entered from the Bürgerschule at 15), which confirms the suspected defect and sizes it: pok. = pokračovací = gewerbliche Fortbildungsschule, an apprentices' part-time continuation school, is scored Upper secondary on 8,805 records (8.3 % of the non-empty field). Where pok. belongs is now sourced too: Lassnigg (2015, IHS WP 108) shows the Fortbildungsschule was legally part of compulsory mass schooling, enclosed in the same communal governance as the Volksschule while the full-time schools were run centrally, and that it selected on nothing — apprenticeship required no educational credential, and the school obligation followed the contract, where obch./prům./odb. required Bürgerschule grades plus an entrance exam. Still deferred, on two residues: no source here gives the duration of a Fortbildungsschule or Fachschule year (regional laws 1907–1931), and both sources are silent on Hungary, so a Cisleithanian/Transleithanian split is still needed. 2026-09-02, code: education.py is rebuilt as 2.0.0 on both sources — a six-rung ladder (No schooling / Elementary / Burgher / Vocational secondary / Upper secondary / Tertiary) over an itinerary parser, with pok. off the ordinal and carried by educ_apprentice, completion split into its own missing-able column, and eleven columns wired through schema.py 1.10.0. Non-empty coverage 96.7 → 99.1 %; the old Upper secondary bucket of 18,756 redistributes 4,598 → Elementary, 2,606 → Burgher, 3,973 → Vocational, 7,579 unchanged. Notebook 02 re-run 2026-09-02, QA gate 66/66, data/final current. Rung 0 was renamed None → No schooling between the two runs: a level label identical to the missing marker is the fabricated-exit_N shape again. Still Open on the Hungarian ladder alone — the scheme is established for Cisleithania only, and Slovak and Rusyn births ride on it unvalidated. 2026-09-07, external check: the ladder now has one. Österreichische Statistik XCIII/1 (Wien 1912), the administration's own census of its schools, is the source EDUCATION_LADDER.md §5 had nominated by name; it confirms the taxonomy and pok. off the ordinal — the volume files the Fortbildungsschule as its own category (p. XX d, p. XVI) and it carries 84.0% of Bohemian vocational pupils — and it fails education_complete (Table 34: 10 eight-class Volksschulen in Cisleithania), logged as D-53. It also shows pok. is materially under-recorded in Vzdělání (5.3 : 1 in the enrolment vs 0.86 : 1 in the records), so education_apprentice is a lower bound rather than a rate. It cannot touch Hungary — the volume is Cisleithania by definition — so this decision stays Open for exactly the reason it already did. See VALIDATION_EDUCATION.md. Open Audit §B3; Roadmap; Education ladder
D-09 2026-07-20 csol.cz records that contradict the historical record (~130 regiment/theatre pairs) are documented, not silently corrected. Correcting them would hide a source-quality signal the reader is entitled to see. Settled External validation §2.5
D-10 2026-07-23 Ehnl's Tabelle I is transcribed faithfully; wartime Landsturm units are absent by construction and stay absent. The source is a peacetime order of battle — completing it would be invention, not transcription. Settled Habsburg army & recruitment
D-11 2026-07 Weak place matches are kept and tiered (fuzzy, megye, muni) rather than discarded. The tier is a filterable column; dropping would silently shrink the sample for every user. Settled Place matching
D-12 2026-07-20 This documentation is generated from the repository; the HTML is never hand-edited. Every stale claim the audit found was a hand-maintained copy of a fact that lived elsewhere. Settled Overview
D-13 2026-07-23 Habsburg-unit coverage is reported twice — "recorded" and "matched to Tabelle I" — never as one number. A recorded unit string is not the same thing as one that resolves to a real 1914 regiment. Settled Descriptives
D-14 2026-07-30 The record of change is split three ways: Roadmap forward-only, What's new curated, Commits & versions machine-generated. Three lists narrating the same week is how documentation starts to drift. Settled the strip at the top of this sheet
D-15 2026-07-30 The NATO band of svobodník — and with it the whole enlisted run — is not settled. ranks.py codes it OR-3; two of the project's own sources put it lower. Nothing changed pending a source check. Contemporary pay and the successor army's own note both deny it a leader grade. Open Rank ladders §G2 and the note under the bridge figure
D-16 2026-08-01 A bare municipality may no longer resolve onto an osada — a named part of another village. The 1910 statutory cities are curated into the crosswalk instead. The ČSÚ register leaves a city's okres empty because the city was its own district, so the city vanished from the lookup and a like-named hamlet won by default: every bare "Brno" birth landed in okres Jilemnice. Settled Place matching — the muni tier
D-17 2026-08-01 One naming convention for the ten city/rural twins — city X (město), rural bare X — so a bare district name always means the rural district. Brno's two backbone rows were renamed to match the other nine. The municipality then decides the member: naming the city puts the record in the city unit, naming anywhere else in the ring. A Statutarstadt was a single municipality, so the municipality is not a heuristic but the definition. The bare name had meant opposite things (city for Brno, rural for the other nine), and the ČSÚ lexikon writes the rural district bare — all 118 Brno ring villages record okr. Brno — so the old naming put 117 of them in the city okres and cost Brno-venkov every coordinate. 3,827 records move; 66 with no municipality go to the ring, flagged undecided. Settled Place matching — city and rural twins
D-18 2026-08-01 The pooled city+ring pair is the baseline analytical unit for a district-level covariate merge (148 units, key census_okres_id), with shares recomputed from summed counts. Member-level court-derived columns are set to <NA> on the eighteen affected twins; the okres-level (406) covariates stay split for all twenty as a robustness axis. Not a conservative choice about ambiguity — the source cannot make the split. A statutory city had no Gerichtsbezirk of its own, so the court-level workbooks keep one row per pair: Opava (město)'s 0.463 Czech share is Gerichtsbezirk Troppau, city plus ring, and Opava's 0.069 is only Odry and Vítkov. Nine of the ten pairs are affected; only Brno genuinely splits. Settled Place matching — city and rural twins
D-19 2026-08-01 The same convention on the Slovak side, for the 26 járás/chartered-town twins: a bare district name means the járás, a trailing rural qualifier (Košice vonkov) is stripped by rule, and the municipality promotes a record to the town unit. Both members keep their own census covariates — there is no pooling here. The unit that is named is the district; the town sat outside it and was a single municipality, so naming the town is the definition of belonging to it, exactly as for a Statutarstadt. The asymmetry with D-18 is the source, not a second opinion: the Hungarian census carries a separate row for all 52 units, so nothing has to be merged away. 1,888 records move to a town unit (729 legion / 1,159 VÚA); 589 stop resolving through fuzzy; 71 with no municipality go to the járás, flagged undecided. Settled Place matching — járás and town twins
D-20 2026-08-01 The Majo (2012) lexicón's chartered-town blocks and its right-aligned number columns are parsed by what the page actually prints, not by the commonest case. Both defects were one mistake — a regex narrower than the source. ^mesto so missed the plural mestá so zriadeným magistrátom, the only header seven counties use, so 27 town rows kept the county's last járás (obec Nitra filed under okres Nové Mesto nad Váhom). And bucketing numbers by their centre against the header anchors — which are the columns' left edges — drifted narrow values one column: Nitra's 9 Croats and 13 Serbs merged into 913 Serbs. Reading the right edge fixes 27 broken row sums and 135 rows that reconciled while sitting under the wrong heading. Settled Place matching; Data sources #9
D-21 2026-08-01 The lexicón's adjectival okres headers (Hradný okres, Pohronský okres, Medzibodrocký okres) are matched, and the three district names are curated onto the backbone rather than derived. Same family as D-20, and the worst of the three because it failed silently: the 19 Orava obce under Hradný okres inherited the previous heading and were filed under járás Trstená instead of Tvrdošín — plausible, in the right county, and wrong for ~98 records. Curation is not laziness: the Magyar→Slovak bridge cannot reach these, because Vári reduces to Vár, which is no chartered town and so is absent from the backbone's own dictionary. Settled Data sources #9; district_sk_curation.csv
D-22 2026-08-01 A Slovak district string whose fuzzy match ties at the top score across two different units is refused, not resolved. A token-set ratio scores 100 against every district a cell names, so Ilava nebo Zlaté Moravce and Hlohovec Nitra matched both — and the winner was whichever key the lookup happened to hold first. That is a coin flip wearing a match's clothes. 38 records across 32 strings, every one a multi-district or truncated cell; the settlement pass already refuses on the same grounds. Settled Place matching — the fuzzy tier
D-23 2026-08-01 The same tie on the Czech side is broken by the record's own municipality — the okres it actually sits in — falling back to the Prague-suburb rule, and refusing only when neither decides. The settlements-less call (the ČSÚ lexikon's own okres resolution) keeps first-match and is untouched. Refusing outright, as D-22 does, would have been wrong here: the municipality is evidence, and it overrules the arbitrary winner on 59 of the 224 birth records it can decide. 22 births recorded as Ostrava are in okres Frýdek — the Silesian side — not Moravská Ostrava; 14 Nové Město are na Moravě. A base-rate curation, which was the obvious alternative, would have got every one of those backwards. The Prague fallback is not a competing rule: on all 217 rows where both can speak they agree, and it exists only for the 79 with no usable municipality — the capital's suburbs were separate okresy in 1910, so Praha - Smíchov names Smíchov. The lexikon is exempt because the ČSÚ register writes the 1910 period name for the Bohemian member of a Bohemia/Moravia twin (bare Krumlov, Budějovice) and always qualifies the Moravian one, so first-match is already correct for those 481 settlement rows. 372 legion + 58 VÚA birth records change; 285 refuse. Settled Place matching — the fuzzy tier
D-24 2026-08-02 A compound whose qualifier changes the trade is curated as a whole string rather than decomposed by the head rule. Nine keys, 258 records. mlynářský dělník is a worker in a mill and the head noun is right; akademický malíř is an academically trained fine artist and the head noun is a house painter. Left to decompose, all eleven malíř compounds took 93120 Building Painter, HISCLASS 9. The German link bridge independently returned the same code for six of the nine. Settled Occupations §4; Occupations validation
D-25 2026-08-02 ADJECTIVE_KEYS (lesní, mlynářský, sladovnický) may match only as an exact whole string — never as the primary of a dual, a head or a prefix. ~150 records. dual is tried before head, so a curated key that is really a qualifier was winning the compound and becoming its primary occupation: of 384 records on 63220 Forest Supervisor, HISCLASS 3, about 150 were forest labourers, attendants, clerks and gamekeepers. hostinský looks like one of these and is deliberately absent — it is a genuine noun and its duals are correct. Cost: 19 records over 16 strings whose head noun is uncurated now stay <NA> rather than wrongly class 3. Settled Occupations §4; Occupations validation
D-26 2026-08-02 hajný and myslivec → 64960 Trapper and Hunter, HISCLASS 10, replacing 63230 (class 12). A forest warden is estate staff — sworn, housed, patrolling against poachers — so 63220 Forest Supervisor (class 3) makes him a lower manager and 63230 Forestry Worker (class 12) ranks him with the men who fell the timber. 64960 is the rung between, and OccCANINE returned exactly it for Waldheger independently. Same reasoning as domkář → 61115. HISCAM, SOCPO and HISCLASS_5 are unchanged, so only the 13-class HISCLASS moves. Settled Occupations §6
D-27 2026-08-02 zámečník (2,162) stays 83930 Locksmith although three independent sources point away from it. German Schlosser links to Machinery Fitter and Plumber, OccCANINE returns 87430 at its lowest confidence in the set, and CZ-ISCO splits the modern word three ways with none of them a locksmith. All three describe later usage: 1910 Bohemian zámečník genuinely included lock and key work that has since left the trade, and modern drift is not evidence of a historical error. HISCLASS is 7 either way. Superseded in scope 2026-08-02: the warning used to be that HISCAM moved 60 → 66, but HISCAM is no longer emitted (D-30), and the 1,204 strojní zámečník records — what the dissenting sources were really pointing at — are now 84100 Machinery Fitter in their own right (D-29), with OccCANINE returning that trade at p=0.999. What is still unresolved is the 5-digit code on the ~1,900 bare records, which no longer moves any emitted variable. Provisional Occupations validation — Open
D-28 2026-08-02 CZ-ISCO is used only to corroborate a reading and never to assign a HISCO code — at enrolment or in the uncoded tail. It is the 2025 classification: the words carrying the biggest decisions (rolník, 18 % of the sample; hajný; příručí; tkadlec; cihlář) are absent from it at every level. And there is no path to HISCO — ILO's correspondence tables reach ISCO-88 while HISCO is built on ISCO-68, so the chain would cross two lossy crosswalks and forty years of occupational change into a scheme designed for 1690–1970 data. Tail candidates are emitted as a review queue with empty accept/hisco columns. Settled Occupations validation Check 5; Data sources #13
D-29 2026-08-02 The qualifier is kept as its own variable (occupation_qualifier, 13,563 records), and the 25 compounds where it changes the code are curated whole (~3,250 records, 1,058 class moves). strojní zámečník and zámečník shared a HISCO code and a HISCLASS and are not the same job: within Russia they desert at 3.40 % and 5.79 %, and bankovní úředník at 0.54 % against 1.66 % for a generic clerk. (Figures as measured on 2026-08-02, the evidence this decision was taken on; on the post-adjudication build of 2026-08-27 they read 3.38 % / 6.06 % and 0.54 % / 1.64 % — the contrast is slightly wider, and the live numbers are carried in OCCUPATION_CODING.md §4.) The head/prefix rules were finding the noun and dropping the modifier on the floor — 9,869 records' worth. The qualifier is extracted from the string structure, not from the match kind: keying it off "whatever head discarded" would have stopped recording it for precisely the compounds curated here, the moment they became exact. Excluded on dual, where the leading run is a second occupation already coded in *_2; a QA invariant asserts this. Compounds whose code was already right (soustružník železa, tovární dělník) are deliberately not curated — the column carries them anyway. Eleven of the fifteen largest keep their HISCLASS, so this buys a correct code and a new variable, not a new class gradient. Externally checked the same day: OccCANINE reached all 25 compounds — the only check that can, since CZ-ISCO matches on a head noun and the German link table holds no Maschinenschlosser — and returned the exact same 5-digit code on 13 of 25, against 55.7 % for the dictionary as a whole. strojní zámečník came back Machinery Fitter at p = 0.999, the model's joint-highest confidence, while the bare zámečník is its lowest at 0.12. The 7 class disagreements are all administrative-grade compounds, where a gloss probe shows the model returns the same code for Bediensteter, Beamter and Diener and so cannot adjudicate them; none was acted on. Settled, amended by D-41 Occupations §4
D-30 2026-08-02 HISCAM is dropped from the emitted columns — occupation_hiscam, _hiscam_2, occupation_interwar_hiscam. HISCLASS stays. It is non-monotonic on this sample: by Russia-only quintile the desertion rate runs 4.38 / 4.31 / 4.13 / 4.71 / 2.71, so only the top quintile separates and the continuous scale looks more sophisticated than it is. It was also the one scale carrying the zámečník 60 / machinery-fitter 66 split that D-27 leaves unresolved — which is now explicit in occupation_qualifier and in the split code, so nothing is lost by removing it. The column is still in hisco_authority.csv; only the emission stops. Consequence: D-27's stakes fall. Its warning was that "HISCAM results are not safe"; with HISCAM gone and strojní zámečník split off to 84100, the residual bare zámečník is a narrower and more defensible set than the 2,162 the decision was written about. Settled Variables — Occupation; Occupations validation
D-31 2026-08-02 Career stage is read off the title (occupation_career_stage, 3,502 records) rather than curated into HISCO's status modifier, and it never touches the code or the class. HISCO's own status slot reaches ~2.7 % of coded records and cannot be improved: csol.cz records a job title, not a labour-force return, so for ~97 % of men the source never states a position. A naive token scan suggests 14.5 %, but 16,224 of those are dělník, zřízenec or příručí standing alone — occupations, not stages — and counting them would double-code. nádeník is excluded on the same grounds: a day labourer is an employment status and a job, and belongs with the 1910 census day_laborers column. The ordinal _ord folds the guild ladder and the clerical ladder into one scale, which are not commensurable; it is a convenience and the string column is the variable. Settled Occupations §4; Variables
D-32 2026-08-02 železniční zřízenec → 36000 Transport Conductor, Specialisation Unknown (HISCLASS 9, replacing 36090), and železničář (47) moved onto the same code from 99900 — HISCLASS 13 → 9. Czech lexicography gives železniční zřízenec as a souhrnný pojem: a collective term for výpravčí, průvodčí, posunovač and traťový dělník, with železničář/drážník as its modern successors. The named grades are already coded individually wherever csol.cz states them (výpravčí 22235 cl.4, strojvedoucí 98320 cl.7, průvodčí/vlakvedoucí/výhybkář all cl.9), so the plain-zřízenec records are the unspecified residual and class 9 is its modal value; 36000 replaces 36090 because "Other" asserts a specialisation we do not have. Internal evidence agrees — education 1.44 vs 2.91 for železniční úředník and 1.04 for železniční dělník, 5.7 % NCO vs 77.4 %. železničář had only ever been on 99900 to escape 99999, which carries no HISCLASS; class 13 was never argued for, and the same men must not sit four classes apart under two names. OccCANINE's 39960 Railway Clerk (p=0.966) is refused: a gloss probe shows it returns one code for Bediensteter, Beamter and Diener alike. Settled Occupations validation — Check 2, closing note
D-33 2026-08-02 rolník (21,633 — 18 % of the sample) stays 61110 General Farmer, HISCLASS 8; the 62105 Farm Worker (HISCLASS 12) alternative is refused on primary-source evidence. The project's other four checks structurally cannot reach this word — the German bridge confirms only Landwirt → 61110, OccCANINE is fed that same gloss, CZ-ISCO does not contain rolník, and education does not discriminate (1.13 against domkář's 1.00; schooling did not track landholding). The 1910 Volkszählung Berufsstellung columns can, and were unused until now. Within-agriculture composition across 99 okresy and 15,584 farm-occupation legionnaires: Spearman +0.402 between the legionnaire own-account share and the census Selbständige share, monotone from estate-dominated okresy (census 0.241 → legion 0.862) to smallholder ones (0.643 → 0.931); share-of-births placebos on nádeník and dělník are flat. The level gap (0.907 vs 0.463) is not a coding defect: counting mithelfende Familienangehörige as own-account closes it to +0.273, and the residual is selection into the Legion. Decisive either way, because 62105 is hired labour and neither a holder nor a farmer's son is that. Ecological, at okres level — it cannot clear any individual record; partly addressed by D-34. Settled Occupations validation — Check 6
D-34 2026-08-05 Amends D-33, which stands. The ecological limit D-33 declared is narrowed, not closed: the Vienna Database household listings supply the individual-level evidence the census could not, and 61110 is corroborated a second time from a source rather than a coder. The listings pair a title from a closed 521-term German list with a household position recorded independently, for 163,400 persons. BAUER is 0.3 % Gesindeperson against LANDARBEITER/KNECHT's 94.9 %, in different systematic classes; among males 18–30 — the Legion age band — BAUER is 97.5 % household head and 0.3 % son, which answers the holder vs farmer's son half D-33 left open, since a farmer's son on these listings is written down as SOHN. Stable across 32 localities and 49 survey years, and not an artefact of the 1857 Vienna bulk (45 records). Why it only narrows: title and position are not independent — one clerk assigned both under a convention giving the farm title to the holder — and the evidence reaches rolník only through an assumed Landwirt ≈ Bauer synonymy in the wrong territory and mostly the wrong period. Complementary to D-33 rather than a replacement: the census is the right territory and decade at the wrong aggregation, the listings the right level in the wrong territory. Settled Occupations validation — Check 7
D-35 2026-08-05 A settlement whose coordinate lands in a court belonging to a different okres than its name is snapped back into its own okres when it sits within 1,000 m of it (spatial.SNAP_TOL_M, locate_snapped); the move is flagged in court_snapped. The MPIDR outlines are an empire-wide generalisation, so a city border can be drawn a kilometre from where it ran. Prague is the case that forced this: the polygon labelled Prag/Praha St. is the right size — 20.8 km² against the 1910 city's ~21 km² — but its eastern edge sits ~500 m too far west, so the single Wikidata coordinate every Praha birth carries (Old Town Square, 50.0875 N 14.4214 E, unambiguously inside the 1910 city) fell into neighbouring Královské Vinohrady. All 2,691 court-placed Prague births went there and the Praha court polygon received none — visible on the map as a district with 7,148 births at okres level and nothing at court level. The tolerance is read off the measured distribution rather than chosen: of 5,660 geocoded settlements 624 (11.0 %) disagree, median distance 765 m, tail out to 318 km; 1,000 m captures 382 and stops well short of that tail, where the disagreement is about which place this is and must be left standing. Effect: okres agreement 89.3 % → 95.9 %, court fill 5,892 → 5,899. Cost: the court layer is no longer fully independent of the name layer, so court_snapped exists to be excluded whenever it is used as a cross-check. Settled Place matching; utils/parsing/spatial.py
D-36 2026-08-05 A record filed under okres Praha whose municipality was a separate 1910 municipality is moved to the okres that municipality actually had (resolve_prague_suburbs, curated in reference/places/prague_suburb_curation.csv). Carried in birth_okres_prague = city / suburb. Prague absorbed 37 municipalities on 1 January 1922, and csol.cz and the VÚA database were both compiled afterwards — so a clerk copying the card of a man born in Žižkov in 1890 wrote the okres he knew, Praha, for a town that in 1910 was the seat of its own political okres. The okres tier matches "Praha" outright and never consults the municipality (unlike the twin rule, D-17), so 518 of 553 Žižkov births and 398 of 419 Smíchov births carried inner-Prague census covariates. That is not a small substitution: Žižkov was a working-class district whose 1910 profile is nothing like the old inner city, and the covariate is the whole reason the okres key exists. Scope is deliberately narrow. Only five municipality strings are settled — Žižkov, Smíchov, Vinohrady, Královské Vinohrady, Karlín — each the seat of the okres it moves to, which is certain by definition: a town that gives an okres its name sits in it. Moves 1,468 legion + 69 habsburg records. The other 106 curated rows (~2,083 records: Nusle, Vršovice, Břevnov, Košíře, Michle…) carry a blank okres and are inert until established from the 1910 Spezialortsrepertorium; an unverified reassignment would be worse than the anachronism it replaces, because it would look settled. A cadastral number (Praha 2 = Nové Město, inside the 1910 city) matches nothing and correctly stays. Provisional Place matching; reference/places/prague_suburb_curation.csv
D-37 2026-08-11 If a district-level Czech national-mobilisation covariate is built, it is built from resident institutions — amateur-theatre localities, Czech záložny and savings banks by founding wave, Czech vs German gymnasia — not from the tábory lidu of 1868–71, even though the assemblies are the better-known measure and their underlying source (Purš 1958) is now acquired and public domain. Nothing is built yet; this fixes the order if it is. The tábor is an event on a symbolic hill — Říp, Blaník, Zvičín, Oreb — chosen for its Hussite resonance, not for where its participants lived, and Purš records crowds arriving "z dalekého okolí". It geocodes (hill X near village Y) but its catchment is wide and undefined, which is the one property a district covariate cannot tolerate. It is also thin at the merge unit — ~102 Bohemian events over ~100 okresy, so near-binary and zero-inflated — and plausibly collinear with the Czech-share and urbanisation already available from the 1910 census. An institution sits in its town, so the locality is the associational base. The exception is repression (banned assemblies, dispersals, prosecutions, troops billeted on villages that refused taxes), which only Purš carries and which no institutional map can supply. Provisional Data sources §3d
D-38 2026-08-11 A reader of the 1910 register — the ALTO parser, a vision model, a human — is never shown the invariants it will be graded on, and a block that fails is re-read, never repaired. na is never counted as a pass. The grader is the table's own arithmetic (lexikon_1910.INVARIANTS), and it is the only check the source supplies: there is no transcription to compare against and never will be for 480 pages. That makes it worth exactly as much as its independence. A model told what the numbers must add up to returns numbers that add up — the pass rate then measures the prompt, not the read — and a repair applied to a failing cell converts a detected error into an undetected one while raising the score. The same logic forces the na rule: a verdict that silently absorbs the rows the parser could not read makes a rate that improves as the reading gets worse. The cost is accepted: unreadable cells stay unreadable and the pilot's rates are lower than they could be made to look. Settled Digitising the 1910 register
D-39 2026-08-11 Reverses the working assumption. For the footnote apparatus the image read is the primary record of which symbols a place has, and the ALTO's unresolved symbol slots are a lower bound, not an index of where the marks are. The pipeline was designed around a division of labour — the text layer knows where every marker is, the image read knows what it is — and the division is false. Measured over the 20-page pilot: 291 unresolved slots in the text layer against 893 printed symbols found by glm-ocr and 545 by qwen3.5; only 16 of 221 municipality entries agree exactly and 185 hold more symbols than the ALTO saw. The mechanism is that a pictogram the engine rendered as Щ, ф or $ becomes a slot, while the same pictogram rendered as g, f, p or S — the commoner outcome — is indistinguishable from type and is never emitted at all, so which third of the markers the text layer knows about is decided by how the OCR happened to fail. Consequence: an institution inventory must not be built from a reader that transcribes words and drops symbols (glm-ocr does exactly this), because a place with no post office would be indistinguishable from a place whose post office was never read. Settled Digitising the 1910 register; utils/lexikon_1910/lexikon_1910_institutions.py
D-40 2026-08-24 Prague is not a gold page. The statutory-cities front matter (scans 15–17, Města s vlastním řádem obecním: Praha) is excluded from the symbol gold sample, and the sample is left exactly as drawn. Its prose is instead read as a source for institution_abbreviations.csv. The premise for adding it was that the empire's largest city would print the widest variety of symbols. It prints none. The statutory-cities section is not the numbered m, n) apparatus at all — it is continuous prose with every institution spelled out in full words (okresní hejtmanství, hlavní celní úřad I se 7 pobočkami), and not even the abbreviations appear. Measured on the text layer with lexikon_1910_institutions._GLYPH_RE: 4 glyph-shaped characters on scan 16 and 13 on scan 17, mostly quotation marks, against 92 on scan 19 and 46 on scan 136; the rendered image of page 2 confirms it. Adding it would have cost reading time for ~zero symbol claims. What is symbol-dense is the okresní seat, and the drawn sample already holds Jičín (136), Nusle/Vršovice (196), Písek (316) and Tábor (436) — including the multi-paragraph 12, —) Jičín entry. Prague's prose keeps a separate value: it is the fullest expansion of the abbreviations the curated table is otherwise inferring. Had it gone in, it would have had to sit outside both halves — substituting a purposive page for a drawn one breaks the certification interval, which is the whole point of the probability draw. Settled Symbol accuracy plan — The gold sample
D-41 2026-08-26 The 172 review flags on the occupation dictionary are adjudicated, one question per word, and the answers are written back to a register (czech_glosses.ADJUDICATED) rather than to a spreadsheet. 41 keys are recoded (4,359 records), 23 of them moving HISCLASS; 10 keys stay open. review in the curation now means unsettled, not unread. A flag that cannot be cleared is not a flag, it is a permanent condition: before this, a key looked the same whether nobody had examined it or somebody had examined it and decided the code was right, so every rebuild of the workbook asked the same 172 questions again and a day of adjudication left no trace in the repository. Three verdicts are recorded — keep (130), recoded (41), open (10) — and the register is the only writer of review, so the bridge can go on flagging genuinely new ambiguity without re-raising anything answered. The new code always lives in HISCO_OVERRIDE, never in the register, so a reader looking a word up finds one answer; run_link_bridge asserts both halves (a recoded key with no override entry, or an override still claiming the flag, is a hard failure). Biggest single move: slévač (890) 72500 Metal Moulder → 72420 Metal Pourer, HISCLASS 7 → 9, on OccCANINE at p=0.99 and the adjudicator's reading of the word as a caster. Three answers were reconciled against the dictionary before being applied, each defensible about its own word and wrong about the file it sits in: knihvedoucí back onto účetní's 33110 (the same row, answered both ways, and D-32 forbids two names for the same men sitting two classes apart); profesor back to 13290, since a 1910 gymnázium master was styled profesor; hospodářský úředník onto 39310 Office Clerk rather than 31090, which named a government body an estate official did not work for — keeping the class move and dropping the sector claim. Cost, accepted and named: three sharper codes carry no HISCLASS in HSN at all — štukatér 95530 (95), cementář 95240 (55, class only under a modifier), rukavičkář 79475 (44) — so 199 records buy a better 5-digit code with the class variable. Amends D-29: 159 of the 178 records its compound curation moved 5 → 3 as estate and forest officials go back to 5, leaving its net class effect at ~899 rather than 1,058. Measured on the built frame once the bridge had run (2026-08-26): 4,731 legionnaire records take a different HISCO code and 203 more in the interwar column; 1,383 move HISCLASS, of which 1,028 are slévač going 7 → 9; 219 lose the class to a sharper code HSN never classed; 36 gain one; coverage rises 98.46 → 98.48 %. Found on the way past and fixed: D-32 had been applied to the curation CSV and not to czech_glosses.py, so železničář (47) would have been pulled back from 36000 to 99900 — four HISCLASS rungs — on the next bridge run, silently. Settled Occupations §5–6; Occupations validation — Open
D-42 2026-08-26 No HISCO sentinel carries a stratification scale. -1 (no occupation), -2 (an employer or institution named instead of a job) and -3 (nothing recorded) are emitted with a code, a label and a major group, and with <NA> in HISCLASS, HISCLASS_5 and SOCPO alike. A QA invariant asserts it on both occupation fields. The exclusion was believed to be in force and was not. For a sentinel there is no occupation, so the status modifier alone carries the scales through the authority key, and HSN's crosswalk reads a modifier as a social position where this project reads it as a refusal to code — the two were never reconciled. student (−1, status 41) had no HISCLASS, which is exactly why the policy looked enforced, but carried SOCPO 42, middle class — supervisors of skilled workers, on 3,098 records: 19 % of that cell. absolvent and abiturient (−1, status 42) failed in the headline variable — HISCLASS 2, Higher professionals, and SOCPO 5, Elite — for 160 men whose qualification was a school-leaving certificate, 3.3 % of HISCLASS 2 and 5.3 % of the elite. Nothing was fabricated: HSN observed those combinations 378 and 9 times, and its values are answers to a different question than the one this project asks of them. The defect survived because no invariant covered SOCPO at all, and the HISCLASS invariant only checked the 1–13 range, which 2 satisfies. Cost: 3,258 SOCPO values, 160 HISCLASS and HISCLASS_5 values, and 3 secondary classes are withdrawn — all of them from records the project already declines to code. label_en and hisco_major stay, because they describe the code and do not rank a man. Settled Occupations §8; utils/parsing/occupations.py 1.3.0; utils/validation/data_checks.py 1.8.0
D-43 2026-08-26 The ten keys that survived the adjudication stay exactly as they are, and nothing is guessed to fill them. Seven — čsd, zaměstnanec čsd, čsa, pošta and three spellings of the ČSD workshops at Nymburk, 134 records — name an employer or a workplace and keep the -2 sentinel, which since D-42 means a code, an English label, a major group and no stratification scale at all. The other three keep their current code and their flag: vlakvedoucí 36020, politik 20110, správce 21000. review stays up on all ten. Coding them would not complete the sample, it would bias it. The obvious guess for the ČSD family is the railway residual D-32 already documents (36000 Transport Conductor), but the source records where a man worked, not what he did — a clerk, a fitter and a stoker in the Nymburk workshops sit on three different HISCLASS rungs, and picking one puts 134 men in a class nobody ever recorded them as holding. The exposure is concentrated, not diffuse: 156 of the 163 records are in the interwar occupation column, which codes only 4,295 records in all — so these ten keys are 3.6 % of that variable and the employer family alone 3.1 %, landing entirely in the one occupational measure the interwar-electoral project runs on. A 3 % hole is visible and can be conditioned on; 3 % of invented railwaymen cannot, and would look like a finding. The project has met the failure before at scale — the 2026-07-16 audit found ~26.4k empty exit cells fabricated into code N — and the sentinel exists precisely so that unknown has somewhere to live. Reopening needs a source, not another pass over the same evidence: a ČSD staff schematism or the Nymburk workshop rolls. Per D-32's rule the family then moves together or not at all. Settled Occupations validation — Open; Occupations §5
D-44 2026-08-27 Zdroj informací becomes two variables, not one, and the extra citation is left uncoded. The archival half is source_spis / source_karta (which VÚA-VHA personnel document backs the record); the editorial half is source_extra + source_extra_ref, the citation verbatim. No published-vs-archival classifier is built. The field concatenates a wartime document and a modern bibliographic citation, and they answer opposite questions about recording completeness — one says what the archive holds, the other that a local historian worked the okres over ninety years later. Kept as one variable they cancel: among Russia records the personal-card rate is identical for deserters and demobilised (87.11 % both) while source_extra runs 1.05 % against 5.47 %, so a single flag would have averaged a flat signal with a steep one and shown neither. Split, they also settle what the field does not explain: conditioning on the card being present, occupation is missing on 7.96 % of Russian deserters against 0.16 % of the demobilised — the document is there and the occupation line inside it is blank, which is a fact about how the file was kept, not about archival coverage. The refusal to classify source_extra_ref is the same rule as D-43 and D-38: the available heuristics mislabel in both directions — the obvious test, presence of a year, files the unpublished OTÁHAL, FIŠER. Nevydané paměti obce Luká as an archive and the archival CEVH ; Deník legionáře Jana Kalase 22.2.1895 as a publication — and a wrong label here would be invisible downstream. With 489 distinct values the published subset is a short hand-coding task in the spirit of the occupation curation, not a regex; until someone does it the verbatim string carries everything. Note the field is csol's editorial attribution, not the archive's own provenance stamp — it must not be described in the paper as the latter. Settled Pipeline — provenance.py; utils/parsing/provenance.py
D-45 2026-08-29 The gold transcription reads whole pages, not the symbol column. All three of the register's own documents — the 17-column table, both halves of the two-column apparatus (Části osad and Úřady, ústavy), and the postal legend below the rule — plus the *) / **) footnotes, over 21 pages: the 16 drawn (8 calibration + 8 sealed certification) and 5 purposive pages outside both halves — the 4 dev pages already in draw_gold_sample.ALREADY_READ, plus scan 449, which closes soudní okres Trutnov (449–451; measured on the text layer, 448 is Maršov's last page and 451 hands off to Úpice) and is the only way the district-wide postal legend becomes gold-readable at all — a page-level read of a numbering that runs across the district is otherwise not well defined, which is why 85 of 535 pilot keys come back legend truncated. Adding 449 leaves the realised draw byte-identical, verified by re-running it: the systematic candidates are 16, 46, 76 … 466 and 449 is not among them. One Markdown file per scan, every data cell blank — the name and the register's own sequence number included. The model pipeline widens to match: SECTIONS moves from "offices" to "all". The argument against was that the table is already graded by its own arithmetic (I1–I5) plus the census (I6) and reads at ~88 %, so a human pass buys the weakest of five channels. That is an argument about certification and it misses the other direction: run the gold rows through grade_row / grade_block_sum / the parts sums and the invariants grade the transcriber, per page, while the image is still open. The plan's own blind-spot list says the transcriber's error rate is unknown and "plausibly 2–5 % — the whole margin under discussion"; symbols-only reading cannot be graded at all except by double-coding, whole-page reading is substantially self-grading. It also converts the table's inferred ~88 % into a measured accuracy against a person, which is what makes the switched-off channel A worth running. And Části osad turned out not to be names and digits: every part name carries a topographic mark from the same atlas (Hazlov, Haslau Q 217—2266, Petermühle ф 1—11), so it is a second symbol channel, and a recurring & that nothing in the codebase documents gets settled on the way. Blank cells are the independence condition. Column 1 is the most OCR-damaged text in the volume and one of the things being graded, so pre-filling it would anchor the reading against the channel it grades — the rule D-38 already enforces on the models. Only the position index r1, r2, r3 … is pre-filled, which is a fact about the page rather than a reading of it. Cost, named: ~48–74 h of reading, against ~8–12 h for the symbol column alone; measured over the 8 calibration pages, offices run 177 lines / 93 entries and parts 198 lines / 118 entries, and the table adds ~525 cells a page. The scope is frozen before any certification page is opened and is identical across both halves — deciding after calibration is transcribed means re-opening calibration or shipping an asymmetric instrument. The four purposive pages already carry an old free-form transcription, so reading them whole also measures the transcriber against themselves across two protocols, at no cost to the sample. What it does not buy: certification precision on the symbol channel is unchanged at ~560 claims and ±1.8 pp, and must not be restated as improved. Settled Symbol accuracy plan; utils/lexikon_1910/make_gold_worksheet.py, read_transcription_md.py
D-46 2026-08-29 The bake-off run leaves the notebook. The model matrix, the flags and the run loop move to utils/lexikon_1910/bakeoff.py, driven headlessly by run_bakeoff.py; notebooks/05_ocr_model_bakeoff.ipynb keeps section 6 and becomes a reader of finished CSVs. Pass artefacts are written per host (output/lexikon_1910_pilot/bakeoff/<host>/); the gold set is not. A Dropbox-synced .ipynb cannot be the artefact two machines both run: the run mutates it — outputs, execution counts — on both sides of the sync, and 05_ocr_model_bakeoff (Heavyhorse6's conflicted copy 2026-08-14).ipynb sitting in the working tree is what that produced. Two machines writing inventory_qwen38_r3base.csv to one directory is the same failure one level down, and worse, because whichever copy Dropbox settled last would silently become the one the comparison scored — so the split is per host, and a label found in two host directories is reported, never resolved by lookup order (two machines that ran the same configuration should agree, and if they do not that is the finding). The notebook and the runner are not two copies: the notebook imports the same module the run used, so the table it draws cannot describe a matrix that was never run — which is the 2026-08-14 mislabelling defect in a new place. Widened in the same breath: SECTIONS = "all" (D-45), and the run tag bumped r2 → r3 because the skip-if-done rule keys on the label and an offices-only r2 inventory would otherwise be accepted as this run's answer. Added to the scorer: entry_exact, the share of shared entries read exactly right. F1 pools claims across entries and so rises when a pass moves claims around without finishing a single municipality — and the municipality is the unit of analysis downstream. Measured immediately on the existing passes: the best r2 pass reads F1 0.872 and entry_exact 0.553, so a rate that looked close to usable finishes barely half its municipalities. Amended 2026-08-29, same day: the notebook may drive a run again — section 2 chooses models, kinds and variants and calls bo.run_matrix — but it still does not define what a pass is. The matrix, the flags and the crop geometry stay in the module, so the notebook picks from the matrix rather than restating it, and the table section 6 draws still cannot describe a configuration that was never run: that was the property the split existed to protect, and it survives. What does not survive is protection from the sync hazard — an executed .ipynb mutates on both sides of Dropbox — so the notebook path is documented as one-machine-at-a-time and the headless runner remains the way to run two nights in parallel or to survive a closed laptop. Per-host output means the two can never overwrite each other's passes whichever is used. Settled Digitising the 1910 register; utils/lexikon_1910/bakeoff.py, run_bakeoff.py; utils/lexikon_1910/lexikon_1910_institutions.py 0.4.0
D-47 2026-08-29 A rate-limited pass is not a result, and the run parks the model rather than retrying it. A refusal that names a spent quota (HTTP 429, or a 200 carrying a limit message — Ollama Cloud returns both) raises a distinct RateLimited; the first one parks the model, so every remaining crop of that pass and every later pass of that model are skipped while every other model runs to completion. The pass writes ratelimit_<label>.json, is marked incomplete in the timings, is excluded from every accuracy table, and is left done=False so a fresh quota retries it. The failure mode this replaces was silent and looked like a finding. A refused crop was retried three times at 2 s and 4 s — useless against a window measured in minutes, and on a metered plan each refusal can itself count — then recorded as one more failed crop; the pass completed normally, wrote an inventory, and exited 0, so the runner saw a success and the bake-off scored it. A limit hit on crop three would have been published as a cloud model that reads the register very badly, with nothing anywhere to say otherwise, and ABORT_AFTER never fires because nothing failed. Parking rather than waiting was chosen because the card is the scarce resource: a night that sleeps behind someone else's quota produces nothing, while a night that parks the cloud model still produces a complete local comparison. What the marker can and cannot support is fixed deliberately: crops completed before the refusal is reported as a lower bound on the allowance and never as the allowance, because the window may already have been part spent when the pass started and nothing in the run can see that; and the full-volume projection is computed only where the server sent Retry-After, with the report otherwise saying what would have to be measured. A fabricated window would put an invented number into a feasibility decision — the same refusal D-43 and D-38 make elsewhere. Settled Digitising the 1910 register; utils/lexikon_1910/bakeoff.py, run_lexikon_1910_pilot.py RateLimited
D-48 2026-08-29 The gold sweep is 13 pages, atlas on against atlas off, both documents — and the sealed half is not swept. Every model reads the 8 calibration and 5 purposive pages at SECTIONS="all" under exactly two variants, and the run reports F1, entry_exact and seconds per crop side by side. The 8 certification pages get their own run, from this same config, once the pipeline is frozen. Two variants rather than the six of the crop-geometry matrix, because this asks a different question: not which crop a model reads best, but whether showing a reader the printed Výklad značek plate helps it name glyphs — measured over enough pages that precision, recall and speed are all decidable on the same footing. The certification exclusion is the substantive half. The seal is about what gets looked at, not about who reads it: a threshold, a prompt or a variant chosen in response to a certification number spends the sealed half, and the ±1.8 pp figure stops being independent of the tuning it is supposed to audit. Running it later from the same config keeps both halves read by identical code, which is what D-45's "identical across both halves" requires. Page-set is part of a pass's identity (…baseg against …base), because the same model at the same variant over 4 pages and over 13 are two measurements and one label for both would let the skip-if-done rule accept whichever ran first. Settled Symbol accuracy plan; utils/lexikon_1910/bakeoff.py GOLD_VARIANTS
D-49 2026-08-29 Části osad is extracted part by part, by one splitter shared with the gold reader, and only a bracketed mark is ever returned as a code. lexikon_1910_notes.split_parts cuts an entry into name / mark / houses / population; parts_records keys those to the entry's own municipality, part) numbers. A mark from the closed vocabulary ([ves]) is a code; anything else is kept verbatim in topographic_raw and never coded. The topographic marks beside the part names are a second population of the same atlas, which is much of why D-45 reads this document at all — and nothing extracted them: parts_check sums an entry's figures and discards its names, so the marks were read into the crops and then thrown away. The splitter is shared rather than copied because three of its rules are exactly the ones two copies would drift on, and drift on the entries where the two readings are compared: a comma inside a bilingual name (Hazlov, Haslau 217—2266) must not split the part, the & residual is an entry with no name, and a parenthesised cross-reference ((viz Milevsko 14 a 52)) is part of the name while a parenthesised glyph ((«“■), (Jj;)) is not — separated by length and by the absence of a space, measured on the Úpice sections. The refusal to name a raw mark is D-39 applied one document down: the text layer cannot name a pictogram, and measured over scans 19 and 449–451 it does not — 78.9 % of the 218 named parts carry a mark it returned as Q, ©, » or («“■), and 0 % carry a vocabulary token. A vision pass runs behind guard_tokens and returns the token, so the channel that can be graded against the gold is the image read, exactly as for the institutions. Settled Symbol accuracy plan; utils/lexikon_1910/lexikon_1910_notes.py 0.5.0
D-50 2026-09-03 The party genealogy moves from Wikipedia to a source: Akademický atlas českých dějin plate IV.37, the register of 55 parties of the Czech lands 1861–1918. affiliations.py 2.0.0. pokr is abolished as a party family and its tokens go to nar_dem; volnomyšlenkář becomes its own civic org; rolník → agr is dropped; and three things the plate cannot settle are recorded as refusals rather than coded. The module's own docstring cited Wikipedia for a genealogy that carries an ideological axis in every figure. The register settles it from the print: Č13, Č15 and Č17 are each "1918 spoluzakladatelka Č 23", Č23 is "1919 přejmenována na Národní demokracii", Č7 merged in 1908 into Č15, and Masaryk's realists are Č12 "realisté, 1905 transformace v Č 13". So every pokroková party in the data ends in the National Democracy, and a separate Liberal bin standing apart from nar_dem was an artefact of the Wikipedia reading. It confirmed two calls the project had already made on weaker evidence — lid.pokr. → nar_dem (Č17) and real. → nar_dem (Č12→Č13) — and overturned one. Effect, measured: pol_pokr (869) disappears; 663 of those already carried nar_dem from a stát.pokr. / lid.pokr. string and only lose a spurious second flag, 78 newly gain nar_dem, 42 become volna_myslenka, and the rest fall to the refusals. nar_dem 1,225 → 1,358. Three refusals, recorded in REFUSED and printed by the report so they are visible, not silent: a bare lidová (8 register parties carry it, across Catholic, liberal-national and Masarykite — v1.x had no such token, and that silence was right); a bare demokrat (social democrat, national democrat and státoprávní demokracie all reduce to it); and SL plus every Slovak formation, because plate IV.37 is v českých zemích and contains no Slovak party at all. rolník → agr dropped: it mapped an occupation into a party dummy while dělník in the same field mapped to nothing — an asymmetry with no source, the D-43 error shape. Every token now carries the atlas code(s) that justify it, and pol_atlas_codes takes any coded record back to a row of the printed table. The three gymnastic organisations stay dummies of their own (author's call, 2026-09-03): Sokol as a nationalist proxy, Orel Catholic, DTJ social-democratic — mobilisation currents, not parties, and worth a regressor each in a study of loyalty. The party alignments v1.x claimed for them are dropped with the rest of the Wikipedia genealogy, and the field contradicts the Sokol one: of the 1,323 Sokol members who also name a party, 59 % are national socialist against 18 % national democrat. That spread argues for the proxy rather than against it — Sokol ran across the Czech national parties instead of inside one. Recorded because it is easy to lose: pol_sokol is 26.1 % of every non-empty affiliation cell and the sole entry on 87 % of them, so a specification that reads pol_* as party affiliation without excluding the civic organisations is measuring gymnasts. Settled Parties; reference/parties/atlas_iv37_parties.csv; Data sources #17
D-51 2026-09-07 A tie on the top rung is broken by a written rule, not by word order. 1,512 records name two schools on the same rung; max alone resolved them by which one the clerk wrote first, so obch. + odb. gave commercial on 20 records and trade on 164. education.TIE_BREAK states the order: a generic word (odb., střední) yields to the school the record names, and the terminal professional school (uč.ústav, kadetka, obch. akademie) outranks the general school that feeds it. střední is dropped outright when any rung-3+ school is named (83 records). Ordering the ties is a judgement and had to be written down; leaving it to max meant the same pair of schools got two different answers, and it moved educ_track and educ_complete, whose standard course lengths differ across the tied schools (realschule 7 vs uč.ústav 4). Reading the field as an itinerary is supported: ordinals never fall along the string on 92.4% of multi-rung records. Settled Education ladder; education.py 2.1.0
D-52 2026-09-07 An unrecorded political affiliation is <NA>, not False, and pol_recorded says which. Every canonical in PARTY_ORDER now gets a column whether or not it occurs in the frame. A record that is filled in but matches no token keeps False. Politická příslušnost is blank on 93,377 of 132,993 records (70.2%); False there asserts "not a Sokol" about a source that said nothing — the shape that fabricated exit_N on 26.4k empty cells (audit A3), and the rule provenance.py already states for its own flags. It matters here because missingness on occupation was found on 2026-08-27 to track the outcome. Settled Variables — pol_*; affiliations.py 2.1.0
D-53 2026-09-07 education_complete conflates years attended with classes completed, and is left standing until that is decided. For a bare Volksschule the module scores completion against 8 years (education.py: "the standard here is always the full eight years (Moore p. 30)"). Österreichische Statistik XCIII/1 Table 34 counts allgemeine Volksschulen by ascending classes: 10 eight-class schools in all Cisleithania, 2 in the Czech lands, against 1,114 five-class schools in Böhmen alone. Among 11,986 Czech-lands-born records with education_top == "elementary" and a stated year, 3,894 are scored complete against a form that barely existed and 3,137 reading ob. 5 — the modal full Bohemian Volksschule — are scored incomplete. Moore's eight years are a legal obligation spanning institutions (Volksschule 5 + Bürgerschule 3); Table 34's classes are an institutional form. A bare ob. N does not say which it records, and the column currently resolves that ambiguity by assumption — the "rule narrower than its source" shape (cf. ranks 1.4.0, occupations 1.1.0, education 1.0.0). Not patched on discovery because the fix changes an emitted column and the choice is a judgement, not a bug fix: either (a) set education_complete to <NA> for a bare ob. N, refusing to judge as the vocational branch already does, or (b) re-base the standard on 5, the modal complete Bohemian Volksschule. (a) is recommended — (b) resolves by assumption exactly where the source shows the ambiguity is real. education_ord, education_top, education_track and education_years_elementary are untouched either way. The same check confirms the rest of the ladder, and in particular pok. off the ordinal: the volume files the Fortbildungsschule as its own category and it is 84.0% of Bohemian vocational pupils. Open Education validation; utils/reference_tools/education/run_unterricht_check.py; amends D-08, which stays Open on the Hungarian ladder
D-54 2026-09-17 The register's symbols are read by a cloud flash-tier model, one apparatus section per call, in a call that asks for nothing else. The whole-page schema of notebook 06 keeps the table; it no longer carries the symbols. Supersedes the local-models-only default of the bake-off. Measured on the 280-claim gold: down the Gemini geometries the reading improves every time a call carries less of the page — 0.676 → 0.755 → 0.810 → 0.845 → 0.964 — while the whole-page schema asks for ~10–11k output tokens a page; the size effect is causal for multimodal models in general (Zhang et al. 2025). E1 (2026-09-17) took the model version out of that comparison: with the crops and every flag held fixed and three repeats a model, Gemini 3.7 Flash reads F1 0.964 / entry_exact 0.780 (mean; the 0.830 first quoted was the best of three draws) and 3.8 reads 0.947 / 0.730, against 0.830 / 0.507 for 3.8 on whole pages. Holding the model at 3.8, the view alone is worth +0.117 F1 and +0.224 entry_exact — about seven-eighths of the gap — and the version +0.017 / +0.050. ~~So 3.7 is the reader, with 3.8 as its fallback.~~ Amended 2026-09-22 by the thinking-off arms of E1: with reasoning_effort="none" both versions read the crops at F1 0.962–0.965 (3.8: entry_exact 0.802, the best and least variable; figures as re-decoded 2026-09-23), thinking buys 3.7 nothing (−0.001) and costs 3.8 accuracy (−0.014 F1, −0.071 entry_exact), so the "version effect" was 3.8's thinking. The reader is Gemini Flash, section crops, thinking off — 3.8 by default, 3.7 interchangeable — at $0.33 a 13-page pass; with thinking off on both sides the crops beat the whole page by +0.132 F1 / +0.295 entry_exact. Cost no longer decides anything: ≈ $12 a Čechy apparatus sync, ≈ $6 batch, with thinking off — E1 ran with Gemini's default thinking, which the ledger never counted and which makes it ≈ $72 / $36 on 3.8 (corrected 2026-09-22; accuracy with thinking off was measured the same day and holds). Still Provisional because inside the whole-page cost, crop size, task load and thinking remain confounded (none argues against crops), and because the ranking comes from four purposive pages — the calibration pages (E0) must reproduce it. 2026-09-22: Claude Opus 5.5 was priced for the full pipeline (≈ $160–340 on batch, against ≈ $6 for the Czech apparatus on Gemini) and not pursued; Gemini stays the default, and the second-family reader D-58 needs is to be chosen by a cheap-reader screen on the same crops (plan, E1b). Provisional Symbol accuracy plan — Revision, 2026-09-17
D-55 2026-09-17 The symbol channel is certified by a validation sample and used through debiased estimators (DSL / PPI / MAR-S), not by driving the reader to a fixed accuracy. Agreement between readers — models, repeats, editions — routes a claim and never validates it. The validation draw may lean on reader disagreement, with known probabilities and a floor, and never on a covariate. For the symbols it samples entries across many pages rather than whole pages. Per-glyph sensitivity and specificity are reported beside entry_exact. A criterion measurement exists — a person at 399 DPI settles every glyph, and the mark is caused by the institution — which is the case where Dell & Rambachan (2026) put the validation-sample design first. Reader agreement cannot stand in for it: errors are strongly correlated across models and more so across prompts of one model (3–20× the independence critical value in their replications; Kim et al. 2025), and five Gemini repeats voted 3-of-5 gain only ~+0.04 F1 over the average run. With a validation sample, reader error costs precision rather than bias, roughly in proportion to the error rate, so a reader improvement is priced in transcription hours instead of chased to 99 %. Eight certification pages are eight clusters, and ±1.8 pp treats ~560 claims as independent. Sampling on disagreement with inverse-probability weights (Zrnic & Candès 2024) is the rigorous form of the plan's rule against population priors. Amends Stages 3 and 5 of the plan, and D-45 / D-48 for the symbol channel only — the table keeps whole-page reading, and the certification seal is unchanged. Provisional Symbol accuracy plan — Revision, 2026-09-17
D-56 2026-09-22 Every accuracy figure for the register's symbols is computed at the unit the analysis uses — (entry, settlement, symbol) with the printed count — beside the municipality figure, and the decoder's part-marker defect is fixed before any new model run. Intervals are clustered by page; per-glyph sensitivity and specificity are reported for every institution that enters a regression; entry_exact stops being the headline. The scorer's key was (page, district, muni_seq, code), so no pass rate could see which part of a municipality a symbol was filed under. Re-scored at settlement level the best reader falls from 0.964 to 0.856 F1 (3.8: 0.947 → 0.842), and the cause is the decoder, not the model: Gemini returns 1, 1) … — 2) [fire-brigade]. — 3) [doctor], … correctly, but _ENTRY_KEY_RE (lexikon_1910_institutions.py:70) recognises only m, n) and never splits the — n) continuation, so 35 of 273 correctly read claims (12.8 %) land on the first part — the "rule narrower than its source" shape. entry_exact is dropped as headline because per-page it runs from 0.00 to 0.93 and the zero is one 27-claim town entry weighted like a hamlet. Decoder half done 2026-09-23 (lexikon_1910_institutions 0.5.0): — n) continuations split into their own entries (fitted to 733 in the volume's text layer), one inventory row per part, one part per joined marker; replayed from cache, settlement F1 rises 0.854 → 0.915 for the default reader with municipality-level claims unchanged. Verifying it exposed a second silent defect — the pilot joined a tall section's strips in the order they finished, not the order they were cut — fixed the same day, after which two replays of all twelve E1 passes are byte-identical. Still open: the main scorer's key, the page-clustered intervals and per-glyph rates, and ~0.05 of part attribution not yet diagnosed. Closed 2026-10-05: the scorer by D-63, the residue by D-64 — it was the decoder again, a part that opens its own printed line. Provisional Symbol accuracy plan — Revision, 2026-09-22
D-57 2026-09-22 The gold is split by purpose. Symbol validation is a blind, entry-level random sample with known probabilities (D-55); D-45's whole-page protocol is kept for the pages where the table's arithmetic is graded; exemplars and cluster labels may be labelled with the model's reading shown. Minutes are logged per layer on every sheet, and the symbol sections of the calibration pages are finished before their text abbreviations. E0 has stalled: three weeks after D-45, two of eight calibration sheets are partly read and minutes: is empty everywhere, so its 48–74 h budget is still a guess — and most of the reading time goes on text abbreviations the symbol channel does not use. The anchoring literature (Berzak et al. 2016; Mikulová et al. 2023) shows pre-annotation speeds labelling and pulls it towards the machine's errors, which matters only where the labels measure a reader; so only the validation sample must stay blind. Amends D-45 (its scope, not its reasons, which stand for the table) and extends D-55. Amended 2026-10-05 by D-67: the German edition gets its own gold, and the notes are read before the tables. Provisional Symbol accuracy plan — Revision, 2026-09-22
D-58 2026-09-22 The German edition is read only once its sections can be cut from the image, by a different model family from the Czech reader, and it serves as a second measurement in the Kane–Rouse–Staiger / Black–Berger–Scott sense only if the conditional-independence test on the gold passes. E1 showed the section crop is worth about +0.12 F1, and the German volume has no ALTO, so the crop the Czech edition gets for free has to come from image-side heading detection first. The two-measure identification of a misclassified binary regressor needs errors independent given the truth; one model reading both editions shares its glyph confusions (E1: identical in 3.7 and 3.8), so a different family is the minimum, and the independence is tested rather than assumed (Dell & Rambachan's permutation check). Provisional; for Bohemia's final inventories superseded by D-129 (2026-10-25) — the readers of record are still tested Symbol accuracy plan — Revision, 2026-09-22
D-59 2026-09-22 A linked pair must match on the surname and on the given name separately, on a phonetic key; the match is one-to-one with exact ties refused; two full dates of birth that disagree refuse the pair; and the note criterion counts only a note that names the Legion. Surname ≥ 90 (≥ 85 when the birth okres agrees as well), given name ≥ 80, blocking on birth year and phonetic initial. note_legion is explicit Legion vocabulary (legionář, legie, KLEG, Kancelář legií, čs. dobrovolník, rota Nazdar, Česká družina, the twelve Russian rifle regiments); the register unit the note usually names moves to note_register_ref. Probable duplicate persons inside one file are flagged in dup_group, never merged. Amends D-01. The old test scored "surname given" as one string at ≥ 80, so a shared given name could carry two different surnames — 389 of the 2,239 pairs had a phonetic surname similarity below 60 (BERAN ↔ Bureš) — and 111 VÚA records were claimed by two to four legionnaires. Of the 11,042 "legion notes", 10,509 record which interwar Czechoslovak regiment keeps the death register (median death 1915; 1,246 died in Serbia in 1914), and a record carrying one matched a legionnaire less often than a record with no note (2.9 % vs 4.8 %). The thresholds were set against pairs that are wrong by construction: exact date of birth, same given name, surnames with different initials. D-01's principle — precision first — stands; its criteria were not what they claimed to be. Effect: 2,239 → 1,340 confirmed overlaps (1,776 → 1,201 DOB-confirmed, 463 → 139 note-confirmed); 0 VÚA records linked twice. Settled Audits A1–A3; linkage.py 1.3.0, habsburg.py 1.2.0
D-60 2026-09-22 A US application city imputes France, not Britain/Dominions, and the Polish Legion is not a theatre signal. Pass 3 of theater maps the American application places to France; Polská no longer sends a record to Serbia/Balkans; Armée polonaise → France. Of US applicants whose own unit names a theatre, 1,659 served in France and 26 in a British or Dominion unit (98.4 %). The Polish-Legion units are Haller's army (France) and Russian formations (Dutova armáda, Krymský pluk). Fidler's check (2026-07-20) covered CS-Legion records only, so neither imputation had been tested. Effect: US applicants in France 1,659 → 1,911; Britain/Dominions 681 → 433 records in all; 40 of 41 Polish-Legion records leave Serbia/Balkans. Settled Audits A5; theater.py 1.1.0
D-61 2026-09-22 A date stated only to the month or the year is kept at that precision, not dropped, and the exit date is range-checked like every other date. capture_date, enroll_date and exit_date fall back to the 1st of the month or 1 January and say so in *_date_precision (day / month / year); the exit date is read from the text before its parenthesis and must fall in 1914-01-01 … 1939-12-31. Birth and death dates are unchanged. Dropping a partial date keeps only the records a clerk dated fully; imputing a day would fabricate precision — so the value is kept and the column states which it is, and a timing analysis filters on it (notebook 03's monthly plots do). The exit date is the hazard model's event time and ran from 1818 to 2192, with 3,930 strings that state a year given no date at all. Effect: exit_date 101,700 → 105,527 (3,865 month- or year-precise), deserters with an exit date 3,631 → 3,697, capture_date +1,441, enroll_date +2,026. Settled Audits A8, B15; dates.py 1.2.0, exit_codes.py 1.2.0
D-62 2026-09-22 D-52's rule covers every flag read off a field that can be blank: the VÚA cause_* flags and in_captivity, habsburg_rank_jd / legion_rank_jd and legion_noncombat are <NA> where the source cell is empty. The same shape as the fabricated exit_N (first audit, A3) and the pol_* dummies (D-52): False asserted not killed in action about 8,297 VÚA records whose cause is blank. Effect: 8,297 (cause_*), 34,337 (habsburg_rank_jd) and 19,270 (legion_noncombat) cells move from False to <NA>; no True value changes. Settled Audits B17; habsburg.py 1.2.0, ranks.py 1.4.3
D-63 2026-10-05 The symbol scorer keys on (entry, part, symbol) and prints the municipality figure beside it; the printed count, per-glyph sensitivity and specificity, a page-clustered interval and the list of institutions filed under the wrong part are reported with it. score_against_reference keeps every earlier key unchanged and sorts on the settlement F1. A rate with an empty denominator is blank, not zero. Closes the scorer half of D-56. The settlement figure lived in the E1 runner as a local helper, so every other report still ranked passes on a unit no analysis uses. Presence stays the headline and the count is scored beside it, because two schools read as one is a smaller error than a school read as a chapel. Two limits are printed with the numbers rather than hidden: specificity is a lower bound, since an inventory has no row for a part with nothing printed, and the page interval rests on four pages. Effect: none on any earlier figure — re-scoring the cached passes reproduces the default reader's 0.898 / 0.918 / 0.930 (mean 0.915). Settled lexikon_1910_institutions.py 0.6.0 (score_against_reference, symbol_rates, clustered_interval, part_attribution); utils/lexikon_1910/rescore_passes.py; tests/test_lexikon_1910_scoring.py
D-64 2026-10-05 A part that opens its own printed line is a part: the bare 2) under the 1) splits the entry exactly as — 2) does, provided its number rises above the part before it. Up to two specks before the number are tolerated; a dash is the other printed form and already splits; an asterisk is not accepted. A number that does not rise is folded in unmarked, as before. Closes the "~0.05 not yet diagnosed" of D-56. Read off the image (scan 0118, Jirkov 13): the register prints the dash only when a part runs on inside a line, and sets a part that starts a new line with no dash at all. _PART_CONT_RE was fitted to the text layer's dashed form alone, so the reader's faithful 2) … line was folded into the part above — the "rule narrower than its source" shape a second time. 207 of the 211 misfiled institutions in the twelve E1 passes sat in three entries (Jirkov 13, Trutnov 23 and 29). Effect: on the gold D-56 was measured on, the default reader's settlement F1 goes 0.915 → 0.961 and now equals its municipality figure; thinking-off passes misfile nothing. Still open: Gemini 3.8 with thinking on writes the ink speck beside Jirkov 13's marker as *2), which is refused, so that arm keeps 11 misfiled institutions a pass. Measured by replay on a machine whose crops hash differently (D-68): 26 of 81 cached answers were matched by position, and the old decoder then reproduced all twelve stored inventories byte for byte. Settled lexikon_1910_institutions.py 0.7.0 (_LINE_PART_RE, _open_line_part); tests/test_lexikon_1910_parts.py; output/lexikon_1910_pilot/bakeoff/rescore_r3_part_attribution.csv (what is still misfiled; the 211 were counted on the same passes before they were re-decoded)
D-65 2026-10-05 The scored gold is the whole-page sheets, not the August free-form worksheet; where the two readings differ the legend plate decides, by eye; scan 0449 stays out of the scored gold. inventory_human.csv is written by read_transcription_md.py --write from transcription_gold/cz/purposive/; the first reading keeps its own file, inventory_human_freeform.csv. Both scripts wrote the one filename, so whichever ran last was silently the gold — and it was still the first reading (286 claims, 47 entries) a month after the sheets were finished. The two readings of the same 47 entries agree at settlement F1 0.977: the gold's own noise was the size of the reader's remaining error. Seven claims were settled against the plate on 2026-10-05 — the arch-and-cross with A is [ev-preacher-ab] (Jirkov 9 and 13, Česká Skalice 3), the circle-and-cross with A is [ev-parish-ab] (Jirkov 12), round brackets are [rc-parish-exp] (Jirkov 9), and a fire brigade is printed at Jirkov 14 and Most 30 — which corrected four cells of the sheets and showed that the reader's "missed [rc-vicariate]" had been the first reading's error. Scan 0449 is FEWSHOT_PAGE, the whole-page reader's worked example; scoring it would grade a model against its own prompt. Effect: the gold is 372 claims over 61 entries on the same four pages; against it the default reader's settlement F1 is 0.958 (0.961 / 0.955 / 0.958). Hazard left standing: --role calibration --write writes the same filenames and would replace this gold. Settled utils/lexikon_1910/read_transcription_md.py (write), check_transcription.py (OUT); output/lexikon_1910_pilot/bakeoff/e1_gemini_version.txt
D-66 2026-10-05 A section heading whose word okresu the text layer damaged still opens its section. The heading's tail accepts any short word ending in su where okres used to be required; the marks rule that tells a parts heading from an offices heading is unchanged and remains the guard. A missed heading does not lose its lines, it files them under the section before. On scan 0451 Části osad v soudním otřesu Trutnov was carried into Trutnov's offices section from scan 0450, the reader was given the offices vocabulary for a parts section, and thirteen place-type symbols a pass were filed as institutions — about 12 of the default reader's ~20 false claims. 34 of the volume's 1,238 headings are damaged this way (26 parts, 8 offices), otřesu in most of them. Over all 99,240 text-layer lines of scans 15–494 the new rule gains those 34, changes none, loses none and claims no line of an entry. Measured 2026-10-06 on the r4 baseline, read fresh on Heavyhorse6: no place-type symbol is filed as an institution any more, the default reader's false claims fall from 20.3 to 10.3 a pass, scan 0451 goes from 0.905 to 0.955–0.964 and settlement F1 from 0.958 to 0.972 — fresh readings, so part of the step is the reader's own run-to-run variation. Settled lexikon_1910_notes.py 0.5.1 (HEADING_TAIL_RE); tests/test_lexikon_1910_headings.py
D-67 2026-10-05 The German edition gets its own gold — table and notes — matched to the Czech by court district and municipality name, never by page; and from here the notes are transcribed before the tables. All human reading lives in reference/places/transcription_gold/: cz/ and de/ by role, _superseded/ for earlier layouts. The first German sheets are Gerichtsbezirk Trautenau (scans 0478–0480), whose Czech side (0449–0451) is already complete. Amends D-45, D-48 and D-57. Mähren and Schlesien exist only in German, so the German reader has to be certified on its own and cannot borrow the Czech gold. Each edition orders a district's municipalities alphabetically in its own language: the municipalities of one Czech page fall on two or three German scans, and 7 of the 21 gold pages have only a weak or ambiguous German twin in the page map. A whole page takes 40–60 minutes to transcribe; the Czech table is already read on eight pages and checks itself through its sums, while every institution that will enter a regression is in the notes, which have no self-check. The German sheets are blank — the UBW scan has no text layer fit to pre-fill from — and reading them back needs a name-based join that is not built. Provisional reference/places/transcription_gold/README.md; make_gold_worksheet_de.py, gold_sample_pages_de.csv
D-68 2026-10-05 Every paid pass and every replay of the register runs on one machine, Heavyhorse6, with its package versions recorded. The crop cache is keyed on the crop's PNG bytes, and those are not the same everywhere: on a second PC (PyMuPDF 1.28.2, Pillow 11.3.0) 26 of 81 crops came out with different pixels, so a third of the cached answers were not found and a replay would have tried to buy them again. openai, which the pilot's reader imports even to replay from cache, was not in requirements.txt until this date. A cache that only one environment can read is a result that only one environment can reproduce. Settled requirements.txt; utils/lexikon_1910/bakeoff.py (HOST, PASS_DIR); D-64 for the workaround
D-69 2026-10-05 E3's second method is nearest-neighbour classification of each boxed glyph on a self-supervised image encoder — DINOv3, with DINOv2 and a small trained classifier as the comparison — and its answer routes a glyph to a second look, never overwrites the reader's. The exemplar bank is cut from entries on calibration pages at printed size, never from the legend plate and never from a page the method is scored on. The plan asked for "a second method, not only a second model" (2026-09-17) because shared architecture is what correlates readers' errors, and what the default reader still gets wrong is the question an image encoder answers: which of a closed set of 88 printed symbols a mark is ([castle] 4 of 15, [ruin] 2 of 12 over three repeats). It runs locally on Heavyhorse6, so it is free and gives the same glyph the same answer every time (D-68). It reads no text, so the school's class numeral and the cooperative's letters stay with the language model. Not built, and two things could sink it: nothing yet produces a box per glyph, and a glyph is a few dozen pixels of line art, far from what these encoders were trained on — hence the cheaper baselines beside it. The licence of the DINOv3 weights has not been read for this use. Provisional Symbol accuracy plan — Revision, 2026-10-05, E3's second method
D-70 2026-10-06 What the register prints beside a glyph is data, read by one parser for the reader and for the gold, and a pass writes it out: a school's count, classes and language; a cooperative's kinds with their count and language; and the named parts of Části osad with their place-type symbol. The numeral after a school is its classes, the number before the glyph its count, and the two are never summed. A school with no language note has a blank language, not "Czech". A cooperative's c is read as č with its háček lost, and the raw letter is kept. A token the reader wraps in the glyph's own parentheses — ([cottage-group]) — is that token. The variables a regression will use live here and nothing scored them: the symbol scorer sees [school] and stops before (1 č., 4 něm.) XXVI. One parser for both sides is the rule split_parts already follows, for the same reason — a difference must be a difference in what was transcribed. The parentheses rule is read off the legend plate, which prints skupina chalup and kolonie inside parentheses; without it, and with a full stop taken for a comma, 22 of 175 named parts looked missed that had been read. Measured on the six r4 passes: every school found with count, classes and language right (56 of 56, and 5 of 5 burgher schools); cooperative kinds 55 of 55 with count and language right in five passes, 50 in the sixth, which skipped a printed line; named parts 175 of 175 in five passes and 172 in the sixth (three & written [?]), houses and population exact on every one; place-type symbols 0.92–0.93 for the default reader and 0.96–0.97 for 3.7. Two cells of the gold were corrected on the author's ruling the same day, both found by this scoring and both checked against the print: Česká Skalice 3 has 2 Sch, not Sch, and the & of Trutnov 30 part 2 is one square, [single-house], not [scattered]; the figures here are against the corrected sheets. Ruled the other way, the sheet standing: myslivna at Jirkov 13 and 15 and Trutnov 17 (both versions write [gamekeeper] in every pass — the solid block is printed with gaps in it), město at Pilnikov, and the cottage-groups of Trutnov 23 and 29. The residual written as its atlas token [besides] is the &, and a speck the print leaves after a mark is not part of the name (0.6.1) — with the parentheses and the full stop, four ways a faithful reading had been counted as a missed part. Blind to: how a cooperative's German mark is read — the scored pages hold 16 kinds marked Czech and none marked German, and only 3 schools carry a language note, all three mixed (č. a něm.). Settled utils/lexikon_1910/lexikon_1910_attributes.py 0.1.0; lexikon_1910_notes.py 0.6.1 (split_parts, score_parts); utils/lexikon_1910/rescore_passes.py 1.2.0; Symbol accuracy plan — Revision, 2026-10-06, Step 2
D-71 2026-10-06 A second reader is chosen on which of the first reader's errors it disagrees on, not on its own accuracy; and the bar is what a second Gemini pass already catches. For a pair of readers against the gold: caught (errors of the first the second disagrees on — they reach review), shared (errors both make — invisible to the pair, and what a perfect review still leaves), alarms (claims the first had right and the second did not). The candidate has to beat a catch rate of 0.30 at no more than about eight alarms a pass. The second reader routes and never overwrites (D-38, D-55), so an accurate reader that repeats Gemini's mistakes adds nothing and a weaker one that errs elsewhere adds a great deal; ranking candidates by F1 would pick the wrong one. Measured on the six r4 passes, nothing sent: the default reader makes 20.7 errors a pass on the four gold pages; another repeat of itself catches 5.3 of them (26 %) and Gemini 3.7 catches 6.2 (30 %), leaving 14–15 that no Gemini pair can see; F1 0.972 becomes 0.979–0.981 after a perfect review. The conditional-independence test of D-58 fails inside the family as predicted: of 372 gold claims the two versions each miss 9–12 and miss the same 4–9 where 0.3 is expected (Fisher's exact test, one-sided, p < 0.0001 on each of nine pairs), and 8–9 of the 10–12 false claims each makes are the same ones. Blind to: the rate rests on about twenty errors on four pages; the test treats claims on one page as independent, which makes p too small rather than too large; false claims have no such test until the universe of possible claims is supplied. Open: which reader — the screen is written and has not been run. By the author's condition it starts with the two readers that need no new account or billing: Claude Haiku 4.5 on the Anthropic key on file, and qwen3.8 on the local daemon; Qwen3-VL, MiniMax M3 and Kimi K2.5 through OpenRouter are on file and off by default, to be tried only if neither clears the bar. Run 2026-10-08 on Claude Haiku 5.5, not on the two named here — see D-72. Settled (the criterion); the choice of reader is open utils/lexikon_1910/lexikon_1910_institutions.py 0.8.0 (second_reader); utils/lexikon_1910/run_e1b_second_reader.py 1.0.0; notebooks/08_lexikon_1910_second_reader.ipynb; Symbol accuracy plan — Revision, 2026-10-06, Step 3 prepared
D-72 2026-10-08 The register is read by two readers of different families and by an image encoder over the whole volume; a claim they do not agree on goes to one costly verify call and then to the author — and the routing is fixed code, never a model deciding in the loop. Gemini 3.8 Flash with thinking off stays the reader of record. Claude Haiku 5.5, thinking at high effort with the prompt's note (haiku55nh), is the second reader, provisionally. DINOv3 nearest-neighbour (D-69) names every boxed glyph, not only the disputed ones. Any disagreement among the three goes to Claude Opus 5.5 — the glyph enlarged, a closed choice, beside page-cut exemplars — and what that leaves open goes to the author by eye. Local language models re-reading the disputed sections are not the arbiter. Two tests come before anything is built: (A) whether DINOv3 or DINOv2 separates the fill pairs at all, on hand-cut glyphs, each page classified from a bank that leaves it out; (B) whether the Opus verify call settles the gold's disputed claims better than always siding with Gemini. Amends D-71 (the choice of reader, and the alarm half of its bar). The screen, run 2026-10-08 on Heavyhorse6 (twelve Haiku 5.5 passes, the 81 r4 crops, ≈ $1.0–1.4): as second reader to the default one, haiku55nh catches 18.0 of its 20.7 errors a pass (87 %) and shares 2.7, where a second Gemini catches 30 % and shares 14; F1 after a perfect review 0.996 against 0.981. It raises 24.3 alarms a pass against the eight D-71 allowed, so one claim in nine is disputed — 11.1 % counted on all 13 pages (10.1 % on the four gold pages, 11.7 % on the nine calibration ones), roughly 5,100 of 46,000 claims over the Czech apparatus — and in a dispute Gemini is right 57 % of the time. That is too many for the author alone and too few to refuse a costly model: a whole volume on Opus was priced at $160–340 and dropped, 5,100 small calls are ≈ $30–90 on batch. The encoder reads everything because it alone can reach the errors both readers share, it is free, and it is deterministic (D-68). Routing by code because a model choosing its own next call is a degree of freedom inside the measurement and cannot be replayed; every stage routes and none overwrites (D-38, D-55). Local models were weighed and set aside as arbiter: best pass 0.872 under the old decoder and gold, none read under r4; Ollama at temperature 0 repeats itself; and the residue is fill pairs, the blind spot of the encoders those models read through — qwen3.8 stays in test B because it is free. Departures from D-71's plan, recorded: the screen ran on Haiku 5.5, not on Haiku 4.5 and qwen3.8, and ahead of the calibration transcription, so on four pages. Blind to: about 21 errors on four purposive pages, with the alarms running 17–29 between repeats; Haiku 5.5 cannot be read at temperature 0; haiku55h (the effort without the note) is unrun; Opus has never read these crops and is Haiku's family, so it may lean to Haiku rather than arbitrate; nothing produces a box per glyph, and boxes taken from Gemini's output cannot show the encoder a glyph Gemini missed; the alarms a third signal adds are unmeasured; the volume figures are scaled by crop count. Nothing from the encoder on is built. Provisional Symbol accuracy plan — Revision, 2026-10-08; output/lexikon_1910_pilot/bakeoff/e1b_second_reader_r4.txt; utils/lexikon_1910/run_e1b_second_reader.py 1.2.0
D-73 2026-10-13 The printed order of the marks inside an offices entry is a routing signal: a mark standing where the print never sets it is sent to review, and an order that begins again inside one part is reported as a missed boundary, not as a symbol. The order is learned from the gold sheets, never typed in and never taken from a model; the gendarme, canteen and transport-station marks are never judged; and the check changes no claim. Pairwise precedence over the bracketed symbols, from every filled offices line of the purposive and calibration sheets (the certification sheets are refused in code). A pair is reversed only if the sheets print it the other way at least twice and never this way. Two verdicts: out_of_place, the smallest set of marks whose removal leaves no reversed pair; order_restarts, where every reversed pair runs across one point and at least two marks are out of place. A reading of a gold page is judged on the order of the other pages. The order restarts at each numbered part and at a named part (Valdice:); text in parentheses is left out. The volume states the order by example (Návod p. VIII, the entry of Bílina), so a mark out of it is evidence that costs no model call, is deterministic, and — unlike the second reader — does not need the two readers to disagree. Learned rather than typed because the worked example names a third of the atlas and the sheets show the rest; a pair the sheets never show together is not judged. Measured 2026-10-13, nothing sent: on ten sheets, 182 runs and 818 symbols, 72 of 3,478 pair observations run against the majority direction and all 72 involve the gendarme block, which has two printed positions (after the offices at a court seat, after the fire brigade elsewhere); without it, 0 of 2,902. Before the sheets were corrected it was 9 of 2,863, all from two calibration entries — Jičín 19 (scan 0136) and Nusle 4, part 2 (scan 0196, a [poorhouse] after the telephone that is a [castle]) — and the author, ruling by eye the same day, confirmed both as slips of the sheet. On the stored r4 passes against the 372-claim gold it flags 1 of the 10–12 false claims of each of the six Gemini passes (the same one every time: a castle read as a poorhouse at Trutnov 23, part 5) and 1 / 5 / 4 of the 9 / 13 / 14 of haiku55nh, with 0 of about 360 true claims flagged for Gemini and 1 in one Haiku repeat. Of the 42.3 claims a pair of Gemini 3.8 and haiku55nh passes disputes it flags 3.7, all false; over the 105 pairs among the fifteen passes that read at 0.94 or better, 262 flagged and 262 false. It flags 1.0 of the 8–9 false claims two Gemini passes agree on, which no pair of readers can see. On the nine pages outside the scored gold a Gemini pass has 11–14 [poorhouse] marks flagged at the end of a line, and wherever a calibration sheet transcribes the part (6–8 a pass) it prints [castle]. Two adjacent gold parts run together show as a mark out of place in 28 of 35 joins, 7 of them called a restart. Blind to: a same-slot substitution — station for stop, revenue office for customs, one church for another, a savings bank before the cooperative — which is most of what the readers still get wrong; a missing mark; which of two marks in a single reversed pair is the wrong one (the later is named, both are shown); the gendarme block; a one-mark part run on from below, which reads as that mark; two neighbouring misread marks, which read as a restart (Jirkov 9 in two thinking-off Haiku passes); a pair seen fewer than twice in a reference of ten pages, single-coded outside the scored four; the German edition. A stored pass keeps no entry text, so a named part that opens with words is not cut there (Aš 1, called a restart in 13 of 18 passes). Not done: the order is not put in the reader's prompt — an open arm of E2, needing a paid pass on Heavyhorse6 (D-68), with the risk that a stated order encourages the phantom savings bank the reader already writes before a cooperative; and nothing in the reading pipeline or in the route of D-72 calls the check yet. Settled (the check); its place in the route is open utils/lexikon_1910/lexikon_1910_institutions.py 0.9.0 (learn_order, order_runs, out_of_order, order_restart, check_order); utils/lexikon_1910/read_transcription_md.py (read_order_reference); utils/lexikon_1910/rescore_passes.py 1.3.0; tests/test_lexikon_1910_order.py; output/lexikon_1910_pilot/bakeoff/rescore_r4_order*.csv; Symbol accuracy plan — Revision, 2026-10-13
D-74 2026-10-13 The readers read without the order; the order acts after the read and before the readers' disagreement; and a mark it flags is fixed by a re-read of that glyph, never by rule. The route, in the author's order: (1) both readers read every crop blind — the order is not stated in the prompt, in whole or as a position cue for a single pair; (2) the order check of D-73 runs over each reader's output: a mark out_of_place gets one verify call — the glyph enlarged, a closed choice among the marks the sheets print in that position and the token the reader wrote — and an order_restarts goes to the parsing queue, not to a call; (3) what the two readers still disagree on goes to the verify call of D-72; (4) what the calls leave open goes to the author by eye, who reads the disputes himself if they number few enough. Amends D-72 (the order stands ahead of disagreement at its stage 3; the encoder of its stage 2 is untouched and still waits on test A) and closes what D-73 left open (the check's place in the route; the prompt arm of E2, which is not run). The order is an invariant the reader is graded on, and D-38 already rules that a reader is never shown one and that a failing block is re-read, never repaired. Stated in the prompt it would stop being evidence: a reader told the order writes to it where it has misread, may add the mark it expects (it already writes a [savings-bank] before a [cooperative] that has none) and may regularise the gendarme block, which has two printed positions. Read blind, the order is the one signal here that depends on neither reader and not on their disagreeing (D-73, finding 6). A re-read and not a rewrite because the order says a mark is misplaced, not what it is: a rule exists for one pair only — a [poorhouse] standing last in its line is a [castle] on every part the calibration sheets cover, 6–8 a Gemini pass, on single-coded sheets — and a claim changed without the image is the repair D-38 and D-55 refuse. What reaches each step, from the r4 figures of D-72 and D-73: a Gemini pass has 17–21 marks flagged over the 13 pages, 11–14 of them that [poorhouse]; scaled by crops (2,991 in the Czech apparatus against 81) that is roughly 630–780 flagged marks a reader over the volume. Of the 42.3 claims a pair of Gemini 3.8 and haiku55nh passes disputes on the gold the order flags 3.7, all false, so it takes about a tenth of the ≈ 5,100 disputes before step 3; what is left is more than the author reads by eye unless the verify call thins it. Blind to: the order-triggered call has never been run and joins test B of D-72, where its bar for the castle is the rule's own record; nothing produces a box per glyph, so its first crop is the printed lines of the entry; the choice it is offered is learned from ten pages; where one reversed pair does not say which of two marks is wrong, both are re-read; the volume figures are scaled from 13 pages, four of them purposive, and counted for Gemini only; and everything the order cannot see (D-73) still reaches step 3 or passes it. Settled (the order of the route); the re-read is designed, not built or measured Symbol accuracy plan — Revision, 2026-10-13, The order's place in the route; amends reference/SYMBOL_ACCURACY_PLAN.md — Revision, 2026-10-08, The route for a disputed claim
D-75 2026-10-13 A section crop runs to the foot of its column: the last segment of each apparatus column ends at the divider rule's foot, not at the last line the text layer recognised. The foot is the one notes_boxes already takes from the printed rule and is where the footer band opens, so the postal legend stays out; an earlier segment still ends where the next heading begins. section_segments(..., extend_to_foot=False) keeps the geometry before 0.7.0, for measuring the defect and for replaying a pass cached under it. The extension is unconditional — a column is extended whether or not anything is printed in the gap — because the function is handed the text layer and no image, and the text layer is the channel that failed. A segment's box was built from the lines the ALTO clustered, and a short line that is only a key and a pictogram is the line the engine drops. Such a line was then outside the reader's crop and outside the gold crop alike, so the two agreed on its absence and no comparison of them could show it; it was found collating the calibration sheets against the print (scans 0076 and 0316). Measured 2026-10-13, no model called (measure_segment_foot.py, which prints every figure in this row): over table scans 15–494, 908 columns on 454 pages; the gap between the old bottom and the column foot is 0 or less on 574, 1–6 px on 265, 7–17 px on 46 and 18 px or more — enough to hold a line — on 23. All 23 were read by eye off the strip sheet (by Claude; the verdicts are the dated table EYE in the script, and the sheet is there for the author to rule on): 13 columns on 13 pages, every one an offices section, 10 right and 3 left, had print below the old crop — 12 whole entries, each a key and the same single mark as scan 0076's 22, 3), and 2 wrapped tails (scans 0368 and 0485). The loss was therefore not spread over the atlas: it fell on one mark. The automatic screen — a band at least 12 rows tall carrying at least 70 pixels darker than 95, set from scans 0076 (114 px) and 0316 (145 and 172 px) and not moved afterwards — caught 10 of the 13 and raised one false alarm (scan 0165); the three it missed print at 50–65 px, so the count of entries is the eye's. Cost: the crop cache keys on the crop's bytes, so any gap above zero moves the crop, print or none — 334 columns on 260 pages — and 6 of the 81 crops of a gold pass (scans 0076, 0316, 0376, 0118 and 0449; 9 of 138 for the half variants) match no cached entry and are read again when that pass is replayed. Nothing was re-read. The gold crops of scans 0076 and 0316 were re-cut; no sheet was changed. Of the three entries, the two on scan 0316 (35, —) and 36, —)) were already in its sheet, read off the page; only scan 0076's 22, 3) is still to be transcribed. Blind to: print below the divider's own foot, which lies in the footer band (a recognised line ends 11 px below it on scan 0076 and 6 px on scan 0316); a line dropped between two recognised ones, which is inside the crop under either geometry; a gap under 18 px, not read by eye; the certification pages, whose four changed crops (1–4 px) the screen passes and nobody looked at; scans 495–756, which no pass reads; the German edition. Settled (the geometry); the affected crops are not yet re-read utils/lexikon_1910/lexikon_1910_notes.py 0.7.0 (section_segments); tests/test_lexikon_1910_segment_foot.py; utils/lexikon_1910/_archive/measure_segment_foot.py; output/lexikon_1910_pilot/segment_foot_gap.csv and segment_foot_gap_strips.png
D-76 2026-10-14 The apparatus crops tile each column: nothing printed between the table's foot and the divider's foot lies outside every crop. lexikon_1910_notes 0.8.0 does three things. A divider the body band hid is found by a fallback (fallback_divider: the central rule whose foot is lowest on the sheet); the band opens at the foot of the table's own column rules (table_foot) rather than at the body band's percentile foot; and every section segment runs to where the next one begins, the first from the top of the column (tile=True), with D-75's last-segment rule kept. fallback=False, band_from_table=False and tile=False give the 0.7.0 geometry for replay. A read-only coverage audit (2026-10-14) found print no crop held. Measured over table scans 15–494, no model called (measure_apparatus_coverage.py): 22 pages whose apparatus had never been read at all are recovered — 1,175 text-layer lines, 470 of them opening with an entry key, 68 headings; the band opens lower on 227 of 454 pages (median 32 px), and the 318 lines it moves out are table rows — none opens with an entry key or is a heading — so the crops made of table rows and filed as a carried offices section fall from 51 to 5 (the five left are specks above a heading); and 25 printed lines on 18 pages are newly inside a crop, among them Votice 13, 3) and 13, 4) on scan 0386. The D-75 measurement reproduces byte for byte with the flags pinned. Blind to: a heading the text layer misses entirely — its section is still filed under the previous section's kind and court; the screen found 13 candidates, which read by eye are 7 missed headings (scans 0090 0141 0190 0241 0304 0384 0456, every one a Části osad heading) and 6 missed first entry lines that tiling now covers; the four statutory-city prose pages (D-82); the German edition. 48 of the 84 gold-pass crops change (D-81). Settled (the geometry); the 7 headings ruled by the author 2026-10-19 — every one a Části osad heading naming the court as read (Falknov, Sobotka, Kralovice, Litomyšl, Pacov, Sedlec, Veselí nad Lužnicí; 0304 had been filed under Přelouč), reference/places/heading_rulings.csv; not re-read utils/lexikon_1910/lexikon_1910_notes.py 0.8.0; tests/test_lexikon_1910_apparatus_coverage.py; utils/lexikon_1910/_archive/measure_apparatus_coverage.py → output/lexikon_1910_pilot/apparatus_coverage.csv and apparatus_coverage_heading_candidates.png
D-77 2026-10-14 The table's rows end where the apparatus opens. lexikon_1910 0.2.0 ends extract_rows at table_bottom, the top of the apparatus band from lexikon_1910_notes.apparatus_band, so table and apparatus split the page on one line; a column rule that runs past the divider's top is not counted, which keeps what the body band's percentile protected. body_band is unchanged for its other callers; foot_from_rules=False / --table-foot band is the old ending. body_band ended the table at the 15th percentile of its rules' feet, which is right for clustering rows and wrong as the table's end: on 452 of 476 scans with an apparatus it sat above the rules' foot (median 16 px). Measured, no model (measure_table_foot.py): 137 rows gained on 49 pages and 26 rows the old foot cut through read whole (scan 0196 Hrnčíře and Šeberov, 0023 Tisem, 0083 Nové Záboří); on the 22 pages of D-76, 551 apparatus lines are no longer read as table rows; I4 pass +207, fail −59, unreadable −754; I5 fail −98; I1–I3 pass +115 / +104 / +89. The purposive gold tables match row for row before and after. Sealed scans 0106, 0166, 0466 gain 13, 1 and 1 rows, counted and not opened. Blind to: channel B grades I4 one page at a time, so a block continued on the next page still reads short (0124). Settled; not re-read utils/lexikon_1910/lexikon_1910.py 0.2.0; utils/lexikon_1910/lexikon_1910_notes.py 0.8.1 (apparatus_band); tests/test_lexikon_1910_table_foot.py; utils/lexikon_1910/_archive/measure_table_foot.py → output/lexikon_1910_pilot/table_foot_rows.csv
D-78 2026-10-14 The table's rows start under the rule that closes the column header. lexikon_1910 0.3.0 added header_foot and table_top behind top_from_header; 0.4.0 makes it the default, and the pilot's --table-top defaults to header (band is the old top, for replay). body_band keeps only the column rules at least 80 % as tall as the tallest; where one over-tall rule is left alone in that set, it starts mid-table and the first rows were never read. The header's closing rule is in the ALTO on 477 of 480 scans and agrees with the image's reading of the header within 15 px on all but one sealed scan. Measured, no model (measure_table_top.py): 233 rows (3,451 words) gained on 55 scans, scan 0399 alone 19 (Dejvice, Dobrovíz, Dolní Liboc …); no header word read as a row; 17 rows the old top cut mid-row read whole; I1 pass +236 (fail +5), I5 pass +214, no block that closed stops closing. The purposive gold tables match row for row. Some gained rows are district summary rows (0061 P. o. Český Brod), which now set the district for the blocks after them. Sealed scans 0106, 0166, 0226 gain 3, 1 and 3 rows, counted only. Made the default on the author's decision (2026-10-14), the re-read to follow (D-81). Blind to: the running head still reads everything above body_band's top; cutting it at the table's top was measured and not adopted, because it loses Prague on scan 0015. Settled; default since 0.4.0; not re-read utils/lexikon_1910/lexikon_1910.py 0.4.0; utils/lexikon_1910/run_lexikon_1910_pilot.py --table-top; tests/test_lexikon_1910_table_top.py; utils/lexikon_1910/_archive/measure_table_top.py → output/lexikon_1910_pilot/table_top_rows.csv
D-79 2026-10-14 A page's okres is read off its running head with the folded matcher. lexikon_1910_names 0.2.0 added assign_okresy(..., fold=True): letters the OCR turns into look-alikes from another script are folded, names are matched on shape with ties refused, and only the political names the head prints are read; the stored text is untouched. 0.3.0 makes it the default; the pilot's --okres-fold is on (--no-okres-fold is the exact lookup of before). The print is Latin; the text layer emits some head letters as Cyrillic-looking garbage (Nová Рака, ТасЬот for Tachov), and the exact lookup assigned the Nová Paka scans 0282–0287 to Německý Brod and scan 0438 to Tábor, and found Aš inside vlastním on the Prague pages. Measured, no model (measure_running_heads.py): the okres changes on 24 non-sealed scans; on 19 of them the old answer is wrong and the new one right, and the other 5 were not settled. Made the default on the author's decision (2026-10-14). Settled; default since 0.3.0 utils/lexikon_1910/lexikon_1910_names.py 0.3.0; utils/lexikon_1910/run_lexikon_1910_pilot.py --okres-fold; tests/test_lexikon_1910_running_head.py; utils/lexikon_1910/_archive/measure_running_heads.py → output/lexikon_1910_pilot/running_head_okres.csv
D-80 2026-10-14 Every volume of the series is described by a profile, and a page's geometry can be read from the image alone; both are built, measured and opt-in — not the default. A TOML profile per volume (reference/places/volumes/: Czech Bohemia, German Bohemia, Moravia, Silesia) states the page ranges, statutory-city pages, heading phrases, abbreviations and column schema; the Czech one restates the pipeline's constants and czech_drift() keeps them identical (moving the constants onto it waits on the tomllib dependency). lexikon_1910_image finds the frame, column rules, header, table foot, divider, footer, headings and blank-row strip cuts from pixels. An ink account lists any printed line outside every crop — a check, never a filter. The pipeline must run on any volume of the series, including crown-land volumes not owned yet, whatever their text layer. Measured, no model (measure_image_geometry.py, measure_ink_accounting.py): on 478 Czech table scans the image agrees with the ALTO on the table foot on 473, the divider's position on 473 of 476 and 17 columns on 472 (Prague and Liberec correctly 15); it finds 1,195 of the 1,245 headings the ALTO finds (96.0 %, the rule fitted on odd scans and held on even ones) and 41 the text layer drops, all real on the 40 non-sealed scans, including the 10 on D-76's seven pages. Under image crops the only real print outside every crop in four volumes is 2 lines in German Bohemia; no strip cut passes through ink. Silesia prints a fourth language column (18 columns). The German PDFs carry a positional word layer, unused. It also exposed a broken ALTO divider on 16 Czech scans, whose column tails land in the footer crop, and apparatus read into the table on 0369 and 0472 — recorded, not fixed. Blind to: headings and the column header still have to be read for their text; the reader-side design (report headings and header per crop, compare printed-line counts with the lines returned) is drafted, not built. Making image geometry the default is gated on a gold re-read on Heavyhorse6. Opt-in; reader side designed, not built utils/lexikon_1910/lexikon_1910_volume.py, lexikon_1910_image.py; reference/places/volumes/*.toml; tests/test_lexikon_1910_volume.py, test_lexikon_1910_image.py; utils/lexikon_1910/_archive/measure_image_geometry.py, measure_ink_accounting.py → output/lexikon_1910_pilot/image_geometry_cechy_cz.csv, ink_accounting_*.csv
D-81 2026-10-14 The bake-off moves to run tag r5, and the gold passes are read again on Heavyhorse6 once the transcription is finished. D-76 to D-79 change what the readers are shown: 48 of the 84 gold-pass crops (71 of 149 at half height) and the table block crops of 55 scans no longer match their cache entries, so an r4 pass cannot be replayed under the new geometry. The re-read is the six Gemini arms of notebook 07 (≈ $2 from the r4 logs) and the Haiku arms of notebook 08 (≈ $1.1), ≈ $3 in all; it waits until the calibration and German sheets are transcribed, so it is scored on the full gold (the author, 2026-10-14). rescore_passes.py --run-tag r4 still scores the r4 passes. 2026-10-21: both golds are finished and checked; the scored frames now cover 12 Czech pages (read_transcription_md.py --role gold --write: 890 office claims, 0449 held out), and notebooks/11_r5_gold_run.ipynb runs every r5 part (Czech Gemini arms D/E/G, Czech Haiku haiku55nh and haiku55h, the German arms of D-92, the table arm of D-90) and every scorer, ≈ $8–9 in all. Decided; ready to run on Heavyhorse6 (notebook 11) utils/lexikon_1910/bakeoff.py (RUN_TAG); notebooks/07_lexikon_1910_baseline.ipynb, 08_lexikon_1910_second_reader.ipynb
D-82 2026-10-14 The statutory towns' prose pages are read as prose. Prague (scans 0015–0017) and Liberec (0018) print their institutions as running text, not as the keyed apparatus, so no divider is found there and no crop holds them. extract_prose_pages.py writes their text layer out as paragraphs (below the table on 0015 and 0018), and reads them with the vision model on request (--vision --spend, Heavyhorse6 only, eight calls, under $0.10, cached). The text layer reads the prose but not its numbers: Prague's field artillery regiment is printed 23 and read 28, VIII reads Vlil, 9. reads i). The text-layer files are a reading to check, not data. Text layer extracted; vision read prepared, not run utils/lexikon_1910/extract_prose_pages.py → output/lexikon_1910_pilot/prose_pages/
D-83 2026-10-15 The pilot cuts the apparatus on the page image by default, labels each crop with the heading's words, and reads the postal legend from the image. run_lexikon_1910_pilot.py --geometry image (default; alto is the text-layer cut of 0.8.x, kept as the comparison arm) with lexikon_1910_notes 0.9.0 image_section_segments: the columns, their foot and the headings come from lexikon_1910_image; an image heading the text layer also found takes its label; a text-layer heading the image missed is kept as a boundary; a heading only the image sees is cut out and read once by the vision model, and the label comes from find_heading on that reading — with no reading that is a heading, no boundary is made and the heading is reported (D-38). Strips are cut through blank image rows. --notes-legend (default on) adds the footer band as a legend crop read with the general prompt, resolved by the same postal_legend code and set against the text layer's. The bake-off's r5 plan reads the image geometry in arms D and E and the text-layer geometry in arm G. The text layer drops whole lines and headings (D-75, D-76) and the image does not depend on it. Measured over the 476 Czech table scans with an apparatus, no model called (measure_pilot_geometry.py): the image geometry cuts 475 (one falls back); 1,640 sections against 1,628, with the same sequence of labels on 464 pages; headings 1,193 found by both, 53 by the text layer only (kept), 38 by the image only — the ones a pass reads, among them the seven of D-76; the printed lines outside every crop the pilot reads are 76 on 67 pages, all in the margin (51) or under the legend (25), the specks and blots D-80 found. A gold pass is 98 crops against 97 at --notes-max-height 500 and 169 against 169 at 250. The post and station keys of the table, which the legend resolves, read 100 % and 98 % right in the whole-page vision read against 39 % and 15 % from the text layer (purposive gold, one run). Made the default on the author's decision (2026-10-14), before the r5 re-read; arm G measures what the switch did. Blind to: the 38 heading reads have not been made; the German volumes still need the reader to report the column header and headings (D-80). Default; not re-read utils/lexikon_1910/run_lexikon_1910_pilot.py --geometry, --notes-legend; utils/lexikon_1910/lexikon_1910_notes.py 0.9.0; tests/test_lexikon_1910_pilot_geometry.py; utils/lexikon_1910/_archive/measure_pilot_geometry.py → output/lexikon_1910_pilot/pilot_geometry.csv; utils/lexikon_1910/run_e1_gemini_version.py (arm G)
D-84 2026-10-15 The apparatus band opens at the foot of the table's column rules, always — not at the lower of that foot and the body band's. lexikon_1910_notes 0.9.1, apparatus_band; the table's rows end on the same line (lexikon_1910.table_bottom, D-77). D-76 took the lower of the two feet on the reasoning that the rules could only end too early. On scans 0369 and 0472 the body band overshoots instead (2,657 and 2,401 px against rule feet of 2,451 and 2,057), and a Části osad heading with its first entries was read into the table. The image's own reading of the table foot agrees with the rule foot to the pixel on both (2,451 and 2,056). They are the only two table scans where the two feet disagree that way. Both now end the table on its last real row (Solany; mezi tím vojsko under Vrchlabí). Settled utils/lexikon_1910/lexikon_1910_notes.py 0.9.1
D-85 2026-10-15 Every pass writes a crop audit: printed lines the image shows against lines the reader returned, and any heading the reader wrote out against the crop's label. crop_audit{label}.csv, one row per section; a section is flagged when the reply holds under 60 % or over 1/0.6 of its printed lines, or names a different section than the crop was labelled with. The reader is never told the label, so a heading it transcribes is an independent reading of it. A flag routes a crop to a look; it never changes what was read (D-38). The pipeline had no check that a reader returned every line it was shown, and no way to see a mislabelled section from the reply. Calibrated with no model on 101 sections of 30 non-sealed scans: the image's printed-line count (lexikon_1910_image.lines_in, a band taller than one line counted by the 38-px line pitch) against the text layer's line count has median ratio 1.00 (10th–90th percentile 0.91–1.04), and the 60 % rule flags none of them. The r5 passes are the first to write it. Settled; first data in r5 utils/lexikon_1910/run_lexikon_1910_pilot.py (printed_line_count, crop_audit); tests/test_lexikon_1910_pilot_checks.py
D-86 2026-10-15 Channel B grades a municipality split by a page break with the settlements printed overleaf. lexikon_1910.carry_block (0.5.0) appends the next page's orphan rows to the page's last block; the pilot regrades that block's I4 sums when the next page is in the run, and leaves every row's I1–I3 verdict and the per-page I5 sequence as they were. Graded one page at a time such a block read short in every column it sums (scan 0124, Holetín; the gold checker had the same defect and got the same fix on 2026-10-14). Measured over table scans 15–494, no model: 90 blocks continue on the next page; regraded whole, 334 of their column sums pass and 75 still fail (Holetín passes in every column). Settled utils/lexikon_1910/lexikon_1910.py 0.5.0; utils/lexikon_1910/run_lexikon_1910_pilot.py; tests/test_lexikon_1910_pilot_checks.py
D-87 2026-10-15 A running head that names an okres the authority does not match is unresolved, not carried; a clear leader below the threshold is accepted for the first name; and a statutory city's head is read after its colon. lexikon_1910_names 0.4.0–0.4.1: a weak tier (≥ 75 and ≥ 10 ahead of the runner-up) for the first head name only; named_unmatched where a head prints a name that matches nothing; Města s vlastním řádem obecním: Praha. reads the city after the colon. Amends D-79. D-79's matcher carried the previous page's okres whenever the first name fell below its threshold, which on six pages was a confident wrong answer: Loket and Kralupy nad Vltavou are not in the authority at all (the project's 1910 district backbone has neither as a political okres) and became Litomyšl and Královské Vinohrady; Fri diami became Falknov. The weak tier was read off the eight heads that fell below: it takes the register's spelling Fridland to Frýdlant (75 against 62) and Chotětooř to Chotěboř (75 against 59), and no other. Against the old exact lookup (measure_running_heads.py) 27 non-sealed scans change: 21 are now right where the old answer was wrong (Prague 0016–0017 and Liberec 0018 among them); 5 are now unresolved where the old answer was wrong or was the page's second okres (Kralupy 0198–0199, Loket 0242–0243, 0091); 2 lose an answer the old lookup had by accident (0015, whose head prints no city, and 0077, Dufoá). Open: Loket and Kralupy nad Vltavou as 1910 political okresy are missing from the district backbone. Settled; the backbone gap open utils/lexikon_1910/lexikon_1910_names.py 0.4.1; tests/test_lexikon_1910_running_head.py; utils/lexikon_1910/_archive/measure_running_heads.py → output/lexikon_1910_pilot/running_head_okres.csv
D-88 2026-10-15 The register is authoritative for its own political okresy: Loket and Kralupy nad Vltavou are okresy of the register, mapped to the backbone okres that holds their courts. reference/places/register_okres_curation.csv names them, with the courts the register prints, and lexikon_1910_names 0.5.0 (add_register_okresy) makes each name resolve — in the running head and in a district summary row — to that backbone okres: Loket to Falknov, Kralupy nad Vltavou to Slaný. Blocks keep the register's label; anchoring to obce and the census check pool them with the parent, as D-18 pools a city with its rural okres. Resolves the open item of D-87. The running heads print Loket: Loket. (scans 0241–0243) and Kralupy nad Vltavou: Kralupy nad Vltavou, Velvary. (0197–0199) as political okresy, read by eye off the print on 2026-10-15. The district backbone (MPIDR 1910) and the 1910 census carry court Loket inside Falknov and court Velvary inside Slaný, and have no court Kralupy (the town sits in court Velvary there); the settlement crosswalk's own okres timelines show both as okresy (Falknov|Loket|Sokolov, Slaný|Kralupy nad Vltavou|Mělník) but outside the 1910 segment. The author ruled the register authoritative (2026-10-15). With the curation all six scans resolve to their printed okres and only 0077 (Dufoá) and 0091 (Fri diami) stay unresolved. The backbone is right for 1910 and is kept. The German edition of the same work dates both offices in its Änderungen (Spezialortsrepertorium … Böhmen (Wien 1915), Änderungen A.1–2 and C.2, PDF p. 575 of library/lexicons/UBW_Spezialortsrepertorium-Boehmen-1910_de.pdf): the Bezirkshauptmannschaft Elbogen/Loket, the court district of that name taken out of political okres Falkenau, active from 2 December 1913 (RGBl. 1913 Nr. 226); the Bezirkshauptmannschaft Kralup an der Moldau, courts Kralup and Welwarn taken out of political okres Schlan, active from November 1913; and the court of Kralupy itself, carved out of 21 communes of court Welwarn, active from 1 March 1912 (RGBl. 1912 Nr. 27). Its preface says why the register prints them: population is counted at 31 December 1910, but districts and communes are drawn as at the end of 1914. The backbone, the census and the lexikon's 1910 segment (Loket 1869–1910 okr. Falknov, 1921–1930 okr. Loket; Kralupy 1900–1910 okr. Slaný, 1921–1950 okr. Kralupy nad Vltavou) all describe 31 December 1910, when neither okres nor the Kralupy court existed — nor at any sample member's birth. An author request (2026-10-15) to make both okresy of the backbone was checked against this and not carried out; a post-1913 layer belongs to the interwar okres panel of the secondary project, where both are okresy. Settled: register names mapped; backbone unchanged (1910 state) reference/places/register_okres_curation.csv; utils/lexikon_1910/lexikon_1910_names.py 0.5.0; tests/test_lexikon_1910_running_head.py; utils/lexikon_1910/_archive/measure_running_heads.py
D-89 2026-10-19 The Opus verify call is gated: it is run only after r5 has measured how often Haiku 5.5 and Gemini disagree, and only if its cost over the volume beats the author ruling those claims by hand. r5 reads both families on the twelve gold pages (the arms of D-81) and sends nothing to Opus. From it: the disagreement rate per claim, scaled to the Czech apparatus; Gemini's share right inside a disagreement (the free baseline, always side with Gemini); and the author's time per ruling, taken from the ruling page. Only then is the verify call tried on the disagreements — and it enters the route only if it beats the baseline by enough to be worth its cost against hand-ruling. Amends the order of D-72 and of the ROADMAP's next steps; D-72's route is otherwise unchanged. The author's call (2026-10-19). Everything that sizes the verify step comes from the four purposive pages: one claim in nine disputed (11.1 %, roughly 5,100 of 46,000 over the Czech apparatus), Gemini right in 57 % of disputes, ≈ $30–90 on batch (D-72). r5 is the first measurement on twelve pages with the D-76–D-87 crops, and the disagreement rate sets both the Opus bill and the author's workload, so the choice between them is made on it, not before. Decided; waits on r5 notebooks/08_lexikon_1910_second_reader.ipynb; utils/lexikon_1910/bakeoff.py
D-90 2026-10-19 r5 gets a table arm: the seventeen printed columns read by Gemini 3.8 Flash (thinking off) and Claude Haiku 5.5 (thinking at high), three repeats each, on the 13 gold table pages, scored cell by cell against the gold beside the text layer. run_table_arm.py drives channel A of the pilot on the same municipality-block crops channel B reads, so the two channels differ only in who reads the crop; the pilot now writes every row each channel read (table_rows_<label>.csv) and takes --reader-reasoning-effort; score_table_passes.py pairs gold and read rows per page by an order-keeping alignment and reports recall, whole-row exactness, per-column accuracy, names on their words (and strictly), and, for any two readings, where they agree wrongly and where only one is right. Certification sheets are never read. The author asked to opt out of the text layer, which makes too many mistakes, and to compare a pure model reading. Measured first, no model called (channel B alone on the 13 pages, 403 gold rows): recall 98.3 %, number cells 91.4 % right (the confession and language columns 72–90 %), names 60.6 % right word for word (27.5 % strictly), rows whole 15.1 %. So the names are where the text layer fails. The gold was itself corrected from the text layer's reading, so a reader that shares its errors is not favoured. Estimated before running, from the pilot's dry run on 199 blocks: at most ≈ $1.1 a Gemini pass and ≈ $0.25 a Haiku pass, ≈ $4 for the arm. Blind to: a row no crop holds (both channels miss it; recall says how many); the crops' edges still come from the page geometry, so this is a pure model reading, not a pure model segmentation; the mezi tím vojsko lines, which the gold does not transcribe. Built; not run (Heavyhorse6) utils/lexikon_1910/run_table_arm.py; utils/lexikon_1910/score_table_passes.py; utils/lexikon_1910/run_lexikon_1910_pilot.py (table_rows, --reader-reasoning-effort); tests/test_score_table_passes.py
D-91 2026-10-19 Test A is set up: the fill-pair glyphs are boxed by hand on the Token Review page, cut from the gold crops two ways, and classified leave-one-page-out by DINOv3 (ViT-S/16, ViT-B/16), DINOv2 (small, base) and a plain pixel baseline. build_token_review.py --boxes calibration,purposive queues one question per entry and glyph (124 questions, 141 glyphs: castle 26, rail-station 26, solitary 19, rail-stop 19, poorhouse 12, ruin 11, gamekeeper 11 entries), each asking for every such mark the entry prints to be clicked; build_glyph_bank.py cuts each click as tight (the ink under it, joined components) and window (a fixed square of 1.6 line heights) and draws a contact sheet per token; run_glyph_test_a.py reports castle/poorhouse, ruin/solitary and station/stop on their own and all seven together, 1-NN and 3-NN, CLS and mean-patch features. Certification is refused by the builder. Weights load from the local cache only unless --download is given. The author asked for the encoder to be readied while the German gold is transcribed. The bank is cut from gold pages and each page is classified only from the others, so typesetting on one page cannot carry its own answer — the leave-out D-69 asks for, applied to every page rather than to a calibration-only bank, because test A asks whether the pairs separate at all, not how a frozen bank performs. The pixel baseline is the floor: an encoder that does not beat 32 x 32 grey pixels on a fill pair has nothing to add. Checked end to end on clicks placed blindly (pixels at chance, as they must be), then removed. The DINOv3 licence, read 2026-10-19 (Meta DINOv3 License; the weights are gated on Hugging Face behind accepting it and sharing contact details): use, modification and redistribution are granted royalty-free; research submitted for publication must acknowledge the DINO materials; military and warfare, nuclear, espionage and weapons uses are prohibited — reading a 1910 place register is none of them. DINOv2 is Apache-2.0 and ungated. Blind to: the class sizes are small (11–26 entries), so a pair's accuracy carries a wide interval; a click on the wrong mark is cut faithfully, which the contact sheets are there to catch. 2026-10-19, boxed and the bar moved. The author boxed all 141 glyphs (124 entries, every one clicked as often as the gold has, 114 clicks on the whole-section view; median 13 s an entry, 34 min in all); the contact sheets show each cut whole once a line-height estimate that merged the lines of a 340-px image was replaced by the scan's other section images (scan_line_height). On the tight cuts plain 32 x 32 pixels separate every pair at 100 % (1-NN, leave-page-out): with a clean box the fill pairs are easy, and the bar as first written (beat pixels by 10 points on the tight cut) could not be met by anything. On the window cut — a rough box, what a detector delivers — pixels fall to 67.5 % (castle / poorhouse), 76.3 % (ruin / solitary) and 85.4 % (station / stop). The bar is therefore set on the window cut, on the author's decision and before any encoder was run: DINOv3 at least 90 % 1-NN on castle / poorhouse and on station / stop, and at least 10 points above pixels there. What the pixel result already says, whatever DINOv3 does: the hard part of the fill pairs is where the glyph is, not what it is — a box per glyph is worth more than a better classifier. DINOv2 dropped on the author's call (2026-10-19): DINOv3 is the question; DINOv2 stays runnable by name but is not run by notebook 09. 2026-10-19, Heavyhorse6 — test A passes on the bar of the window cut. DINOv3 ViT-B/16, 1-NN leave-page-out: castle / poorhouse 95.0 % (38 of 40, CLS and mean-patch alike; Wilson 95 % interval 83.5–98.6 %) against pixels 67.5 %, station / stop 100 % (48 of 48) against 85.4 %, ruin / solitary 100 % (CLS) and 94.7 % (patch); all seven together 97.9 %. ViT-S/16 reaches castle / poorhouse at exactly 90.0 % (CLS; 87.5 % patch) and station / stop 100 %. The errors left run one way, a solid-roofed poorhouse read as an outline-roofed castle (2 of 12 poorhouses for ViT-B), the readers' own confusion. DINOv2 (run once before it was dropped) is lower on the hard pair, 85–90 %. On the tight cut ViT-B/16 mean-patch matches pixels at 100 %. The encoder of record for E3 is DINOv3 ViT-B/16. The interval on the hard pair still reaches below 90 %: 12 poorhouses are few, and the bank grows with the calibration boxes of E3. Weights load locally; with transformers 5.x and no torchvision the image processor fails, so run_glyph_test_a.pixel_values prepares the images by hand from the model's own preprocessor_config.json. Settled: test A passes (DINOv3 ViT-B/16, window cut); E3 next (notebooks/09_glyph_test_a.ipynb) notebooks/09_glyph_test_a.ipynb; utils/lexikon_1910/build_token_review.py (--boxes); utils/lexikon_1910/build_glyph_bank.py; utils/lexikon_1910/run_glyph_test_a.py; output/token_review/index.html
D-92 2026-10-19 r5 gets a German notes arm: the German edition's apparatus, cut on the image alone, read by Gemini 3.8 Flash (thinking off) and Claude Haiku 5.5 (thinking at high), three repeats each, on the seven calibration and purposive German gold pages, scored against the German gold, and set beside the Czech r5 passes municipality by municipality, paired by court and name. run_r5_german_notes.py renders each page at its native resolution, cuts the sections with lexikon_1910_image through image_section_segments on an empty text layer, reads every heading with the pass's own reader and labels it with the German profile's matcher (lexikon_1910_notes 0.10.0 and lexikon_1910_vision 0.4.0 take the matcher as an argument; the Czech default is unchanged), takes the section open at the top of a page from a read of the previous scan's last heading, reads the column header once a page against the profile's seventeen labels, and files each German court under its Czech slug through district_cs_curation.csv (and the built backbone); passes go to bakeoff/<host>/de/, out of sight of the Czech tables. The German side of institution_abbreviations.csv gains the plate's 80 pictograms under the Czech tokens and the cooperative letters K, R and Sch (Abkürzungen, p. X, scan 0020), and build_symbol_atlas_de.py cuts the German key from the plate (80 of 80). score_r5_german_notes.py scores with the Czech arms' scorer and runs the D-58 test through lexikon_1910_editions: joint errors against independence with a permutation p-value, within the truth class and within (truth class, symbol), on the symbols both plates print. Certification sheets are never read. D-58 asks for exactly this: a German reading is a second measurement only once its sections are cut from the image, by another family than the Czech reader, and only if the two errors are independent given the truth, which is tested rather than assumed. The German plate is not the Czech plate (Zeichenerklärung, p. XI, scan 0012, read by eye): 80 symbols, without [cooperative] (the German volume prints the cooperative letters alone), [port] and [rc-parish-exp] (written Exp.). A German reader shown the Czech key would be told of three symbols its volume never prints, and a comparison over them logs every Czech cooperative as an edition disagreement, as the first scorer run did three times on one page. The plate's scan is fainter than the Czech one (thin outline symbols hold 1.7 % ink at 80 % of paper brightness against 0.2–1.4 % at the Czech 72 %), so its ink test and its column split were measured on it. Measured, no model called: the image geometry finds 17 columns, both apparatus columns, the headings and the legend on all seven pages; a pass is 51 crops in strips of ~375 px (500 px × the page's scale of 0.75–0.76, the print a Czech strip holds), 26 heading reads and 7 header reads; estimated at ≈ $0.32 a Gemini pass and ≈ $0.09 a Haiku pass, ≈ $1.2 for the six (Haiku's thinking at the pilot's placeholder budget). On the German sheets filled so far (0142, 0276), 69 municipalities pair with the Czech gold by name and none is refused; on the 18 filled in both, the two golds agree on all 70 shared claims. A pass with a fake reader runs end to end on scan 0479. Blind to: the settlement level across editions, since parts are numbered in each edition's own order, so the test is at municipality level; the German gold is still being transcribed, so every figure here will move; a heading the reader cannot label leaves its section under the label before it (reported, D-38); a court the crosswalk cannot place is refused, not guessed. Built; not run (Heavyhorse6) utils/lexikon_1910/run_r5_german_notes.py, score_r5_german_notes.py, build_symbol_atlas_de.py, symbols/de/; utils/lexikon_1910/bakeoff.py 1.2.0 (§10); utils/lexikon_1910/lexikon_1910_de_notes.py, lexikon_1910_editions.py; institution_abbreviations.csv (de pictograms); tests/test_r5_german_notes.py
D-93 2026-10-19 E3 is built: a locator puts every claimed mark on a box with no hand label, and DINOv3 ViT-B/16 names each box by its nearest neighbours in a bank cut from the calibration pages only; where it and a reader disagree the claim is routed, never rewritten (D-38, D-55, D-69). locate_glyphs.py splits each gold section image into ink clusters on baselines found from the feet of the letters, scores how much each looks like a pictogram, and places the claims of a section in printed order (D-73) by a monotone dynamic programme that keeps each claim inside its own entry's lines and may leave one unplaced. What a glyph looks like — a logistic glyphness and a shape template and size per token — is learned by self-training on the calibration pages from the gold's counts and order alone, and each page is placed by a locator trained without it. The legend plate's shapes seed only the first round, never the bank (D-69). run_glyph_e3.py cuts each placed box tight and as test A's 1.6-line window, embeds it (DINOv3 ViT-S/16 and ViT-B/16; 32 × 32 pixels as the floor), and names it leave-page-out, 1-NN and 3-NN, CLS and mean-patch. Below a page's threshold (the 5th percentile of correct nearest-neighbour similarity on the other calibration pages) it answers not in the bank. It places the claims of every reader pass of a run tag the same way and scores them in D-71's terms: caught, shared, alarms, and who is right in a dispute. Certification is refused in code. Test A (D-91) showed the box is the hard part: on a clean box even pixels separate the fill pairs. Measured on ZenBookTH, nothing sent. Against the 141 glyphs the author clicked for test A, every one lies inside a candidate cluster, and 137 (97.2 %) inside the box placed for its own claim, at a median centre error of 0.17 line heights (90th percentile 0.31). Of the 1,629 gold marks on the 13 pages, 94.5 % are placed. In an entry, 0.38 glyph-like clusters carry no claim, and 79 % of entries have none. Order alone is not enough: before the templates, a claim landed on 57 % of the clicked glyphs, but mostly on its neighbour's (16 % right). Two geometry fixes were needed. The bands of text_lines merge touching lines (a 139-px band at line height 66 on scan 0136). A line holding only a key and one glyph (2) [castle].) has too few letters to make a baseline, so leftover glyph-sized marks now form a line of their own. Placed token-blind (no claimed token's template) the locator falls to 76.6 %, so claims are placed with their own token's template, and the bias that brings is measured. Given its fill-pair partner instead of its true token, a claim keeps its box 91.2 % of the time and is left unplaced 8.8 %; it never moves to another mark (147 claims). Given a random wrong token, it keeps its box 26.8 %, is left unplaced 61.8 % and moves 11.5 % (400 claims). So a fill-pair error is shown to the encoder on the right glyph, and a gross one mostly comes back as an unplaced claim, which is itself counted. On the placed boxes the pixel floor already names 93.8 % of the marks the bank holds (window, 1-NN; 95.0 % on the tight cut; 92.1 % on calibration, 96.0 % on purposive pages). It answers not in the bank on 8.2 % of those, and on 77.4 % of the 31 marks whose type no other calibration page prints. The DINOv3 figures are Heavyhorse6's to produce (notebooks/10_glyph_e3.ipynb). The reader score was checked end to end on the eighteen stored r4 passes with the pixel floor. Those figures are for the code, not a result: the pixel floor is not the encoder of record. Blind to: a mark no claim names is never placed (glyphness is written for every cluster, for a later pass that looks for marks nobody read). The click measure covers seven glyph types, and the starting glyphness heuristic was set after looking at those glyphs' features, so it is not wholly blind. The per-glyph accuracy is optimistic, because each box is chosen partly by its gold token's template. The click measure takes no account of the 90 unplaced gold marks, and in a few entries the glyph-foot vote splits one line in two (Rychnov 8). Section 2a of Chomutov (scan 0118) has no image on its sheet and is skipped (15 marks). Rare types have few or no bank exemplars. And truth for a reader claim is the gold mark whose placed box it shares, so an error of the locator on both sides cancels unseen. 2026-10-19, Heavyhorse6 — the first DINOv3 run. ViT-B/16 (window, CLS, 1-NN) names 95.2 % of the in-bank marks, against 93.8 % for pixels. But it named whole tokens on a box that held the base alone, so the composite marks failed: [school-exp] came back [school], [rc-parish-exp] [rc-parish], [colony] [cottage-group] (15.4 %, 2 of 13), and [vet] and [ev-parish-ab] 0 of 4 each. The fix, on the author's ruling: a mark is named in parts — base × brackets × letter — and composed from the plate. glyph_composites.csv writes down the plate's 22 composites. Eleven are bracketed: (school), (rc-parish), (house-group) = colony, (castle) = manor-house, and so on. Four are in square brackets, the pilgrimage marks [osada], [rc-chapel], [rc-filial] and [rc-parish]. Seven are lettered: ♁ + A/H, (♁ + A/H), the vicariate arch + A/H, and the staff + V for [vet]. That is more than the message listed: square brackets and the letter V too. [cottage-group]'s cross of four exists only bracketed. [cooperative] (the savings-bank dome + waves) is left whole. locate_glyphs 1.1.0 places each claim on its base, with templates kept per base. A bracket is read off its shape and position, never off the claim. It is a thin stroke taller than a capital that reaches below the baseline and has no crossbar; it may print broken into stacked pieces; it is round if the stroke curves and square if it is straight. A bracket never joins a cluster and never takes a claim. Beside each placed base the locator then looks, token-blind, for an opening bracket on the left, and on the right for a capital and/or a closing bracket, never across a comma. A comma is now told apart from a full stop by its descent, and the solid blocks of a block of four join across their 0.25-lh gap. run_glyph_e3 1.1.0 names the parts separately. The base is named on a base cut, the window with the found modifiers painted white, against a bank of bases in which the bank's tokens are taken apart, so colony's block pools with [house-group]'s. The brackets are named by rule (both found: bracketed). The letter is named only where the encoder's base has lettered forms on the plate: the staff's only letter, V; the nearer of A and H by 1-NN among calibration crops for the two crosses. Whatever the plate does not print, or a letter the bank cannot decide, becomes [not-in-legend]: routed, never forced. Measured on ZenBookTH, pixel floor. The click measure is unchanged for the seven test-A glyphs (137 of 141). Both brackets are found on 118 of 129 bracketed marks (91.5 %), of the right kind on 117, and on none of 1,396 plain marks. A capital is found after the base of 12 of the 16 lettered marks. The letter is named right on 6 of those 12, and the other 6 are unread. No letter is read after a plain cross or staff once capitals under 0.60 lh are refused (the old-style figures there are 0.47). Composite tokens named right go from 71.7 % to 73.1 % (145 marks), and all marks from 88.5 % to 89.3 %. On the six r4 Gemini passes, errors shared with the reader fall to 1–2 a pass from 1.0's 2–5, while alarms rise to 31–37 from 29–34 (1.0 named whole tokens on the window cut). Most composite marks are still wrong on pixels. Colony's base finds [house-group] nearest on 5 of 12 marks, but below the threshold. And the letter bank is nearly empty under leave-page-out: A is printed on one calibration page (0019) and H on one (0136), so on those pages the other letter has no exemplar. The encoder's figures need the re-run on Heavyhorse6. Blind to: the composite counts are small ([school-exp] 1 placed, [rc-parish-exp] 2, [colony] 12, the lettered marks 16), so every per-token rate is a few marks. Brackets printed faint enough to break into specks are missed: both [rc-parish-exp], one [school-exp] and 2 of 4 [creche]. A letter, once found, is right only as far as the A/H bank reaches; the cooperative letters in the gold (: R, H) are a source of H crops not yet used. Built; first DINOv3 run on Heavyhorse6 2026-10-19 (whole tokens); the composite naming awaits its re-run utils/lexikon_1910/locate_glyphs.py 1.1.0; utils/lexikon_1910/run_glyph_e3.py 1.1.0; reference/places/glyph_composites.csv; notebooks/10_glyph_e3.ipynb; tests/test_glyph_e3.py; output/glyph_e3/ (locator.txt, locator_modifiers.csv, locator_stability.csv, e3.txt, e3_composed.csv, e3_letters.csv); Symbol accuracy plan — E3's second method
D-94 2026-10-21 E3 gets an independent arm: the symbol apparatus read from the image alone, with no reader claim and no text layer anywhere in the chain, as a third pass e3 in the scorer's own columns — a check whose disagreements route a claim (the order check, then Opus or the author, D-89) and never settle or overwrite one (D-38, D-55). run_glyph_independent.py 1.0.0, in four steps. (1) Token-blind detection: every ink cluster is scored by a logistic detector on the locator's shape features, its glyphness and the best match to any glyph template. The detector is trained on the calibration pages, where the gold says which clusters are marks, and each page is scored by a model that never saw it; one threshold, set on the calibration pages at the best F1, calls a mark. Brackets, specks and the key column are excluded. (2) Naming: the base + brackets + letter composition of D-93, against a calibration-only bank (D-69) to which the clusters the detector fires on there that are not marks are added as [text]. [text] is dropped; [not-in-bank] and [not-in-legend] are routed, never written as claims. (3) Keys without OCR: the key column (13, —), 2)) is cut by column projection of each line's own ink, because the bold figures print broken into slices. Comma, dash and closing bracket are told apart by shape and place; a faint bracket is taken to be the last narrow piece. Only the figures go to nearest neighbours, in a bank the calibration gold labels; a figure that printed in two halves may also be read joined. Each section's key lines are decoded together by a monotone dynamic programme: municipality numbers rise, parts rise within an entry, and what the order cannot fix is flagged. Key lines are aligned to the gold on structure, with each line's own leave-page-out reading as a tie-break (scan 0019's labels had all come out one place off). (4) Inventory: each named mark is filed under the key line at or above it, so score_against_reference and second_reader (D-71, D-58) take e3 unchanged. The 13-page gold is read from the sheets (read_sheet), since the stored inventory_human.csv holds only the four purposive pages. The author's terms (relayed 2026-10-21): the gains are needed — omissions both readers make, a valid D-58 test, a second measurement for D-55 — and the text layer is too poor to lean on, even for key positions. Measured on ZenBookTH with pixels, nothing sent; DINOv3 is Heavyhorse6's to run. Detection: 88.2 % of the placed gold marks found (82.6 % of all 1,629) at 96.0 % precision. Keys: 344 of the 406 printed key lines found; of those, 79.0 % read right on the calibration pages and 66.4 % on the purposive ones. Two first designs failed and are recorded: per-character counts (41 %, broken brackets and dashes shift the count) and component-level pieces (bold figures in slices). A bonus for a municipality one above the last was tried on the calibration pages and did not help (79.0 % at 0, 77.1 % at 0.1), so it is 0. As a third reader, offices sections: settlement F1 0.697, municipality F1 0.811, entry_exact 0.283, on 145 of 187 entries (42 lost to unread keys). Against the gold it errs mostly by missing: at municipality level 24 false claims and 226 misses, the misses on the commonest glyphs (school 50, fire brigade 33). Of its 107 false claims at settlement level, 103 are a true mark filed under the wrong part. Against the readers (r4; reader first, e3 second): about 400 alarms a pass against each Gemini 3.8 pass, so the alarm bar (≤ 24.3, Haiku's as second reader in D-72) is not met. Of the errors a Gemini 3.8 and a Haiku 5.5 (haiku55nh) pass share, it catches 16–21 of 21–28 a pair, so the shared-error bar is met. Of the marks both readers missed, it finds 9–10 of 14–17. The D-58 test against Gemini shows no shared blind spot (missed_p 0.15–0.64), with little power. Measured beside D-71, as a design for the author to weigh: if only what e3 claims routes a claim and its silence does not, the alarms fall to ≈ 107 a pass (≤ 24 at municipality level) while still catching 19–21 of the 36–40 marks a Gemini 3.8 pass misses. The bar is written into notebook 10, section 6, before the Heavyhorse6 run, as relayed, and is to be confirmed with the author. Blind to: the gold section crops were cut by the pilot's geometry, which places a section by its heading, so the court of a section is taken from the cut, not read (in the volume, the image-only geometry of D-80/D-83 would cut them). Counts in front of a glyph (6 [gendarme]) are not read: every mark counts once, which count_exact shows and presence ignores. Detection truth is the gold-placed boxes, so the 5.5 % of gold marks the locator never placed are counted as misses only. The detector's threshold is one scalar, chosen on the pooled calibration scores including each page's own held-out score. Every figure here is pixels: the encoder of record has not read a key or named a detection. Parked (D-95): built and measured on pixels; kept runnable for the D-58 test (notebook 10 §6) utils/lexikon_1910/_archive/run_glyph_independent.py 1.0.0; utils/lexikon_1910/locate_glyphs.py 1.1.0 (line_components, Section.keys); notebooks/10_glyph_e3.ipynb §6; tests/test_glyph_independent.py; output/glyph_e3/independent/
D-95 2026-10-21 E3 in the route is the reader-based design, with every mark given a verdict; the independent arm of D-94 is parked. Gemini and Haiku read every crop (D-72). The locator places each claim on its glyph, and DINOv3 ViT-B/16 names the glyph in parts against the calibration-only bank (D-69, D-93). Every mark then gets a verdict: each claim is agree, disagree, not-in-bank, not-in-legend or unplaced. Every cluster that the token-blind detector of D-94 calls a mark but that no claim landed on is flagged unclaimed, with DINOv3's name for it. It is filed under the reader's own entry structure (the nearest claim before it in reading order), so no entry number has to be read without OCR. A verdict other than agree routes the claim (the order check, then Opus if D-89 lets it in, then the author); nothing is overwritten (D-38, D-55). run_glyph_e3.py 1.2.0 writes e3_verdicts_<tag>.csv, one row per mark. The detector and the text-column geometry moved into locate_glyphs (detect_marks). The author's call (2026-10-21), after the independent arm's pixel run. That arm finds and names marks well, but files them under entries it reads itself, and its digit reader gets ≈ 62 % of the 406 entry numbers right. Under D-71's two-sided rule that alone raises ≈ 400 alarms a pass. The readers read entry numbers well, so the reader-based design keeps their structure, and the one thing the independent arm added that the readers cannot — the marks neither of them claimed — is kept as a flag. Measured on ZenBookTH, pixel floor, r4 passes, 13 gold pages. The claim verdicts are as in D-93: against each Gemini 3.8 pass, 41–46 errors, 38–43 of them disagreed with (caught), 1–2 shared, 31–34 alarms. The unclaimed flag fires 81–120 times a Gemini 3.8 pass. Of those flags, 29–68 sit on a gold mark the pass did not place (named right by DINOv3 on 27–65); the rest, ≈ 50 a pass, are alarms. That rate matches the flag beside the gold's own placement: 51 flags on 13 pages (3.9 a page), none on a gold mark. Most of what it catches is one systematic omission: the bracketed [cottage-group] that Gemini leaves unnamed (141 of the real flags over the three passes, D-70). The rest are castles, ruins and filial chapels the pass left out (20). On haiku55nh it fires 163–170 times, 111–117 of them real. Blind to: a claim the locator cannot place is routed as unplaced without being named; an unclaimed mark is filed by reading order, so one at an entry's head can land in the entry above; a real flag may be a mark the reader claimed but the locator placed elsewhere, as well as a true omission; the detector's threshold is set for detection F1 on the calibration pages, not for the flag's alarm rate; every figure is pixels — the DINOv3 run is Heavyhorse6's (notebook 10 §3–5). Decided; built; DINOv3 run waits (Heavyhorse6) utils/lexikon_1910/run_glyph_e3.py 1.2.0 (unclaimed_marks, file_under, verdict_rows, score_unclaimed); utils/lexikon_1910/locate_glyphs.py 1.1.0 (detect_marks); notebooks/10_glyph_e3.ipynb §5; tests/test_glyph_e3.py; output/glyph_e3/e3_verdicts_<tag>.csv
D-96 2026-10-21 The German gold tables (section 1 of the seven German sheets) are read from the image by Claude, not by the author, and are marked so in each sheet; the German notes stay the author's reading. 194 rows over 7 pages, in the Czech sheets' format. Each sheet carries a comment saying who read section 1 and how it was checked. The author would not transcribe the German tables and asked for them to be read and checked against the Czech gold (2026-10-21). Checked, nothing sent: every row passes the register's own arithmetic (sex, confession, language, parts to municipality; Altenbuch across the page break); the 144 rows whose place is on a matched Czech gold page agree with the Czech gold in every number cell; area and houses of the other 53 rows were read a second time at higher zoom and agree; each sheet's pre-cut blank rows equalled the rows read. One cell is [?] (Pikow's walking time, clipped at the page edge). With the tables in, the parts check (I8) exposed three slips in the author's German notes (Mühlhausen 410-2744 for 440, Jawornitz part 2 written on part 1, 270m & for 270, &) and the Stehlowitz misprint already ruled; with the Trautenau line the earlier review kept, they are on the review page for the author. Blind to: a misread that keeps every sum and has no Czech partner (only area and houses were re-read); post and station keys, which are page-local footnote numbers with no check. Because this is a model reading, a German table score from it measures agreement with Claude, not with a person, and is not used to certify anything (D-55). Done; the notes slips it exposed ruled and applied 2026-10-21 (review/decisions_2026-10-21b.json) reference/places/transcription_gold/de/*/04*.md, 0142.md, 0276.md, 0380.md (section 1)
D-97 2026-10-21 The r5 gold run is read and scored; the D-89 cost test favours the Opus verify call, so test B (Opus on the gold's disputed claims against always siding with Gemini) is next; and the castle/poorhouse confusion is a blind spot both reader families share, which only the image encoder sees. Read on Heavyhorse6 from notebook 11 (Gemini arms D/E/G ×3, Haiku haiku55nh and haiku55h ×3, German Gemini and Haiku ×3, the table arm), scored on ZenBookTH after the last pass landed (the in-notebook scores ran before the Haiku and most German passes existed; all scores here are from the complete set, the Haiku table passes and E3 included). Czech notes, 12 pages, 881 claims (settlement level): Gemini 3.8 thinking off F1 0.955 [0.952, 0.957], entry-exact 0.762; Gemini 3.7 thinking off 0.961, +0.035 entry-exact [+0.008, +0.060] over 3.8; the image geometry against the text layer's +0.002 [−0.005, +0.009], so the OCR-free cut costs nothing; Haiku haiku55nh 0.937–0.948, haiku55h 0.932–0.941. Named parts 97.9 % found with houses and population exact; schools 131/131, cooperatives 102/102. Second reader (D-71): of Gemini 3.8's ≈ 81 errors a pass, haiku55nh catches 47 (58 %), shares 34 and raises 68 alarms; haiku55h 50 %; another Gemini 34–37 %. On four pages (D-72) Haiku had caught 87 %: the twelve-page figure replaces it. D-89: Gemini and haiku55nh dispute 114.6 claims a pass, 13.0 % of claims, ≈ 6,000 over the ≈ 46,000 claims of the Czech apparatus; Gemini is right in 59 % of disputes (the always-Gemini baseline). The author's rulings on the review page take a median 10 s (mean 14.3 s, 27 rulings of 2026-10-21; glyph boxing 13 s, n = 124), so ruling every dispute by hand is ≈ 17–24 hours against ≈ $35–105 for the verify call scaled from D-72: the cost side clears, and Opus enters the route if test B shows it settles disputes clearly better than 59 % without leaning to Haiku. The shared blind spot: on the twelve pages nearly every printed [castle] is read as [poorhouse] by all fifteen passes of both families (five castles missed by all, thirteen found by at most two; ≈ 18 castles missed a Gemini pass). The gold stands: the author's 141 test-A clicks show the outline roof on every castle cut and the solid roof on every poorhouse, and the encoder separates them (D-91). A shared error never becomes a dispute, so neither Opus nor the author would see it; E3 does — against the Gemini r5 passes DINOv3 ViT-B/16 catches ≈ 35 of ≈ 40 glyph errors on placed marks and shares 1–3 (e3_readers_r5.csv, re-run with every pass); against Haiku haiku55nh it catches ≈ 74 % (43–49 of 61–64) and shares 3–4, at ≈ 30 alarms a pass for either reader. The independent arm (D-94) is independent of Gemini as D-58 asks (both-missed p 0.10–0.70, where Gemini and Haiku give p < 0.0001), catches ≈ 80 % of the errors Gemini and Haiku share and ≈ 12 of the ≈ 19 marks both miss a pair; but it reads only 70–83 % of the entry keys right, so under the two-sided rule it raises ≈ 400 alarms a pass (≈ 110 when only its own claims route), far over its bar of 24.3; as a reader alone F1 0.701. Also shared: the evangelical preaching station read as a Catholic vicariate (Jirkov 13,2), as at r4. German notes, 7 pages, 371 claims: Gemini F1 0.944, Haiku 0.856; legend keys (14/58) and the column header (0–1/7) read poorly. Editions (D-58): joint Czech–German errors exceed independence raw (p ≈ 0.0005) but not within glyph type (p sym 0.07–1.0): the errors share hard glyphs, not a mechanism, so the German edition counts as a second measurement within glyph strata. Table arm (D-90), 403 gold rows: Gemini rows wholly right 91.8–94.0 % against the text layer's 15.1 %, number cells 99.6–99.7 % against 91.4 %, names 95.0–96.5 % against 60.6 %; Haiku 82.4–84.4 % rows; where Gemini and Haiku agree 0.1 % of cells are wrong; Haiku's three passes 80.1–84.4 % rows wholly right, losing mostly on the two language columns (86–94 %). Measured; test B next (Opus verify on the r5 disputes); E3 joins the route for the shared blind spot
D-98 2026-10-21 Test B's bar is written before anything is sent: Opus 5.5 at effort low enters the route only if, on the disputes of the reader-of-record pair, it is right on at least 90 % of the claims it settles, settles at least 70 %, lifts the dispute accuracy at least 15 points over always siding with Gemini with a page-clustered interval above zero, does not lean to Haiku, and gives the same answer in its three repeats on at least 85 % of claims. The set: the claims only one of gemini38_r5atlasntg1 and haiku55nh_r5atlasg1 makes, settlement level, on the entries both and the gold read — 106 claims, always-Gemini right on 67 (63.2 %) (rescore_r5_order_disputes.csv). One call per disputed part, batched, three repeats: the part's band of the section image with the disputed boxes outlined, each box enlarged, and for each disputed token its plate glyph and up to two cuts of it from other gold pages; the marks both readers agree on are given as context; it is never told which reader made which claim. It answers each disputed token printed, not printed or cannot tell. The measures, means over the repeats: settled (share not cannot tell), accuracy_settled, route_gemini (Opus where it answers, Gemini where it does not, on all 106), the gain route_gemini − always_gemini with a page-bootstrap 95 % interval, the lean (of the settled disputes Haiku has wrong, the share Opus decides Haiku's way, minus the same share for Gemini) and stable (claims with one answer in all three repeats). The bar, all five: accuracy_settled ≥ 0.90; settled ≥ 0.70; gain ≥ 0.15 with its lower bound > 0; lean ≤ 0.10; stable ≥ 0.85. A medium-effort arm is a test of its own with its own record, never a second try at this bar. Amended 2026-10-21, before anything was sent: on the r5 gold the four claims no reader reaches are all look-alike swaps, and three of them sit in parts already sent, where the call could only accept or reject the readers' tokens. So each outlined mark is now asked which token is printed here, among the readers' tokens, DINOv3's, the same base with other brackets or letters (glyph_composites.csv) and the confusion partners measured on the other gold pages (leave-page-out), with none of these and cannot tell; a disputed claim with no box keeps the printed / not printed question. Two sets join the call, each reported beside the bar, not under it: the agreed claims DINOv3 disagrees with (38 on the pair, 14 of them false; baseline accept the agreement), and the agreed marks DINOv3 leaves unconfirmed inside parts already sent (no extra calls). Also reported: gold claims both readers miss that Opus names (recovered) and tokens it names that no reader and no gold has (invented). The bar and its 106 claims are unchanged. So 65 parts and 177 questions (152 marks, 25 presence), ≈ $1.5–3.8 for the three repeats; three of the four unreached claims now have the right token on offer, the fourth (0451 Trutnov 30,1, never boxed) is not sent. The thresholds turn D-89's cost test into a quality test. 90 % on settled claims: disputes are 13 % of claims, so an arbiter that errs on one in ten adds ≈ 1.3 % of claims in error where always-Gemini adds ≈ 5 %; any lower and the author would have to re-check its rulings, which is the hand work it was meant to save. 70 % settled: below it the ≈ 6,000 volume disputes still leave more than ≈ 1,800 hand rulings (≈ 5–7 h at D-97's 10–14 s), and the saving no longer clearly covers a second route. 15 points: smaller is inside the run-to-run spread of the disputes (104–132 a pair). The lean test is D-72's open worry, since Opus is Haiku's family. Stability, because a ruling that changes between repeats cannot be replayed (D-68). Blind to: 106 claims on twelve pages; the images are the gold section images, not the crops the volume run will cut; the boxes are E3's alignment of the readers' claims and the exemplars are its alignment of the gold's (boxes_gold.csv), not hand cuts; a claim with no box is shown its entry band only; shared errors never become disputes, so nothing here reaches the castle/poorhouse blind spot (D-97); Opus cannot be read at temperature 0. The wider choice puts the right token in front of Opus, it does not make Opus pick it; and a token offered as a partner can pull it to a wrong answer it would not have reached alone — which invented counts. Bar set; measured — passes (D-100) utils/lexikon_1910/run_verify_test_b.py; notebooks/12_verify_test_b.ipynb; output/verify_test_b/
D-99 2026-10-21 The readers are shown a contrast sheet — the look-alike pairs they swap, set side by side in cuts from the calibration pages — and it is kept only if, on the purposive pages it never saw, it halves the swaps it shows without adding errors. The sheet (build_contrast_sheet.py) takes every pair the r5 readers swapped at least three times on calibration pages against the gold (e3_verdicts_r5.csv; castle/poorhouse 230, gamekeeper/huntsman 71, customs/revenue office 22, canteen/transport station 21, station/stop 20, … ruin/solitary house 3), 21 pairs — three, not five, so ruin/solitary house, half of the claims no reader reached, is on it; a few rows are the locator boxing the glyph beside a claim (fire brigade/savings bank) and are kept by rule, each token as up to three cuts from calibration gold boxes at printed size (the plate's glyph where none is cut). It is sent between the key and the crop (--notes-contrast, lexikon_1910_vision 0.5.0: the prompt names the page the third image), cached with the key for Haiku. The arm: Gemini 3.8 thinking off and haiku55nh, as in r5, three repeats each, on the same twelve gold pages and crops (run_contrast_arm.py). Scored on the four purposive pages only (0118, 0270, 0450, 0451), settlement level, on the entries every compared pass and the gold read; calibration is in-sample for the sheet and reported, never judged. The bar, per reader, against its own three r5 passes: (1) sheet swaps — a gold claim missed while the pass makes its sheet partner in the same part — fall by at least half, mean of the repeats; (2) all errors (missed + false) rise by no more than 10 % — not do not rise, since r5's own repeats run 19–39 errors on these four pages and an exact bar would fail a sheet that does nothing half the time; (3) F1 falls by no more than 0.005. A reader that clears all three gets the sheet at the freeze; the pair's shared errors and disputes are reported beside it, since they set what Opus and the author see. The r5 errors are look-alike swaps both families make (D-97), and a key that draws each symbol once, larger than printed, does not show the difference at printed size. Whole worked examples would show each glyph once, with nothing beside it. Calibration-only, because the purposive pages are the development test until the freeze; at the freeze the sheet is rebuilt from all non-certification gold (the gold teaches every learning component then; certification stays sealed, D-55). r5 makes 5.3 sheet swaps a pass on the purposive pages for Gemini and 7.3 for Haiku, so a halving is two or three swaps fewer a pass. Halving, because one repeat to the next moves a reader's errors by a handful and a smaller drop on four pages would not be told apart from that. Blind to: four pages and their few castles; the pairs are chosen from the readers' own swaps, so a confusion they have not yet made is not on the sheet; a third image can pull attention from the crop, which bar (2) catches only in total; Haiku 5.5 cannot be read at temperature 0. Bar set; measured (D-102) utils/lexikon_1910/build_contrast_sheet.py; utils/lexikon_1910/_archive/run_contrast_arm.py; notebooks/13_contrast_arm.ipynb
D-100 2026-10-21 Test B passes every D-98 bar by a wide margin: Opus 5.5 at effort low enters the route as the verify call, and the author rules only what it leaves open. On the 106 claims gemini38_r5atlasntg1 and haiku55nh_r5atlasg1 dispute (three repeats, batched and cached, 195 calls, $1.58): settled 0.991, right on 0.981 of what it settles, Opus, else Gemini 0.972 against always-Gemini 0.632 — a gain of +0.340 [+0.200, +0.490], page-bootstrap — lean to Haiku −0.011, and the same answer in all three repeats on every claim (3 of 177 questions changed answer, none a scored claim). Its errors: one printed that is an attribution slip (0019 Aš 6, the manor farm is printed, for the part Schafhütten — a mark question asks what is printed, not for which part, and its own note on the next question says so), one filial church it does not see (0076 Duba 12), one savings bank it cannot tell (0270 Most 25). Beside the bar: on the 38 agreed claims DINOv3 doubts (14 false), Opus, else accept is 0.921 against 0.632 for accepting the agreement — it removes all 14 false claims and 3 true ones (0019 Aš 1 tourist hut, 0196 Nusle 6,1 pharmacy, 0270 Most 28,2 midwife); of the 21 gold claims neither reader made in the parts sent it names 19 — but not ♁H at Jičín 12 or the ruin at Rychnov 8,1, the two the wider choice was added for, though both were on offer; and it names 1 token a repeat that no reader placed and the gold lacks. ≈ $0.008 a call, so the Czech volume's ≈ 6,000 disputes are ≈ $30 a reading. Every bar cleared with room: accuracy 0.981 against 0.90, settled 0.991 against 0.70, the gain's lower bound +0.200 against +0.15. The cost test of D-89 is settled with it — $30 a reading against 17–24 hours by hand — and the author's hours go to what the route still cannot reach: the shared misses no reader or encoder flags (one on the gold, 0451), Opus's cannot tell, and a sample of its rulings. Scorer corrections before this was written (run_verify_test_b.py 1.1.1–1.1.2): recovered and invented counted tokens the answer mapping keeps, not only those Opus chose, and counted part-type marks of the parts section (a part printed as a mill or a house group), which the readers place but the institution inventory does not score, as inventions — 157 a repeat, then 16, now 1; the D-98 measures were unaffected. Blind to: 106 + 38 claims on twelve pages; the images are the gold section images, not the crops of the volume run; a mark question cannot catch a mark filed under the wrong part, so attribution needs its own check; the look-alike options did not reach the two hardest swaps, which stay with the contrast arm (D-99) and the author. Decided: Opus enters the route; attribution and the two hard swaps open output/verify_test_b/test_b.txt, answers_raw.csv, usage.csv; notebooks/12_verify_test_b.ipynb
D-101 2026-10-21 The verify call gets a second question per outlined mark — whose mark is it — tried as a test-B arm, and kept only if it does no harm. Options: 0 (an institution of the municipality as a whole), a numbered part (an institution of that part), type:<name> (the type mark of a part: the one mark after a part's name that says what kind of place it is — a mill, a manor farm, a hamlet — and no institution), or cannot tell. The parts and their type marks come from the readers' own parts lists, as in the volume run; the question is asked only where there is more than one answer. A mark filed under another part moves there; a type mark leaves the inventory (run_verify_test_b.py 1.2.0 --arm part, the same 65 parts and three repeats as D-100, answers in output/verify_test_b_part). The bar: the D-98 bars still hold; accuracy on settled disputes falls no more than 0.01 below the base arm's 0.981; and the whose-rulings that go wrong (a true claim moved away or ruled a type mark) are at most one a repeat and no more than those that go right. D-100's one wrong printed (0019 Aš 6) was a part's type mark, Schafhütten's manor farm, taken for an institution: in that municipality every part files under 0, so asking only which part could not have fixed it. On the r5 gold both kinds of slip are rare — a type mark claimed as an institution about once a Haiku pass and never in Gemini's, a correct mark under the wrong numbered part about once a pass in either — against 35–48 false claims a pass. So the gold can show that the question does no harm, hardly that it helps, and the bar is written that way. Blind to: one or two slips on twelve pages; a part list a reader got wrong offers wrong options; a part with no number cannot be told from the municipality as a whole, so only its type mark is reachable. Bar set; measured — fails (D-110) utils/lexikon_1910/run_verify_test_b.py; notebooks/12_verify_test_b.ipynb
D-102 2026-10-21 The contrast sheet goes to Haiku and not to Gemini (D-99 bar, purposive pages). Haiku nh: sheet swaps 7.33 → 3.00, errors 27.3 → 26.3, F1 0.963 → 0.965 — passes. Gemini 3.8: swaps 5.33 → 4.00 (not halved), though all errors fell 28.3 → 19.0 and F1 0.962 → 0.975 — fails bar (1). The pair's shared errors on purposive fell 6.7 → 2.7 a repeat, disputes 42.3 → 40.0. By the bar as written. But on the calibration pages (in-sample, not judged) Gemini got worse with the sheet, errors 50.7 → 62.0: castle read as poorhouse rose (false poorhouse 37 → 49, missed castle 50 → 55 over three passes) and false savings banks 14 → 27 — so the sheet does not fix Gemini's main blind spot and can push it the wrong way; Haiku improved there too (74 → 57). Open: why Gemini's castle/poorhouse worsens with the cuts beside it. Measured; Haiku gets the sheet at the freeze, Gemini does not output/lexikon_1910_pilot/bakeoff/contrast_arm_r5.{txt,csv}; notebooks/13_contrast_arm.ipynb
D-103 2026-10-22 Sheet v2 for Gemini: each token's cuts span print weight and look-alikes are paired from the same page; Gemini alone is re-read with it, and it is kept only if it clears D-99 and finds no fewer calibration castles than r5. Why v1 failed Gemini (D-102): ink varies by page, and inside a page a castle's roof is lighter than a poorhouse's (0019 0.31 against 0.38, 0376 0.25 against 0.39, 0436 0.33 against 0.42 — the share of dark pixels in the roof), but across pages the darkness overlaps (Nusle 0196 castles 0.36, Milevsko 0256 0.34, neither page printing a poorhouse). v1 cut all three castles from light pages (0019, 0076, 0136), so a heavy castle looked like the sheet's poorhouses: Gemini found 2 of the 57 calibration castle readings with it against 7 without, and read Nusle 3,1 and 3,2 as poorhouses in all three passes. Haiku, judging the roof's shape, gained (22 → 43 castle readings of 72). Resolution was ruled out: Gemini spends ≈ 1,150 tokens on the sheet (≈ 0.54 scale), and at that scale the roofs still differ. v2 (build_contrast_sheet.py 1.2.0, contrast_sheet_v2.png): per pair, up to two columns cut from pages that print both tokens — the lightest and the heaviest such page — then each token's heaviest remaining cut; the page number under every cut; calibration pages only, as v1. The re-read: gemini38_r5atlasct2ntg{1,2,3}, otherwise as the contrast arm. The bar: D-99's three on the purposive pages against r5 (sheet swaps at most half, errors at most +10 %, F1 within 0.005), and on the calibration pages castle readings found ≥ r5's 7 of 57. The fix targets the cue the failure points to, darkness, and changes nothing else. The calibration condition is in-sample (the cuts are calibration cuts), so it can only show the sheet no longer misleads on the pages it was cut from, not that it generalises — the purposive bar still decides. Blind to: a cut chosen by ink share inherits whatever else sits in its box; three repeats of Gemini; 7 of 57 is a low floor. Bar set; measured — fails bar 1 (D-106) utils/lexikon_1910/build_contrast_sheet.py; utils/lexikon_1910/_archive/run_contrast_arm.py; notebooks/13_contrast_arm.ipynb
D-104 2026-10-22 The Böhmen gold serves Mähren and Schlesien: the German Böhmen gold is the German gold for all three German volumes and every learning component may use it, with a one-to-two-page spot check per volume before its error rate is trusted; and after the final run the author back-checks a sample of results against independent sources. The three volumes are one series (k.k. Statistische Zentralkommission, one legend and one entry layout), and the scans match: Böhmen 2178×3265 px and Mähren 2115×3303 px (both Universitätsbibliothek Wien), Schlesien 2232×3193 px (MSVK), against the Czech Čechy at 2922×4253. The spot check: one or two pages of each, transcribed as a gold sheet (≈ 40–60 minutes a page, D-67) and scored against the routed output; if the error rate falls within the Böhmen German range, the volume is read on the Böhmen gold alone. The back-check: after the frozen run, a sample of results set against what is independently known — a castle, a parish, a school, a railway station named in another source — weighted toward the look-alike tokens (castle/poorhouse, ruin/solitary house, ♁A/♁H, station/stop). The author's call (2026-10-22). It corrects the reading of D-67 given in the session: D-67 requires the German volumes to be certified on a German gold rather than the Czech one, not a gold per volume, and the Böhmen German gold is one. The spot check stays because a separate print run and a different library's scan can shift a reader, and the plan holds that nothing transfers untested. The back-check is the one test independent of the page itself: the gold and certification sheets measure the reading against the print, while a source outside the volume can catch an error the print and its transcriber share. Blind to: the spot check is one or two pages, so it can catch a large shift, not a small one; the back-check reaches only places another source records, and a source can be wrong or describe another year. Decided; the spot checks and the back-check wait on the frozen run reference/SYMBOL_ACCURACY_PLAN.md; reference/DATA_SOURCES.md #7b
D-105 2026-10-22 Local vision models are tried again as a third reader, not as arbiter: each vision model pulled on Heavyhorse6 reads the twelve gold pages once at r5 settings, temperature 0, and joins the route only if it reaches the errors the reader pair and DINOv3 leave without flooding Opus. The settings are r5's for the pair it is set against — the atlas variant of the gold kind (bakeoff.flags_for), the same crops, no contrast sheet — through the local daemon; one pass, since Ollama at temperature 0 repeats itself (D-72) (run_local_third_reader.py, notebooks/14_local_third_reader.ipynb). Scored as a third reader against gemini38_r5atlasntg1 × haiku55nh_r5atlasg1, settlement level, on the entries all read: the pair's shared errors (a gold claim both miss, a false claim both make) are reached when the local reader disagrees with the pair on them — it makes the missed claim, or leaves the false one out — since a three-way disagreement routes the claim to Opus; alarms are the pair's correct agreed claims it leaves out and the false claims only it makes. What counts is the shared errors DINOv3 does not already reach (an agreed claim it disagrees with, or a missed claim it names on a reader's box). The bar: (1) it reaches at least half of those; (2) its alarms are at most 10 % of the pair's agreed claims — each is an Opus question, ≈ $0.004 a claim (D-100), so ≈ $17 a Czech volume at the limit; (3) it read at least 90 % of the gold entries. A third reader is worth what it adds to the route, not its F1 (D-71). The old r3 passes show why it is worth asking again: qwen38_r3thinkg (F1 0.80 under r3's crops and decoder, where Gemini 3.7 scored 0.84 and later 0.96 at r5) disagreed with the pair on 26 of their 37 shared errors, at ≈ 313 alarms — and r5's crops lifted every reader it touched. A local reader costs no API money and replays exactly (D-68); the residue the route leaves on the gold is a handful of look-alike swaps, which is what it would have to reach. Blind to: "reached" by leaving a false claim out also credits a reader that simply sees less; twelve pages and a handful of residual errors; a model's encoder class is read from the bake-off file or taken as dynamic; GPU time is measured, not budgeted. Bar set; measured — all fail (D-113) utils/lexikon_1910/_archive/run_local_third_reader.py; notebooks/14_local_third_reader.ipynb
D-106 2026-10-22 Sheet v2 fixes Gemini's castles but still fails D-99's swap bar, so Gemini reads without a contrast sheet; Haiku keeps v1's (D-102). Gemini 3.8 with contrast_sheet_v2.png, three passes (gemini38_r5atlasct2ntg1–3), on the purposive pages against its r5 passes: sheet swaps 5.33 → 5.67 a pass (not halved — bar 1 fails), all errors 28.3 → 20.3 (−28 %), F1 0.962 → 0.973; on the calibration pages castle readings found 7 → 15 of 57 (v1: 2), so D-103's added condition holds, and errors 50.7 → 49.0 (v1: 62.0). By the bar as written, which was set before either run. Two things beside it, recorded and not acted on: the print-weight diagnosis of D-103 is confirmed — v2 undoes v1's harm on the calibration castles and doubles r5's — and both sheets cut Gemini's errors on the purposive pages by about a third (v1 −33 %, v2 −28 %) while leaving the swaps the bar counts (≈ 5 a pass, 16 over three) where they were. So the sheet helps Gemini, but not through the mechanism the bar tested. Changing the bar after seeing this would be choosing the test by its result; a new bar on total errors would need pages neither run has seen, and the only such pages are the sealed certification set (D-55). Open: whether to pre-register that test for the freeze. Measured; Gemini without a sheet, Haiku with v1 output/lexikon_1910_pilot/bakeoff/contrast_arm_r5_v2.txt; notebooks/13_contrast_arm.ipynb
D-107 2026-10-22 For the frozen run, the verify call shows the municipality's whole entry — every line, in both sections and across a section break — not a band around the asked marks. run_verify_test_b.py 1.3.0, entry_bands: anchored on every box the two readers placed for the municipality, any part; on each image the entry runs from the entry number above the topmost box to the next one, and is also bounded by the neighbouring municipalities' marks, the tighter winning each side; it carries over to the previous or next image of its section when it reaches an edge; 8 px above, 10 px below. A part with no reader box falls back to the whole section, as before. Checked on all 65 test-B parts against the gold's own mark positions: every mark of the municipality inside the bands in 64 of 64 with an anchor (the old bands cut 12 of 71 images), the 65th falls back; 5 also show the start of the next municipality's entry. The old band was the locator's lines for the municipality plus the asked boxes, and the locator labels too few lines: on Nusle's offices image none, so the band was the boxes alone and cut part 2's castle off. Opus is told the municipality's number and every band is captioned with it, so one neighbouring entry in view is a small risk next to a cut one, where a not printed is a ruling on lines it never saw. The entry numbers alone were not enough: they are not found on 8 of the 42 gold images (a heading or frame defeats the key-column rule), which is why the neighbours bound it too. Blind to: in the volume run the bands come from the readers' crops and their placed marks, not from gold section images; a reader's mark misplaced into another entry stretches or cuts the band; test B's two arms ran on the old bands, so its measured accuracy is for those. Decided for the frozen run utils/lexikon_1910/run_verify_test_b.py 1.3.0; tests/test_verify_test_b.py
D-108 2026-10-22 The frozen run reads every page once with Gemini 3.8 and three times with Haiku 5.5. Amends D-72's one read each. Measured on the twelve gold pages (881 claims, means over the combinations of r5 repeats): with one, two and three Haiku reads beside one Gemini read, the claims routed as disagreements are 114 / 164 / 201, the gold claims no read makes 18.7 / 14.0 / 10.7, and the false claims every read makes 14.4 / 9.3 / 6.7. Priced for all four volumes, batched (Gemini ≈ $6 and Haiku ≈ $2.10 a read per Bohemia-sized volume, Opus scaled from D-100 by claims routed, tables ≈ $25 a volume): ≈ $170 / $195 / $215. The author's call (2026-10-22). Haiku's repeats differ more than Gemini's, so its extra reads bring back marks the others miss (one Gemini + three Haiku leaves 10 missed by every read, three Gemini + one Haiku 19); the cost is mostly Opus, ≈ 50 more disagreements a read per 881 claims. Blind to: the counts are before the route — with one read each, DINOv3 and Opus already left only ≈ 4 errors on the gold, so what the extra reads add after it is a few errors per 881 claims at most and unmeasured; Opus was tested on one-read disagreements only (D-100), and three reads add more marginal ones. Decided this session; output/lexikon_1910_pilot/bakeoff/heavyhorse6/inventory_*_r5*
D-109 2026-10-22 The frozen run keeps a history of every mark: what each step read it as, in order, so that how a glyph's reading changed through the pipeline can be read back and counted. One append-only ledger per edition; a row is one step's reading of one mark. A mark is a box on the page where the locator placed one (image and box), otherwise the part and token. The steps, in order: each reader's read (Gemini, Haiku ×3), the order rule's flag, DINOv3's name for the box, the Czech–German comparison, Opus's answer (each repeat), the author's ruling, and the final value. Each row carries what was read (token, absent, cannot tell), who read it (model and repeat, encoder, rule, author), a pointer to the evidence (batch request id, crop, cached reply), and the versions in force (code, model, prompt and sheet digests, the freeze manifest). The final inventory is computed from the ledger, never edited beside it. Reported from it: each mark's trail, and per step how many marks it changed and from which token to which (castle → poorhouse, and back). The author's call (2026-10-22). Every stage already writes its own file and none overwrites another (D-38, D-55), but nothing joins them per mark, so "what did Opus overturn", "where did DINOv3 and the readers part" or "which castles were fixed at which step" cannot be read off directly. It also gives the paper its account of the route — the transitions each step makes — and lets the validation sample be stratified by a mark's path (open literature point, stratified PPI). Blind to: a mark no reader claimed and the locator never boxed has no row until someone finds it; marks are joined across readers by box, so a box placed differently by two readers can split one glyph into two marks. Decided; built 2026-10-22 and filled from the r5 gold run (1,581 marks, 9,938 events: the reading of record right on 1,479, the route's final value on 1,520; Opus changed 57 marks, 48 to the gold's value) utils/lexikon_1910/lexikon_1910_ledger.py; utils/lexikon_1910/build_mark_ledger.py; output/mark_ledger/
D-110 2026-10-22 The whose question fails D-101: the verify call stays as in D-100, with the whole entry of D-107. Part arm, three repeats, 195 calls, $1.23: the D-98 bars still hold (settled 0.991, right on 0.975 of settled — within 0.01 of the base arm's 0.981 — gain +0.333 [+0.203, +0.475], lean −0.001, stable 0.972); but the whose rulings go wrong 6 times a repeat against the bar's 1: 5 moves a repeat, all wrong, and 20.7 type rulings, 19.7 right. By the bar as written. The moves all went one way, to 0 (the municipality as a whole): Rychnov 12 part 1 poorhouse, Most 28 part 2 fire brigade, Nusle 7 part 2 steamer and telegraph — and for Most 28 and Rychnov 12 the right part was not even on offer, because the options come from the readers' parts lists and those did not number it. So the question as built offers wrong answers and Opus leans to the safe-sounding one. The type rulings were mostly right but mostly moot: nearly all were part-type marks the institution inventory never counted (D-100's invented fix). Not worth a second arm for one slip a pass (D-101's own measurement). Measured; not adopted output/verify_test_b_part/test_b.txt; notebooks/12_verify_test_b.ipynb
D-111 2026-10-22 In the frozen run Opus answers each question once; a cannot tell goes to the author on the Token Review page. Test B's three repeats were a measurement of Opus's stability, not part of the route: in the base arm all three agreed on every scored claim (3 of 177 questions changed answer, none a scored claim; the part arm 0.972). The cost estimates (≈ $30–45 of Opus a volume, ≈ $215 for all four, D-108) already assume one call; three would take the total to ≈ $440. Cannot tell was 0.9 % of disputes on the gold, ≈ 60 rulings for the Czech volume. The author's call (2026-10-22). Blind to: stability was measured on the gold's disputes at effort low; it is not re-measured during the run. Decided output/verify_test_b/test_b.txt
D-112 2026-10-22 The finalised pipeline runs from one notebook, every stage in order: freeze, read (Gemini ×1, Haiku ×3, marks and tables, batched), check (locator, DINOv3, order rule), compare (readers; Czech against German), Opus (once a question, whole entry, look-alike options), the ruling queue for cannot tell, the merge into the mark ledger and the final inventory, the score against the gold and the validation sample. Two switches pick what it runs on — the edition (Czech Bohemia; German Bohemia, Moravia, Silesia) and the pages (gold, certification, the full volume) — and one spend flag; every stage resumes where it stopped and writes to the ledger (D-109). The notebooks 10–14 stay as the record of the tests that set its parts. The author's call (2026-10-22), and the project's rule for every stage (CLAUDE.md: one notebook per stage, run top to bottom without manual steps, feeding the final master notebook). One notebook is also what makes the frozen run one run: the gold pass and the volume pass are the same cells with a different page set, so nothing is tuned between them. Built 2026-10-22 for the Czech edition on the gold (run_frozen.py 0.1.0, rehearsed on the r5 passes: 81 Opus calls ≈ $0.65–1.62); the certification pages, the full volume and the German editions stop at the guard until the section crops, reader batching and the German E3 are built. Notebooks 04–14 archived utils/lexikon_1910/run_frozen.py; notebooks/15_frozen_run.ipynb
D-113 2026-10-22 No local model joins the route: all six pulled vision models fail D-105. Each read the twelve gold pages once at r5 settings on Heavyhorse6 (10–28 min a pass). Against the Gemini × Haiku pair, of the 5 shared errors DINOv3 does not already route: Qwen 3.6 reaches 4 but with alarms on 67 % of the pair's agreed claims; Qwen 3.5 35B 3 (67 %); Muse Glimmer 3 (44 %, read 85 % of entries); Gemma 4 31B 2 (47 %); Qwen 3.8 27B 1 (19 %), the best reader of them at F1 0.897 (0.80 at r3); Nemotron 3 33B 0 (read 60 % of entries, F1 0.06). The bar asked ≥ half of the residue at ≤ 10 % alarms. By the bar as written. The trade is the one D-105 foresaw: the models that reach the residue reach it by disagreeing with everything — Qwen 3.6's 542 alarms on 881 claims would be ≈ 25,000 extra Opus questions a Czech volume — and the one accurate enough to stay quiet (Qwen 3.8) shares the pair's blind spots. r5's crops lifted Qwen 3.8 from 0.80 to 0.90, short of Haiku's 0.94, which costs ≈ $2 a read. Measured; not adopted output/lexikon_1910_pilot/bakeoff/local_third_reader_r5.{txt,csv,_timings.json}; notebooks/14_local_third_reader.ipynb
D-114 2026-10-22 The frozen run is finished for the Czech edition: one notebook reads the gold, the certification pages or the whole volume with the same cells — the readers batched, the check on any page. run_frozen.py 0.2.0, notebooks/15_frozen_run.ipynb. (a) The section crops are saved as the readers read them (--notes-save-sections, section_images.py): each section and column of the page, enlarged ×2 and named as the gold's (make_gold_worksheet), with an index. (b) E3 runs on any page (run_e3_pages.py): sections from the saved crops and the readers' own municipality lists, one locator trained on every calibration page, the mark detector fitted on every calibration section at the gold run's threshold, DINOv3's bank of every gold glyph with one pooled not in the bank threshold. The route, the Opus crops (D-107) and the ledger use the saved crops for every page set, the gold included, so the gold run tests exactly what reads the volume. (c) The readers are batched (--batch-prepare, reader_batches.py): each uncached crop is queued, sent as a batch — Claude through Message Batches, Gemini through its batch mode (upload by google-genai) — and the replies written into the crop cache, so a last pass reads every crop from it; a reply that fails is not cached and that crop is read live. (d) Page sets: gold (the 13 gold-sweep scans), certification (the sealed half of the drawn sample, read here and never scored here), full (scans 15–494); each its own labels (…g, …c, …v). Rehearsed on the r5 passes with the gold images standing in for saved crops: E3 on any page placed 1,519 of the reader of record's marks with 110 unplaced (the gold-trained run: 1,472 and 120); 77 Opus calls priced at $0.60–1.52; ledger, inventory, score and sample built; the notebook runs top to bottom with spending off; 95 tests pass. D-112 asked for one notebook for gold and volume; the gold section images existed only for the gold, so the check and the Opus crops could not have run on the volume, and the readers' live prices were twice D-108's. Blind to: Gemini's batch mode is untried here — Google documents the OpenAI-compatible batch but not whether it takes our structured output and thinking-off settings (a refused setting costs the batch and reads those crops live); the table has no crop cache and is read live (≤ $41 a Czech volume); the saved crops are cut by the image geometry, the gold's were cut by the text layer, so the bank was learned on slightly different cuts; on saved crops the ledger has no gold per box, so the gold run is scored on claims; the German editions stop at the guard. Built; the gold run is next utils/lexikon_1910/run_frozen.py; section_images.py; reader_batches.py; run_e3_pages.py; notebooks/15_frozen_run.ipynb
D-115 2026-10-22 The frozen run reads the three German editions, batches the table too, gates DINOv3 on the German gold, sets the two editions of Bohemia side by side unreconciled, and sends the author's queue to the Token Review page. run_frozen.py 0.3.0, notebooks/15_frozen_run.ipynb (17 sections). (a) German reading (run_r5_german_notes.py 1.1.0, frozen_pass): any pages of Böhmen, Mähren or Schlesien, cut on the image as in D-92, read by the route's readers — Gemini 3.8 once, thinking off, and haiku55nh three times with the contrast sheet after the key (the Czech-cut v1 sheet: the German volumes print the same pictograms, D-104, and the German gold run measures whether Haiku reads them as well with it). The reader of record saves its sections under the Czech court's slug, enlarged by 2 / the page's scale (≈ 2.62 on Böhmen) so the print is the size E3 learned it at; a pass writes institutions_<label>.csv; passes go to bakeoff/<host>/de/, which every lookup now also searches (bakeoff.find_pass_file). Mähren and Schlesien run full only; the German sample has no certification half yet. (b) The table batched (pilot channel A): a cache keyed like the crop cache, and --batch-prepare queues each uncached block; reader_batches 1.1.0 reads the reply under the reader's own key (rows). The German table has no reader: a German run reads the apparatus only, and says so. (c) The E3 gate, written before any German page was checked: on the German gold, DINOv3 (bank built on the Czech gold) routes German marks to Opus only if it keeps the reader of record's true claims at most 5 points less often than on the Czech gold, and catches its false claims at most 10 points less often (the second judged on ≥ 10 false claims); otherwise its verdicts are in the ledger and route nothing. The Böhmen gold decides for all three volumes. (d) Opus on German pages sees the German key, the German plate's glyphs and the German volume's name; the look-alike list stays the Czech gold's. (e) The two editions of Bohemia (compare_editions): municipalities paired by court and printed name (lexikon_1910_editions.pair_units, one to one, ambiguous names refused), compared on the symbols both plates print, written to editions_diff.csv and as a cz_de event on every paired mark in both ledgers — neither final value moves. (f) The author's queue: every mark Opus could not tell (a ruling, back as an author event) and the validation sample (blind: no reader's or the route's reading shown; back as answers scored against the route, never written into the inventory), as a Token Review queue with its crops (review_queue, apply_review, score_validation). Also fixed: the ledger always dropped scan 0449 (the readers' worked example) — right for a gold score, but on the volume it would have dropped the page from the final inventory (build_mark_ledger 1.2.0); [?], [!] and cannot tell are never a final claim. The author's call (2026-10-22: wire everything we want into 15 first). The gate is there because D-104 lets the Böhmen gold serve the German volumes, but DINOv3's bank was cut from the Czech print and nothing transfers untested; without the Czech gold run's E3 on disk there is no baseline, so the German gold run comes second. The editions stay unreconciled because the two-measurement analysis (D-58) needs each edition's errors as they are. Rehearsed on the r5 gold with nothing sent: F1 0.957 without Opus as before, review queue built; the German path cut, saved and queued scan 142 offline (Gemini: key + crop; Haiku: key + sheet + crop, the contrast note in the prompt). Blind to: no German page has been read through this path, so the German Haiku-with-sheet arm, the German section crops under E3 and the gate itself are unmeasured; the German table is unread; the edition comparison pairs municipalities only where the two printed names share a form, and the readers' part-0 names are its locator. Built; the gold runs wait on Heavyhorse6 utils/lexikon_1910/run_frozen.py 0.3.0; run_r5_german_notes.py 1.1.0; reader_batches.py 1.1.0; build_mark_ledger.py 1.2.0; tests/test_frozen_run.py
D-116 2026-10-22 EDITION = "bohemia" runs both editions of Bohemia through the frozen run in one pass: Czech first, then German, every section in turn, read in the same batch window and cross-checked in section 14. run_frozen.py 0.4.0 (editions_for, BUNDLES); notebooks/15_frozen_run.ipynb loops every section over the editions the setting names. Certification stays Czech-only (the German sample has no certification half). The author's call (2026-10-22): the two editions are run to be cross-checked, so they should be run together. Czech first because the German E3 gate (D-115) is held to the Czech gold's E3. Blind to: nothing new is measured; it changes the order of work, not what is done. Built; checked spend-free on the laptop utils/lexikon_1910/run_frozen.py 0.4.0; tests/test_frozen_run.py
D-117 2026-10-23 Gemini, the reader of record, is read live, not batched; Haiku stays batched. Gemini's OpenAI-compatible batch endpoint refuses the structured-output field the reader of record sends live — response_format: {"type": "json_schema", …} → 400 no such field: 'type' on the first frozen gold submit (2026-10-23; Google's page does not say which request fields its batch mode takes). reader_batches 1.3.0 (GEMINI_BATCH = False) leaves Gemini's queued crops unsent, and the finishing pass reads them live into the same cache. Fixed on the way (1.2.0): the Gemini client is held for the whole upload (built inline, it was collected mid-call and closed its connection), the batch state is saved after every batch sent, and a failed upload leaves no copy behind. Sending the batch without response_format, or through Gemini's native batch API with a hand-mapped thinking setting, would make the reader of record a reader nobody measured; Gemini 3 thinking cannot be switched off natively, and the compatibility layer's reasoning_effort: none has no documented native equivalent. Cost: Gemini at list price rather than half — ≈ +$0.6 on the Bohemia gold, ≈ +$6 a Bohemia-sized volume (D-108's ≈ $6 a read batched). Blind to: whether the batch endpoint takes structured output in some other form; worth a one-request probe before the full run, if the saving matters. Decided utils/lexikon_1910/reader_batches.py 1.3.0
D-118 2026-10-23 The first frozen gold run: Czech settles at F1 0.995; the German route lost claims because the Czech-trained locator misplaces German boxes and the E3 gate missed it. Three fixes: Opus's "none of these" on an outlined box is no ruling; the gate also requires coverage; where it fails, Opus is asked token questions only. Heavyhorse6, EDITION = "bohemia", ≈ $1.25 of Opus. Czech (881 gold claims, 171 entries): Gemini alone F1 0.955 (31 false, 48 missed), the route 0.995 (2 false, 7 missed); 221 Opus answers in 75 calls, none cannot tell. German (343 claims, 72 entries): Gemini alone 0.941, the route 0.899 (5 false, 59 missed). Cause: on the German sections the locator puts boxes on entry numbers, Roman numerals and the lines above (DINOv3 not in the bank 246, agree 48, against 47 and 802 on Czech), so Opus answered none of these 88 times — rightly — and the ledger read that as absent, deleting 46 true claims. The gate passed because it compared only the 50 claims DINOv3 could judge. Fixed: (a) lexikon_1910_ledger 1.1.0 NO_RULING: none of these on a box rules on nothing, and the mark is settled as if Opus had not been asked (build_mark_ledger 1.3.0); a token question's answer now governs every mark of the part a reader read as that token; (b) the gate also requires DINOv3 to judge a share of the reader of record's claims at most 10 points below the Czech share (German 0.121 vs 0.860: fails); (c) where the gate fails, every disputed or flagged token is asked as is it printed for this part? with the whole entry in view, no outlined box (run_verify_test_b 1.7.0 BOXES, run_frozen 0.5.0). Replayed offline: Czech unchanged at 0.995 under (a); German with the old box answers and (a) 0.953. The author's call (2026-10-23). None of these means the outline is on no offered mark, which on a misplaced box says nothing about the claim. ⚠ The German gold is no longer a clean test: (a) and (c) were chosen after reading it, and the German sample has no certification half to check them on. Blind to: whether token questions settle German disputes as well as box questions settled Czech ones (unmeasured until the re-run); a German locator would need clicked boxes on the German gold pages. Decided; German sections 8–16 to re-run on Heavyhorse6 (the box-mode answers kept in opus_boxes_2026-10-23/) utils/lexikon_1910/lexikon_1910_ledger.py 1.1.0; utils/lexikon_1910/run_frozen.py 0.5.0; run_verify_test_b.py 1.7.0; build_mark_ledger.py 1.3.0
D-119 2026-10-24 All code lives under utils/; reference/ holds data only. The 1910 register is one package, utils/lexikon_1910/ (its 14 library modules, moved from utils/parsing/, and the 31 scripts that drive them, moved from reference/places/); finished experiments and one-off measurements are in utils/lexikon_1910/_archive/; the scripts that build and check the curated tables of reference/<topic>/ are in utils/reference_tools/<topic>/. Archived (kept runnable, off the docs site): the bake-off (run_bakeoff, compare_symbol_passes), the finished test arms (run_contrast_arm, run_local_third_reader, run_glyph_independent), make_transcription_worksheet (superseded by make_gold_worksheet), the eight measure_* one-offs, and lexikon_1910_symbols (the glyph matcher that failed at entry size, 2026-08-06). Kept active because the route imports it: run_glyph_test_a (its encoders), run_e1_gemini_version, run_e1b_second_reader, run_table_arm (their flags). 598 path references rewritten in 143 files, byte for byte; notebook cell outputs and output/ left as written. The author's request (2026-10-23): the code was spread over reference/places, reference/occupations, reference/units, reference/education, reference/parties and reference/background, against the repository's own rule that reference/ holds curated artifacts. A script keeps its depth under utils/<package>/, so its ROOT line holds; the archive and the topic folders sit one level deeper and their roots were moved by one. Verified: every moved module imports as it runs (84 of 86; two CLI tools need arguments and compile), every Python file parses, all 359 tests pass, the docs site builds, notebook 15 runs sections 1–7 spending nothing. Blind to: a script run by hand from an old note or shell history with its old path; the archived notebooks' cell outputs still print old paths. Done reference/AUDIT_2026-10-24.md §A; CLAUDE.md
D-120 2026-10-24 The frozen route's defects found by the 2026-10-24 audit are fixed: a box answer naming the majority token of another mark of the same section rules on nothing; a mark takes its part from a reader's claim; a token answer rules on the claim; a mark Opus settled nothing on is open unless the readers agree; the final inventory is one row per claim with its printed count; sealed pages never reach the author early. (1) Box answers (build_mark_ledger 1.4.0): Opus's token is no ruling when it is the readers' majority reading at another mark of the same section and part — E3 had filed the claim on the neighbouring glyph (3 of the Czech gold's 7 misses); a minority reading elsewhere, or the token as a part's type in the list of parts, does not count. (2) Part of a mark: from a reader's claim, the reader of record first; Gemini's unclaimed rows only where no reader claimed the box (both Czech false claims). (3) Token questions rule on the claim (part, token) over every mark of the part (lexikon_1910_ledger.claim_rulings 1.2.0); spreading them over boxes let "T not printed" erase another token a misplaced German box held. (4) Unsettled marks ("cannot tell", "none of these", no ruling): the readers' value if all agree, else open — the author's queue (D-111) — where D-118 had let the reader of record decide. (5) Opus items (run_verify_test_b 1.8.0): a call never goes without a page image (the fallback searched the Czech gold only); untrusted boxes give the whole section; order-only flags are asked as token questions. (6) Answers to changed questions are moved aside and re-asked, never re-applied. (7) German courts (lexikon_1910_de_notes 0.2.0): heading abbreviations written out (Böhm. Aicha); the two editions paired by the backbone's court_id, so the Czech register's Maršov meets Horní Maršov. (8) Final inventory (run_frozen 0.6.0): one row per claim with count and count_source. (9) Sealing: no validation or review item from a certification page; no validation item from the in-sample gold pages on a full run. (10) Freeze: once reads exist the contrast sheet is kept and a changed manifest archived (with package versions and host) — a laptop rebuild overwrote the gold run's sheet and manifest on 2026-10-23 before this rule. (11) A batch queue is named only when complete; no Opus call on the worked example. Replayed offline on the gold run's files: Czech 0.995 (2 false, 7 missed) → 0.999 (0 false, 2 missed), one of the two open for the author; German token-only with gold-answered questions (the ceiling) 0.975. Each is a confirmed defect, found by an independent review of the code and checked on the real outputs; the rules were tested on small invented cases (tests/test_route_fixes.py). A first version of (1) counted any reader's reading elsewhere and made the Czech result worse (0.994): it was narrowed to the majority reading of the same section before adoption. Measured and not adopted: routing DINOv3's unclaimed boxes to Opus — 52 on the Czech gold, 8 naming a gold claim, all 8 already held by the route. ⚠ The gold is in-sample for the route and for these fixes; only the sealed pages give its error rate. Blind to: German token-only answers are not yet measured (the re-run is due); the German gold reads keep the old court labels until re-assembled (no German gold page is affected). Done; German sections 8–16 to re-run on Heavyhorse6 reference/AUDIT_2026-10-24.md §B; utils/lexikon_1910/build_mark_ledger.py, lexikon_1910_ledger.py, run_frozen.py, run_verify_test_b.py, lexikon_1910_de_notes.py; tests/test_route_fixes.py
D-121 2026-10-24 Before the gold re-run: the printed count is the readers' majority; D-58's independence test runs in section 14 on the gold; and the German Böhmen edition gets a sealed certification half of four pages, drawn by rule. (1) Count (run_frozen.claim_counts): the most common count among the four reads, a tie going to the reader of record; a claim no reader made — Opus named the glyph — counts one per mark (count_for). On the Czech gold, right on 875 of 879 true claims against 866 for the reader of record first, and wrong on none the old rule got right. (2) Independence test (independence_test, gold only): lexikon_1910_editions.compare_editions on the route's two final inventories (the two measurements the analysis will use) and on the two readers of record, within symbol, written to editions_independence.csv/.txt — the D-58 test the r5 readers passed (D-97), now on the frozen outputs. (3) German certification, rule written before any page was looked at: the German twin of each sealed Czech page that the page map rates matched (not weak or ambiguous), each the German scan holding most of that Czech page's municipalities — scans 0189 (Nechanitz, twin of cz 0106: 8 of 15 names), 0165 (Hohenfurth, cz 0166: 3 of 7), 0356 (Winterberg, cz 0346: 5 of 12), 0441 (Strakonitz, cz 0406: 4 of 16); read whole; blank sheets cut (make_gold_worksheet_de.py --role certification). Sealed like the Czech half: outside every gold sweep and learning component, no validation or review item from them, read once by the frozen run. (4) Court keys (court_id): a slug the text layer damaged is matched by a unique prefix (≥ 6 letters) or a unique close fuzzy match (ratio ≥ 85, ≥ 10 above the next court) — the sealed Czech sheets file courts as mechanice, cliabarovice, novaraka, hluboka. The author's call (2026-10-24), after the audit's §B–D. The count rule was chosen after looking at the gold's 13 wrong counts (in-sample); it is the obvious rule — four reads outvote one, and a claim the readers never made has no reader count to copy. The twins follow D-67's own pairing (by court and name, never by page) and inherit the Czech pages' random draw (seed 1910), so no German page was chosen by eye; the four weak Czech twins are left out because their German side is not reliably the same municipalities. Blind to: four pages are few — they certify the German reading's headline, not its rare symbols (the audit's entry-level sample does that); the court fallbacks are untested beyond these slugs; the independence test on the gold covers 65 municipalities. Done; the German certification sheets await transcription utils/lexikon_1910/run_frozen.py 0.7.0; reference/places/gold_sample_pages_de.csv; transcription_gold/de/certification/; tests/test_route_fixes.py
D-122 2026-10-24 Every section of the register gets its 1910 court beside its label. lexikon_1910_courts resolves the court a heading prints, in steps, keeping the step: the backbone of the edition's land (exact, tail, prefix, close spelling — D-121's rule, Cyrillic look-alikes folded); the register's own names (reference/places/court_aliases.csv, 18 rows, each with the scans it was read off); the courts the page's running head prints; the courts of the okres the head names; and last the readers' own transcription of the heading (the crop audit, D-85), a court at least two readings name. Section 12 writes courts.csv and puts court_id and court_source in the final inventory; section 14 pairs the editions' municipalities by court id. Measured on the 1,248 headings of the Czech text layer: 1,247 resolve (backbone 1,145, alias 65, head 23, okres 14) and none lands outside the okresy its head names; the one left (scan 0138, damaged in the heading and the head alike) is the readers' to settle. On the 650 headings of the German volume's OCR 641 resolve, every close-spelling match checked right; the two gold runs' 18 Czech and 12 German sections all resolve, and section 12 replayed on the Czech gold keeps its score (0.999) and counts (875 of 879), with a court on all 879 claims. Two courts the register prints were created after the census and are filed under the one 1910 court each was cut from: Neustadt u Tafelfichte → Frýdlant (7 Ortsgemeinden, from 1 July 1913) and Kralupy nad Vltavou → Velvary (21, from 1 March 1912; Spezialortsrepertorium … Böhmen 1915, Änderungen C.1–2). The author's call (2026-10-24), after 122 of the 1,248 headings were found not to resolve: the court is what ties an institution to a 1910 court district, the census and the other edition (audit §C, place key). The okres check found what exact matching had been getting wrong: on the Broumov pages Teplice is Teplice nad Metují, on the Děčín pages Benešov is Benešov nad Ploučnicí, on the Krumlov pages Planá is Horní Planá — each another court's whole name — so a court of the head's own okres wins. Keying municipalities by label would have pooled the two Benešovs in the full run. Restricting the backbone to the edition's land settles Krumlov and Budějovice, which Moravia made ambiguous. The label stays every table's key and the sealed sheets are untouched (D-55); the court sits beside it. Blind to: a court the readers cannot read either stays unresolved (the gold runs left the readers' step nothing to do); the German side has no running head to use (no text layer); the alias table covers Böhmen only — Mähren and Schlesien get the backbone until their heads are measured. A wrong court costs nothing to fix later: section 12 recomputes it without reading. Done; 13 offline tests utils/lexikon_1910/lexikon_1910_courts.py 0.1.0; run_frozen.py 0.8.0; reference/places/court_aliases.csv; tests/test_court_key.py
D-123 2026-10-24 Notebook 15 runs unattended, and its batch queues live outside Dropbox. (1) 7c and section 11 wait until their batches have ended (run_frozen.wait_for: asks every 2 minutes, reports every 15 and the moment a batch ends or fails, gives up after 26 h; a failed poll is retried, an interrupted cell resumes when run again) — WAIT = True in section 1. (2) 7d will not finish the passes while a reader batch is still processing. (3) An Opus call whose reply failed is sent once more and waited for. (4) The queues — every request with its images, ≈ 135 MB a Czech page on the gold (1.75 GB for 13 pages), ≈ 150 GB for the four volumes — are written to lexikon_1910_queues beside the Dropbox folder on the same drive (LEXIKON_QUEUE_ROOT overrides); 7a sends each pass as soon as its queue is complete, and a line's images are dropped once a batch holds it, so the disk holds about one pass at a time. The author asked for a notebook that runs top to bottom on its own, stopping at each collect cell until its batches return (2026-10-24). The 7d guard closes a trap Run All had: 7d reads every crop not yet in the cache live, so reaching it while the batches were processing would have read them all at twice the price. The queue sizes were measured on the gold run's own queues (2026-10-24): left in Dropbox, the full run would have been copied to every machine — this laptop has 66 GB free. Blind to: a machine that sleeps stops the wait (Heavyhorse6's power settings); a crop a batch failed is still read live in 7d, by design; the queue folder holds no run outputs and may be deleted once the run's passes are finished. Done; 8 offline tests, the notebook executed end to end in a sandbox with SPEND = False run_frozen.py 0.8.0; notebooks/15_frozen_run.ipynb; tests/test_batch_wait.py
D-124 2026-10-24 Gemini goes through its native Batch API — the live reader's own requests, translated field by field — and is switched on only after an equivalence check on Heavyhorse6 has passed. reader_batches 1.4.0: to_native maps every field of the live chat-completions body (the system text to systemInstruction; each image to inlineData with the same base64, in order; temperature; max_tokens to maxOutputTokens; the strict json_schema to responseJsonSchema as sent; reasoning_effort: "none" to thinkingConfig {thinkingBudget: 0}) and refuses any other, and each line is checked against its live request before it is written — all 98 notes and 199 table requests of the Czech gold translate exactly. Input files stay under the File API's limits, a refused job stops only its own queue (its crops are read live), and queues are read a line at a time (so are run_frozen's count and slimming, D-123). The check (notebook 15, 7e; ≈ $0.66) sends 98 notes crops and 24 table blocks of the Czech gold through the batch and, alongside, once more live; it passes only if every request comes back, the prompt tokens equal the live ones, no reply thinks (≤ 200 thought tokens), every reply holds its schema, and the batch agrees with the earlier live reads no worse than the fresh live control (paired one-sided test, z = 1.96, ≥ 20 pairs a kind; EQUIVALENCE_BAR, written before anything was sent). GEMINI_BATCH is a setting of notebook 15, refused unless the check's verdict on file is PASS, and the freeze manifest records it. The author asked for Gemini to be truly batched (2026-10-24): about $45 over the four volumes. D-117's concern stands — a Gemini read without the measured request would be a reader nobody measured — so the request is the same request and the replies are measured before the switch. "Identical replies" could not be the bar: Gemini 3.6 Flash and newer ignore the temperature, and the four live reads of each gold request (byte-identical) agree on a crop in only 48.6 % of pairs (57 % the same day, 40 % two days apart), so the batch is held to a live read made alongside it. Built in its own session (gemini-native-batch) and reviewed here; 399 offline tests pass. Blind to: whether the native endpoint takes thinkingBudget: 0 on 3.8 (the check fails at once if not, after its ≈ $0.44 live control is spent); the account's batch token limit — 3M enqueued tokens on Tier 1 against ≈ 16M for a volume's notes — where a refused job leaves its crops to be read live, so the run is never stuck, only dearer; the German crops are covered by the same translation, not by a measurement of their own. Built; 29 offline tests; the check waits to be run on Heavyhorse6 utils/lexikon_1910/reader_batches.py 1.4.0; notebooks/15_frozen_run.ipynb (7e); tests/test_reader_batches_gemini.py
D-125 2026-10-24 A certification or full run says whether it reads on its gold re-run's frozen route. The freeze manifest now records a digest of every module of utils/lexikon_1910, version bumped or not, and when it was frozen. Section 5 of a certification or full run compares its freeze with its gold re-run's — the runner, the frozen components' versions and digests, every module's code, the packages, how Gemini reads and the machine — and prints the gold re-run's frozen route or names each difference, keeping the answer in freeze/against_gold.json; Mähren and Schlesien are held to the German Böhmen gold (D-104). The author's call (2026-10-24), with the certification run placed right after the gold re-run, on the frozen route (ROADMAP): a certification read on another route certifies that other route, and a version string is not bumped for every edit, so the check compares the code itself. It reports and does not stop the run: a change that cannot alter a reading — a fix to the batching or the waiting — is the author's to judge. Blind to: what lies outside the package and the recorded components (data in data/final other than the digested tables, the operating system), and a provider changing the model behind a fixed name. Done; 6 offline tests; the notebook executed in a sandbox in certification mode run_frozen.py 0.8.0; notebooks/15_frozen_run.ipynb (section 5); tests/test_freeze_check.py
D-126 2026-10-25 An image component is digested by its pixels as well as its bytes, and a certification or full run reads the gold re-run's own contrast-sheet file. run_frozen 0.9.0: _image_digest hashes mode, size and raw pixels; the manifest carries a pixels block beside digests; freeze_differences drops an image component whose picture is unchanged and against_gold.json records it under re_encoded; a freeze written before 0.9.0 is reconciled by reading the two sheets off disk, which is what the manifest's recorded path is for. freeze copies the gold re-run's sheet for a non-gold run instead of rebuilding it, contrast_pairs.csv with it. The Czech certification run of 2026-10-25 was reported as a different route from its gold re-run, naming the contrast sheet — and the sheet was the same one: pixel for pixel identical, 946,050 bytes against 945,996, same PNG chunk structure, no metadata chunks, same host, same packages, same code, same versions. The gold run had kept a sheet built on 24 October under runner 0.5.0 (D-120: once reads have begun the sheet stands); the certification run rebuilt it under 0.8.0. The digest was over the file, and the file is re-encoded whenever a run rebuilds it, so D-125's check was measuring the PNG encoder and would have failed every certification and full run from here. Copying the gold's file makes the seal's claim true of the bytes and not merely of the picture. Measured: replayed on the two manifests, the differences go from ['digests: contrast_sheet changed'] to none, pixel digests equal. Blind to: an image whose pixels are equal but whose colour profile or resolution metadata differs, which no reader sees either. Settled utils/lexikon_1910/run_frozen.py 0.9.0; output/frozen/cz_certification/freeze/against_gold.json
D-127 2026-10-25 The sealed pages are scored by one script whose metrics were written before any of them was scored, over two populations, with the entry keyed on its 1910 court; the Czech half reads F1 0.996 (95 % page-clustered interval 0.991–1.000). score_certification.py 1.0.0: claim-level precision, recall and F1 on the settlement key; a 95 % interval from a page-clustered bootstrap (10,000 draws, seed 1910); entry_exact; count accuracy; per-symbol sensitivity, and specificity over the entries where the gold does not print the token. Two populations are reported — as printed, every entry the sheets print, which is the headline, and shared entries, the rule the gold score uses — and claims the author settled are reported apart. It refuses an edition whose sheets are not all complete (the German four), a run that is not on disk, and a second scoring unless forced, which the record then states; it never writes the gold's own inventory_human.csv. Czech, 2026-10-25: 505 claims printed, 503 by the route, 502 right, 1 false, 3 missed; entries exact 104 of 107; count right on 501 of 502; every token with ten or more instances at sensitivity 1.000. The false claim and one miss are one mark — [school] for [school-exp] on 0286 6/3, where the register prints the plain glyph beside the word exp. and the sheet corrects it by decision; the others are a [vet] on 0286 and an [rc-filial] on the one entry of 107 the route never reached. The seal is spent the moment a metric is chosen after seeing how the route did, so the metrics are dated and fixed first (audit §D). Two populations because the score of record intersects the entries the gold and every reader reached: right for comparing readers, wrong for asking what the route will do to a volume, since it drops the entries the route never reached — on the German gold that is ten entries and 29 claims, and the same reading scores 0.945 shared against 0.905 as printed. The court is the key (D-122): the route keys an entry by the slug the text layer gave it, damaged and all — the sealed pages file Nechanice as mechanice and Nová Paka as novaraka (D-121) — while a sheet carries the transcriber's name, so the first scoring put 170 of 505 claims in the missing column and left two pages with no entry in common. Keyed on the court, 106 of 107 entries meet and the one that does not is a real absence. Validated before use on the gold, where the answer is known: 881 claims, 0 false, 2 missed, F1 0.9989, count right on 875 of 879 — the notebook's own figures. Blind to: a claim both the sheet and the route get wrong the same way; recall of a symbol the sheet itself missed; and eight pages are eight clusters, so the interval is the result and no symbol under ten instances carries a rate. Settled utils/lexikon_1910/score_certification.py 1.0.0; output/frozen/cz_certification/certification_score.{json,txt}
D-128 2026-10-25 A court whose parts section was cut and whose offices section was not is flagged beside the inventory, and the inventory carries an entry key built on the court rather than on the printed label. run_frozen 0.10.0: cut_sections reads the crops the reader of record saved and groups them by court id, never by the slug on the file; sections_missing_offices returns each (page, court) with osady and no offices, and build_ledger prints it beside the claim count. final_inventory writes entry = page/court/muni/settlement, falling back to the label where no court resolved; district stays as what the page prints. A section whose heading the page image misses runs on under the label of the one before it (D-38), and is_institution takes a claim only from a crop whose name says offices — so a missed offices heading does not misfile those claims, it deletes them. That is what German scan 0380 cost: the crop saved as rychnovnadkneznou_parts_3 holds the printed heading Ämter, Anstalten usw. im Gerichtsbezirk Reichenau an der Kněžna in its middle and every entry under it, and those 28 claims are 28 of the 61 the route never made on the German gold. Read by eye off the crop, 2026-10-25. The grouping is by court because a slug is not an identity: grouped by label the check also flagged sealed Czech 0286, which saves its parts as nova and its offices as novaraka, and German 0480, which saves Český Dub's parts as ceskydub and its offices under the German bohmaicha — both false. Grouped by court, one flag over three runs, and it is the real one. The entry key is the same lesson written into the table every consumer reads: keyed on the label, the first scoring of the sealed half lost 170 of 505 claims (D-127). Blind to: a missed heading between two sections of the same kind; a court printing no osady at all on the page; and the flag reports, it does not re-cut — recovering 0380 needs the geometry to find that heading, which changes the freeze. Settled (the check); the geometry that missed the heading is open utils/lexikon_1910/run_frozen.py 0.10.0; tests/test_section_audit.py (5 offline tests)
D-129 2026-10-25 German Bohemia is reconciled against the certified Czech inventory: on every municipality both editions read, a shared symbol on which the two final inventories differ is put to Opus as a token question on the German entry; and the editions are paired on every name a municipality prints, not on one. run_frozen 0.11.0 (anchor_claims, _anchor_items, edition_forms), lexikon_1910_editions 0.2.0 (pair_by_forms). (1) Pairing: a municipality is known by every part name the reader of record listed for it, and on the Czech side by its name off the table too; each Czech municipality takes the German one of its own court with the highest share of shared forms, kept only if strictly best on both sides. (2) The anchor: Czech-only claims are asked at the German part whose names match the Czech claim's part (part 0 is part 0; a part the names cannot match is asked as part 0 and says so); German-only claims at their own part. Opus is shown the German print only and is not told what the Czech edition says. The calls are separate (a-<part>), so no answered call is re-asked (D-111), and a claim the German route already put to Opus is not asked again. An anchored institution Opus rules printed enters the German inventory; not printed leaves it out; cannot tell goes to the author. The Czech inventory never changes. (3) editions_diff.csv gains both_read: a row where one edition has nothing for the municipality is a coverage gap, not a disagreement. The author's call (2026-10-25), replacing D-115(e)/D-116's never reconciled. The Czech route is certified (D-127: F1 0.996 on the sealed pages) and the German one is not; the two editions print the same institutions (the gold: 300 of 300 shared claims agree). With the Czech error rate that low, the two-measurement correction D-58 was kept for has little left to correct in Bohemia. The unreconciled German readings stay in the ledger, and D-58's test on the two readers of record (Gemini × Gemini) still runs; the two final inventories are no longer independent measurements. Replayed offline on the gold re-run (2026-10-25, nothing sent): pairing 22 → 49 municipalities, 12 → 30 read in both; 20 claims differ, 19 of them only Czech — every one on the German gold, and every claim the German route missed on those municipalities; the one German-only claim is not on the German gold. Of the 19, 12 had already been put to Opus by the German route and come back not printed or cannot tell — the view defect of D-130. Blind to: municipalities that print no list of parts are known only by their table name, which the German route does not read (30 of 88 German municipalities with claims on the gold), so they cannot pair; a part the names cannot match is asked as the whole municipality; nothing is measured on a paid run yet; the German certification (D-121) will measure the route with the anchor in, from the Czech certification run's own inventory (route output, never the Czech gold). Built; replayed offline; the German gold re-run on Heavyhorse6 next utils/lexikon_1910/run_frozen.py 0.11.0; lexikon_1910_editions.py 0.2.0; tests/test_cz_anchor.py
D-130 2026-10-25 An Opus call without trusted boxes shows the offices section for every part, not only for the municipality as a whole; and a call whose images changed has its old answers moved aside and is asked again. run_verify_test_b 1.9.0 (_section_list), run_frozen 0.11.0 (_retire_stale_answers compares the images shown; the manifest records them as shown). A numbered part's institutions are printed in the offices section under its own n), but the whole-section fallback — every German call, since E3 failed its gate (D-118) — showed a numbered part only the list of parts. Opus said so in its notes ("institution marks for part 1 are not in this parts listing") and answered not printed on 14 and cannot tell on 5 of the German gold's true claims: 19 of its 61 misses. Replayed in memory: every German call now carries the offices section. A manifest written before 1.9.0 records no images, so its answers are retired where the call now shows whole sections — the only view the fix changed. Blind to: the Czech calls mostly show an entry band and are unchanged, but a Czech call that fell back to whole sections (no entry band found) now shows more; the effect on the German answers is measured only by the German gold re-run. Settled; to be measured by the German gold re-run utils/lexikon_1910/run_verify_test_b.py 1.9.0; run_frozen.py 0.11.0; tests/test_cz_anchor.py
D-131 2026-10-25 The place types in the list of parts are routed like institutions: a type some but not all four readings name — one reading alone included — goes to Opus, and the route writes final_types.csv. run_frozen 0.11.0 (type_claims, routed_types, _type_items, final_types), build_mark_ledger 1.5.0. The calls are their own (t-<part>), so the institution calls the Czech certification measured are unchanged; a type answer lands on a mark of its own (…#type), never on an institution of the same token ([castle] is both). final_types.csv: one row per settled type, with the rule and how many readings named it. The author's question (2026-10-25): a symbol only one reader sees should reach Opus. For institutions it did; the place types never entered the route at all — they existed only in the reader of record's list of parts, so a type only Gemini named was kept and one only a Haiku read named was dropped, with nobody asking (on the Czech certification pages 7 and 4; on the Czech gold 7 and 9). Disputed types: 30 on the Czech certification pages (13 named by one reading), 48 on the Czech gold, 63 on the German gold — about 6–7 % of named parts in Czech and 16–17 % in German; roughly a thousand short calls for the Czech volume, ≈ $10–15. Blind to: the type is keyed by part, so two named localities of one part with the same type are one claim; the place types are not part of D-127's score — the sealed sheets transcribe them, so their score is unspent, and its metrics must be written down before it is computed. Built; replayed in memory utils/lexikon_1910/run_frozen.py 0.11.0; build_mark_ledger.py 1.5.0; tests/test_cz_anchor.py
D-132 2026-10-25 On the German editions a section heading that fills its line is found: both margins at least 0.04 of the column, within 0.02 of each other, one printed line, white above. lexikon_1910_image 0.2.0 (HEADING_FULL_MARGIN, HEADING_FULL_BALANCE, HEADING_FULL_HEIGHT), lexikon_1910_notes 0.11.0, lexikon_1910_de_notes 0.3.0, run_r5_german_notes 1.2.0. Off for the Czech volume. The heading D-128 flagged on de 0380 — Ämter, Anstalten usw. im Gerichtsbezirk Reichenau an der Kněžna — stands at 0.053 / 0.052 of its column, under the 0.08 the rule fitted on Czech pages required, so its 28 claims were deleted. Fitted on the seven German gold pages (the sealed four not opened): it adds 0380's heading and changes nothing else. A first version without the one-line condition took a three-line entry block on 0478 and lost a real heading; the condition is a fixed height (45 px at the reference width), because a column's median line can be a merged block. Checked on 30 other German Böhmen pages drawn at random (seed 1910): one heading added — de 0157, Ämter … im Gerichtsbezirk Kamenitz an der Linde, a real one the full run would have lost — and no false one. Off for Czech because the Czech route is certified on the old rule (D-127). Blind to: Mähren and Schlesien are untested; a heading that fills two lines; the heading crop is still labelled by its read (D-38). Settled; German gold re-run next utils/lexikon_1910/lexikon_1910_image.py 0.2.0; lexikon_1910_notes.py 0.11.0; lexikon_1910_de_notes.py 0.3.0; tests/test_cz_anchor.py
D-133 2026-10-25 Mähren and Schlesien get their own drawn sample — 4 calibration and 4 sealed certification pages each (2 sealed each since D-136) — instead of being certified only through the Böhmen gold. draw_gold_sample.py --volume (the Czech draw's design: systematic, random start, seed 1910, alternate halves; a drawn page that is a prose page, already drawn, or shows no apparatus divider moves forward, judged by the geometry code, never by eye). Drawn 2026-10-25, the rule written before the draw: Mähren calibration 25, 54, 88, 122, certification 37, 71, 105, 139; Schlesien calibration 16, 23, 33, 43, certification 18, 28, 38, 48 (in each, draw 1 landed on a statutory-city prose page and moved forward). The sealed scans are in each profile's sealed list; blank sheets are cut (make_gold_worksheet_de.py --volume, 1.1.0) into transcription_gold/de-moravia/ and de-silesia/, Silesia's with its own Polish language column. The author's question (2026-10-25). D-104 let the German Böhmen gold serve all three German volumes with a one-to-two-page spot check, on the ground that they print the same template. But these two volumes are where the route stands on its own: Bohemia now has the Czech anchor (D-129) and they have no Czech twin; they are other scans (Silesia's MSVK images carry the scanner bed and strips of the facing page, no text layer), and Silesia prints Polish names and a fourth language column. A route measured only on Bohemian pages is unmeasured on theirs. Four sealed pages is what the German Böhmen half has: the headline, not the rare symbols. Ruled 2026-10-26: the calibration pages are look-pages, not transcribed. Nothing in the route is fitted on them — the E3 gate and every threshold come from the Böhmen gold (D-104, D-115) — so a typed sheet would measure what nobody adjusts. They are the only pages of each volume the author may look at before the seal is broken: the full run's output on them is checked by eye against the page image, a defect specific to the volume (Silesia's Polish column, its scans) is fixed there, and the sealed run comes after. Their blank sheets stay blank. The sealed four could have been eight by folding the calibration half in; refused, because the first Silesian page the route reads would then be a sealed one. Blind to: a look-page check finds what is visibly wrong, not a rate — the volume's rate is the sealed four's; Silesia's 40-page frame makes 4 pages 10 % of the volume, Moravia's 139 under 3 %; wired 2026-10-26 (run_frozen 0.13.0): PAGES = "certification" reads each volume's sealed pages, which are sealed from the author's queues, and the calibration pages are kept out of the blind validation sample; there is still no gold run for them — their route is the Böhmen gold's; score_certification 1.1.0 finds each volume's sealed sheets, and the sheet reader takes a sheet's own table header, so Silesia's Polish column is read (the table's arithmetic checks still know only Bohemia's columns). Drawn; runner and scorer wired; the sealed sheets wait to be transcribed blind, the calibration pages are looked at, not transcribed utils/lexikon_1910/draw_gold_sample.py; make_gold_worksheet_de.py 1.1.0; reference/places/gold_sample_pages_{maehren,schlesien}_de.csv; reference/places/volumes/*.toml
D-134 2026-10-26 The institutions printed as text abbreviations are routed like the place types and counted: an abbreviation all four readings make stands, one some but not all make — one reading alone included — goes to Opus as a token question, and the route writes final_abbreviations.csv. run_frozen 0.12.0 (routed_abbreviations, _abbreviation_items, final_abbreviations, score_abbreviations), build_mark_ledger 1.6.0. The calls are their own (b-<part>), token questions only — DINOv3's verdicts are blanked on the part so no pictogram box is outlined or ruled on there — and an answer lands on a mark of its own (…#abbr), so the institution calls the Czech certification measured are unchanged. The output carries the entry key (page/court/muni/part), the printed count, the expansion in the edition's language, the English, category and kind, the qualifier and, for a cooperative, the language suffix. The rules. (1) Count: the number printed before the code, read past a qualifier (7 Adv. = 7, 2 prům. pokr. šk. = 2, 2 notáři = 2; none = 1); a code printed twice in one part is one claim and its counts add (kup. pokr. šk., 2 prům. pokr. šk. = 3); the readers' majority, a tie to Gemini (D-121). Read from each reading's per-occurrence records (institutions_<label>.csv), because the inventory merges repeats into one row and lost the second count. (2) Qualifiers (měst., er., gw., lw., prům., kup. …) are never claims; before an abbreviation they are kept in its qualifier, beside a pictogram they belong to the pictogram and are not here. (3) An abbreviation printed beside a pictogram is a claim of its own and never folds into it or changes its count: gw. Fortbsch., [school] V is one continuation school here and one school in final_inventory. A course (table kind course, e.g. lw. Fortbk. beside [school]) is listed with establishment = False: it is taught at the school, not an institution of its own. (4) Cooperatives: the letters are the cooperatives and carry their counts; [cooperative] is the list's heading. In the Czech edition a letter outside the [cooperative]: list is a misread and no claim; the German edition prints them bare and every one counts. (5) The text layer's own rows in a Czech inventory are no reading. On the gold, roughly 1,200 of Gemini's 1,213 decoded rows were abbreviations that reached no final file (audit 2026-10-24 §C). Replayed offline on the stored gold runs, scored against the gold sheets' 2b lines decoded by the same decoder: Czech 195 claims on 171 entries — Gemini alone F1 0.974 (3 false, 7 missed); the agreed claims alone 0 false, 9 missed; the 12 disputed hold all 9 (4 named by one reading), in 5 calls, ≈ $0.04–0.10; ceiling 1.000; printed count right on 195 of 195. German 91 claims on 72 entries, 0 missed, counts 91 of 91; the one agreed claim the gold lacks is the gold's typo FKI. for FKl. (scan 0142, Jičín 14), the readers are right; 2 disputed, 2 calls. Counting the text layer's rows would have added 51 false Czech claims; the Czech "A a H" beside the preachers' marks was the one agreed false claim before rule 4. Blind to: what the table does not decode — roughly half the non-pictogram text items on the gold (Kreisgericht, BezKrankenk., Oberrealsch., Fortbk., o. nemoc. pokl. …) — and a cooperative letter printed with its suffix (Rč, Schb: 16 of the Czech gold's 110 letters, 2 of the German 50), which the decoder refused until D-139 fixed it (and found the German Sch lost to sch.); the first still needs the table extended, then every pass re-decoded and the gold re-frozen. The gold is decoded by the same decoder, so the score measures reading, not decoding. No validation sample draws abbreviation marks, and they are not part of D-127's score. Built; replayed offline, no call sent utils/lexikon_1910/run_frozen.py 0.12.0; build_mark_ledger.py 1.6.0; tests/test_abbreviations.py; The reading route
D-135 2026-10-26 The twelve sealed German sheets have their table drafted by Claude; the notes stay the author's, blind, and are then double-coded. (1) Table, keys and names: read off the print by Claude (the procedure the German calibration sheets were made with on 2026-10-15 and 10-21), from a crop cut above the apparatus divider by the geometry code, so the sealed notes under it are never seen; written into the sheets by a one-off script that refuses a sheet holding any typed row. Every row passes the register's own arithmetic (total = male + female = confessions = languages + foreigners, Silesia's four language columns included) and every municipality is the sum of its parts — 383 numbered rows over the 12 pages (399 with the court totals and darunter Militär lines, which are not summed), 0 failures; area, the place-type marks and the post and station keys have no such check, and the place-type marks were read against the plate's glyph atlas. Each court is the 1910 backbone's own name (all 17 resolve, D-122), and a page with two courts gets one block per court in every section, as the Czech sheets do. What was read but not certain is listed in each sheet's note above its table. (2) Notes (2a, 2b, legend, footnotes): the author's, blind. (3) Double coding: once a sheet's notes are complete, Claude reads the same notes on its own and every disagreement goes to the author on one ruling page; a cell changes only by the author's ruling, and the sheet is then double-coded. The author's call (2026-10-26), after the question whether Claude could transcribe the sealed pages first for the author to verify. Refused for the notes: they are what the German certification scores (score_certification reads the offices only), Claude is the model the route's verification step runs on, and a gold a model drafts and a person confirms shares the route's errors and is anchored toward them — D-55's rule that agreement routes and never validates. Accepted for the table, which nothing scores on the German side, so a drafted table does not touch the measurement. Blind to: if a German table reader is built and scored, this table gold is Claude-anchored, the condition D-66 refused for the Czech table; the double coding flags only the author's slips that Claude disagrees with, and Claude agrees with the route more often than chance, so it corrects the misses the gold wrongly charged the route with more than the reverse — closer to the truth than one coder, leaning slightly the route's way. Tables drafted on all twelve; four of them removed by D-136; the eight left wait for the author's notes reference/places/transcription_gold/{de,de-moravia,de-silesia}/certification/*.md
D-136 2026-10-26 The German half is certified pooled over eight sealed pages — Böhmen's four, Mähren's two, Schlesien's two — with Böhmen scored without the Czech anchor; the other two of each volume's four drawn sealed pages are removed. (1) Which two: every second sealed page of each volume's draw, from the first — Mähren 37 and 105, Schlesien 18 and 38 — a systematic sample from the same random start, fixed by rule before any of them was transcribed. 71 and 139, 28 and 48 are marked dropped in the draw files (the draw record keeps all eight rows) and their sheets are deleted; they are neither sealed nor in any sample. (2) Böhmen twice: run_frozen 0.14.0 writes, for German Böhmen, final_inventory_no_anchor.csv beside the final inventory: the ledger without the Opus answers to the anchor's own calls (a-…) and without the author's rulings on marks only the anchor asked about (without_anchor). score_certification 1.2.0 scores Böhmen as run and without the anchor, in one record. (3) Pooled: --edition de-pooled scores Böhmen's four pages without the anchor, Mähren's two and Schlesien's two as one population of eight page clusters, the metrics of D-127 unchanged, with each volume's figures beside it. The pooled metrics were written before any German sealed page was transcribed or scored. The author's call (2026-10-26): the German notes take the most transcription time, and the pipeline has to run. What carries over between the German volumes is the reading of one plate's glyphs in one language by the same readers; what does not is the scans (Silesia's MSVK images show the scanner bed and strips of the facing page), the page geometry they drive (on the German gold, 28 of 61 misses were one lost heading), the mix of symbols the headline is weighted by, and the Czech anchor, which Bohemia alone has. So Böhmen's number cannot stand in for the other two without a check of their own, and with the anchor it overstates what they get. Two pages each are that check: about 80 claims read with no error bound the error near 4 %, and a geometry failure shows as a cluster of misses. The author's note: the glyphs are the part all four volumes share and the hardest to read, so their error is measured where the sample is largest — per symbol, by the entry-level sample, drawn across all four volumes. Blind to: two pages are not a rate for Mähren or Schlesien, so their own figures are descriptive; a failure confined to pages unlike the drawn two goes unseen; pooling assumes the German route is one route, which holds once the anchor is taken out. Wired; the eight sheets' notes wait for the author utils/lexikon_1910/run_frozen.py 0.14.0; score_certification.py 1.2.0; reference/places/gold_sample_pages_{maehren,schlesien}_de.csv; reference/places/volumes/{maehren,schlesien}_de.toml; tests/test_pooled_score.py, tests/test_ms_sample.py
D-137 2026-10-27 The amateur-theatre chronicle is read by rules, each leading date as an interval, and a bare decade takes its century from the dated line above. kolem 1850 is read as 1845–1855 (CIRCA_YEARS = 5); před 1900 is a start before 1900 and do 1920 an activity that may lie wholly after 1914, so neither is ever read as no; 191x stays a decade. A bare V 80. letech (about 1,340 lines) is put in the earliest century that does not end it before the last dated line of the same block, a heading or separator starting a new block. Associations are word-anchored labels of what a line names, not of who organised it; the free-standing dramatic society (SDO, ochotnická jednota) is its own label and a bare DS/DrO is not it. The chronicle runs forward in time, and where the century is printed the rule recovers it on 95.4 % of 1,374 decade phrases (century_inference_check); the misses mostly push a decade later, which cannot invent pre-1914 activity. An LLM reading was not needed for the date: about two lines in three open with a plain year or range. LS, OB and other abbreviations are left unread until the database's own key is in hand. Provisional utils/parsing/amateur_theatre.py 0.2.0; tests/test_amateur_theatre.py; Roadmap
D-138 2026-10-27 The chronicle's abbreviations are read from the database's own key, case-sensitively, and the one it reads two ways is labelled and flagged, not dropped. The key (index.php?data=zkratka, both lists) is kept as data/raw/theatre/abbreviations.csv. SDO, OS, OJ, DJ, JDO, KrDO are the free-standing dramatic society; ČOS, ČOJ, ČOB the reading-and-dramatic society, and ČOB a beseda too, like its spelled-out form; ČB the Česká beseda; a bare NJ the national union; LS, Lkr, DLS puppet theatre; DTJ written tělovýchovná as well as tělocvičná. ČOS keeps the reading-society labels and is flagged (ambiguous_abbreviations, ambiguous_abbreviation_pre), since the key also reads it as the Sokol union, which existed under that abbreviation from 1904. ÚMDOČ is a membership flag (umdoc), not an association, being a national union rather than a local host. DS and OB stay unread. Guessing had left OS, JDO, KrDO, OJ, DJ, NJ unlabelled and LS outside puppet theatre. Case matters because OS and nj are also ordinary letters. Flagging keeps the 33 pre-1914 ČOS events usable either way. Provisional utils/parsing/amateur_theatre.py 0.3.0; utils/scraping/scraper_amaterskedivadlo.py 0.2.1 (--step abbreviations); tests/test_amateur_theatre.py, tests/test_scraper_amaterskedivadlo.py
D-139 2026-10-27 The German volumes print no cooperative mark — the letters stand bare — so their certification scores the letters as the Czech [cooperative] heading, and the decoder reads the letters as printed. (1) Scoring: for each German edition score_certification 1.3.0 adds one [cooperative] claim on every part where at least one cooperative letter is decoded — on the gold from the sealed sheet's 2b lines (run_frozen.sheet_abbreviations, the decoder and rules the readings go through), on the route from its final_abbreviations.csv — count 1, as the Czech heading is always counted; the claim is the author's only if every letter under it was ruled by the author. A German run without final_abbreviations.csv is refused, not scored as missing its cooperatives. Which letters, and how many, stay in final_abbreviations and outside D-127's score, as in Czech. (2) Decoder (lexikon_1910_institutions 0.10.0): a cooperative letter may carry its language suffix set hard against it (Rd, Bb, Schb., Rč); a word code whose match is, as printed, a whole-word cooperative letter is refused it (the German sch. = Schule is case-blind and took every Sch; GB. and S. D. would take Gb and Sd); a suffix form row (sch., bhf.) matches only as a whole word. Pictograms and the Czech word codes are untouched. The author's observation (2026-10-27), while transcribing the first German sealed sheet: the German edition sets the letters where the Czech one sets the cooperative mark and then the letters. The Czech certification counts the mark — 52 of its 505 claims, third commonest, read 52 of 52 with no false claim — so a German score without it would be weighted toward the harder symbols and read low against the Czech for a reason in the plate, not the route. On the seven unsealed German gold sheets the letters stand on 35 parts against 371 pictogram claims (8.6 %), beside the Czech 10.3 %. Checking it found the decoder had lost part of them on both sides alike, which is why D-134 measured 0 missed: all ten sch. decodes on the German gold were wrong — eight the Schulze-Delitzsch Sch, two the start of Bez.Schulrat and Schlachth. — and the suffixed letters were refused. Re-decoded, the gold's abbreviations go 91 → 95 claims (German; cooperative letters 49, Sch 8, sch. 0) and 195 → 220 (Czech, the suffixed letters D-134 named). The Czech certification score is unchanged to the claim. Blind to: a printed capital that is not a cooperative counts as one in German, where there is no list to anchor it (D-134 rule 4); a letter split from its suffix by anything but a space keeps no suffix. The stored readings were decoded by 0.9.0, so until they are re-decoded from the cache (no call) a gold run's abbreviation report sets the old route against the new gold. Built and tested; the German runs on Heavyhorse6 decode with it. Written before any German sealed page was scored utils/lexikon_1910/lexikon_1910_institutions.py 0.10.0; score_certification.py 1.3.0; run_frozen.py 0.14.1; tests/test_abbreviations.py, tests/test_pooled_score.py
D-140 2026-10-28 A chronicle line is cut into clauses only where a new date opens after a comma, semicolon or full stop; a dated newspaper report is never cut; and the event types are drafted as word cues that the author rules on before any becomes a code. chronicle_clauses() keys each clause (geo_id, line_no, clause_no). No cut falls inside brackets, a quoted title, the line's own leading date (1887, 1888 a 1890 hráli), or after an abbreviation's full stop (zal. 1895, zah. 1921). A line opening with a full day-and-month date or with YEAR -, or ending in a periodical citation, is a report and stays whole. Its trailing citation goes to citation and is never the event's date. The first clause keeps the line's date exactly. A later clause reads its own, and a bare decade in it takes the century from the clause before (D-137). Every other year the clause prints stays in years_inside (zanikl 1931), and the years of a leading list in years_listed (two years, not a span). EVENT_CUES gives 22 word-anchored types plus the structural listed (a date and at most 8 words naming a company). These are cue hits, not codes: which types enter the place × year measure, and how, is ruled on the codebook page. About 8,800 lines print two or more years. Cutting at dates alone, and only after punctuation, lets each clause carry its own date without guessing where a sentence ends inside a newspaper extract. Checked by eye on 85 lines (45 cut, 40 left whole with years inside) plus the words before every full-stop cut. That check caught YEAR - YEAR being read as a report and zah. being read as a sentence end, and both are fixed. Full chronicle: 2,226 lines cut, 1,757 kept whole as reports. Pre-1914 non-puppet units go from 8,219 lines to 8,656 clauses in 2,974 places. Some cue reaches 95.1 % of them. The commonest types are listed (30 %) and active (27 %). Provisional utils/parsing/amateur_theatre.py 0.4.0; utils/reference_tools/places/run_amateur_theatre_pilot.py; tests/test_amateur_theatre.py; Roadmap
D-141 2026-10-28 The amateur-theatre measure follows the author's codebook rulings: Czech activity is any record with an Activity type, less German, bilingual, puppet, and pre-1848 school and church theatre. Every place-year is measured as certain and as possible, over 1780–1913. The roles, ruled on the codebook page, are in CODEBOOK_ROLES. Activity + column: listed, active, founded, performance, count, statutes, permit, first, school and church, travelling companies (a hosted company means a Czech play reached the place). Column only: titles (a quoted title also counts as a performance), attendance, proceeds, National Theatre fund, German. Coded only: venue, building, members, renamed, ended, pause, ban. The calls: (B1) a record with no end is certain only in a start year printed exactly, and possible to 1913. (B2) one company in the ensemble list and the chronicle is matched on starts within one year and ends that do not contradict, not on names, since a label like SDO names many companies; chronicle company clauses left over join one another by the same rule. (B3) the place page's první zpráva is activity certain in its year. (B4) a hedged record is never certain. (B5) ČOS stays the reading-and-dramatic society. (B6) school and church theatre enters the Czech measure from 1848. (B7) travelling companies count as activity. (B8) German and bilingual theatre stay out of the Czech counts, in columns of their own. (B9) the panel runs 1780–1913. (B10) a date known only to its decade is possible across it and certain nowhere; activity_decade_certain keeps the decade. (B11) undated clauses printing a year go to the model pass. (B12) a recorded pause turns certain activity into possible for its years. Rules of the coder's own: the German cue is narrowed to německy, němčina, deutsch or a German theatre noun, so place names and německý národnostní tlak no longer count; Latin gets a column. An end printed after the date (činný do 1938, zanikl 1931) closes a span. A count over a span (1883-1910 173 her) is an average, performances_period_avg, never a per-year minimum. A record known only by its end is possible in that year. The author's rulings (codebook page, 2026-10-28), with notes: SDO is a broad label; a travelling company "got treated"; a title is a performance. Measured on the full cache: 8,656 pre-1914 non-puppet clauses, of which 7,311 are Czech activity, 5,908 company records and 1,551 performances; 2,380 pre-1914 or undated clauses are left to the model. 68 % of pre-1914 chronicle company clauses match an ensemble-list row. Czech activity is certain in 3,063 places and possible in 3,242 of 7,368. In the area tables, 6.2 % of certain place-years sit on places tied to no 1910 settlement. Still open: whether a still-active ensemble with a pre-1914 start and no end (106 rows) should be certain past its start; the company behind a performance (companies_performing) waits for the model pass. Provisional utils/parsing/amateur_theatre.py 0.5.0; utils/reference_tools/places/run_amateur_theatre_pilot.py; tests/test_amateur_theatre.py; codebook page (artifact "Theatre event codebook"); Roadmap
D-142 2026-10-28 The ČSÚ register's bare okr. Beroun in a 1910 segment is okres Moravský Beroun (302-2) wherever the row's own timeline names Moravský Beroun. lexikon._REGISTER_OKRES_1910 substitutes the string the okres matcher reads; okres_1910 keeps what the register prints. Applies to 43 rows (BARNOV, DVORCE, MORAVSKÝ BEROUN, MĚSTO LIBAVÁ…). There was no Bohemian okres Beroun in 1910: the register files the town itself v r. 1869-1930 obec v okr. Hořovice, and the backbone holds Beroun only as a court of okres Hořovice. Every row that prints bare okr. Beroun is Moravian — all 43 carry …\|Moravský Beroun later in their timeline — but the matcher sent them to that court, so 43 Moravian villages sat in okres Hořovice, the town of Moravský Beroun shared the key (beroun, Hořovice) with Bohemian Beroun, and births recorded in Beroun took whichever row was read last. That is the likely source of the 169 Hořovice-named births that land in court Dvorce (Place matching, Why the courts do not add up). Replayed on the final person files: the key is unshared again, so 167 legion and 97 VÚA Beroun births keep a settlement — Bohemian Beroun — that D-144 alone would have refused. Settled utils/parsing/lexikon.py 1.6.0; tests/test_place_matching_precision.py; Place matching
D-143 2026-10-28 The register's text layer is healed before its 1910 segment is read: a year split by a space (186 9-1910, 1 869, 188 0-1930) is rejoined in the two slots a year is printed — after r. and after a range dash — and okr . / ok r. read as okr.. lexikon._heal_ocr. An entry with a broken year or okr. lost its 1910 okres with no error: 182 rows, among them the Prague suburb MICHLE, KUKLENY, LÁZNĚ BOHDANEČ, RUDOLFOV and KRALICE NAD OSLAVOU. Day-dated clauses (od 1.1.1980) are outside both slots and untouched. On the register as read the heal changes no okres, 1910 name, status or parent that was already found; it adds okres clauses to 90 timelines. Rows with a 1910 okres 16,755 → 16,937; geocoded 5,841 → 5,842 (3 QIDs change). Replayed with D-142 and D-144: settlement match 84.54 % → 84.56 % (legion), 84.48 % → 84.82 % (VÚA) of births with a Czech okres; the Czech frame gains a net 5 legion and 4 VÚA births through the bare-name tier, and a few bare names become non-unique and are refused (Bohdaneč, now also Lázně Bohdaneč's 1910 name). Settled utils/parsing/lexikon.py 1.6.0; tests/test_place_matching_precision.py
D-144 2026-10-28 A (name, okres) key that several register rows answer to goes to the one independent municipality (obec) among them; two or more municipalities, or hamlets only, are refused. The modern headword decides nothing. records_geo._pick_homonym in add_settlement_attrs 1.9.0; settlement_homonyms() lists every shared key and its decision. The lookup kept whichever row it read last — 265 keys, 1,341 legion and 1,592 VÚA matched births, 763 and 834 of them on candidates that differ in court or coordinates. Preferring the obec is the bare-name tier's own rule (D-16): a name alone is read as the municipality. Preferring the headword was tested and rejected: Boršice and Boršice u Blatnice, Újezd and Újezd u Boskovic, were each plain Boršice / Újezd in 1910, the qualifier is later, and the register prints two obce named Podbořany in okres Podbořany (the town, and the village now Podbořánky). Replayed: 652 legion / 722 VÚA births lose a coin-flip settlement and 124 / 211 move to the obec (Kaplice 79 VÚA were on a hamlet of that name). The refused keys with the most births (Hluboká, Podbořany, Boršice, Babice) are the ones a base-rate ruling, like D-06, could still settle. Settled utils/parsing/records_geo.py 1.9.0; tests/test_place_matching_precision.py; Place matching
D-145 2026-10-28 An ensemble's own page enters the theatre measure: a decade it lists (Působení) makes the company possible across that decade and certain in no year; a page that lists decades replaces B1's open end, so the company is possible only in its listed decades; a printed end (Ukončení činnosti / poslední zpráva) closes the span, certain from start to end; and the page's own chronicle is cut and coded like a place's, its company clauses tied to that ensemble. evidence_table() gains two sources: ensemble_decade (one row per listed decade, a point as wide as the decade, so activity_decade_certain keeps it, B10) and ensemble_chronicle (ensemble_chronicle_clauses() → code_clauses()), both under the ensemble's own company_id, with no B2 matching. A "…" between two decades names nothing, so the gap stays empty. A hedged 191x(?) counts as listed, since a decade is only ever possible. A German or puppet ensemble's chronicle inherits that from its name. Two consequences of the ruling, which the coder applies: every record of a company whose page lists decades loses its open end, including a place-chronicle clause that B2 matched to it (325 such clauses). And a company's possible years are now the union of its records' ranges plus its certain hull, not the hull of all of them. On the 0.5.0 evidence this union changes nothing (195,589 company-years either way). The scraper's Působení reader now splits on spaces, semicolons, ellipses and a glued 186x187x too: commas alone had dropped 57 decades on 38 pages, 18 of them in 1910 or earlier. The author's rulings, 2026-10-28, each as recommended. What the pages hold (682 pre-1914 open ensembles, step C on Heavyhorse6): an end year on 1 page (1899; four more print "?"); decades on 588; a decade after the start's and before 1914 on 176; a last decade that ends before 1910 on 398; a decade before the list's start on 3 (kept as printed); a dated chronicle line after the start and before 1914 on 284, and 97 % of those lines are not in the place chronicle; nothing new on 367. Verified on the full cache into the scratchpad, 0.5.0 → 0.6.0: places with Czech activity certain 3,063 → 3,068, possible 3,242 unchanged; place-years certain 52,700 → 53,403, possible 93,113 → 91,651; company-years certain 78,493 → 83,410, possible 195,589 → 191,247; settlements ever certain 2,774 → 2,779. Of the 2,511 pre-1914 non-puppet clauses in the ensembles' chronicles, 1,830 are Czech activity; 872 pre-1914 or undated ones join the model pass. Possible years beyond the listed decades remain where the list's start is only a decade or a century wide, or where the ensemble's own chronicle dates a line outside them. Provisional utils/parsing/amateur_theatre.py 0.6.0; utils/scraping/scraper_amaterskedivadlo.py 0.2.2; utils/reference_tools/places/run_amateur_theatre_pilot.py; tests/test_amateur_theatre.py; Theatre measure
D-146 2026-10-28 Each German volume is read with its own plate: Mähren and Schlesien print the registered-cooperative mark before the letters, so D-139 holds for Böhmen alone; a language letter may be d, b or p, and a letter printed without one is a cooperative of no stated language — never Czech. (1) Token sets: institution_abbreviations.csv gains a volumes column (blank = every volume, a slug = those volumes only, -slug = every volume but that one), read by load_abbreviations(volume=…) (lexikon_1910_institutions 0.11.0). Mähren's plate (scan 0017) adds [cooperative], [settlement-part], [hunting-hut], [tourist-inn], [rc-parish-exp], [rc-kuratie], [rc-lokalie], [rc-kaplanei], [rc-primissariat], [customs-exp] and lacks Böhmen's Alpenwirtshaus and Alpenhütte (88 tokens); Schlesien's (scan 0011) adds the mark, Ortsbestandteil, Jagdhütte, Touristenwirtshaus, Lokalie, Zollexpositur and Holzknechthütte, keeps the Alpenhütte and lacks the Alpenwirtshaus and Mähren's four benefices (86). Böhmen's 80 are unchanged, and a caller naming no volume gets them; [alpine-hut], a second row for the Alpenhütte, is dropped. (2) Keys: build_symbol_atlas_de 1.1.0 cuts symbols/de_maehren/ from its plate (fixed column bounds, the vertical rules blanked) and composes symbols/de_schlesien/ from Mähren's crops plus its own two huts; bakeoff.de_atlas(slug) hands each run its own, and the frozen manifest digests the key per volume. Böhmen's 80 crops and sheets are pixel-identical. (3) Prompts: build_system_de(volume=…) (lexikon_1910_de_notes 0.4.0) names the volume and, for Mähren and Schlesien only, describes the mark and the p; Böhmen's prompts are byte-identical. (4) Suffix: the decoder takes p (polnisch, both legends: "d = deutsch, b = böhmisch, p = polnisch"); a bare R keeps language_suffix empty. (5) Scoring: score_certification 1.4.0 adds D-139's heading claim for de-bohemia only; Mähren's and Schlesien's [cooperative] is scored as printed, like any pictogram. (6) Gold: a sealed sheet is read with its own volume's tokens (score_r5_german_notes 1.1.0, run_frozen.sheet_abbreviations), and a token the plate lacks now refuses the edition instead of being dropped from the gold with an unprinted problem line, which would have scored the route's correct claim as false. The refusal at once found two on Böhmen's own sealed sheets — 0356 Vimperk 12,2 [rc-parish-exp] (not on the Böhmen German plate, which prints Exp. as a word) and 0441 Strakonice [psot-agency] — left to the author. The author's observation (2026-10-28): the mark absent from the Böhmen volume appears in Mähren, where Rd, Ld are frequent. The legends answer it: Böhmen's says the letter is "etwa hinzugefügt" — optional — and Mähren's prints "z. B. Kd = Deutscher Konsumverein" with the mark "stets in Verbindung mit dem vorangestellten Zeichen". A suffix is printed where the language is stated, so a frequent d in German-speaking Moravia says nothing about a default, and a bare letter is a cooperative whose language the volume does not state; reading it as Czech would invent a covariate. The two volumes had been read with Böhmen's prompt (calling them Böhmen and their cooperatives bare letters) and Böhmen's key (offering an Alpenwirtshaus they do not print and no Lokalie they do). Nothing run is changed: the Mähren and Schlesien sealed sheets are drafts, and their instruction already fits writing [cooperative]: Rd, 2 K.
D-147 2026-10-28 The religious marks are compared across volumes on their function, not their mark: religion_categories.csv puts parish, vicariate, expositure, Lokalie, Kuratie and curate benefice on [rc-cure-seat] (a resident priest with the care of souls), filial church and chapel on [rc-church-no-priest]; Kaplanei and Primissariat (Mähren only) enter no category; every mark on all four plates keeps its own. The certification score carries the same metrics on these categories as a second block, written before any German sealed page was scored. (1) Table: 22 rows, every religious mark of the four plates (a test holds it to the plates); rc_cure_seat and rc_parish_strict are the two analysis flags — the pilgrimage parish church is a parish under both, though scored as its own mark, being on every plate. Loaded by lexikon_1910_institutions.load_religion_categories (0.12.0). (2) Score: score_certification 1.5.0 adds by_category to every result — the marks folded per part, counts summed, a category claim the author's if any mark under it was — and, pooled, per_volume_by_category. The symbol-level block stays the headline. The Czech score was written on 2026-10-25 without it; a Czech by_category could only come from a forced re-score, which the record names. (3) Analysis: any religious covariate that crosses a land border uses the categories (or the strict-parish flag as a robustness check); the plate-specific marks are for within-land comparisons only. The author's call (2026-10-28), on D-146: the plates' religious marks are where the volumes differ most. Read with Böhmen's key, a Mähren Lokalie becomes a parish or a chapel in the reader's output and in the blind gold alike, an error the score cannot see and one that falls on Moravia and Silesia only; read with its own key, it is right but has no Bohemian counterpart. Folding by function keeps the reading true to the print and the comparison true across lands. Still open: whether Böhmen's parish mark also covers Bohemian Lokalien and expositures, which the 1910 diocesan catalogues (Catalogus cleri) can settle, and a Czech-against-German-Böhmen and a land-border check on the category densities once the volumes are read.
D-148 2026-10-29 The register becomes municipal data in data/final/register/, linked to settlements_1910 by a new key settlement_id; every register municipality and part is anchored against the settlements of its section's 1910 okres (≥ 84, margin 5, ties refused, one settlement per place and level), a tie retried inside the section's own court, a municipality with no name of its own anchored through its parts; and the municipal table is built from three long tables by code. (1) Key: settlement_id = the headword's ASCII slug + its ordinal among identical slugs in register order (blatec:1, blatec:2; curated extras last: praha:1, tesin:2, bilsko:4), added in build_settlement_crosswalk (lexikon 1.7.0). It reads only the headword and the row order, so no matcher, Wikidata or court change renumbers it. The same function was applied to the table on disk, which is what a notebook-02 re-run writes. (2) Names: the run folder holds none; the Czech table reader's rows and the reader of record's parts list carry them. The parts list is alphabetical, so a place is named by its village/town components, then hamlet-like ones (NAME_TYPE_TIERS). (3) Anchor: lexikon_1910_names 0.6.0 (build_authority(land=None, statuses=None, okres_from_court=True); Obec carries settlement_id, status, court), anchor_blocks per okres and level; statuses matched · matched_court · via_parts against ambiguous · unmatched · parts_disagree · no_name · no_court · outside_authority (okres Bílsko, absent from the ČSÚ lexikon; unanchored places in split Těšín and Fryštát counted apart). (4) Tables: register_places, register_institutions (mark, source pictogram/text, rule, settled_by, author_ruled, D-147 common_category, rc_cure_seat, rc_parish_strict), register_cooperatives (the empty language letter its own value, not_stated), register_municipalities (one row per anchored settlement_id; Czech volume first where German Böhmen reads the same place; text and cooperative counts <NA> where the run wrote no abbreviations). Each key asserted by the build and by the QA gate. The analysis needs the register on the place authority the legionnaires' birthplaces already resolve to, so the merge is a join and not a second fuzzy match. (cs_name, okres_id) repeated on 627 rows and okres_id was missing on 2,161 (2026-10-29), so the table had no key to join on. The court constraint does what the D-22 municipality does for births: it is evidence the record carries, so breaking a tie with it is not lookup order. Measured on what is on disk (anchoring tables in REGISTER_VARIABLES.md, generated by the build): municipalities / parts anchored 96.1 % / 93.8 % on cz_certification, 94.9 % / 95.1 % on cz_gold, 66.7 % / 71.8 % on de-bohemia_gold, where 32 municipalities and 18 parts have no name on any list the German reader wrote. No Mähren or Schlesien run exists yet, so the German-only aliases and the Těšín rule are tested on invented cases only. The QA gate goes 107 → 116 checks (keys, the register-after-settlements warning, every register column in the README). Provisional utils/parsing/lexikon.py 1.7.0; utils/lexikon_1910/lexikon_1910_names.py 0.6.0; utils/lexikon_1910/lexikon_1910_register.py 1.0.0; utils/validation/data_checks.py 1.13.0; notebooks/15_frozen_run.ipynb §18; tests/test_register_build.py; reference/REGISTER_VARIABLES.md
D-149 2026-10-29 What the volumes' introductions settle: a language letter has no default, and a mark read twice is asked about the mark it may hide, with the introductions' worked examples added to the order the rule is learned from. (1) Language. No introduction gives a default. The cooperative letter is optional in every legend (Mähren p. X "die etwa hinzugefügte Abkürzung d, b oder p"; Čechy p. X "připojené písmeno č. neb n. znamená"). The worked examples print it where stated — Sternberg's and Odrau's cooperatives all (deutsch) — and Bílina's three cooperatives carry none. Schools get no rule at all: Bílina, a mixed town, states each school's language ("1 česká, 2 německé"); Sternberg and Odrau, German towns, state none; the gold sheets show notes only on mixed places ((d. u. b.)). So the print states a school's language where a place's schools differ, and an unmarked school, like a bare cooperative letter, is not stated — a language imputed from the place's Umgangssprache in the table must be its own, flagged variable. b (böhmisch) is the Czech edition's č, d its n. The German Böhmen scan lacks its Vorwort and Anleitung (pp. V–IX); Mähren's, Schlesien's and the Czech volume's are read. (2) A mark read twice (lexikon_1910_institutions 0.13.0, VERDICT_REPEATED): the print sets a count before one mark, and the ten Czech sheets repeat a mark in 1 of 231 runs (two named stations, learned as repeatable). Every other repeat in a run is flagged with its candidates: the marks the learned order allows and supports in either slot, plus the mark's known look-alikes (the readers' swaps against the gold, a page's own left out) that the order allows; never a mark already read. A repeat across a detected order restart is left to the restart. run_frozen 0.15.0 asks each candidate as a token question in calls of their own (o-), and one Opus rules printed enters the inventory as "a mark read twice, candidate ruled", with and without the anchor. (3) Worked examples as order evidence: reference/places/order_worked_examples.csv transcribes the symbols of the three decoded entries (Sternberg 29, Odrau 21, Bílina 24) with the volume's own words; read_transcription_md.read_worked_examples adds them to the sheets' runs in run_frozen.order_flags, never left out by a leave-page-out. They contradict the sheets on no fixed pair (0 against the majority, before and after), add 708 pair observations, and give the one order the ten pages never printed: kindergarten before the day nursery. The author's question (2026-10-29): what is the default between deutsch and böhmisch, and does the order catch [doctor] [doctor] for a vet, or a kindergarten for a day nursery? It did not: a mark is never reversed with itself, and claims are pooled per part, so the second [doctor] became a count of two and the vet was simply missed. Measured on the four r5 readings of the Czech gold pages, leave-page-out: 43 repeats, 89 candidate questions, the gold's missed mark among the candidates on 27 (the second [doctor] on 0076 Duba is the vet); as the route merges them, 39 questions on 8 parts, 12 naming a printed mark. A route change: it applies to every run made after it, the German sealed pages first; the Czech certification was scored before it. D-73 learned the order from the sheets and not the Návod because one example names a third of the atlas; the examples are added beside the sheets, never in place of them.
D-150 2026-10-29 A German Böhmen place is paired with the Czech volume's place, court by court: by a shared settlement_id, by name against the Czech cell's forms (≥ 84, ties refused), and by position — the German volume numbers a court in alphabetical order of the German names (accents folded, ß = ss), which the Czech table prints for every municipality, so the German slots between two pairs are filled with the Czech municipalities whose German name sorts between them, only when their number equals the number of slots; a German place its own names left unanchored inherits the Czech place's settlement_id. lexikon_1910_crosswalk 1.0.0, called by lexikon_1910_register 1.1.0 after each edition is anchored (D-148). New register_places columns cz_muni_seq, cz_settlement_seq, crosswalk (settlement / name / order), crosswalk_agrees; statuses via_czech_name, via_czech_order. A court's ends close only on a full run; a court whose pairs do not rise with the German numbers is refused the order step. Parts are paired by settlement and name only. The author (2026-10-29): both volumes print both names, so the German places should come through the Czech ones. The German shortfall was not a naming problem: 32 German municipalities had no line at all on the German reader's parts list, and the German table is never read. The order rule was read off Trutnov, where the Czech gold page holds all 33 municipalities and the German-name order reproduces every German number the route had paired. Measured on the gold (REGISTER_VARIABLES.md, generated): German municipalities anchored 66.7 % → 84.3 % (19 pairs by order, 18 inherited); leave-one-out on the 48 pairs the German anchored itself, the order step predicted 34, all 34 correct, and guessed none of the other 14 (a gap the Czech pages read cannot close). The count rule is what makes a wrong fill impossible: a Czech municipality missing from the pages read leaves one candidate short, and the gap is refused. Still open: German parts (18 unnamed on the gold) wait for a page that shows their order; Mähren and Schlesien have no Czech volume here, so they rest on their own names. Provisional utils/lexikon_1910/lexikon_1910_crosswalk.py 1.0.0; utils/lexikon_1910/lexikon_1910_register.py 1.1.0; tests/test_register_build.py; reference/REGISTER_VARIABLES.md
D-151 2026-10-29 A 1910 settlement is located against today's official geography (ČÚZK RÚIAN), not against 1910 boundaries, which do not exist for Bohemia and Moravia: settlements_1910_geo matches each row by name to a modern část obce, obec or ZSJ — first in its 1910 okres (MPIDR courts), then in an adjacent obec within 5 km, then in the modern okres its own okres_all timeline ends on; Geonames and PRNG only where RÚIAN has no such name in scope. Several places of a name are one place only inside one modern obec; else the settlement's modern okres or 1910 parent may choose, and what neither decides is refused. One modern place, one settlement. Wikidata's point is kept, except where it lies outside the settlement's 1910 okres, the crosswalk point inside, and the two are > 5 km apart (11 rows, wd_overridden). A place survives boundary changes; a boundary dataset for 1910 would add nothing a modern part with a point and a 1910 court polygon do not already give. Measured: points for 97 % of 1910 obce (44–60 % with Wikidata alone) and 83–87 % of osady (2–7 %); against Wikidata the median distance is 8 m over 5,793 points, so the closed-form S-JTSK transform is right. The court-polygon test caught the 42 Moravský Beroun villages the current settlements_1910 still files in okres Hořovice (D-142, awaiting a §3c re-run). Wider scopes and the 5 km cap are read off the distribution: before the cap, 1,509 of 1,544 neighbour matches sat within 5 km. Settled Place matching — Today's geography; Data sources #24–#26
D-152 2026-10-29 The German sealed sheets are corrected on the author's rulings, found by reader-independent checks only; and Böhmen's German set reads the bracketed parish mark its pages print though its legend omits it. (1) The checks (no model reading of the sealed pages, so nothing pulls the gold toward the route): every token against its own volume's plate, table and parts arithmetic, a mark repeated in one part, the printed order, a Roman numeral after anything but a school. Each flag was looked up on the print and put to the author in Token Review; all 16 ruled 2026-10-29. Five touch the score: 0441 26,3 [fire-brigade] II → [school] II; 0441 29,0 [school], [psot-agency] → [fire-brigade], [post-agency]; 0037 34,2 [telephone.] → [telephone] (dropped silently before D-146's refusal); 0189 19,2 2 [doctor] (count); 0356 12,2 [rc-parish-exp] kept. Eleven are parts-list slips (commas and underscores for dashes, a missing figure pair, three misread numbers, two missing &, a typo'd type, and 0189's Obědowitz line duplicated under 21,3, removed). After them every sheet parses, every sum holds, and the scorer reads all eight. (2) The expositure: Böhmen's legend has no Pfarrexpositur, but its pages print the parish mark in brackets — 0356 12,2, purposive 0479 — as Mähren's legend draws it, beside the bracketed school its own legend calls a Schulexpositur. The author's ruling: the legend's omission is the editor's slip. [rc-parish-exp] is now read in Böhmen too (institution_abbreviations.csv volumes = boehmen_de maehren_de); build_symbol_atlas_de 1.2.0 borrows Mähren's crop into Böhmen's key (BORROWED), so the key is 81, its other 80 crops pixel-identical; and a German caller that names no volume reads Böhmen's set (lexikon_1910_institutions 0.13.1). This changes Böhmen's key and prompts (D-146 had left them byte-identical): it applies from the German sealed run on. The category score is unaffected (both are a resident priest, D-147). (3) Checker: a Schlesien sheet's lang_polish is now counted in I3 by the sheet reader (read_transcription_md._invariants); it had been dropped before grading, and all 16 Polish-speaking rows failed on tables that add up. The Czech constants are unchanged. The author finished the German sealed transcriptions (2026-10-29) and asked for mistakes to be found without biasing the later F1. A check that reads the route's output would pull the gold toward the route; these rules do not, and each case was ruled by eye. Their blind spot: a wrong mark that breaks no rule.
D-153 2026-10-29 Amends D-151, which stands. The crosswalk reads four sources in a fixed order — RÚIAN, then ČÚZK Geonames, then Wikidata's vanished settlements (CC0, 3,035 of them carrying a zanikleobce.cz ID), then PRNG — a later one only where every earlier one has nothing of that name in scope; and it tries two fallback name forms when the headword finds nothing: a numbered half (Hlásek 1. díl → Hlásek), which shares its village's point, and a positional prefix (Horní Kšely → Kšely), accepted only inside the 1910 parent's modern obec and yielding to any 1910 row of the bare name. Every point is cited (coord_source, coord_ref), every download has a SOURCE.txt. A settlement gone since 1910 is on no modern list, so no name rule could place it; the zanikleobce.cz register is the record of exactly those places, and Wikidata holds its IDs under CC0 — the register itself states no licence and asks crawlers off its query pages, so it is cited, not scraped. The tier order is what keeps a ruin or a mill from outbidding a village. Measured: rows with a 1910 entry matched 92.6 % → 95.7 %; Bohemian osady 84.5 % → 92.7 %; 467 placed on vanished sites (451 with a zanikleobce.cz ID), 118 halves, 3 prefixes (22 of 25 yielded). A half was at first made to yield like a prefix and lost 88 places to the merged village's own row; a half is that village, so it now rides along. Settled Place matching — Today's geography; Data sources #24–#27
D-154 2026-10-29 Mähren's and Schlesien's four calibration pages get a run of their own, PAGES = "calibration", read on the Böhmen gold's route and looked at by the author against the page image before their sealed pages are spent; the full volumes are read after certification, as Böhmen's is. run_frozen 0.16.0: PAGE_SETS gains calibration (label letter k, so no cache, inventory or table row is shared with another set), allowed for de-moravia and de-silesia only (CALIBRATION_EDITIONS) — the Czech and Böhmen calibration pages are part of their gold — and read from each volume's drawn sample (D-133: Mähren 25, 54, 88, 122; Schlesien 16, 23, 33, 43). Like a certification run it is held to the Böhmen gold re-run's freeze (D-125), draws no validation sample (the author looks at the pages whole) and is never scored. Notebook 15's order: Bohemia gold → de-bohemia certification → each of Mähren and Schlesien on calibration, looked at, then certification → the pooled German score → the full volumes. The author's choice (2026-10-29). The ruling of 2026-10-26 put the look at the calibration pages in the full run's output, which placed two whole volumes before the sealed pages. But Mähren's and Schlesien's own plates, keys and prompts (D-146) had not met a single page of theirs, and the sealed pages cannot be spent twice. Eight pages give the same look at a fraction of the cost, and leave the full runs where Böhmen's is: after certification.
D-155 2026-10-29 The 1910 register's court enters the crosswalk: where the register has anchored a settlement to one court, that court narrows a name tie inside the 1910 okres to the candidates in its polygon, decides between two rows claiming one place, and is recorded beside the point as a check (court_1910_register, in_register_court). Only the register's own anchored statuses count; a settlement filed under two courts gives none. The lexicon knows the okres, the register the court — the finer unit that separates two Lhotas of one okres, which is what most remaining refusals are. Built now on the 9-court test register so the gain arrives with its full run without new code: today it settles no tie (the 5 refused rows it reaches are not court-decidable) but checks 221 points, 91.9 % inside the register's court. Settled Place matching — Today's geography
D-156 2026-10-29 The register's text abbreviations are decoded through the volume's own list of abbreviation parts, and a combination of parts is a code of its own. Each volume prints how it abbreviates — the Czech Zkratky a výklad značek (p. X, scan 0012), the German Abkürzungen (scan 0020) — and sets an institution named in words from those parts: hosp. pokr. k. is hospodářský pokračovací kurs, Waisenh. is Waisen + -h. (Haus, "am Schlusse des Wortes"). institution_abbreviations.csv held whole codes, so a combination it lacked reached no claim — D-134 had measured it as reaching roughly half the gold's text items. (1) The parts are transcribed to reference/places/abbreviation_components.csv (forms the print uses without listing them are marked listed=False); lexikon_1910_institutions 0.14.0 spells an item out through them (expand_abbreviated) and returns every item of an entry no code reaches (unlisted_items), the census of report_unlisted_abbreviations.py and notebook 15 §8b. (2) The code table grows by 148 rows (288 → 436), each one read off the census of the four readings of the Czech certification, the Czech gold and the German gold: continuation schools by kind, the agricultural continuation course (kind course), music, handicraft, commercial, weaving and farmers' schools, the upper Realschule and gymnasium, the teacher-training institute, regional courts, prisons, finance, forest and building directorates, police, the garrison (barracks, recruiting and gendarmerie commands, Landwehr battalions, military hospitals and ranges), orphanages, infirmaries, shelters, pawnshops, slaughterhouses, banks, credit and fire-insurance societies, district agricultural unions, spas, museums and libraries; two new categories, military and culture. This revises D-134 on one point: 2 prům. pokr. šk. is the code prům. pokr. šk., no longer pokr. šk. with the qualifier prům. — Aš prints kup. pokr. šk., 2 prům. pokr. šk., which as one code was one claim of 3 whose split by kind could not be recovered; a reader who garbles the qualifier now makes a disputed claim, which is what the route asks Opus about. A word that is part of no code (měst., fil.) stays a qualifier. (3) Three decoder fixes the census found. A code word printed with a period must be read with it or end there: with the period optional, o. s. matched o. svaz, o. stavební správa and osvětlení, šk. škodám, BH. the Bhm. of a place name and Sta. Statthaltereiagenden and Staatsb. — district courts, schools, captaincies and tax offices never printed (17 phantom claims on the German gold's four readings, 5 on the Czech certification's, 3 on the Czech gold's). A word broken at the line end (vzdě- lání) still matches. The look-alike letters the readers write (ĕ ũ ć for ě ů č) are read as the Czech ones, so mĕšť. šk. is a burgher school and Rć a Czech Raiffeisen. Measured — items no code reaches, four readings each, before → after: Czech certification 167 → 33 (with no code at all 99 → 10), Czech gold 414 → 72 (207 → 5), German gold 190 → 9 (129 → 4); what is left is names ([ruin] Hrádek, a bank's), a part's name opening its own list, and garbled reads. Not done: the frozen gold and certification passes keep the decode they were read with (institutions_<label>.csv), so the new codes reach the data with the full run's reading; before score_abbreviations is run on a frozen gold run its passes must be replayed from their crop caches (Heavyhorse6, D-68), or the gold, decoded with the new table, scores the old decode. The expander's spelling is a reading aid, never a claim: the list's -k. is Kurs, but Bezkrankenk. is Krankenkasse. Flagged, not changed: the table reads the German GB. as Bezirksgericht, while the list prints GB. = Gerichtsbezirk, a judicial district.
D-157 2026-10-29 The theatre clauses the rules leave open (needs_model) are read by a model that reports what a clause says — its event types in the cue vocabulary, the interval its years give, a hedge, titles, who performed — and the rulings (D-141) still decide every role and every certain/possible call. The readings replace the clause's cues (and an undated clause's missing date, date_source = "model") and code_clauses runs again. Scope pre1914 by default: a dated open clause with before_1914 yes/ambiguous, or an undated one printing a pre-1914 year (6,225 clauses: 1,077 place, 5,148 ensemble). One clause a request, the codebook a cached system prompt, the reply a JSON schema, batched; each reply cached under its request's hash. A year that only dates a source reporting the activity (source_year) is applied as an end, possible only. Haiku 5.5 and Sonnet 5.5 at the same effort read a ~300-clause gold sample, drawn by source × kind and halved; the prompt is fitted on the calibration half and the certification half stays sealed until the prompt is frozen and a bar is written. The rules read only a leading date and cue words; ~6k clauses that may attest pre-1914 theatre count for nothing today. Letting the model only read keeps the measure's definition in the author's rulings, where D-141 put it; a model that returned roles would have moved it. The doc's earlier "2,380 + 13,193" counted every open ensemble clause, post-1914 included; all (17,927) remains a setting. Offline estimate, batched: ~$0.90 Haiku, ~$18 Sonnet for pre1914; the gold run measures it. Provisional — nothing sent; the scope's post-1914 question, source_year, the bar and the reader are the author's utils/parsing/amateur_theatre_model.py 0.1.0; notebooks/16_theatre_model_pass.ipynb; run_amateur_theatre_pilot.py --model-readings; tests/test_amateur_theatre_model.py
D-158 2026-10-29 The Matice member registers are read by rule into one row per printed entry, and each list is held to the total its society printed. Two printed totals are read, not taken: Matice česká 1873's persons are 2779 − 146 = 2633, since its own line "a osob 1633" contradicts the total above it; and 1890's 4542 counts the dead, so its living persons are 4542 − 1617 − 267 = 2658. Matice moravská 1909 is held to the Seskupení členstva of 1 March 1909 (3 / 10 / 103 / 757), the list's own date, not the report's year-end figures. Residences stay as printed (mostly locative, v Brně) until matched to 1910 settlements. The printed totals are the only yardstick for the parser's recall, and both readings were checked on the scan: the 1873 list holds about 2,750 living entries, the 1890 one about 2,900, neither of which fits the total taken at face value. Against these yardsticks matice 0.1.0 finds 92–100 % of every list (Matice česká persons 99.2 / 99.7 %, deceased 97.8 / 98.9 %; Matice moravská 1909 97–100 %, 1891 92–94 %). Provisional utils/parsing/matice.py 0.1.0 (PRINTED_TOTALS); utils/reference_tools/places/run_matice_pilot.py; tests/test_matice.py; Data sources #28, #29
D-159 2026-10-29 A gold re-run after a reader-side change reads again: notebook 15's REREAD (a date) sets the run's reads and batch queues aside once, so every pass runs anew; never on certification. run_frozen.set_reads_aside (0.17.0) moves output/frozen/<run>/reads/ and the run's queue folder to …_superseded_<date>, nothing deleted, and leaves reads/reread.txt with the date, so a resumed or repeated Run All moves nothing twice. The crop cache keys on crop, model and prompt: a pass whose prompt is unchanged comes back from it free and is decoded again with today's table; a changed one is read anew. Opus answers stay, the stale ones retired as before. Refused on certification (read once, D-55). Every .done in cz_gold and de-bohemia_gold dates from 2026-10-23: the 2026-10-25 "gold re-run" redid routing, Opus and the ledger over those reads. A re-run now would have measured German Böhmen on its old prompt and key (before D-146, D-152) and both editions on the abbreviations as decoded at read time (before D-156), and called that the frozen route. The switch makes the gold re-run what the route page says it is, before the German certification is held to its freeze.
D-160 2026-10-29 The German volumes' table — Böhmen, Mähren, Schlesien — is read like the Czech one: cut into blocks where the page prints them, each block read whole by the Czech table's two readers, the names off the image. D-115 had left the German table unread, so a German place was named only off its parts list (86 of the German gold run's 180 places had no name) and Mähren and Schlesien, which have no Czech volume, had no table at all. The German volumes have no ALTO and a word layer whose OCR names are garbage (Brocnai for Brocná), so nothing is read from it: (1) where — lexikon_1910_de_table 0.1.0 takes the frame, the column rules, the header and the foot from lexikon_1910_image, and the printed rows from the ink of the figure columns (houses … female), with the rules removed slab by slab (the scans are skewed, a rule drifting up to 10 px down the body) and an over-tall band cut into its rows. A row with ink in the area column opens a block, as the area cell does on the Czech side; one with no running number is a district total (P. B., Pol. Bez., GB.), read as its own block so its name labels the blocks under it; rows above a page's first opener continue the previous page's municipality. A wrapped name's lines go to the row whose figures they head, the line under a total (its count of places) to the total. (2) what — run_de_table 0.1.0 crops each block frame to frame at the vision ceiling and reads it with Gemini 3.8 Flash (thinking off, temperature 0, the reader of record) and Claude Haiku 5.5 (adaptive, high), the Czech table arm's settings; the prompt is the Czech one with the volume's title, its own columns and labels from the profile (Schlesien's eighteenth, polnisch), and rules for the names: both printed forms in order, every diacritic, nothing normalised. Replies are cached and batchable like the Czech table's. Every row is checked by the volume's identities (Schlesien's language sum counts Polish) and every municipality's parts against it (table_checks_<label>.csv). (3) wired — run_frozen 0.18.0 gives every German edition the two table passes after its notes, score_table scores a gold run's table (Böhmen against the German gold, which is Claude's reading, D-96: agreement, not certification), score_table_passes --german, and the register 1.2.0 names German places off the table (a total names no place; carried-over parts continue their municipality). Böhmen keeps its German table: the two editions of Bohemia read twice is the robustness check the author asked for. Measured, no model call: on the four Böhmen gold pages the German gold transcribes whole, the cut finds exactly its municipalities (0142 19, 0276 15, 0454 11, 0479 18) and its rows (34, 38, 37, 31 against 33, 37, 37, 31 — the gold leaves out darunter Militär); every column rule on Mähren's and Schlesien's calibration pages (17 of 17, 18 of 18), 74 and 44 municipality blocks, checked by eye on overlays. The dry run prices a Gemini pass of Böhmen's 475 table pages at ≈ $28 live, Mähren's 139 at ≈ $10, half batched. Not done: no German block has been read by a model yet — the first read is a paid pass on Heavyhorse6 (D-68); the German gold table score measures agreement with Claude; Mähren and Schlesien have no gold table, only their sums; a German run already started gets the table passes as unfinished jobs.
D-161 2026-10-29 The Matice registers become an okres-level measure of committed Czech nationalism. A printed residence (locative, v Brně; or an origin in the genitive, z Hostomic) is matched to a 1910 settlement by declining every 1910 name forward into its locative and genitive forms, never by guessing a nominative back; a form claimed by several settlements is decided only by the land or a qualifier the entry prints (na Moravě, u Příbrami), the settlement's own present name, a court seat, or the one obec among osady — and refused otherwise. Members are summed to the 148 pooled census okresy; each okres is measured by its own land's society (Matice česká in Bohemia, Matice moravská 1890 in Moravia and Silesia), as an empirical-Bayes shrunk rate per 1,000 inhabitants standardised within the land (matice_own_z). Posted members (clergy, teachers, officials) and local ones are counted apart. The two societies split by land: of the members placed in an okres, Matice česká has 2,948 in Bohemia and 295 in Moravia, Matice moravská 75 and 431 (1890), so one society's rate measures the other land badly. Within Czech-majority okresy the land's own society tracks the theatre measure (companies active per 1,000, 1880–1890): Spearman 0.25 in Bohemia, 0.47 in Moravia. Shrinkage (Marshall 1991) because most okresy hold a few dozen founders. The seat and obec priors are assumptions, labelled in place_how so a strict reading can drop them. Open for the author to rule: the denominator (total vs Czech-speaking population), whether posted founders count, which Matice moravská list stands for Moravia, and the 1910 census as denominator for 1831–1890 members. Provisional utils/parsing/locative.py 0.1.0; utils/parsing/matice.py 0.2.0 (okres_measure, OWN_SOCIETY, OCCUPATION_CLASSES); run_matice_pilot.py → data/interim/matice/matice_okres.csv; tests/test_locative.py, tests/test_matice.py
D-162 2026-10-29 A Matice residence the OCR left unplaceable is re-read from the scan, and the reading counts only if the matcher then places it. The scanned line of each unmatched entry is cut by its ALTO coordinates into numbered sheets; a vision model (Claude Haiku 5.5, six agents in parallel) transcribed the residence of 954 entries, kept with reader and date in reference/places/matice_residence_readings.csv. A reading replaces the OCR only where it then reaches a settlement, Prague or a place abroad, or says the entry names no place (-); otherwise the entry stays unmatched and the reading is kept for a look by eye. A reading made by eye is never overwritten by a later machine pass. The line scans are clean print, where a vision reading beats the OCR (Eakovníce → Rakovníce, Zemcích → Zerčicích, Kostehií Radouni → Kostelní Radouni), and acceptance-by-matching keeps a misread from inventing a place: a wrong reading of a real name rarely lands on another real settlement. Measured: 954 read, 275 now placed (215 settlements, 16 Prague, 6 abroad, 38 no place); unmatched entries fell from 987 to 694 with the old-spelling variants added beside it (Žehouni, Hořejní, Mezeřiči). The vision pass cost about 670k Haiku tokens in all. Not checked by eye: whether pán na X (a noble's estate) is a residence — two readers decided it differently, on about ten entries. Provisional utils/reference_tools/places/build_matice_read_sheets.py, merge_matice_readings.py; run_matice_pilot.apply_readings; reference/places/matice_residence_readings.csv
D-163 2026-10-30 The intensity maps put five measures per 1,000 on the same 1910 units, each with the simplest definition that can be drawn. Matice: the land's own society, shrunk and standardised within its territory (matice_own_z, D-161). Theatre: Czech companies active in a year (certain), averaged 1880–1913, per 1,000 inhabitants. Legionnaires born in the unit, per 1,000 inhabitants and per 1,000 Czech speakers (left out under 1,000 speakers). Desertion: exit code D per 1,000 legionnaires born in the unit, shrunk (Marshall 1991), left out under 20. At court level a court's legionnaires are scaled up by its okres's ratio of all births to court-located births; at settlement level only counts are drawn, plus the desertion rate. The author asked for the measures side by side as a number per 1,000, at okres, court and settlement level, with the computation still provisional. Two limits shape the result. Only 57.3 % of the 101,274 Czech-lands births name a court or settlement, so unscaled court rates would map recording practice. The repo has no 1910 settlement population, so settlement rates must wait for the 1910 register's full run. Both legionnaire denominators are shown because they ask different questions: the per-inhabitant rate tracks the Czech share; the per-Czech-speaker rate asks where Czechs volunteered. Each panel keeps its own colour scale, clipped at the 97th percentile. Provisional utils/reference_tools/places/run_intensities.py, map_intensities.py; data/interim/intensity/; reference/INTENSITY_MAPS.md
D-164 2026-10-30 The route's last fixes before the final freeze: wherever a second Run All undid the first, Opus was shown or asked the wrong thing, or the data or the gold score came out other than decided, the route now does what was decided — the pre-freeze review's route findings, each ruled by the author. (1) Run All twice. The Czech anchor builds its questions from the German route without its own answers (final_inventory_no_anchor.csv), so they are a fixed point (A1). The author's rulings and validation answers are merged by mark, never rewritten from the current queue (A6). The validation sample is drawn once per run folder and joined by mark, the numbers checked (A8). Section 18 builds the register on "full" only (A10). A finished certification run is refused unless FORCE = True, and the notebook is saved on "bohemia" / "gold" with SPEND = False (A13). A re-read sets the section crops, E3 and the order flags aside with the reads, and a check older than the reads routes nothing (B3). (2) What Opus is asked. The place-type, anchor and repeat calls ask only their own questions — t- the parts list's boxes, a- and o- token questions — and the ledger takes a box answer only from a call that may ask that box (A2). Opus's example cuts come only from the gold sheets' own images, and an answer shown other pixels is asked anew (image_digest, A3). A reply in flight is read against the questions its call was sent with, the route's own submit included (B7). One question per part and symbol across the anchor and repeat calls (C9); a whole-municipality anchor claim is asked on every page the municipality is printed on (C8). (3) The data. A mark read twice gives one count to its ruled candidate ([doctor] 2 → 1 when the hidden [vet] is ruled printed, A5). Cooperative letters are counted per language letter (language_counts: R, Rd is one of no stated language and one German, Rb, 2 Rd three, A9). German table rows carried over from a page the run did not read are dropped and counted, not joined to another municipality (A12). The register's spread columns are unique and mapped back (register_columns.csv, B1). Place types carry their court and entry key, and an author-ruled type says so (C7). (4) The gold score. Section 13 scores the inventory as written, with the no-anchor route beside it for Böhmen (A4); the validation sample leaves out the marks Opus could not tell and is scored against what the inventory holds (A7, C18). (5) The freeze. Every German freeze pins the three volumes' keys, and Mähren and Schlesien are held to their own (B5); any field a run is held to is archived when it changes (C1); the worked examples, volume profiles, court crosswalk and backbone, look-alike table, glyph crops, religious categories and, for a German run, the Czech gold sheets are digested, and the scorers, register build and reports are recorded apart, never compared (C2). Also: one unreadable German table block no longer stalls the pass (B2); each crop cache records the key image its answers were read under (C16); the German header prompt names its volume (C14); the table runner refuses Böhmen's drawn sealed pages (C15); and C6, C10–C13, C17, C19, C20. The scorer's findings are D-165; the decisions the fixes rest on, D-166 and D-167. The author's request (2026-10-29): one last review of the whole route — gold, certification and full — before the truly final freeze, the Czech pipeline the baseline. Six read-only reviews, one per stage of notebook 15; every serious finding checked against the code or the outputs on disk; the 47 findings put to the author in plain words and ruled on 2026-10-30 (B8 rejected, D-167). Measured offline, in scratch, on the runs on disk: on the German gold the anchor asks 20 questions (19 claims only the Czech side holds, 1 only the German), and the same 20 once its rulings are folded in — the old rule asked none then, and dropped every claim it had added; on the Czech gold the type calls asked 21 of their 60 boxes on the institutions section, now none (32 boxes, all in the parts list); 86 of the Czech gold's 448 example cuts were blank paper, none now, and 232 of its 278 cards change; the gold validation keys held 3 (Czech) and 6 (German) open marks, never answered, so they are drawn once more. The Czech certification predates every change and stands as scored on 2026-10-25 (D-127). Settled — built and tested; the gold re-runs on Heavyhorse6 are the first runs on it utils/lexikon_1910/run_frozen.py 0.19.0; build_mark_ledger.py 1.7.0; run_verify_test_b.py 1.11.0; lexikon_1910_register.py 1.3.0; run_de_table.py 0.2.0; run_r5_german_notes.py 1.4.0; lexikon_1910_de_notes.py 0.5.0; run_lexikon_1910_pilot.py (key_image_cache); notebook 15; tests/test_prefreeze_route.py, test_opus_cards.py, test_cache_guard.py, test_de_table.py, test_register_build.py
D-165 2026-10-30 The sealed scorer is amended before any German page is scored: "as printed" counts every claim the route makes on the sealed pages, a run not read on its gold's frozen route is refused, and five smaller corrections. (1) A11: the gold's entries are every municipality and part the sealed sheet prints, a blank one included, and the route's claims are taken by page, not by gold entry: a symbol the route invented where the print has none was never counted false. The "shared entries" population is unchanged; its description now says what it computes. (2) B6: score and score_pooled refuse a run whose freeze/against_gold.json is missing or does not say same_route: true (for de-pooled, each volume's), unless --force, and the result records the comparison. (3) C3: a Böhmen cooperative heading is the author's only if every letter under it was author-ruled — an explicit set, not a substring of the rule. (4) C4: the pooled score's per-volume lines use the pooled block's two populations, with their category blocks. (5) C5: Böhmen without the anchor gets its category block, and a missing file is said, not skipped. (6) D2: specificity is kept as computed — positives are parts, negatives the entries less the positive parts — and its description corrected. (7) D3: a [!] on a sealed sheet (a mark not on the plate) is left out of both sides and counted apart. The metrics were written down before anything was scored (D-127), so changing one is a decision of its own, taken before the German score — the author's choice (D4 of the review). Rescored offline in scratch, the Czech certification moves only where A11 reaches it: every claim, F1 (0.996 [0.991, 1.000] as printed, 0.997 shared), precision, recall, the interval and the per-symbol table are unchanged but one specificity ([school] 0.964 → 0.980, its denominator now the sheets' 129 entries); as printed, entry_exact 104/107 → 126/129, the 22 entries the sheets print blank now counted, all right; [!] 0. The Czech run's freeze record says same_route: false (the contrast sheet re-encoded, its pixels the gold's; the record predates the pixel check, D-125), so it is rescored only under --force, which the result carries. The stored score of 2026-10-25 is not rewritten. Settled — the German score runs on it utils/lexikon_1910/score_certification.py 1.6.0; read_transcription_md.py 1.1.0; tests/test_sealed_scorer.py, tests/test_pooled_score.py
D-166 2026-10-30 Mähren and Schlesien take the Czech rule for cooperative letters: a letter is a cooperative only in the list after the registered-cooperative mark and its colon; a capital elsewhere in the line is a misread. run_frozen.COOP_LIST_ONLY is cz, de-moravia, de-silesia; German Böhmen, which prints its letters bare (D-139), keeps every one. D-146 found that Mähren (p. 37) and Schlesien (p. 18) print the mark and its colon list as the Czech volume does, but D-134's rule 4 had stayed Czech, so a stray capital there — the B of the A. B. / H. B. after the Protestant marks, Baugenossenschaft — would have entered final_abbreviations and the register as a cooperative. The author's ruling (2026-10-30, D1 of the review): the letters after the colon count, each by its type, as in the Czech route. Not measured: no Mähren or Schlesien page has been read yet; their calibration look (D-154) is where it is first seen. Settled utils/lexikon_1910/run_frozen.py 0.19.0; tests/test_prefreeze_route.py
D-167 2026-10-30 How the final runs are run: Gemini batched on gold, certification and full alike once its check has passed; the register built from the full run only; Mähren's and Schlesien's calibration look optional; every volume's readers shown the Czech gold's examples. (1) The Gemini batch check (notebook 15, 7e) runs once, before the gold re-read — it needs the live queues the re-read sets aside, and the notebook now finds them there too (B4) — and on PASS GEMINI_BATCH = True for every run after (D5 of the review). (2) Notebook 15 §18 runs on PAGES = "full" only; the order is notebook 02's settlement table, then §18, then notebook 02's map crosswalk (D6, C17). (3) Certification is not refused until a calibration run exists (B8 rejected); the calibration look stays available. (4) The German readers keep the Czech gold's contrast sheet and Opus's example cuts; the German Böhmen gold is not added as a second source. (1) The freeze compares the Gemini mode (D-124, D-125), so it must be one mode on all three runs; batched, the full run saves about $65 for a check of about $0.66. (2) A gold, calibration or certification Run All after the full run would otherwise replace data/final/register/ with a handful of pages, and a certification run build it from sealed pages (A10). (3) The author: skipping it was intentional, to save time, and the Czech and German Böhmen calibration sheets serve, the symbols being mostly the same. (4) The author's choice on the author's own note to B5: same type and printer, the Czech sheet is the one measured, and German examples would change the German prompts and images, so the German gold would have to be read again. Settled notebook 15; reference/LEXIKON_1910_ROUTE.md; reference/REGISTER_VARIABLES.md (Build order)
D-168 2026-10-30 The Gemini batch check's prompt-token condition is amended after its first run: one offset of at most one token, the same on every request of every kind, is the two endpoints counting one request differently, not a different request — so the check passes and Gemini is batched (D-124, D-167). reader_batches 1.5.0: PROMPT_OFFSET_TOLERATED = 1, _constant_offset; any other difference — larger, of mixed sign or size, or on only some requests — still fails, and the verdict records the offset and the amendment. The check (Heavyhorse6, 2026-10-30, ≈ $0.66) sent 98 notes crops and 24 table blocks of the Czech gold through the native batch, with a live control alongside. Every condition held but one: all 122 answered, none thought, none broke its schema, and the batch agreed with the earlier live reads as well as the control did (notes 0.777 against 0.773, table 0.959 against 0.959, both inside the floor). The prompt tokens differed on all 122 — each exactly −1. The condition is there to show the model was shown the same request; a dropped image or other instructions would differ by hundreds to thousands of tokens and unevenly (an image is ≈ 1,000–4,800), so a uniform −1 on requests of every size is counting, not content. This changes a bar after seeing the result, which D-124 wrote down beforehand to avoid; it is taken because the condition that measures the replies passed cleanly, and it is narrow: the stored results, re-judged without a new call, give PASS with every other figure unchanged. Blind to: what the one token is — the check cannot see inside either endpoint's count. Settled — the author's choice (2026-10-30); re-collect 7e on Heavyhorse6 (no call) to write the verdict utils/lexikon_1910/reader_batches.py 1.5.0; tests/test_reader_batches_gemini.py; output/frozen/gemini_batch_check/
D-169 2026-10-31 Two look-alikes are settled by where the print sets them, not by their glyph: the mark before a cooperative list's colon is the cooperative mark, and a poorhouse in the castle's slot is a castle. (1) The colon rule (lexikon_1910_institutions 0.15.0, cooperative_colon_rule, in the decoder every reading passes through): on a plate that has the registered-cooperative mark (the Czech volume, Mähren, Schlesien), a mark directly before : <cooperative letter> is read as [cooperative], and a mark set against [cooperative]: with no comma is dropped as the other half of one glyph; German Böhmen, which prints its letters bare (D-139), is left alone. (2) The position rule (position_swaps, written by section 8's order check; run_frozen 0.20.0 position_swaps_of, applied to the settled claims): a [poorhouse] that breaks the learned order in its slot, where a [castle] in the same slot breaks none and the run holds no castle, is the castle — in its place, with its printed count, when the poorhouse is read once; beside it, the poorhouse's count less one, when the run also holds a poorhouse in a slot of its own. It applies where more than half the readings that hold the poorhouse on the part flag it; the author's ruling on either claim stands over it. (3) The bracket rule (bracketed_glyph_rule, in the decoder): the plates set four marks as another's glyph in parentheses — the school expositure, the parish expositure, the customs expositure and the sanatorium are the school, the parish, the customs office and the baths in brackets — and the readers copy the brackets and name the glyph inside (Wasser([baths])); a mark written in its own parentheses is read as the variant, where the volume's plate has it. Measured on every reading on disk: 16 readings put right on 9 parts (both golds and the Böhmen and Schlesien sealed pages), none made wrong. Not a rule, by the author's word: the Silesian hut read as a shepherd's hut where the sheet has a tourist hut is one glyph the volumes explain differently. The German certification (D-164's route, 2026-10-31: pooled F1 0.957) put 14 of its 54 errors on a castle read as a poorhouse and 14 on a savings bank the sheets do not print — all of them ruled by the author against the print, every one for the sheet. Both are the print's own syntax: in all 44 gold sheets no mark but [cooperative] ever stands before a colon (178 times) and none stands against it without a comma; a castle sits late in an entry (83 % of the way, after the post and telegraph marks), a poorhouse in its middle (43 %, after the school), the poorhouse before the castle 4 times and never after. The readers wrote the cooperative mark as [savings-bank]: R or [savings-bank] [cooperative]: R; the order rule flagged the late poorhouse and Opus, shown the glyph, confirmed it. Measured on every reading on disk (both golds, all four certification runs): the colon rule rewrites 152 savings-bank readings, 145 on parts that print no savings bank (the other 7 print a real one beside the mark); the position rule, first written to swap in place, fixed 10 claims and broke 6 real poorhouses — every one a run holding the poorhouse twice — and as amended fixes 10 and breaks none, with 0 swaps on the gold sheets' own 234 runs (15 poorhouses). It changes nothing on the certified Czech pages. These rules were found on the sealed German pages' failures, so a score of them there is not blind (D-170). Settled — the author's proposal (2026-10-31) utils/lexikon_1910/lexikon_1910_institutions.py 0.15.0; run_frozen.py 0.20.0; tests/test_lookalike_rules.py
D-170 2026-10-31 The German half is certified a second time on the same eight sealed pages, after D-169, read from scratch; the first certification stays the record of the route as it was. run_frozen 0.20.0: a reading round (ROUND, notebook 15 NEW_ROUND) gives a run labels of its own, so every reader starts from empty caches and new pass files and every call — the readers' and Opus's — is made again, batched; set_run_aside first moves the whole earlier run (reads, crops, E3, Opus's answers, ledger, inventories, freeze) to …_superseded_round<n>, nothing deleted, and needs FORCE on a certification run. The first round's pooled score (2026-10-31 15:22, F1 0.957 [0.927, 0.982]) is kept beside the second. The order: the gold re-read with D-169–D-171 first (its freeze holds every later run), then the second round, scored, then the full run — the full run last, so that the data is read on the route the second round measured. Corrected before the second score, by the author's rulings on the contested cases: Schlesien 0038's sheet — Gotschdorf 3,0 prints a fire brigade, not a midwife; Heinzendorf 5,0 a fire brigade the sheet lacked; Geppersdorf's marks one part down (the school at 2,2; fire brigade, post, rail stop and altitude at 2,3). The author's choice (2026-10-31): fix the two rules and certify on the same pages, the models' new calls not biased by the earlier reads. What it is and is not: fresh calls remove any reuse of the earlier answers, but the rules were found on these pages' failures and the author has seen the pages while ruling on them, so the second score is an in-sample measure of the fixed route, reported beside the first and never in its place; a blind measure of the fixed route would need sealed pages no run has read (Mähren's and Schlesien's other two drawn pages, D-136, untranscribed). The first round, rescored in memory against the corrected sheet (nothing written): 604 hit, 25 false, 18 missed, F1 0.966 [0.949, 0.982] — the sheet's own slips were 8 of its 54 errors. Settled — the author's choice utils/lexikon_1910/run_frozen.py 0.20.0; notebook 15 (NEW_ROUND); reference/places/transcription_gold/de-silesia/certification/0038.md; output/frozen/de-pooled_certification/
D-171 2026-10-31 On a German page, Opus is told that part 0 includes the named places printed inside a municipality's entry without a number of their own; and a call whose wording changes is asked anew. (1) run_verify_test_b 1.12.0 PART0_NAMED, set by run_frozen 0.20.0 for the German editions' institution, anchor, repeat and abbreviation calls about part 0 — never for a place-type call, where the type belongs to the named part, and never on the Czech volume, whose questions stay word for word as certified. (2) The call manifest keeps each call's wording, and _retire_stale_answers moves an answer aside when the words asked differ from those answered (read off the call's prompt.txt for a manifest from before). A German entry sets named places inside it — 37) … Neuleskau: [kindergarten] — and the readers and the transcriptions file their marks under part 0; asked whether the kindergarten is printed for part 0, Opus answered that it stands under Neuleskau and is not. 13 German answers had that shape (none Czech); two cost claims on the sealed pages, both confirmed by the author's rulings (the kindergarten of Mähren 0037 37,0, the midwife of Schlesien 0018 14,0), one on the German gold (the tourist hut of Hornomarsov 19,0). Without the second change the old answers, given to the old words, would have been kept. Rebuilt offline on the golds: the Czech gold's 108 calls keep their words, the German gold's 25 part-0 calls of 82 are asked again. Found on the sealed pages' failures (D-170). Settled — the author's choice (2026-10-31) utils/lexikon_1910/run_verify_test_b.py 1.12.0; run_frozen.py 0.20.0; tests/test_lookalike_rules.py
D-172 2026-10-31 The second German certification is scored and recorded as in-sample; the quotable figures are the Czech 0.996 and German round 1 (0.957, 0.966 on the corrected sheet), and the per-symbol rates are quoted blind first. Round 2 (pooled, 8 pages): 623 printed, 619 by the route, 618 right, 1 false, 5 missed; F1 0.995 [0.991, 0.999], precision 0.998, recall 0.992; entries exact 128/133; Böhmen 0.998, Mähren 0.998, Schlesien 0.990. Covariate symbols, round 1 (blind) → round 2: school 103/104 → 104/104, fire brigade 92/93 → 95/95, cooperative 69/71 → 71/71, midwife 60/62 → 62/62, post 34/35 → 35/35, with 1, 3, 0, 1, 1 false claims → 0 each; castle 6/14 → 13/14; poorhouse 7 false → 0; savings bank 14 false → 0. Text abbreviations are scored on the sealed pages by score_sealed_abbreviations.py (German: round 1 144 of 144 right with 1 false, F1 0.997; round 2 139 of 144 with 1 false, F1 0.979 — five cooperative letters lost on the re-read); the Czech pages wait for the full run. The gold's own figures (Czech F1 0.998, German 0.969, precision 0.947) are in-sample. Round 2 reads the pages the D-169 rules were found on, so it measures that the fixes work on their own evidence, not how the fixed route reads unseen pages. A zero count on about 100 claims bounds the rate at about 3.5 % (exact two-sided upper end, claims taken as independent, which understates it). The overall German figure in round 1 was pulled down by the failing symbols; the five covariate symbols were at precision 0.97–1.00 in both languages. DINOv3 still routes nothing for German (0.115 of claims judged, Czech 0.863); the rescale test has not completed, and a change to the E3 route would void the freeze, and the only pages left to re-certify it are the four sealed reserve pages (D-173), which are all Mähren and Schlesien. Settled (the numbers); the E3 question is open output/frozen/de-pooled_certification/certification_score.{json,txt}; round 1 in …_superseded_round1/; reference/LEXIKON_1910_ROUTE.md ("Everything certified, in one place")
D-173 2026-10-31 The four reserve pages (Mähren 71 and 139, Schlesien 28 and 48) are sealed for the next certification, not dropped; and German DINOv3 is not fixed by rescaling or by a German-trained locator. Role reserve in gold_sample_pages_{maehren,schlesien}_de.csv; run_frozen.sealed_pages includes it (out of the review queue and the validation sample), pages_for("certification") does not; no component may build, tune or train on them. Measured the same day, offline: the German gold sections rescaled to line height 66 lift DINOv3's judged share 0.115 → 0.165 (keep 0.98, catch 4 of 22; cover test fails); a German-trained locator places office claims at 70.7 % leave-page-out, the Czech-trained locator's 70.7 % (parts 66 % → 79 %). The author's choice (2026-10-31): do not use the reserve pages for anything but the next blind certification. The overlays (german_locator_trial.py, then locate_glyphs.draw) put the Czech-placed boxes on key digits and letters, so the placed share counts forced boxes and a higher share would not mean a better bank. Open — the German bank utils/lexikon_1910/run_frozen.py; utils/reference_tools/places/german_locator_trial.py; utils/lexikon_1910/run_scale_e3_test.py (uncommitted); tests/test_ms_sample.py
D-174 2026-10-31 The entry-level validation sample is not drawn. It would take 12–16 hours of the author's transcription (about 290 whole entries) plus 2–3 for a route-negative stratum, the only part that can see misses; the regressions come first. The author's choice (2026-10-31): the marginal gain is a few per cent and the remaining error is taken to be random with respect to the covariates. The sealed pages, the sealed abbreviation score and the per-volume figures are the error evidence; rare-symbol covariates carry no rate. The assumption is checkable on the per-volume figures (Schlesien 0.909 vs Böhmen 0.976 in round 1) and on misses against print features, when the regressions are run. Settled — the author's choice reference/LEXIKON_1910_ROUTE.md ("Not done: the entry-level sample")
D-175 2026-10-31 German E3 is closed for this thesis: DINOv3 stays recorded and routes nothing for the German volumes. The author clicked and named 203 glyphs on gold pages 0142, 0276, 0480; against them (Opus reviewing each step): the Czech-trained placement puts the box on the right glyph for at most 27 % of claims; the candidate step loses 9–29 % of the clicked glyphs (ink 128, a fixed threshold the German print is too light for; 91 % held at 140) and only 43–58 % sit in a cluster narrow enough to use; with the box certainly right DINOv3 names 85–93 % correctly against the Czech bank and, with German exemplars leave-one-page-out, judges about 0.95 at agreement 0.94; but detecting by naming, every candidate against the Czech + German bank, gives precision 0.10–0.26 at recall 0.74–0.86 at each page's own threshold. The stopping rule was fixed with Opus before the last run: one DINOv3 run against written criteria, a miss closes it, and the author needs the time for the regressions. A working version needs a German ink constant, key-line alignment, a reject class and one-to-one matching, a new freeze, and German re-certification on the four sealed reserve pages (D-173), for at most about 1 pp of F1 on symbols mostly not used as covariates. The regressions run with German E3 off, as certified: land fixed effects, a Bohemia-only check, and the Czech-against-German disagreement rate in Böhmen on the covariates to look at non-random error (D-174). Settled — the author's choice, on the result reference/LEXIKON_1910_ROUTE.md (item 10); utils/reference_tools/places/{german_click_recall,german_candidate_diagnosis,run_german_oracle,german_supervised_placement,run_german_detect_by_naming,build_german_click_strips}.py
D-176 2026-11-01 Reader-supplied boxes do not reopen German E3: D-175 stands. One test with bars written before the read: Gemini 3.8 Flash (the reader of record, thinking off) returned a box for every mark it wrote on the offices sections of gold pages 0142, 0276, 0480, and DINOv3 judged each box against the Czech bank + the German click exemplars of the other pages + a reject class, each page held out. 202 marks: right 139, box off 15, misread 30, phantom 18. Box 0.903 (bar 0.90), cover 0.957 (0.835) and catch 0.857 on 42 false claims (0.50) pass; keep 0.872 on 133 judged true claims fails the bar 0.936, which lies above the exact 95 % upper bound 0.924. A miss on any bar was written to close it, and the bar was set at the Czech agreement less 0.05 because in the route a disagreement sends an agreed claim to Opus (routed_claims): one true claim in eight flagged means thousands of extra Opus questions on German, each a chance of a wrong overturn, against agreed false claims at about 1 in 620. Read cost $0.06. Settled — on the pre-written bars reference/LEXIKON_1910_ROUTE.md (item 10, Reopened for one test); utils/reference_tools/places/run_german_reader_boxes.py
D-177 2026-11-01 One declared re-judge of D-176 with each query cut the way the bank is cut. D-176 cut its queries from Gemini's raw box (median IoU 0.35 with the ink-tight box of the matched click) while the bank's exemplars are cut by tight_box at the click: a test-design defect, found on the cached replies after the result. The rule, fixed before any snapped judging: tight_box on the exemplars' ink mask (threshold 128) at the integer centre of Gemini's box, no search, no parameter, raw box kept where no ink is near. The D-176 bars and threshold rule are unchanged, it runs once from the cached replies (--snap), and the D-176 fail stays on the record. "The query is cut by the same operator as the bank" is a rule the test should have had from the start, not a parameter chosen on the result. But the diagnostic, the bank and the thresholds are all from the same three gold pages, so a pass only reopens German E3. Then it is parked until after the regressions and built only if, certified on the four sealed reserve pages (D-173), keep reaches at least 0.95. A miss closes it for good. Opus's expectation before the run: keep about 0.92 against the 0.936 bar. It does not delay the gold re-freeze or the full run, and box requests are not added to the frozen German prompt. Result, run once 2026-11-01: keep 0.856 (119 / 139, exact 95 % 0.787–0.910) fails; cover 1.000 and catch 0.891 (41 / 46) pass. 17 of the 20 disagreements name a true glyph [not-glyph]: even tight-cut, one German pictogram in seven sits nearer the print around it than its own token. German E3 is closed for good. Settled — on the declared bars reference/LEXIKON_1910_ROUTE.md (item 10); utils/reference_tools/places/run_german_reader_boxes.py 0.2.0
D-178 2026-11-01 The full run skips the 17 statutory-city pages, and their institutions are read from the prose apart. Prague and Liberec (Czech 15–18, German Böhmen 21–24) and the six Moravian (19–24) and three Silesian (13–15) statutory cities print their institutions as running prose with no pictogram. The full run reads each volume's table pages less these (run_frozen 0.21.0). The register names the 11 cities and has no row for them, so an okres aggregate is missing, not zero (lexikon_1910_register 1.4.0). The prose is transcribed verbatim by Gemini 3.8 Flash and Opus 5.5, independently; the text layer votes where they differ, and the author settles the rest (statutory_prose.py stage 1). Mapping the spelled-out institutions onto institution_abbreviations.csv is stage 2. The German apparatus cut raised on these pages (Böhmen 21 is the first page of the full run), no reader had anything to read there, and none was ever drawn into a sample (D-40). But these are the largest cities of the three lands, where many legionnaires were born: missing is honest, zero is the error shape that fabricated exit_N, and the prose is the only source that holds them. Two independent vision readers and a third, noisy reading put the author's effort on the disagreements alone. Open — the route part settled; both stages read 2026-11-01 (stage 1 $1.63; stage 2 readers $2.94, judge Sonnet 5.5 $0.70, the default since: neither reader, so not Opus ruling on its own answers; no head-to-head against an Opus judge was run): 621 of 627 open groups adjudicated, 6 left for the author — 601 at first, then 20 more once the judge's rows were accepted with a count printed after se/mit/( and a code given as its vocabulary expansion (statutory_institutions 0.4.0; the readers' strict rule, which picks the groups the judge sees, is unchanged, so its cached verdicts stand). The author ruled five of the six on 2026-11-01 — a council or institution with language sections is the council and a row for its sections, as a branch is (reference/places/statutory_institution_rulings.csv, statutory_institutions 0.5.0) — leaving one: the Egerer and Heiligenberger archpresbyteries, two named and no number printed (output/statutory_prose/institutions/review.csv); printed totals flagged is_total, and with them left out Prague agrees across the two editions within a few per category (cooperatives 186 / 185) utils/lexikon_1910/run_frozen.py 0.21.0; lexikon_1910_register.py 1.4.0; utils/reference_tools/places/statutory_prose.py
D-179 2026-11-01 German pages with faint rules: the header's foot by position and the columns from the volume's template, each only where the strict rule fails. lexikon_1910_image 0.3.0: where no frame-wide rule is crossed by three column rules, the lower of exactly two such rules a header's height apart is the header's foot (HEADER_HEIGHT 240–380 at the reference width). Where only the top rule is frame-wide, one long rule (≥ 60 % of the frame) a header's height below it is the foot (Mähren 28). Where the page gives another column count than its profile's, the found rules are fitted to the profile's column_template (the median boundary over 17–18 random full-run pages), the frame solved for from every pair of found rules on every pair of template boundaries; the missing rules are placed at the template and a wrong frame side is corrected (an outer line, the page edge, a column rule). Kept only with half the inner boundaries matched and 80 % of the rules inside the frame on the template. German callers only (full_width_headings, column_template); the Czech geometry is unchanged. Cutting all 641 German full-run pages offline found the apparatus cut raising on at least 9 table pages (Böhmen 73, 88, 232, 233, 237, 241, 243, 259 among the first 297 scanned; Mähren 28) and no table block on 57 (48 Böhmen, 9 Mähren): a run stopped or a page lost without a word. After the fix the audit finds 0 and 0, with 57 pages through a fallback. The template is safe to use because the layout is fixed by the printer: on every page measured, no boundary strays more than 1 % of the frame's width, against a narrowest column of 3.2 %. A page laid out otherwise keeps what it found and says so. No gold, certification or reserve page (27) takes either path, so the certified pages are read exactly as certified. Settled utils/lexikon_1910/lexikon_1910_image.py 0.3.0; lexikon_1910_volume.py 0.2.0; reference/places/volumes/{boehmen,maehren,schlesien}_de.toml [geometry]; utils/reference_tools/places/audit_full_run_pages.py
D-180 2026-11-01 A run is held to completeness by outcome, not by recognising errors; and every register claim keeps its way back to its glyphs. Notebook 15 checks that every request a pass queued has its reply in the cache (after 7d: a pass with a gap is marked unfinished, on a run that spends and never on sealed pages, so 7d reads only the gap again) and that every Opus call has its answer (before the ledger, section 12, and the anchored ledger, 14b): an edition with a gap is not built on. A gap the author lets through is named by key in ACCEPT_FAILED and recorded in reads/completeness.json. lexikon_1910_register 1.7.0 carries each claim's entry and marks (keys of the run's ledger) into register_institutions and register_cooperatives. The route stopped a pass only on a 429 or a word like quota; anything else — Anthropic's exhausted-credit reply, an outage, a refusal — was retried, dropped and the pass marked finished, its marks missing from the inventory and read as not printed (the error shape of exit_N); an unanswered Opus call fell back to the readers. Checking the outcome catches every cause whatever its wording. The guard lives in utils/validation/ and the notebook, never in utils/lexikon_1910/, whose every module is digested: the full run stays on the 14:31 gold freeze's route. The register dropped the glyph pointer the inventory had: all 1,109 pointers of cz_gold and Schlesien's certification resolve in their ledgers. Settled utils/validation/run_completeness.py 0.1.0; notebook 15 §1, 7d, 12, 14b; lexikon_1910_register.py 1.7.0; tests/test_run_completeness.py
D-181 2026-11-02 The full run's readings of the sealed pages are scored as a test–retest, beside the certification and never in its place. utils/validation/score_sealed_retest.py 0.1.0 scores each edition's <edition>_full run on its sealed sheets with score_certification's own metrics, beside the certification run, and lists the claims the two runs settle differently (output/frozen/sealed_retest/<edition>.{json,txt}). Schlesien, 2026-11-02: F1 0.980 [0.973, 0.989] (195 hit, 3 false, 5 missed of 200) against round 2's 0.990 (197 · 1 · 3). The two runs differ on four marks: the full run misses a fire brigade, reads a cadastre office as a customs expositure and a sanatorium as baths (round 1's confusion, here by an Opus ruling), and finds a filial church round 2 missed. The route doc planned this as a second measurement of one route, not an independent one (The order). It is in sample, since the pages shaped the D-169 rules, and the readers sample, so the gap between the two runs is read-to-read variation on one route. That is the figure it gives: how far one full read of a page can move from another. The scorer is outside utils/lexikon_1910/, whose every module is digested into the freeze, and it copies each run's outputs into a temporary folder rather than editing score_certification.py, so the running full run stays on the 14:31 gold freeze's route. It writes nothing into a run folder. Settled — Schlesien scored; the other editions as their full runs finish utils/validation/score_sealed_retest.py 0.1.0; reference/LEXIKON_1910_ROUTE.md (The full run's readings of the same pages)
D-182 2026-11-02 Matice moravská is measured by its roster of 1 March 1909, not the list of the end of 1890: the 1909 list stands for Moravia and Silesia in matice_own_z, the intensity maps and the Matice maps (matice.MM_OWN). The 1890 counts stay in the tables beside it. Settles the D-161 ruling "which Matice moravská list stands for Moravia". The author's call, 2026-11-02: the measure should describe the society as it stood before the war, and 1909 is the roster nearest the 1910 census that is its denominator; no full roster was printed in 1910–1915. The two lists rank the 45 Moravian and Silesian okresy alike (Spearman 0.84 between the shrunk rates); members placed in Moravia and Silesia 478 → 475, okresy with none 7 → 9. Against the theatre measure (1880–1890) in Czech-majority Moravian okresy, Spearman falls from 0.49 to 0.37, as expected of a list twenty years later than the window; matice_own_z overall 0.69. Matice česká stays on its founders of 1831–1890: its printed founder register ends with the 1891 issue (Czech Digital Library record, 1869–1891), and the 1910 Časopis Musea království Českého (ročník 84) prints only the call for founders, not a list (checked 2026-11-02). So the combined measure joins a 1909 Moravian stock to an 1890 Bohemian one (superseded for Bohemia by D-184: the society's monthly payment lists in the same Časopis Musea name every founder who joined after 1890). Provisional utils/parsing/matice.py 0.3.0 (MM_OWN); run_intensities.py 0.3.0; map_matice.py 0.2.0; run_matice_pilot.py; Matice measure
D-183 2026-11-03 Ostafin et al. (2020) is the external check on the register's German table, and the ČSÚ lexicon 1869–2005 is not a check on any 1910 commune. Their Austrian Silesia dataset (CC0) types the 1910 census of every commune from the same Spezialortsrepertorium Schlesien the full run read; utils/validation/check_ostafin_silesia.py compares it with our census, with the full run's table readings commune by commune, and with the lexicon. The register's table had no outside yardstick: the sealed pages score the apparatus, and the Czech table was graded against the volume's own arithmetic. Run of 2026-11-03 (0.1.0): the census we use equals Ostafin in all 12 political districts; Gemini's table reading equals theirs on 98.6 % of 441 joined communes for total population, sex, houses, languages and religion (Haiku: 98.6 % on population, 94–96 % on languages), and where the two differ on the page the print sides with our reading — Ostafin files some settlements (Pudlau, Neudorf) as communes of their own, and its commune-name field changes within a commune, so communes are grouped by number. The lexicon equals Ostafin on 311 of 382 present-day units; every other difference is a unit, not a number: díl I recomputes 1910 onto 2005 parts (sestaven podle správního rozdělení státu k 1. 1. 2005), so a part either carries other 1910 places (its own notes say so for 12), the whole 1910 commune (16), other parts of it (8), or the settlement where Ostafin has the commune (25); 10 are join problems. So the lexicon stays what it is used for — names, the okres timeline, the link to today — and a 1910 commune's figures are checked against the print. Area is not compared: Ostafin's is measured on its maps. Settled for Silesia; Bohemia and Moravia have no such transcription utils/validation/check_ostafin_silesia.py 0.1.0, utils/validation/lexikon_population.py 0.1.0; data/raw/geo/ostafin_silesia/ (SOURCE.txt); Validation register
D-184 2026-11-03 Bohemia is measured by the Matice česká joiners of 1900–1909, read off the society's monthly payment lists, not by its founders of 1831–1890: a joiner is a person whose first payment towards a founder's share falls in the window (matice.MC_OWN, JOINER_WINDOW). The 1831–1890 founders and the joiners who paid the full 105 K within the series stay in the tables beside it. The lists are the Zpráva o Matici České closing every issue of the Časopis Musea království Českého (Národní muzeum, 1885–1914, Data sources #31), parsed month by month and checked against each month's printed total. The author's call, 2026-11-03: "the recent joiners are the better version". It answers D-182's open point — the two lands now sit at the same date, a stock of Moravian members on 1 March 1909 beside the Bohemians who joined in the decade to it — and it is a flow from a narrow window, which needs no correction for founders who died or moved after 1890 (32 % of those alive in 1873 had died by 1890; 26 % of survivors had changed okres), as Squicciarini & Voigtländer's (2015) Encyclopédie subscriptions are. Joined is dated by the first payment, not by completion: the fee was paid in instalments over years, often 5 K a month, so only 151 of 876 joiners reach 105 K before the series ends, and a payer the lists name differently splits in two, which a completion count punishes and a first-payment count does not. Measured (run_matice_pilot.py, matice 0.6.0 after the audit fixes of 2026-11-03: institutions out of the persons, founders keyed by number and surname): 622 joiners placed in Bohemia, 35 of 103 Bohemian okresy with none; Spearman 0.74 with the 1831–1890 founder rate and 0.61 with the Czech share; ⚠ but 0.04 with Czech theatre of the same years (1900–1909) inside Czech-majority Bohemia (the 1831–1890 founders against 1880–1890 theatre: 0.27). (An earlier figure here, −0.05, set the joiners against 1880–1890 theatre.) Ruled 2026-11-03: a joiner is dated by the first payment, not by completion (the author's call). To rule: the window. Provisional utils/parsing/matice.py 0.4.0 (parse_payments, payment_persons, joiners, MC_OWN); run_matice_downloads.py 1.2.0; run_matice_pilot.py; run_intensities.py 0.4.0; Matice measure
D-185 2026-11-03 The combined Matice measure is a log density, following Squicciarini & Voigtländer (2015): matice_own_z is the z-score, within each society's territory, of ln(1 + own-society shrunk members per 100,000 inhabitants); every regression on it carries a Prague dummy (prague, census okres Praha 301072 = the city only — Karlín, Královské Vinohrady, Smíchov and Žižkov are okresy of their own and not in it). Robustness forms, kept in the same tables: the level z (matice_own_z_level, the measure until now), the any-member dummy (matice_own_any), ln(EB rate), and the sample without Prague. The author's call, 2026-11-03 ("Yes, that baseline"). The construction is Squicciarini & Voigtländer's (2015, QJE 130(4); NBER w20219, §3.1, fn. 35–36) for Encyclopédie subscriber density, lnSubDens = ln(subscriptions per 1,000 of 1750 population + 1), taken "to reduce the influence of extreme values", with a Paris dummy in all regressions, robustness in levels and with a dummy for any subscriptions, and standardised coefficients. In levels Prague is z = 8.31 and its 212 joiners nearly double the Bohemian s.d. of the joiner rate (0.105 per 1,000 with Prague, 0.060 without), compressing every other okres. Per 100,000 rather than their per 1,000: the Bohemian median is 0.03 per 1,000, where ln(1 + x) ≈ x and Prague would still be 7.40; per 100,000 (median about 3 in Bohemia, about 8 in Moravia and Silesia) it is 3.15, next highest 2.02, lowest −1.36, skew 0.36, and the order of okresy is unchanged (Spearman 1.000 with the level z in each territory; court level: Prague 3.61) — figures of the build after the audit fixes, 2026-11-03. ln(EB rate) gives Prague 2.44, but its bottom is set by the shrinkage prior — the 35 Bohemian okresy with no joiner take values driven by population — so it is robustness, not baseline. The Prague dummy's reason is substantive, not their data reason: the Matice česká sat in the Museum in Prague, and students and officials from all of Bohemia paid while living there; the suburbs are a choice to state, as they use the city of Paris. The estimation code does not exist yet, so the dummy is a requirement on it. Provisional utils/parsing/matice.py 0.5.0 (LOG_PER, PRAGUE_CENSUS_OKRES_ID, own_society_z, add_own_society); run_intensities.py 0.5.0; map_matice.py 0.3.0; map_intensities.py 0.2.0; Matice measure
D-186 2026-11-03 The Matice measure is carried on both denominators: per inhabitant (matice_own_z, D-185's baseline) and per Czech speaker (matice_own_z_czech), each built the same way — the own society's members shrunk (Marshall 1991), ln(1 + members per 100,000), z within the society's territory — the second over the units with at least 1,000 Czech speakers (1910 Umgangssprache); a unit with fewer has no per-Czech value. The per-Czech form moves from the intensity maps into matice.own_society_czech, so the Matice tables carry it (matice_okres.csv, matice_court.csv, with matice_own_per_1000_czech). The author's ruling, 2026-11-03 ("both"). Per inhabitant keeps a German okres's zero a true zero, as the theatre measure does, with the Czech share controlled; per Czech speaker asks how committed the Czech population was where it lived. On the build of 2026-11-03 the two agree inside the Czech-majority okresy (Spearman 0.976 over 93) and part outside them (0.774 over 30), where the per-Czech form rests on one to eight members among a few thousand Czech speakers, most of them posted: 14 of the 17 Bohemian joiners placed in German-majority okresy are priests, teachers or officials. Under the threshold: 25 of 148 okresy, 89 of 330 courts. Found on the way: the intensity tables' matice_per_1000 was per million (multiplied by 1,000 twice); the maps draw the z, so nothing displayed changed. Settled — the author's choice utils/parsing/matice.py 0.7.0 (MIN_CZECH_SPEAKERS, own_society_czech); run_intensities.py 0.6.0; tests/test_matice.py; Matice measure
D-187 2026-11-03 SL is the Slovenská liga v Amerike (Slovak League of America) and ČNS the České národní sdružení (Bohemian National Alliance), both émigré organisations in America: SL joins slov_liga, and ČNS — coded Č8 national socialist until now — becomes its own dummy pol_cns. A bare SL counts only as a whole token, since č.sl. and česk.sl.org. are Czechoslovak. The refusal of sl (D-50) is withdrawn; ľudovák stays refused. The records settle what plate IV.37 could not: 188 of the 214 records naming SL or a líga, and 189 of the 220 naming ČNS, mention the United States, with applications filed in Cleveland, Chicago, New York and Omaha, and most enrolled in the Czechoslovak army in France, where the American volunteers went; the two often stand together and with Sokol (ČNS SL, Sokol ČNS). The Czech national socialists wrote themselves nár.soc., never ČNS. The author's go-ahead, 2026-11-03 ("then solved"). Measured on the raw file: pol_nar_soc 6,541 → 6,319, pol_slov_liga 63 → 211, pol_cns 222; recorded-but-unmatched strings no longer include the 108 bare SL. Settled — the author's choice utils/parsing/affiliations.py 2.3.0 (TOKEN_MAP, _WHOLE_TOKEN, PARTY_ORDER, PARTY_IDEOLOGY, self-test); notebook 03's party labels; re-run notebooks 02 and 03
D-188 2026-11-03 The joiner window is 1895–1909 (matice.JOINER_WINDOW), not 1900–1909: Bohemia is measured by the Matice česká founders whose first payment falls in the fifteen years to the Matice moravská roster of 1 March 1909 and the 1910 census. The theatre check sets the joiners against theatre of the same years. The author's ruling, 2026-11-03, on D-184's provisional window. A decade holds too few people: the median Bohemian okres had 2 joiners in 1900–1909 and 35 of 103 had none, so a handful paying in one year or another reordered okresy (Mělník 8 · 1 · 1 and Rokycany 1 · 1 · 5 over 1895–99, 1900–04, 1905–09). The test that decided it: each window's okres ranking against joining outside both windows (1886–1894 and 1910–1914, 1,042 joiners), Spearman 0.826 for 1895–1909 against 0.793 for 1900–1909, and 0.628 against 0.557 inside the Czech-majority okresy. (Split halves do not settle it: 1895–1901 against 1902–1909 gives 0.62, 1900–04 against 1905–09 0.64, but the wider window's halves lie further apart in time.) Rebuilt: 973 joiners placed in Bohemia (622), 25 okresy without one (35); matice_own_z Spearman 0.934 with the 1900–1909 version in Bohemia, the per-Czech form 0.827; Prague 3.00 in logs (8.62 in levels), next 1.83, minimum −1.54; joiners against founders 1831–1890 0.78 (0.74), against the Czech share 0.66 (0.61), against same-years theatre in Czech-majority Bohemia 0.02 (0.04). Figures in D-184 to D-186 are on the 1900–1909 window. Settled — the author's choice utils/parsing/matice.py 0.8.0 (JOINER_WINDOW); build_docs.py (labels from the constant); Matice measure

D-15, in full — the contested enlisted band

Three values are in play for svobodník, and they are one full step apart:

Value Source
OR-3 ranks.py _COMMON_TO_BAND — what the pipeline currently emits
OR-1 Rank ladders §G2, reporting the APersP-01 CZE national note as vojín, svobodník = OR-1, with desátník, četař, rotný = OR-3–4
OR-2 The modern Czechoslovak/Czech ladder as commonly published, and the reading that separates svobodník from vojín without promoting it into the leader band

The decisive qualitative evidence sits in the project's own sources and points away from OR-3. Bednařík (1927) records the Habsburg POW-camp pay scale in Italy from April 1916 — "pěšák a svobodník 15, desátník a četař 30, šikovatel a výše 50 centesimů denně" — which pays the private and the svobodník identically and puts the first real step at desátník. Dolejší et al. (2003, p. 408) gloss the rung as Gefreiter / Patrouilleführer / Oberkanonier, i.e. a nominal patrol- or gun-leading function; that gloss is why the rung was corrected downward to Gefreiter level in the first place, not upward into OR-3.

What holds svobodník above vojín is real but weaker: every ladder orders it strictly higher, and the Rozkaz 15/117 plates give it its own insignia. So OR-2 is the defensible reading — distinct from the private, short of a leader grade.

Why nothing changed. Fixing this rung alone would leave desátník at OR-4 and četař at OR-5, one step above what §G2 implies, so the honest repair is a recalibration of the whole enlisted/NCO run — and that turns on what APersP-01's CZE annex actually says, which has not been read directly (§G2 is a paraphrase). The blast radius is contained: the band is only the robustness axis (rank_band, rank_reset_band). The headline rank_reset differences the fine common scale, which this does not touch. Changing the map requires re-running notebook 02.

What is not here

Decisions internal to a single module — a threshold, a lookup order, a deliberate refusal — live in that module's docstring and surface on its script sheet. This register is for calls that a reader of the results would want to interrogate.

AuditsHistory & decisions

Three dated code+logic reviews (2026-10-24, 2026-09-22, 2026-07-16): findings, fixes, verification. · source: reference/AUDIT_2026-10-24.md

In short

The third audit, after July's and September's. Its scope is the 1910 place register: the frozen run (notebooks/15_frozen_run.ipynb, runner utils/lexikon_1910/run_frozen.py) and everything it calls, read the day after its first gold run (D-118), plus the layout of the repository's code. Three independent read-only reviews ran on a frozen copy of the code: a correctness review of the route, an audit of every register decision against what the route implements, and an audit of what the route delivers as place-level variables. Every finding below was checked against the gold run's own output files, and every fix was replayed offline on those files (nothing sent, no run folder written) before it was kept. Decisions: D-119 (code layout), D-120 (route fixes).

Explain it simply

Twice — once in July 2026 and again in September — the entire codebase was read line by line, deliberately looking for reasons to distrust it rather than reasons to ship it. This sheet is what the two passes found, newest first.

The September pass found its worst problem in the step that matches a legionnaire to the same man in the list of fallen Habsburg soldiers. Two records were called the same person when the names were similar enough — but the test scored the whole name at once, so a shared first name (there are a great many Františeks) could carry two different surnames over the line. A second rule trusted a note field that mostly says which regiment keeps the death register, not that the man served in the Legion. Once each part of the name had to match on its own and the note had to actually mention the Legion, the 2,239 matched men fell to 1,340. Fewer matches, but ones that are really the same person.

The July pass found the one below, and it is worth understanding even if you never touch the code.

It is the most dangerous shape a data bug takes. About 26,000 soldiers had a blank exit field — nothing was recorded about how their service ended. Somewhere in the chain, blank was converted to text, and the text of a missing value happens to begin with the letter N, which is also a valid exit code. So 26,000 blanks silently became 26,000 confident answers. Nothing crashed. Every total still added up. The true count was about 645.

That is why the checks that run on every build are mostly of the form missing stays missing. A bug that turns absence into a plausible value cannot be caught by looking at the output, because the output looks better than the truth.

Status. Layout: done and verified (§A). Route defects: fixed, tested, replayed (§B). The German half of the gold re-runs on Heavyhorse6 with the fixes (sections 8–16, ≈ $0.5). Not fixed — proposals for the author (§C, §D). Done since (D-121): the readers' majority count (875 of 879 Czech counts right), D-58's independence test in section 14, and a sealed German Böhmen certification half of four pages (0165, 0189, 0356, 0441).

A. The code layout (D-119)

All code now lives under utils/; reference/ holds data only.

Where What
utils/lexikon_1910/ the register, all of it: the 14 library modules lexikon_1910*.py (from utils/parsing/) and the 31 scripts that drive them (from reference/places/)
utils/lexikon_1910/_archive/ 14 finished experiments and one-off measurements (bake-off, test arms, measure_*) and the failed glyph matcher lexikon_1910_symbols.py — kept runnable, not on the docs site
utils/reference_tools/<topic>/ the 26 scripts that build and check the curated tables in reference/<topic>/: occupations, units, education, parties, places (district curation, amateur-theatre pilot), background (reading-pass tools)
tests/_archive/ the tests of two archived scripts

Verified: 598 path references rewritten in 143 files, byte for byte (each file keeps its line endings; notebooks change only in their cell sources). 84 of 86 moved modules import as they actually run, each one's ROOT resolving to the repository (the other two are command-line tools that need arguments, and compile). Every Python file parses. Every test passes (343 before the route fixes, 359 with the 16 they add). The docs site builds (39.3 MB; it now documents the register's modules under a role of their own). Notebook 15 runs sections 1–7 with spending off. What still names an old path: cell outputs of archived notebooks (historical printouts) and two audit scripts under output/…/_archive/.

B. Route defects — found, fixed, measured

Defect Effect on the gold Fix
C1 E3 sometimes files a reader's claim on the neighbouring glyph; Opus then rightly names that glyph and the box takes its value — the claim disappears and its neighbour is counted twice 3 of the Czech 7 misses; 13 duplicated rows an answer that is the readers' majority reading at another mark of the same section and part rules on nothing
C2 a mark's part was set from any E3 row, including Gemini's unclaimed rows, which only guess a part both Czech false claims, 2 misses the part comes from a reader's claim; an unclaimed row only where no reader claimed the box
C3 a token answer ("is T printed here?") was put on every box holding T; "not printed" then wiped another token a misplaced box also held German, with perfect answers: 11 true claims lost, 10 marks wrongly open the answer is a ruling on the claim (part, T), applied over every mark of the part (claim_rulings)
— a mark Opus settled nothing on ("none of these", the outline on another mark) fell to the reader of record e.g. Gemini's [mountain-inn] against three [tourist-hut] the readers' value stands if they all agree; otherwise the mark is open (the author's queue, D-111)
I1 a call whose unit had no placed box was sent with no page image (the fallback searched the Czech gold folder only) German 0142-jicin-4-2: "No page image of the entry was provided" the run's own sections first; a call with no image is not sent
I1′ German bands were cut from boxes the gate had called untrusted (risk) token-only mode shows the whole section
I2 a claim flagged by the order rule alone was routed and never asked 5 of the Czech gold's 11 order-only flags asked as a token question
I3 answers are keyed by call and question number: after a rebuild, old answers would be read against new questions (avoided once by moving a folder by hand) answers of calls whose questions changed are moved aside and the calls asked anew
I4 German headings print court names short (Böhm. Aicha); and the Czech register prints Maršov where the backbone has Horní Maršov — one court split, another never paired (outside the German gold pages) abbreviations written out; the two editions paired by the backbone's court_id
I5 the final inventory had no printed count ("2 Sch") and one row per mark 12.6 % of gold claims print a count one row per claim, with count and count_source
— certification pages could reach the author through the validation sample or the review queue, with the route's outlines, before their blind transcription (none read yet) no validation item and no review item from a sealed page; on a full run, no validation item from the in-sample gold pages
— freeze() silently rewrote a run's sheet and manifest — today's gold-run versions were overwritten by a laptop rebuild while this audit ran provenance only (Dropbox keeps the 16:10 versions) once reads exist the sheet is kept; a changed manifest is archived, with package versions and host
— a batch-prepare cut short left a partial queue that was never completed (risk: crops read live at twice the price) queues are written as .part and named only when complete
— calls were spent on the worked-example page, scored nowhere 5 calls on the gold skipped on the Czech gold

Replayed on the gold run's own files (Opus answers as returned on 2026-10-23):

reader of record alone route as run route fixed
Czech gold, 881 claims F1 0.955 (31 false, 48 missed) 0.995 (2 false, 7 missed) 0.999 (0 false, 2 missed)
German gold, 343 claims, token questions answered by the gold (the ceiling) 0.941 0.899 (box questions) 0.975 (3 false, 14 missed)

Of the two Czech misses, one is open in the author's queue (5 open marks on the gold — ≈ 190 for the Czech volume) and one is a mark no reader read and no detector boxed. Printed counts are right on 866 of 879 true Czech claims. These gold figures are in-sample — the contrast sheet, the locator, DINOv3's bank and the look-alike list were built on the same pages — so they show that the route works, not its error rate; that comes only from the sealed pages (§D).

Measured and not adopted: sending DINOv3's unclaimed boxes (glyph-like boxes no reader claimed — D-95's omission flags) to Opus. On the Czech gold there are 52 (15 in the offices section); DINOv3 names a gold claim on 8 of them, and the route already holds all 8 from other marks. It would add ≈ 50 questions per 13 pages and recover nothing.

C. Not fixed — what the frozen output still lacks for place-level variables

The route settles one thing: which pictogram institution each part has, now with its count. Everything else the register prints is read and stays in the passes' side files:

Variable Read Settled into a final output Gold to score it
pictogram institutions (+ count) yes yes yes
institutions printed as abbreviations — district court, captaincy, tax office, notary, lawyers, deanery… (1,197 of Gemini's 1,213 decoded rows on the gold) yes no no
school classes and language, cooperative kinds and language (D-70) yes no yes (264 rows)
the parts list — names, houses, population, part type yes no (type marks settled, then dropped) yes (660 rows)
the table — area, houses, population by sex, confession, language yes, twice no — the two reads are never reconciled yes (403 rows)
postal district, station and walking hours, the legend yes no partly
Prague and Liberec (statutory towns: prose pages, D-82) no no no
a place key — register municipality → settlements_1910 / okres_id no (the old anchors cover 104 of 199 blocks, on OCR names) no no

Small additions, no new model calls (each ≈ ½–1½ days): settle the attributes by agreement across the four passes; settle the parts list (and keep the type marks the ledger already settles); reconcile the two table reads cell by cell with the register's own arithmetic (I1–I5, I8) as the tie-breaker; settle the abbreviation institutions the same way (they need a few gold pages coded by the author first); put the court's court_id and okres_id on every final file; read the Prague and Liberec prose pages (≈ 8 calls, < $0.10). Bigger: the apparatus↔table key (municipality number within the court), anchoring each municipality to settlements_1910 on the model-read names (scored on the gold pages), and a place table in data/final built only in 02.

Decided but not implemented (from the decisions audit): the D-58 independence test on the frozen outputs (the code exists in score_r5_german_notes); the one-shot certification scorer with page-clustered intervals and per-symbol rates (D-55, D-63); double-coding two pages (Stage 0); the order_restarts parsing queue (D-74); the one-request Gemini batch probe (D-117); prompt digests in the manifest (D-109); running heads on scans 0077 and 0091 (D-87).

D. Is the certification procedure enough?

Not yet — for the use it has to serve. It was designed to certify one number: how accurately the route reads symbols, on eight sealed Czech pages (a systematic sample with a random start, seed 1910, scans 46 · 106 · 166 · 226 · 286 · 346 · 406 · 466; ≈ 560–580 claims). For that headline it is sound: the pages are spread over the volume, they are sealed in code from every learning component, and the route will read them with nothing tuned on them. What the thesis needs is more than a headline: an error rate for each variable a regression will use, at the level the regression uses it, for every volume the data come from — and an estimator (DSL/PPI, D-55) that needs a probability sample of units coded by hand. Against that, five gaps:

  1. Rare institutions are not certifiable on eight pages. Common symbols (school, post, parish, fire brigade) occur dozens of times a page; castles, poorhouses, ruins, railway stops, pharmacies, savings banks and cooperatives occur a handful of times on eight pages — and they are exactly where the route errs (the look-alike pairs). Eight pages are also only eight clusters: with ≈ 70 claims a page and modest within-page correlation the effective sample is a few hundred claims, so even the headline carries ± 1–2 points.
  2. Omissions are invisible to the validation sample. It draws ledger marks; a symbol no reader read and no detector boxed has no mark (the one Czech gold miss left is exactly that). Recall needs whole entries transcribed blind.
  3. The unit is wrong for the estimator. DSL/PPI correct a regression for prediction error using a random sample of the regression's own units — municipalities or settlements — coded by hand with known inclusion probabilities, across the whole volume. The sealed pages are eight pages, and the validation sample is marks.
  4. The German volumes have no certification at all. Böhmen has no sealed half, and its gold is no longer a clean test (the D-118/D-120 fixes were chosen after reading it). Mähren and Schlesien are planned to get one or two spot-check pages each — but for them the German volume is the only source (the Czech edition covers Bohemia alone), so their variables would rest on no measured error rate.
  5. Only the symbols are covered. The table (population, houses, area), the attributes (school language), the abbreviation institutions and the place linkage — all analysis variables — have no certification design.

An affordable design (the author's time is the cost; API cost is negligible):

  • Pre-register before anything is opened: write the one-shot scorer and date the metrics in a decision — claim-level P/R/F1 and per-symbol sensitivity/specificity with page-clustered intervals, count accuracy, table cell accuracy, the acceptance bars, and the sample design below.
  • Keep the eight sealed pages as the headline, read once by the frozen run, scored once.
  • Add an entry-level probability sample across each volume — the DSL/PPI sample. Draw municipalities (not pages), stratified by what the route did (agreed / Opus-settled / author) and by whether the route predicts a rare symbol there, with known probabilities; the author transcribes each whole entry blind (no outlines, no readings shown). It yields recall, per-symbol rates for the rare symbols, the DSL weights, and — once the place key exists — the linkage error on the same entries. Czech: start with ≈ 150 municipalities (≈ 3 minutes each, ≈ 8 hours), extend if the intervals are too wide.
  • German: draw a sealed Böhmen certification set of 3–4 pages now, before anything else is tuned on German pages; for Mähren and Schlesien, an entry-level sample of ≈ 60–80 municipalities each instead of 1–2 pages.
  • Table: certify on the sealed pages' table rows plus the register's own arithmetic on every page — once the two reads are reconciled (§C).
  • Double-code two pages (one calibration, one fresh) to bound the transcriber's own error.
  • Back-check the high-value symbols against sources outside the register (D-104): railway stations, post offices and parishes on a sample, against a railway gazetteer, the postal directory and the 1910 diocesan schematism — the one check that can see an error the print and its transcriber share.

In total ≈ 25–35 hours of the author's time across the four volumes; it turns every place variable into one with a measured error rate and makes the regressions on them correctable by design.

Second audit — 2026-09-22

The second full pass over the pipeline, two months after the first. Its scope is data manipulation and cleaning: every module notebook 02 calls (utils/parsing/*), utils/linkage.py, the QA gate and the validation workbook (utils/validation/*), utils/tables, notebooks 02 and 03, and the curated inputs they read (district and Slovak curation, the Prague-suburb table, the occupation curation and HISCO authority, the party register). Out of scope by instruction: retrieval — the csol.cz and VÚA scrapers, the lexicon and Kramerius downloads, the Wikidata fetch — and the 1910-register OCR sub-project (lexikon_1910_*, notebooks 05/06), which does not feed data/final.

Every module was read line by line and every finding below was measured on the data on disk (the 2026-09-03 build), not inferred from the code. Severity: [A] changes an analysis variable · [B] logic or robustness, narrower impact · [C] documentation drift.

✅ Fix status: all A, B and C items fixed in code, notebooks 02 and 03 re-run

The fixes are in the modules listed against each finding; the QA gate (utils/validation/data_checks.py 1.12.0) gained a hard invariant for every defect class, and on the old build those invariants fail exactly where this document says they should (81/97 passing, all 16 failures below). Measured before/after numbers are in §E. Decisions taken on the way are D-59 to D-62.

The headline. The gate passed 72/72 on the old build, and the old build was wrong in the place the thesis leans on hardest: the cross-dataset linkage. Its name test let a shared given name carry two different surnames, and its second criterion rested on a note field that mostly records which interwar regiment keeps the death register, not Legion service. Of 2,239 "confirmed" overlaps, a large share were coincidences of name, year and district. The rest of the findings are smaller and each is one of the bug shapes this repository already knows; two are new (§F).


A. Defects that change an analysis variable

A1. The linkage name test was carried by the given name — linkage.find_overlap

One token_sort_ratio over the concatenated "surname given" string had to reach 80. A long common given name dominates that ratio: STANĚK František ↔ Soja František scored exactly 80, as did ŠVEC František ↔ Sládek František. Measured on overlap_matches.csv:

DOB-confirmed (high) note-confirmed (medium)
pairs 1,776 463
identical surnames 1,121 218
surname similarity < 60 after Czech/German spelling folds 263 126

(BERAN ↔ Bureš, ČERNÝ ↔ Cvrk, MORAVEC ↔ Málek.) And the matcher was not one-to-one: 111 VÚA records were claimed by two to four legionnaires (231 rows, so at least 120 wrong by construction) — one Zdice-born František by Šůra, Škvára, Schoř and Šinágl — while the VÚA-side flag kept only one, so the two files disagreed on 120 links.

Why it matters: an exact-DOB collision is rare for one person, not across 186k × 133k records. Over every exact-DOB, same-given-name pair in the two files, pairs whose surnames start with different letters — chance by construction — still reach the 80–90 similarity band.

Fix (linkage 1.3.0, D-59). Surname and given name scored separately on a phonetic key that folds the orthography the archives mix (sch→s, tsch/cz/tz/z→c, w→v, y/ie→i, umlaut digraphs, doubled letters); surname ≥ 90, or ≥ 85 when the birth okres agrees too; given name ≥ 80. Blocking on the phonetic initial (so Wesely meets Veselý). One-to-one on both sides, with an exact tie refused, never broken by order. A pair whose full dates of birth both exist and disagree is refused.

A2. note_legion read archival provenance as Legion service — habsburg._LEGION_RE

Any "čs. … pluk/prapor N" in the VÚA note counted as a Legion cross-link. That phrase is overwhelmingly provenance: the interwar Czechoslovak army's regiments kept the death registers of their recruiting district, so a note reads "náhradní prapor čs. pěšího pluku 33" or "Opsáno na podkladě: úmrtní knihy, … čs. pěšího pluku 33" for a man who fell in Serbia in September 1914.

note_legion (old) records
"čs. … pluk/prapor" register phrases 10,509
"Seznam vozatajského pluku 8" (a k.u.k. train-regiment list) 145
"Sibiř" (mostly POWs) 4
explicit Legion vocabulary (legionář, legie, Kancelář legií) ≈384
total 11,042

The flagged men died at a median of 1915; 1,246 died in Serbia in 1914, before any Czechoslovak unit existed. A placebo settles it: VÚA records with a provenance note match a legionnaire on name + birth year + okres at 2.9 %, below the 4.8 % of records with no note at all; genuine Legion notes match at 59.7 %. Meanwhile 372 KLEG notes (the abbreviation of Kancelář legií) were missed.

Fix (habsburg 1.2.0). note_legion = explicit Legion vocabulary only (legionář, legie, KLEG, Kancelář legií, čs. dobrovolník, rota Nazdar, Česká družina, the twelve Russian rifle regiments "čs. střelecký pluk 1–12", never with a register page reference). The register unit moves to a new provenance column, note_register_ref, so nothing is lost.

A3. Criterion B ("note-confirmed") was mostly name + year + okres

Of the 463 note-confirmed pairs: 195 rested on a provenance note, 182 on another "čs. pluk … str." register note and 86 on genuine Legion vocabulary. The provenance group looks nothing like a real overlap — 5.6 % died within a month of the legionnaire's capture, 6.2 % after his enrolment, 107 are killed-in-action (65 of them before the legionnaire was even captured) — and 31 pairs carried two full dates of birth that disagree. Criterion B therefore accepted, for 9.5k noted records, the very evidence D-01 refuses as too weak for the other 126k (6,057 such coincidences dropped). Fixed by A1 + A2.

A4. 214 Galicia-born legionnaires in the Slovak járás Halič — records_geo_hu.add_jaras_id

"Halič" is Galicia in Czech and a small Novohrad town (Gács) in Slovak. Resolved by name, 214 legionnaires born in Lvov, Přemyšl, Kraków, Kolomyja, Tarnopol… were placed in járás Halič (131 by the exact string, 83 by fuzzy strings like "Brody - Halič") and carried its census covariates in legionnaires_census_hu_1910 — although their own birth_land already said Galicia. 110 more on the home role. Of 218 records in the unit, 4 were Slovak.

Fix (records_geo_hu 1.4.0). _AMBIGUOUS_SEATS = {"halic"} is never resolved from the district string; genuine Slovak Halič births still resolve through the corroborated settlement pass.

A5. Theatre imputation sent US applicants to Britain and Poles to the Balkans — theater

Pass 3 mapped US application cities to Britain/Dominions; in the data, US applicants whose own unit names a theatre served in France 1,659 times and in a British or Dominion unit 26 times (98.4 %). 248 unit-less US applicants were imputed against that. And "Polská" sent all 41 Polish-Legion records to Serbia/Balkans, though their units are Haller's Armée polonaise (France), Dutova armáda and Krymský pluk (Russia). Fidler's check (2026-07-20) covered CS-Legion records only, so neither was ever tested.

Fix (theater 1.1.0, D-60). US cities → France; the Polish rule removed; Armée polonaise → France; a self-test.

A6. data/final predated the code, and the gate could not see it

Education 2.1.0 and affiliations 2.1.0 (D-51, D-52) landed on 2026-09-07; data/final was written on 2026-09-03. pol_recorded, education_semesters, education_top_imputed did not exist, the pol_* dummies were still False on 93k blank cells, and the eight checks written for those releases were guarded on the missing columns and silently skipped. CLAUDE.md said the build was current.

Fix (data_checks 1.12.0). A hard check that data/final carries every column the current schema emits; it fails on any stale build. Notebooks re-run.

A7. birth/home_cisleithanian contradicted the corrected land — locations, records_geo

The flag came from locations.py's text-only land and was never re-derived after the land was corrected: 2,270 Czech-lands VÚA births were flagged not Cisleithanian — 1,747 of them in okres Uherské Hradiště or Uherský Brod, because the bare substring uhersk meant Hungary — and 8,997 Slovak births were flagged Cisleithanian.

Fix. records_geo.sync_cisleithanian derives the flag from the corrected land after every land update; locations 1.1.0 matches country nouns as words.

A8. exit_date was never range-checked — exit_codes.add_exit_columns

Every other date is clipped; the exit date — the hazard model's event time — ran from 1818 to 2192: 66 before 1914 (1818–1825 century slips), 149 after 1923 (28.6.19520 read as 1952), 308 exits before enrolment (34 deserters). And 3,930 exit strings that state a year (1920 ( Vrátil se do vlasti )) got no date at all (69 deserters).

Fix (exit_codes 1.2.0 + dates 1.2.0, D-61). The exit date is parsed from the text before the parenthesis (so the "1918" in Sloužil v čs. armádě po r. 1918 can never be read) through the shared date machinery: range 1914–1939, the century-slip fix both ways, month-year and year fallbacks, and a new exit_date_precision.


B. Logic and robustness

# Where Defect (measured) Fix
B1 units.extract_army Army read from the first parenthetical: 14 "Royal Fusiliers (R.F.)" → French Army (r.f.), ~150 British records got a regimental nickname as their army last parenthetical, trailing ? stripped (units 1.3.0)
B2 affiliations Bare socialista coded soc_dem while bare demokrat is refused — though Č24 Česká strana socialistická is the national socialists' own name; národní socialista became a social democrat; tokens unanchored (karpatorus → Russian) socialista refused; national-socialist forms → nar_soc; word-start anchoring (affiliations 2.2.0); 18 records change
B3 occupations ABBREV["zem"] zem.dělník (18) expanded to zemský → 99900 / HISCLASS 13 instead of zemědělský 62105 / 12; "rolník, zem. dělník" lost the farmer context expansion before dělník
B4 occupations compound rules 454 comma lists (464 records) coded on the last segment's head noun, the first occupation filed as a "qualifier" ("rolník, tovární dělník" → 99900, qualifier rolník tovární) lists coded segment-wise as dual, first occupation primary (occupations 1.4.0)
B5 habsburg.RANK_TERMS Unanchored: 428 notes with dělostřelec returned střelec, 5 generálmajor returned major word-start anchored, generálmajor listed
B6 records_geo, records_geo_hu Land never refreshed after the Slovak passes (1,041 legion / 2,707 VÚA járás-placed births with land NA); substring markers mislabelled Polská Ostrava → Poland, Nemecká Ľupča → Germany, Kapušany → Overseas ("usa"), Ruskov, Prusko → Russia set_land_from_jaras; whole-word country nouns
B7 records_geo.resolve_prague_suburbs 1,537 births (1,468 csol + 69 VÚA; 1,682 more on the home role) moved to their 1910 suburb okres lost their standardised municipality (attributes keyed on the pre-move okres) attributes re-keyed after the move
B8 wikidata.add_settlement_coords 95 settlements in different 1910 okresy shared 46 Wikidata points (BĚLÁ Semily / Turnov) — one of the two places got the other's coordinates a point claimed from > 1 1910 okres is refused
B9 QA gate Coordinate-accuracy check counted rows the D-35 repair snapped into agreement (95.9 %) snapped rows counted as the disagreements they were (90.0 %)
B10 notebook 03 "Bolshevik Coup" drawn at 1917-09-07 on three figures 1917-11-07 (25 Oct Julian)
B11 notebook 03 The occupation-qualifier table's "(bare)" rows were 11–91 % dual records (zřízenec 111/122) families by whole-token head, duals excluded
B12 notebook 03 Rank-reset histogram and table on the full column, not the D-04 combat-rank headline headline on legion_noncombat == False, full column as robustness row
B13 — Probable duplicate persons inside each file — same surname, given name, full DOB and birth municipality (e.g. ADÁMEK Karel 11917 / 130959): 625 csol and 281 VÚA rows flagged in new dup_group; nothing dropped
B14 dates Legion death_date clipped to 1914–1923 drops 8,034 of 12,480 recorded deaths (veterans); the docstring said "a few"; raw string not kept docstring corrected; death_date_raw kept
B15 dates 2,440 capture and 2,463 enrolment strings with only a month or a year (4.1918, 1918, impossible 29.2.1918) dropped without a flag month/year fallbacks + *_date_precision
B16 build_validation_workbook Re-ran the Slovak passes without resolve_jaras_twins (D-19) twin resolution + land refresh added
B17 several Missing coded as False (the D-52 shape): cause_* on 8,297 VÚA records with a blank cause, in_captivity, *_rank_jd on 34,337 blank rank cells, legion_noncombat on 19,270 <NA> on blank source
B18 ranks.RANK_COMMON podkapitán had an ordinal but no common grade (4 records, 1 reset lost) common grade 12
B19 data dictionary 9 columns per file undocumented (linkage flags, *_court_snapped, *_obec_sk, *_okres_prague); exit_* had no descriptions documented; hard check
B20 census codebook 12 justice/personnel columns renamed after the codebook was built were missing; three pairs (deposits vs withdrawals ×2, passbooks at start vs end of year) differed only by _2; lit_illiterate described as "read write" curated names; codebook rows added; hard check that every panel column is documented
B21 units regiment_plot always "Other (Unknown)" (theatre added later); unit_report and the nb03 size scatter on regiment, not unit_key reports and scatter on unit_key
B22 ranks._resolve Tied top Legion ordinal (praporčík / kadet / důstojnický zástupce at 7) resolved by word order into different common grades — latent, 0 cases ties broken by the higher common grade
B23 lexikon (D1 residual) 116 register entries whose headword carries a mixed-case parenthetical, a t. alternate or a spaced separator never started an entry; they vanished and their text polluted the previous settlement's okres timeline headword pattern widened; alternates kept as aliases
B24 records_geo okres lookup PDF-broken spaces (Prachat ice): 81 settlements unmapped, 169 more only via fuzzy space-free exact lookup, collisions refused
B25 districts Six MPIDR German names mangled (Sziszkow = Žižkow, Rosznau, Kneszna…) in delivered okres_de/court_de GERMAN_NAME_FIX

C. Documentation drift (fixed in the same pass)

CLAUDE.md called data/final current and quoted a 66/66 gate; D-01, PIPELINE, VARIABLES and the linkage docstring called the linkage "near-certain"; notebook 02 cited D-24 for the HISCAM drop (it is D-30); DECISIONS listed D-49 after D-50; spatial.py still quoted "7,248 … 68 % … structural" (the 68 % was a coordinate bug, D2 of the first audit); districts.py described a gazetteer tier retired in July; schema.py named notebooks that no longer exist, offered a low linkage tier never emitted, omitted the continuation track and described a education_complete branch removed in 2.1.0; the education TRACKS comment contradicted 2.1.0; occupations quoted 3.1 / 4.8 % against 3.38 / 6.06 % elsewhere; affiliations said 93,319 blank cells, not 93,377; districts_hu said 184 units, not 192; and the two datasets' theater columns share a name but not a label set (only Russia, Italy and France compare) — now said where the column is described.


D. Checked and found sound

clean; the exit-code token parser after the July fix; provenance; religion; census pooling (the justice columns are three-year means, so summing is valid); census_hu; districts_hu; lexikon_sk (no seat-key collisions, every row reconciles); spatial projection, parity and snap logic; frame exclusivity; the twin and Prague label guards; rank self-resolution; theatre pass 2 (175 rows, no ties); occupation fold collisions (8, all the same code); the curated inputs (district and Slovak curation, Prague suburbs, occupation curation and HISCO authority, party register 12/12); latex_tables.


E. Post-fix verification — measured on the re-run data/final

Notebook 02 re-executed top to bottom (nbconvert, exit 0) and then notebook 03 (exit 0; 28 figures and the LaTeX tables regenerated). The QA gate passes 107 / 107 on the new build; on the old build the same gate passes 81 / 97, every failure one of the findings above. Every figure below is read off the rebuilt files and compared with a copy of the 2026-09-03 build.

check 2026-09-03 build re-run 2026-09-23
rows legion / habsburg (no join blow-up) 132,993 / 186,192 132,993 / 186,192
confirmed overlaps (DOB + note) 2,239 = 1,776 + 463 1,340 = 1,201 + 139
VÚA ids linked more than once 120 0
linked pairs with disagreeing full DOBs 31 0
old pairs kept — 1,176 of 2,239; 164 new — 86 note-confirmed (74 on a KLEG note the old pattern missed) and 78 DOB-confirmed, mostly men whose csol name carries a second spelling or a Russian baptismal name (DRAXL DRAKSL, Josef Sergej) that the old whole-string score penalised
note_legion (VÚA) 11,042 966
note_register_ref (provenance, new) — 10,548
legion births in járás Halič (of which Galicia-born) 218 (214) 7 (0)
járás-placed births whose land is not Slovakia (legion / VÚA) 1,278 / 2,724 0 / 0
VÚA Czech-lands births flagged not Cisleithanian 2,270 0
VÚA Slovak births flagged Cisleithanian 8,997 0
US applicants in France / Britain-Dominions 1,659 / 274 1,911 / 26
Polish-Legion records in Serbia/Balkans 41 of 41 1 (France 21, Russia 12, Unknown 6, Italy 1)
legion_army British / French Army 162 / 175 306 / 161
deserted 3,717 3,717 (unchanged)
exit_date non-null (range) 101,700 (1818 – 2192) 105,527 (1914 – 1939): 101,662 day, 461 month, 3,404 year
deserters with an exit date 3,631 3,697
capture_date / enroll_date 98,995 / 118,842 100,436 / 120,868 (1,415 / 1,999 month- or year-precise)
pol_recorded (D-52, now in the data) absent 39,616
pol_soc_dem / pol_nar_soc 17,334 / 6,536 17,317 / 6,541
education_semesters / education_top_imputed (D-51, now in the data) absent 722 / 1,112
records whose HISCLASS changed (B3, B4) — 403
occupation_match dual / head / prefix 6,518 / 4,252 / 2,091 7,039 / 3,790 / 2,018
rank_reset, combat ranks (the D-04 headline) — N 85,698 · demoted 17.9 % · unchanged 78.0 % · promoted 4.2 % · mean −0.36
legion_noncombat / habsburg_rank_jd / VÚA cause_KIA → <NA> on blank source 0 / 0 / 0 19,270 / 34,337 / 8,297
Prague-suburb birth moves with a standardised municipality (legion / VÚA) 0 of 1,468 / 0 of 69 1,364 of 1,468 / 69 of 69
probable duplicate-person rows (dup_group, legion / VÚA) — 625 / 281
settlements / with 1910 okres / geocoded 18,666 / 16,538 / 5,912 18,916 / 16,755 / 5,841
settlement timelines carrying another entry's text 116 4
Wikidata points shared by settlements in different 1910 okresy 46 (95 settlements) 0 (5 points remain shared inside one okres)
birth coordinates (legion / VÚA) 58,314 / 68,616 58,074 / 68,450 (legion: 516 lost, 386 of them on a refused shared point; 276 gained from recovered settlements)
settlement coordinates in their own okres, before snapping (gate reported 95.9 % incl. snapped rows) 90.0 %
data-dictionary rows 314 341 (every column documented)

What moved in the results, and what did not. Desertion itself did not move (3,717). The theatre shares barely did (France 7.8 → 8.0 %, Britain/Dominions 0.5 → 0.3 %). The linkage lost 40 % of its pairs, and that is the change a reader of the comparison design must know about: the overlap used as a control is 1,340 men, not 2,239, and the pairs that left were disproportionately the note-confirmed ones (463 → 139). The combat-rank reset reads 17.9 % demoted against 18.9 % on the inclusive column.

Re-run protocol

notebooks/02_data_cleaning.ipynb → notebooks/03_visualisation.ipynb, then DOCS_NOINDEX=1 python utils/docs/build_docs.py. The gate's first check now fails on any data/final older than the code, so a stale build can no longer pass silently.


F. Two new bug shapes

The six shapes in the first audit's lessons all recur here — an unguarded name match (A4), missing values coded as content (B17), unanchored substrings (A7, B2, B5, B6), rules narrower than their source (B23, B1), order deciding a tie (A1, B22), a permissive rule winning the race (B4). Two are new:

  1. A similarity score dominated by its uninformative part. Concatenating surname and given name let the part everyone shares (František) carry the part that identifies (the surname). Rule: score each field on its own and require each to pass; check the threshold against pairs that are wrong by construction (here: different initials).
  2. A flag read off a field that records something else. The VÚA note mostly records where the register is kept; reading it as what the man did produced 10k false flags that then fed a "gold standard". Rule: before a free-text field becomes evidence, tabulate the flag against a variable that cannot be true for it (death in 1914 vs. a unit founded in 1917).

First audit — 2026-07-16

Full review of the pipeline (all utils/ modules, the three stage notebooks, the reference docs) for code errors, logic/theory errors, and doc–code drift. Every finding below was verified against the actual code and, where it matters, against the raw/final data on disk. Companion docs written in the same pass: PIPELINE.md (what each script does) and VARIABLES.md (every constructed variable, with build formulas).

Severity: [A] fix before analysis · [B] fix when touching the module · [C] cosmetic / documentation.

Sequel (2026-07-20). This audit was internal — it read the code against itself and against the data. An external pass followed once Fidler's (2025) encyclopedia became available: VALIDATION_FIDLER.md. It confirmed the repairs below (notably that the exit_N fix leaves the Russia exit codes reconciling to within 5 % of the encyclopedia's repatriation totals) and found two defects this pass had missed — Legion generals scored as field officers, and regiment conflating armies. Both are now fixed and guarded. The lesson is the obvious one: checking a source against itself cannot find a gap that is absent from it, such as a rank the ladder never contained.

✅ Fix status: APPLIED and notebook 02 re-run (2026-07-16) — data/final is current

All A, B and C items are fixed in source except the two the author deferred: B2 (nadlékař stays deliberately unscored — documented in rank_ladders.md §A1b instead) and B3 (education classification to be revisited later — code untouched). B3 was closed on 2026-09-02, once two sources arrived that could settle it: education.py 2.0.0 rebuilds the variable on the 1869 ladder, with pok. off the ordinal entirely (8,117 records leave the Gymnasium bucket) and the dot-less tokens this section listed now parsing — coverage 96.7 % → 99.1 %. The section's own suggestion, a Vocational continuation level, was not taken: a continuation school was compulsory schooling and selected on nothing, so it is a flag, not a rung. See Education ladder and D-08, which stays open on the Hungarian frame alone. The verification pass surfaced one major upgrade to A3: the old parser fabricated exit code N on all 26,386 empty cells (pd.NA → the string "<NA>" → letter N) — the final data's 27,034 "Missing" are ~97% fabricated, true count 644 (details + full delta table in A3). It also corrected a stale doc number: spatial-court vs name-okres agreement is 68%, not 91% (§E). Beyond the items below, the fix round also added, prompted by the author's "Praha in Czechia vs. Slovakia" concern:

  • Praha added to the Czech settlement crosswalk (curated row in lexikon.py — the register has no PRAHA headword, leaving ~1.8k bare-"Praha" VÚA births with no Czech candidate at all);
  • cross-frame homonym guard on BOTH bare-name tiers (a municipality name present in both countries' settlement frames is refused without a positive signal — records_geo.settlement_name_set + exclude_names, wired in notebook 02 §4);
  • corroborate-or-refuse in the SK settlement pass (a district string that contradicts every candidate obec now refuses the match instead of falling back — kills Volhynian Dubno → SK) + a positively-foreign land veto;
  • a frame-exclusivity assertion in notebook 02 §4 (okres_id XOR jaras_id);
  • _norm now expands interior "n." → "nad" (recovers Ústí n. Labem, 50 rec.);
  • curation fix: Neubenatek cs = Nové Benátky (1910), cs_modern = Benátky nad Jizerou (46 rec.); alias Česká Třebová → okres Lanškroun (21 rec.);
  • Habsburg podpraporčík (n≈9) dropped like podporučík (B1 resolved as drop);
  • affiliations: rusín (n=9) now its own pol_rusyn (was mislabelled russian).

Notebook 02 was re-executed top-to-bottom on 2026-07-16 (all 21 code cells, 174 s, no errors; the notebook's stored outputs are from that run). data/final now reflects the fixed code — measured results in §E. A copy of the pre-fix data/final was kept for the comparison in §E.

Still to do: re-run 03_visualisation — its exit-code figure/table change materially (see §E), and any LaTeX table quoting exit_N is currently wrong.


D. Second-pass findings (found while acting on the author's questions; all fixed + re-run)

The author's "could we infer Praha from names?" question sent me back into the geography, and the trail from the 68 % spatial-court agreement — which §E originally, and wrongly, dismissed as "structural" — uncovered the largest geography bugs in the build.

D1. The lexikon parser silently dropped 982 register entries — lexikon.py

_UP, the ALL-CAPS headword alphabet, was "A-ZÁČĎÉĚÍŇÓŔŘŠŤÚŮÝŽ" — no German umlauts. Every Sudeten-exonym headword (ADLERHÜTTE, ADOLFSGRÜN, ALTBÜRGERSDORF — 531 of them) therefore failed to start an entry, and _entries() appended its text to the previous settlement, corrupting that settlement's okres_all timeline. A further 451 entries use the separator od r. ("from year X onwards") which _HEAD did not know. Symptom that exposed it: ARCHLEBOV's timeline read "hodonin arletzgrun viz arnoldov (karlovy vary)".

Fixed (_UP += ÄÖÜ; _HEAD accepts v r.|od r.|viz; open-ended od r. Y → y1=9999). Measured: settlements 18,151 → 18,664 (+513), aliases 9,559 → 10,028, settlements with a resolved 1910 okres 15,607 → 16,364 (+757), corrupted timelines 390 → 1. Note the corrupted timelines also made the settlement tier's temporal gate over-permissive.

D2. A third of settlement coordinates pointed at the wrong village — wikidata.py

add_settlement_coords carried the same unguarded unique-name fallback as A1: "only one Wikidata point has this name → take it", with no okres check. Wikidata lists ~6.2k present-day municipalities; the lexikon holds ~18k historical settlements (incl. osady and absorbed hamlets), and 2,250 lexikon names are shared by several settlements. So wherever five like-named settlements had one surviving modern obec, all five inherited its coordinates — a median 75 km (p90 223 km, max 387 km) from their own okres.

Decisive measurement: coordinates that pass okres corroboration land in their own name-based okres 87.4 % of the time; those taken by the fallback, 30.8 %. Fixed by requiring okres corroboration, keeping a fallback only for names unique on both sides. Measured: geocoded settlements 7,248 → 5,907; coordinate accuracy 68.3 % → 88.1 % (and 88.1 % is a floor — a settlement whose okres changed since 1910 counts as a "miss"). Wrong coordinates would have silently corrupted the planned Kelly-2019 spatial SEs; missing ones merely reduce n, hence correctness over coverage.

Vindicated in passing: spatial.py's hand-rolled LAEA is correct — .prj confirms EPSG:3035 with the exact parameters used, forward(10,52) hits the false origin exactly, and a ±3 km offset grid peaks at (0,0). The 68 % was never a projection or polygon problem.

D3. Bare-"Praha" → okres Praha (author's decision, evidence-backed)

Implemented as records_geo.CROSS_FRAME_WHITELIST — a curated, documented exception to the cross-frame guard, assigned at the weakest muni tier so it stays filterable. All 1,915 VÚA Praha-born now resolve: 1,909 → okres Praha (301072), 6 genuinely Slovak (okres "Lučenec") → járás, 0 unresolved.

Tested first, since names were the obvious alternative: a char-n-gram classifier separates capital-born from Lučenec-born at AUC 0.955 — but against ~1:2,250 prior odds it flips 0 of 1,780 records (max posterior 0.22), while 4.9 % of confirmed Prague-born score LR>10, i.e. name-based assignment would systematically expel the capital's Slovak/Magyar-named migrants — differential misclassification correlated with ethnicity, which is both a covariate and plausibly correlated with the outcome. Base rates, not names. Expected cost ≈3/1,780.

D4. Cross-frame attribute leak — records_geo.add_settlement_attrs

Fifth instance of the same family. Its bare-name fallback required lexikon-uniqueness (so it was milder than D2) but had no cross-frame guard and ran before the Slovak pass — so a Slovak-born record with an unresolved land and a lexikon-unique village name silently took Czech municipality_cs / court_id / lat / lon / qid, then received a járás afterwards. The §4 frame-exclusivity assertion did not catch it (it only checks okres_id vs jaras_id, not attributes). Measured: 124 records. Adding exclude_names cut it to 21; the residue were records whose Slovak district string simply isn't in the Majo crosswalk, so the name guard is blind to them. Closed by making the fallback consistent with the muni tier it mirrors — an unresolved raw okres string is positive evidence the place is not Czech, so the bare-name path now requires no okres string at all. Result: 124 → 21 → 0.


A. Bugs that affect the data

A1. Slovak settlement pass double-places Czech-born records — records_geo_hu.add_settlement_jaras ⚠ largest finding

What: the village pass gates only on {prefix}_jaras_id being empty and the municipality being present (records_geo_hu.py:190):

gate = out[jid_col].isna() & out[muni_col].notna()

It never checks {prefix}_okres_id. Since add_jaras_id (correctly) skips Czech-placed rows, those rows reach the village pass with an empty jaras_id — and any record whose municipality name uniquely matches a Slovak obec gets a Slovak járás on top of its correct Czech okres.

Verified impact (final data on disk):

frame role rows with okres_id and jaras_id (all tier settlement)
legionnaires birth 6,320
legionnaires home 5,576
habsburg birth 4,287
habsburg home 2,290

Examples: Praha (okres Praha) → járás Halič (there is a Slovak village Praha in okres Lučenec); Poruba (Bílovec) → Prievidza; Březolupy (Uherské Hradiště) → Bánovce nad Bebravou.

Downstream contamination: data/final/analysis/*_census_hu_1910.parquet joins on birth_jaras_id, so ~6.3k Czech-born legionnaires currently carry Upper-Hungary census covariates; any rule of the form "Slovak-born ⇔ birth_jaras_id notna" is wrong for these rows. (birth_land is unaffected — it says "Czech lands", which is how the contradiction shows.)

Fix (one line + re-run notebook 02 §4/§4b):

gate = out[jid_col].isna() & out[muni_col].notna()
if f"{prefix}_okres_id" in out.columns:
    gate &= out[f"{prefix}_okres_id"].isna()          # only rows the Czech frame left unplaced

Optionally also veto positively-foreign lands (Austria/Galicia/Russia…), mirroring records_geo.add_birth_settlement_okres's muni-tier veto — the pass currently has no land gate at all, so e.g. a Galician village uniquely matching a Slovak obec name would still be placed in Slovakia.

Note: PLACE_MATCHING.md correctly states the intent ("runs only on records the Czech frame left unplaced") — the code violates the doc. utils/validation/ build_validation_workbook.py re-runs the same passes in-process and inherits the bug.

A2. illegitimacy_rate is silently never created — census_labels.add_shares_cis

The gate checks for births_livebirths_illegitimate_total and births_livebirths_total_total (census_labels.py:395), but the rename actually produces births_livebirths_illegitimate and births_livebirths_total — the trailing _z (Zusammen) token is dropped by _compose's single-letter filter ([a-z] matches z, and z is not in the ("a","b","c","d","m","w") keep-tuple). Verified: the final census_okres_1910 panel has literacy_rate and pop_total but no illegitimacy_rate, while census_codebook.{csv,md} claims it exists.

Fix: use the real names in add_shares_cis:

if {"births_livebirths_illegitimate", "births_livebirths_total"} <= set(out.columns):
    out["illegitimacy_rate"] = _safe_div(out["births_livebirths_illegitimate"],
                                         out["births_livebirths_total"])

(Do not instead add "z" to the keep-tuple — that would rename ~100 existing columns and break the codebook crosswalk.) Re-run notebook 02 §3b.

A3. Exit-code tokenizer fabricates codes — exit_codes.parse_exit_codes ⚠ upgraded

Three related defects (the third — by far the largest — surfaced only during the fix-verification pass, because it needs the notebook's normalize_missing step to trigger):

  1. _DROP_TOKENS is a no-op: found -= _DROP_TOKENS runs after tokenisation, but the tokenizer only ever adds canonical codes, and no drop token is canonical, so nothing is ever removed.
  2. The greedy letter-scan walks through unknown tokens, so an unrecognised token containing a code letter emits that code: "MD" → D, "DMD" → DM + D, "Prop." → P (59 rows!), "Super" → S (16 rows).
  3. The NaN guard only caught float NaN — but clean.normalize_missing turns missing/? cells into pd.NA, which sailed through as the string "<NA>", whose letter N was scanned into a fabricated "Missing" exit code on every empty cell: 26,386 rows (26,336 missing + 50 ?). The final data's exit_N = 27,034 is ~97% fabricated; the true count is 644.

Fixed (v1.1.0), verified against the old build — every delta named:

code old build post-fix Δ cause
N 27,034 644 −26,389 "<NA>"/"?" fabrication (item 3) + one N? recovered
P 152 93 −59 "Prop." fabrication
S 2,096 2,080 −16 "Super" (+ DMCSA) fabrication
D 3,719 3,717 −2 MD/DMD refused by decision (legion_id 113629, 18666 — plausibly DM, not certain)
DM 61,506 61,505 −1 DMD refused
X 17,531 17,530 −1 "Xne" refused (would also have fabricated N)
ČSA 2,986 2,987 +1 glued DMCSA now correctly parses as DM + ČSA

Mechanics of the fix: NA guard covers every missing scalar (not isinstance(raw, str) and pd.isna(raw)); extraction is token-wise — drop-list checked first, ? uncertainty marks stripped (( D? ) keeps its D), glued variants substituted (DMM→DM, DMCSA→DM+ČSA), and a token must parse completely as codes or is refused whole. Downstream: any prior output using exit_N (the 20% "Missing" share!) is wrong — treat "no exit code" (now 30,782 rows) as censoring, not as a Missing outcome. deserted was effectively unaffected (−2).

A4. Slovak county-form lookup misses ~6 adjectival forms — records_geo_hu._county_key

_STOLICA stripping only removes literal suffixes ("ska", "stol." …), so adjectival county names whose noun does not end at the ska boundary never reduce to the curated megye_sk noun:

record form (count in unplaced legion births) after _county_key curated noun hit?
Nitranská stol. (20) nitran Nitra ✗
Hontianská stol. (22) hontian Hont ✗
Trenčianska / Turčianska / Oravská / Bratislavská (tail) trencian / turcian / orav / bratislav Trenčín / Turiec / Orava / Bratislava ✗
Zvolenská, Liptovská, Spišská, Šarišská, Zemplínska, Gemerská, Abovská, Novohradská, Tekovská (noun) — ✓

Also: "Bereg" (20 records) misses because the curation stores sk = "Berehovo" (the seat town), and the megye lookup has no Magyar-name or alt-name keys. Curation rows Ung → "Už" and Ugocsa → "Uhoča" look suspect (expected Užská/Už region form and Ugoča) — verify against the record strings actually observed.

Fix: add a small explicit adjectival→noun map for the ~16 counties (or sk_alt megye rows in district_sk_curation.csv, and key megye_lut on Magyar + alt names too). Total recovery ≈ 100+ records into the megye tier.

A5. Point-in-polygon ignores holes — spatial.CourtLocator.locate

if any(_point_in_ring(px, py, r) for r in rings):   # WRONG for polygons with holes

Even-odd containment over a multi-ring ESRI polygon requires parity, not any: a point inside a hole (a city court district cut out of the surrounding rural court's polygon) still tests "inside" the rural polygon's outer ring, so whichever polygon comes first in .dbf order wins. This plausibly explains part of the documented 9% disagreement with the name-based okres ("city/rural twins").

Fix:

if sum(_point_in_ring(px, py, r) for r in rings) % 2 == 1:

Affects court_id on settlements_1910 and hence birth_court_* — re-run §3c/§4 after fixing and re-check the 91% agreement stat (it should rise).

A6. pop_total sums without min_count — census_labels.add_shares_cis

out[inh].sum(axis=1) turns an all-NaN row into 0, not NA. Currently no okres has missing settlement-size classes (verified), so this is latent — but one missing workbook away from silently writing population 0. Fix: .sum(axis=1, min_count=1) (or min_count=len(inh) to require all six classes).


B. Logic & robustness (fix when touching the module)

  • B1 · ranks.py — Habsburg podpraporčík mapping is undocumented. The Habsburg rules map podpraporcik → praporčík (ord 9, common 9), while the Legion system scores podpraporčík at ord 6 / common 6. The k.u.k. army had no podpraporčík; rank_ladders.md §A documents dropping podporučík/rotmistr but is silent on this token. Either drop it (like podporučík) or document the choice. Check n first.
  • B2 · ranks.py — nadlékař (n≈20) documented but unparsed. §A1b calls it officer-equivalent (90% · P408); the parser leaves <NA>. Align doc and code (either add a rule or state in the doc that medical officers are deliberately unscored).
  • B3 · education.py — pok. (pokračovací škola) scored as Upper secondary. Continuation/apprentice schooling is conventionally below reálka/gymnasium. Because rules are first-match, "ob. 5 mě. 3 pok. 3" → Upper secondary. Consider a separate Vocational continuation level or fold into Lower secondary; the module docstring itself flags the mapping as unverified. Also: dotted-token rules miss full words and dot-less variants (gymnasium, měšťanka, 2mě, 3mě, bare pokr., hosp., odb., kadetka) — coverage is 96.7%, unmatched ≈3.5k with systematic classes recoverable by ~8 extra tokens. → Fixed 2026-09-02 (education.py 2.0.0, D-08). Not by extra tokens on the same design: the string is an itinerary, so it is tokenised and the highest institution taken. pok. became a boolean rather than a level, because Lassnigg (2015) shows the Fortbildungsschule was legally part of compulsory schooling and required no credential to enter — so folding it into Lower secondary, as this item proposed, would have been the same error one rung down.
  • B4 · affiliations.py — "demokrat" → pokr double-count is real but tiny. Verified: 13 rows match both soc_dem and pokr (e.g. "sociální demokrat"). Fix by skipping the bare demokrat token when a more specific party already matched. (CLAUDE.md lists this as a known issue — still open in v1.2.0.) Also "rus" ⊂ Rusín would mislabel a Rusyn as Russian — check the raw values.
  • B5 · theater.py — fragile substrings. "RA" (uppercase) → Russia and "Battalion" → Britain/Dominions are one odd string away from misfiring; case-sensitivity is what keeps "FRANCIE"-type strings safe today. Fine at the achieved 96.5% coverage, but document that pass-2 majority vote propagates any pass-1 error regiment-wide.
  • B6 · records_geo.py — dead code + silent tie-breaks. _FOREIGN_LANDS (line 173) is never used (the muni-tier veto uses != "Czech lands"); delete or use it. build_okres_lookup keeps the first of two same-named court districts in different okresy silently — print/log duplicates once so the ambiguity is auditable. add_birth_settlement_okres and add_settlement_jaras both reset_index(drop=True) — harmless in the notebook (RangeIndex) but a footgun for any caller with a meaningful index.
  • B7 · records_geo.py — add_settlement_attrs unique-name test via id(rd). Settlements without a Wikidata QID use the Python object id as a stand-in key; CPython id-reuse makes the uniqueness test opportunistic. Outcome-safe today (a qid-less "unique" name can never resolve to a row), but replace with an explicit per-row counter for clarity.
  • B8 · records_geo.py — okres-name normalisation gaps. "Ústí n. Labem" (50 records) fails because _norm does not expand n./n → nad (token-set ratio lands just under the 90 threshold). "Nové Benátky" (46) fails because the curation stores the modern name (Benátky nad Jizerou) in the 1910 cs slot — against the stated cs = 1910-name convention. Add an n. → nad expansion in _norm (or aliases), and fix the curation row (put Nové Benátky in cs, Benátky nad Jizerou in cs_modern). "Česká Třebová" (21) names a town that was not a 1910 court/okres — candidate for ALIAS_OVERRIDE → okres Lanškroun after a quick historical check.
  • B9 · linkage.py — recall limitations worth stating in the paper. Surname-initial blocking cannot match V/W-type first-letter spelling variants (Veselý/Wesely); both criteria require exact birth year, so off-by-one archival years are dropped by design. Fine (precision-first), but say so in the linkage appendix. _LEGION_RE could in principle hit non-Czechoslovak "legie" mentions (Polish legions) — the okres+year+name conjunction makes a false confirmed match unlikely.
  • B10 · census.py — silent duplicate-column drop. When two census files map to the same final column name, the later file's column is dropped without a message (dup check in build_census_panel). Add a print.
  • B11 · census_labels.py — ~20 duplicate dict keys in DE_TOKENS/HU_TOKENS (e.g. vergleich, endurteil, pensionsfonds, nepes10). All duplicates map to identical values, so behaviour is unaffected — deduplicate for hygiene.
  • B12 · wikidata.add_birth_coords is unused (the notebook gets coordinates via records_geo.add_settlement_attrs); if it is ever revived, note it lacks the foreign-land veto that add_settlement_attrs has.
  • B13 · units.py — pol.děl.pl. labels as generic "Artillery regiment" because děl.pl. precedes pol.děl. in UNIT_TYPE_MAP. Reorder if the field-artillery distinction ever matters (plot-level only).

C. Documentation drift (code is right, docs lag — or vice versa)

  • C1 · README.md is two reorganisations stale. Points to 02_eda.ipynb, lists 5 of ~20 parsing modules, pre-reorg tree (reference/ described as "reading list", no library/, no data/final split, no geo/census stage, no 03 notebook). Rewrite against the current CLAUDE.md tree.
  • C2 · linkage.py docstrings. "~90 pairs satisfy both" → actual 144; "~2,200 individuals" → 2,239 (1,776 high / 463 medium; verified from overlap_matches.csv — CLAUDE.md's numbers are the correct ones). Also the gold-standard letters are swapped between the module header (A = DOB) and _tier's docstring (calls DOB "gold standard B").
  • C3 · rank_ladders.md. Status section still says the common scale is "still to be built" and cites ranks.py v1.2.0 — it is built (v1.3.0: _common, _band, add_rank_reset; §G3 already uses it). §G's worked example "četař → vojín = −2" disagrees with the implemented common scale (−3); §G3 acknowledges the schematic looseness but §G itself should be corrected. §B header: štábní rotmistr is "6⁺" in the doc, ties at 6 in code (documented as intentional in the module — add one line in §B).
  • C4 · census_codebook.md / census_codebook.csv list illegitimacy_rate (see A2) — true only after the A2 fix and §3b re-run.
  • C5 · notebook 02 cell 27 (data-dictionary rows) documents census columns under their German names (umgspr_*, rel_romisch_katholisch, bild_*…) although the panel has been English since census_labels v1.1 (lang_*, rel_*, lit_*). Rewrite those rows with the English family names (and point at reference/census/census_codebook.csv as the authoritative list).
  • C6 · ranks.add_rank_columns docstring says "seven rank columns"; it appends nine (_clean, _en, _ord, _ord_low, _jd, _branch, _tier, _common, _band).
  • C7 · dates.py docstring says "Defaults to all three configured columns" — there are four; the Added-columns list omits death_date/death_year_only.
  • C8 · clean.strip_place_markers docstring says "trailing +" but the code replaces all + occurrences (fine in practice — document it).
  • C9 · PLACE_MATCHING.md — correct on intent; after the A1 fix its "runs only on unplaced births" claim becomes true again. Re-verify the coverage table after A1/A4/A5 fixes.

Checked and found sound

  • Rank ladders vs. code: both ordinal maps match rank_ladders.md §A/§B row by row (incl. kadet-aspirant 6 < kadet 7 < důst. zástupce 8 < praporčík 9; Legion podporučík 8 < poručík 9 < nadporučík 10; štábní kapitán collapsed to 11, podkapitán to 10; kornet 9; pracovník/občan at 1; non-ranks dropped).
  • Rank rule ordering: every substring-containment trap checked (podplukovník before plukovník, nadporučík/podporučík before poručík, podpraporčík before praporčík, štábní/pod- captains before kapitán, generálmajor before major, oberstleutnant before oberst, feldmarschalleutnant before feldmarschall, kadet-aspirant before kadet); drop tokens are word-bounded so zak cannot strike zakopnik. First-match-wins resolves correctly in all checked cases.
  • Common scale / reset: RANK_COMMON is name-anchored exactly as §G specifies; rank_reset propagates <NA> when either side is unresolved; the band index is monotone (OR-1…9 → 1…9, OF-1…10 → 10…19). §G3's reliability decomposition (82% of demotions on the 100%-confidence spine; 99.8% sign agreement between fine and band reset) matches the implemented machinery.
  • Census joins: exact-code joins verified end-to-end (IDCourtdistr = court_id; court_id // 1000 = okres_id; kod = unit_id); court-level aggregation sums counts only (all court-level workbooks are counts); merged-header expansion via openpyxl is the right call for the multi-row German headers.
  • Linkage: precision-first tiering implemented as documented; add_overlap_flags deduplicates per side keeping the highest tier; blocking on (year, initial) is consistent with both criteria requiring exact year. Numbers in CLAUDE.md verified against overlap_matches.csv.
  • Date parsing: century fix (>1930 → −100 if in range), double-date rescue, year-only fallback with precision flags — all correct; clip ranges sensible.
  • Land classification: the positive-classification design (okres_id ⇒ Czech lands; curated Slovak district list; text markers for the rest) supersedes the weaker text-only locations.py land, as documented.
  • sum_ok in lexikon_sk: treating unprinted mother-tongue columns as true zeros (fillna(0) in shares) is correct given the source prints nothing for zero.
  • requirements.txt covers every import (rapidfuzz, pypdf, pdfplumber, openpyxl, pyarrow, requests, bs4/lxml).

E. Post-fix verification — measured on the re-run data/final (2026-07-16)

Notebook 02 re-executed in full (21/21 cells, 174 s, exit 0); every figure below is read off the freshly written files and compared against a copy of the pre-fix build.

check pre-fix post-fix
rows in BOTH frames (okres ∧ járás) — birth 6,320 legion / 4,287 habsburg 0 / 0 ✅ (asserted in §4)
rows in BOTH frames — home 5,576 / 2,290 0 / 0 ✅
row counts (unchanged, no join blowup) 132,993 / 186,192 132,993 / 186,192 ✅
birth_okres_id 101,297 / 120,230 101,439 (+142) / 120,116 (−114)
birth_jaras_id 18,188 / 28,554 11,473 (−6,715) / 21,539 (−7,015)
Czech-born rows carrying HU census in *_census_hu_1910 6,320 / 4,287 0 / 0 ✅
SK megye tier ~0 630 legion / 43 habsburg (adjectival county forms now resolve)
SK settlement tier 6,320 / 4,287 (mostly bogus) 284 / 1,020 — corroborated or positively-Slovak
SK county resolved, of Slovak-land births — 99.7 % legion · 99.3 % habsburg
bare "Praha" births all 2,693 legion + 1,909 habsburg → járás Halič legion 2,680 → okres Praha (301072), 0 in any járás; habsburg 129 okres-resolved + 6 genuinely Slovak (okres "Lučenec"), rest NA (open decision below)
illegitimacy_rate absent present, 158/158 okresy, mean 0.108
pop_total (latent 0-risk) no zero rows; min_count guard
exit_N 27,034 645 — 26,389 fabricated values removed
deserted 3,719 3,717 (−2: the refused MD/DMD)
rows with no exit code (⇒ censored) 4,321 30,770
linkage 2,239 = 1,776 + 463 unchanged (2,239 = 1,776 + 463; 144 both) ✅
settlements crosswalk 18,150 18,151 (curated Praha row)
spatial parity fix — changes 0 live assignments (only 7/339 polygons multi-ring); correctness hardening
point-in-polygon sanity — Praha centre→Vinohrady, Brno→Brno, Plzeň→Plzeň, Olomouc→Olomouc (město), Cheb, Trutnov — all historically exact

Downstream consequence — 03_visualisation must be re-run: the exit-code distribution changes materially (the "Missing" bar collapses from 20.3 % to 0.5 %, and "no code parsed" rises to 23.1 %). Any existing LaTeX table or figure quoting exit_N is wrong.

Corrected doc claim → then corrected again (see §D)

spatial.py claimed 91 % agreement between the spatial court's parent okres and the name-based okres_id; the measured figure was 68.3 %. This section first attributed that to 1910 city structure — that was wrong, and chasing it properly (§D2) showed it was mostly a coordinate bug: a third of settlement geocodes pointed at a like-named village a median 75 km away. After the D-fixes the figure is 88.1 %, and the residual is genuinely structural (okres boundaries changed since 1910; city/rural twins; modern centroids). court_id remains a complementary juridical layer — do not use spatial parentage to validate or replace the name-based okres_id. Lesson recorded: a suspicious aggregate deserves a root-cause test, not a plausible story.

Resolved — bare-"Praha" births → okres Praha (author's call, 2026-07-16)

Whitelisted; see §D3 for the mechanism and the evidence (incl. the name-classifier test that argued against using names). All 1,915 VÚA Praha-born now resolve; 6 genuinely Slovak ones keep their járás; expected cost ≈3 misplacements in 1,780.

Done post-run: PLACE_MATCHING.md coverage table refreshed with the measured numbers; PIPELINE.md / VARIABLES.md aligned. Outstanding: re-run 03_visualisation.

Validation registerHistory & decisions

Every check, and what each one is blind to. · source: reference/VALIDATION.md

In short

Every check this project runs, in one place, with the column that matters most: what each one is blind to. A check that cannot see a thing is not evidence about that thing, and a register that only lists successes invites the reader to assume otherwise.

Explain it simply

Most projects report the checks that passed. This one has a column for what each check cannot see, and that column is the honest half.

The distinction that organises it: an external check compares this project against a source that could genuinely contradict it — a published encyclopedia, an official census, an independently trained model. A self-check only confirms that the output obeys the rules it was built from. Self-checks are necessary and they are not evidence that the coding is right; a rule applied perfectly to the wrong data passes every one of them.

Reading the blind-spot column is the fastest way to find out what this project does not know. Place matching below the district level, for instance, has no external check at all — so it is written down as unchecked rather than left to look like it passed.

The organising distinction is between external evidence — a source outside this project that could contradict it — and self-checks, which can only catch a result disagreeing with its own rules. Both are necessary; only the first is evidence that the coding is right.

Last updated: 2026-11-03.


1. External checks

Check Source What it validates Headline result Blind to Detail
The sealed certification pages Eight Czech pages of the 1910 register drawn at random (seed 1910) and sealed in code, transcribed whole on 2026-10-18 blind to any run, read once by the frozen route on 2026-10-25 the route's reading of the printed symbols, out of sample — the only number the analysis may quote for them F1 0.996, 95 % page-clustered interval 0.991–1.000: 502 claims right, 1 false, 3 missed of 505; 104 of 107 entries exactly reproduced; the printed count right on 501 of 502; every token with ten or more instances at sensitivity 1.000 an error the sheet and the route make the same way; a symbol the transcriber missed; rarity — 8 pages are 8 clusters and most tokens have too few instances to carry a rate, which is what the entry-level sample is for; and the German half, which has no certification until its four sheets are transcribed and the section its route loses is fixed The reading route — Certification; D-127
Fidler 2025 Encyklopedie československých legií (Universum) — 1,289 entries, in copyright Legion units, theatres, ranks theatre 99.6 % (71,588 / 71,854, build of 2026-09-23); every officer rank resolves; exit codes within 5 % of the encyclopedia's repatriation totals occupations, places, anything below unit level — and the imputed theatres: the comparison needs a numbered CS-Legion regiment, so the US-applicant and Polish-Legion rules it never reached were both wrong (audit 2026-09-22 A5) Units & ranks
German link bridge History of Work hisco_occupation_link_combined.csv, 1,199 German labels (IISG, CC-BY-SA 4.0) the HISCO code for a German gloss 39 of 495 curated keys coded by the HISCO project itself, not by us; 14 ambiguous glosses refused rather than guessed the 454 hand-assigned keys, and every qualified compound; and it starts from a gloss we wrote Occupations
OccCANINE Dahl, Johansen & Vedel (2024), transformer coder, 13 languages HISCLASS and the HISCO major group 88.1 % of records on HISCLASS, 96.6 % on the major group (re-run after D-29) Czech — it is not among the 13 languages, so it had to be fed our German gloss Occupations
CZ-ISCO ČSÚ classification 80146 (2025), Czech and English under one code the Czech reading itself — what a Czech occupational word means in English reaches 55 % of coded records; 15 keys / 457 records disagree the historical core (rolník, hajný, příručí, tkadlec, mlynář, cihlář, havíř) is absent, and it matches on a head noun so it reaches none of the 25 qualified compounds; no path to a HISCO code Occupations
1910 census Berufsstellung Volkszählung 1910, agricultural workforce by Selbständige / Pächter / Arbeiter / Taglöhner / mithelfende Familienangehörige rolník — whether the largest key in the dictionary is own-account or wage labour Spearman +0.402 within-agriculture over 99 okresy, monotone, with flat nádeník / dělník placebos it is ecological — okres-level composition cannot clear any individual record, and it separates own-account from wage-earning but not holder from farmer's son Occupations — Check 6
Vienna Database listings MOSAIC unharmonized Austrian numeric file — 163,400 persons, 44 localities, 1632–1947, each with a title from a closed 521-term German list and a household position rolník again, at the level Check 6 could not reach: whether the German farmer term names a holder, and whether it was used for a farmer's son BAUER 0.3 % Gesinde vs LANDARBEITER/KNECHT 94.9 %; among males 18–30, BAUER is 97.5 % household head and 0.3 % son; holds across 32 localities and 49 survey years title and household position are not independent in a household listing — one clerk assigned both, so this is corroboration, not measurement; and it validates Bauer, reaching rolník only through an assumed Landwirt ≈ Bauer synonymy. Wrong territory (no Czech lands) and mostly the wrong period Occupations — Check 7
Austrian school statistics 1908/09 Österreichische Statistik XCIII/1, Statistik der Unterrichtsanstalten (k.k. Statistische Zentralkommission, Wien 1912) — 41 tables by Land and by Unterrichtssprache the education ladder: whether its rungs are the school kinds the state actually operated, and whether pok. belongs on the ordinal taxonomy confirmed — the volume files the Fortbildungsschule as its own category and it is the mass institution (Böhmen 84.0 % of vocational pupils); but Table 34 fails education_complete: an 8-class Volksschule existed 10 times in Cisleithania, twice in the Czech lands, so 3,894 records are scored complete against a form that barely existed and 3,137 ob. 5 are scored incomplete (D-53) Hungary — Cisleithania by definition, so Slovak and Rusyn births are untouched and D-08 stays open; flow (1908/09 enrolment) vs stock (men born 1875–1900); school counts are not pupil counts; Table 39's attendance data is by Land, not Bezirk, so the attendance caveat narrows but does not close Education
Ostafin et al. 2020 Historical dataset of administrative units with social-economic attributes for Austrian Silesia 1837–1910 (Harvard Dataverse doi:10.7910/DVN/K7YPAF, CC0): every Silesian commune and its map-drawn parts, with the 1910 census typed from the same Spezialortsrepertorium Schlesien (1917) the register's full run read the register's German table, commune by commune over a whole volume (D-160) — the only outside transcription of a German volume; and, in passing, the census this project uses and the ČSÚ lexicon's 1910 column census: identical in all 12 political districts (756,949 people; languages where our frame keeps them). Register table (Gemini, the table reader of record): 98.6 % of 441 joined communes equal on total population, sex, houses, languages and religion; the 6 that differ are Ostafin's choice of unit where checked on the page (Oderberg: the print's commune is 5,810 = the town 2,794 + Pudlau, which Ostafin files apart). Lexicon 1869–2005: 311 of 382 present-day units equal, every difference a 2005 unit, not a misprint (run of 2026-11-03, check_ostafin_silesia 0.1.0, D-183) Silesia only — Bohemia and Moravia have no such transcription; the apparatus (institutions, pictograms) — Ostafin carries none; area — theirs is measured on their maps; the 55 communes the name join misses (stray marks in the readers' court labels, spelling); and an error both transcriptions make the same way D-183; output/validation/ostafin_silesia.txt
Ostafin et al. 2020 (Austrian Silesia) Ostafin, Kaim, Siwek & Miklar, Historical dataset of administrative units … Austrian Silesia 1837–1910, Harvard Dataverse doi:10.7910/DVN/K7YPAF (CC0); its 1910 commune figures are an independent hand transcription of the same Spezialortsrepertorium XI. Schlesien (1917) — data/raw/geo/ostafin_silesia/ the census we use and the German table read by the frozen route (de-silesia full run) census: all 12 political districts identical, total 756,949 both, languages identical where our frame keeps them; register table: of 440 communes joined by name, 98.9 % identical in total population (Gemini; Haiku 98.6 %), ~99 % on sexes, houses, languages, religion. No systematic difference in the readings: every residual checked against the page (Oderberg 5,810 = Oderberg + Pudlau; Gröditz 800 = Gröditz + Neudorf) is the print, and Ostafin files a settlement under another commune. That unit difference is one-sided: our commune sums run +0.4 % over the joined communes, the Czech-speaker sum ~1.8 % lower and German/Polish ~1 % higher — a handful of reassigned communes (Oderberg, Wagstadt area), attributed from sums only, not commune by commune. Area is not comparable: Ostafin measures it from its maps (16 % identical, totals within 0.1 %) institutions — Ostafin has none (population, sex, houses, language, religion, area; livestock by district); Silesia only; an error shared with the print; the 55 communes (160k people) the name join missed. The ČSÚ Historický lexikon obcí 1869–2005 is not a 1910-commune check: it re-counts to 2005 obec parts, so it equals Ostafin only where units coincide (319 of 395 present-day places) run of 2026-11-03 (scratch scripts; the repo check is being written)

Why five and not one. The four occupation checks are not redundant, because they test different links in the same chain. The German bridge and OccCANINE both begin from a German gloss this project authored, so neither can test whether the Czech word was read correctly — OccCANINE in particular had to be fed German precisely because Czech is outside its training set. CZ-ISCO tests that step and only that step. The 1910 census and the Vienna Database both bypass the coders entirely and go to a source, and they fail in opposite directions: the census is the right territory and decade at the wrong level of aggregation, the listings are the right level in the wrong territory. Fidler tests a different part of the record entirely. Each is weak exactly where the next is strong.

Why external evidence is the point. csol.cz and VÚA share an archival lineage, so their agreement proves nothing about whether the parsing is right. Until Fidler (2026-07-20) the project had no outside yardstick at all, and it surfaced a real rank defect within an hour.


2. Self-checks

These cannot contradict the project from outside. They catch a build disagreeing with its own stated rules — which is most of what actually goes wrong, but is not the same thing.

Check Where What it holds Caught
The sections a page cut run_frozen.sections_missing_offices, printed by the ledger stage of every run; tests/test_section_audit.py that a court with osady on a page also has an offices section there — the shape of a heading the page image missed, which drops that section's institutions rather than misfiling them German scan 0380, where 28 institutions ran on as parts (D-128); blind to a missed heading between two sections of the same kind, and to a court that prints no osady on the page
\1 utils/validation/data_checks.py v1.12.0, last cell of notebook 02 107 checks the built data/final must satisfy (107 / 107 on the build of 2026-09-23) — the build is not older than the code, missing stays missing, every code exists in its authority, scales and dates stay in range, no record placed in two frames, a járás-placed birth is Slovak, the linkage is one-to-one with no date-of-birth conflict, every column is documented the fabricated exit_N (27,034 → 645); 6,320 double-placed births; a HISCO code absent from the authority; a data/final four days older than the code, on which eight checks had been skipping silently
Behaviour self-tests habsburg, theater, linkage, records_geo_hu, affiliations, education, provenance *_selftest(), called by the gate rules a finished frame cannot express: a register note is not a Legion note, dělostřelec is not střelec, a US city is France, STANĚK František is not Soja František, Halič is not resolved from a district string the linkage name test and the note flag (audit 2026-09-22 A1–A2) once written as cases
Matcher self-test occupations.matcher_selftest(), 17 cases, called by the gate behaviours of the occupation matcher that must not regress, each one a bug that shipped once generálmajor scoring as major; cihlář destroyed by abbreviation expansion; lesní hijacking its compound
Coding robustness VALIDATION_OCCUPATIONS.md Check 4 the desertion gradient across social classes with the compound rules switched off nothing — which is the result: the gradient does not move, so the compound rules are not manufacturing it
Linkage placebos Audits 2026-09-22 A1–A3 the name thresholds against pairs that are wrong by construction (exact date of birth, same given name, surnames with different initials); the note flag against a variable it cannot be true for (death in 1914 vs a unit founded in 1917) and against the match rate of records with no note a name test carried by the given name (389 of 2,239 pairs with surnames under 60 % similar); a "legion note" that recorded the register-keeping regiment and matched legionnaires less often than no note (2.9 % vs 4.8 %)
Internal reconciliation lexikon_sk, census_hu row sums published totals must equal the sum of their parts column-bucketing drift in the Majo lexicon
1910 register invariants lexikon_1910.INVARIANTS + the block, sequence, okres, parts and key checks; run by run_lexikon_1910_pilot.py the register's own arithmetic, and the reader graded blind against it (D-38): sexes and confessions and languages each sum to the total (I1–I3), settlements sum to their municipality (I4), the interleaved column-1 sequence reconstructs (I5), okres totals reconcile against the 1910 census (I6), the Části osad pairs match the table row (I8), keys resolve against the district legend (I9) three summary rows adding okres Kraslice back into itself (~4× its population); 137 of 1,146 section headings filed under the neighbouring district; 36 % of offices entries merged into their predecessor; and the threefold symbol undercount that reversed D-39
Symbol reader against the hand transcription (E1) utils/lexikon_1910/run_e1_gemini_version.py v1.2.0; output/lexikon_1910_pilot/bakeoff/e1_gemini_version.{txt,csv}; 2026-09-17 / 09-22 the vision reader of the register's symbol apparatus against a person's blind reading of the same print — 280 claims, 47 entries, 4 pages — over 17 Gemini passes in five arms, three repeats each, one variable changed at a time the view is the lever: section crops beat the whole page by +0.132 F1 / +0.295 entry_exact with thinking off on both sides; Gemini's default thinking costs 3.8 accuracy (−0.016 F1) and bought 3.7 nothing, so the reader is Gemini Flash with thinking off at F1 0.962 / entry_exact 0.80 and $0.33 a 13-page pass (D-54); the cost ledger was counting ~1/6 of Gemini's bill (thinking billed as output, invisible in completion_tokens); a decoder defect filing 12.8 % of correctly read claims under the wrong part of a municipality (D-56; fixed 2026-09-23, settlement F1 0.854 → 0.915); a concurrency defect that joined a tall section's strips in the order they finished, so the same answers scored differently live and on replay (fixed; replays now byte-identical); and two confusions no model version fixes — [castle] read as [poorhouse] (outline against solid roof) and, with thinking on, the cooperative glyph split in two
Printed order of the marks (I22) lexikon_1910_institutions.check_order 0.9.0; scored by utils/lexikon_1910/rescore_passes.py 1.3.0 → output/lexikon_1910_pilot/bakeoff/rescore_r4_order*.csv; 2026-10-13 (D-73) the marks of an offices entry stand in the order the volume prints them (Návod p. VIII), learned as pairwise precedence from ten gold sheets — 0 of 2,902 pair observations against it once the gendarme block is set aside. A mark out of place is routed to review; an order that begins again is reported as a missed part boundary. No claim is changed. Blind to: a same-slot substitution (station / stop, revenue office / customs, church variants, a savings bank before the cooperative); a missing mark; a reference of ten pages; the Czech edition only the castle read as a poorhouse: 1 of a Gemini pass's 10–12 false claims on the scored pages (the same one in all six), and 11–14 [poorhouse] marks a pass at the end of a line on the other nine pages, where every transcribed part prints [castle]; 262 of 262 flagged disputed claims false over 105 pairs of passes; and two slips in the calibration gold itself (Jičín 19 on scan 0136, Nusle 4 part 2 on scan 0196), both confirmed by the author by eye and corrected 2026-10-13
Section crop against the foot of its column utils/lexikon_1910/_archive/measure_segment_foot.py → output/lexikon_1910_pilot/segment_foot_gap.csv and segment_foot_gap_strips.png; tests/test_lexikon_1910_segment_foot.py; 2026-10-13 (D-75) the last section crop of an apparatus column reaches the foot of the column, so no printed line lies below the image the reader and the transcriber are handed. Measured on the rendered page, no model called: the gap between the last text-layer line and the divider rule's foot, a pixel screen for print in it, and every gap of 18 px or more read by eye (23 of 908 columns over table scans 15–494). Blind to: print below the divider's own foot; a line dropped between two recognised ones; gaps under 18 px, not read by eye; the certification pages; the German edition; and the verdicts are one reading, by Claude 12 entries and 2 wrapped tails cut off 13 pages, all offices sections and all outside both the reader's crop and the gold crop, so no pass and no sheet could disagree about them (scans 0076 and 0316 among the gold); every one of the 12 a key and the same single mark, so the loss fell on one mark and no other; and a pixel screen that, set from the two known cases, finds 10 of the 13 with one false alarm
Apparatus coverage utils/lexikon_1910/_archive/measure_apparatus_coverage.py → output/lexikon_1910_pilot/apparatus_coverage.csv and apparatus_coverage_heading_candidates.png; tests/test_lexikon_1910_apparatus_coverage.py; 2026-10-14 (D-76) every page with an apparatus is cut; the band opens at the table's own foot; the crops of a column tile it, so print between the text layer's lines is inside a crop; a heading the text layer missed is screened for by print above a carried section's first line a missed heading still mislabels its section (7 found, awaiting the author's ruling); 6 missed first lines are covered, not read for their text
Table rows at the table's own rules utils/lexikon_1910/_archive/measure_table_foot.py, measure_table_top.py → output/lexikon_1910_pilot/table_foot_rows.csv, table_top_rows.csv; tests/test_lexikon_1910_table_foot.py, test_lexikon_1910_table_top.py; 2026-10-14 (D-77, D-78) the table's first and last rows are read, and no apparatus or header line is read as a row; graded by the register's own I1–I5 and against the purposive gold tables row for row a block continued on the next page still reads short in channel B (0124); the running head is still read above the body band's top
Okres from the running head utils/lexikon_1910/_archive/measure_running_heads.py → output/lexikon_1910_pilot/running_head_okres.csv; tests/test_lexikon_1910_running_head.py; 2026-10-14 (D-79) each page's okres against the folded matcher and the exact lookup; disagreements read by eye 5 of the 24 changed pages were not settled
Image-only geometry and ink accounting utils/lexikon_1910/_archive/measure_image_geometry.py, measure_ink_accounting.py → output/lexikon_1910_pilot/image_geometry_cechy_cz.csv, ink_accounting_*.csv and _sheet.png; tests/test_lexikon_1910_image.py, test_lexikon_1910_volume.py; 2026-10-14 (D-80) the page read from pixels agrees with the ALTO geometry (table foot 473 of 478, divider 473 of 476, 17 columns 472 of 478, headings 1,195 of 1,245 plus 41 the text layer drops); no printed line outside every crop and no strip cut through ink, on four volumes opt-in, not the pipeline's default; a heading's text and the column header still have to be read; the German, Moravian and Silesian volumes have no gold
The pilot's two geometries utils/lexikon_1910/_archive/measure_pilot_geometry.py → output/lexikon_1910_pilot/pilot_geometry.csv; tests/test_lexikon_1910_pilot_geometry.py; 2026-10-15 (D-83) the image cut against the text-layer cut on every table scan: sections, labels, headings found by one channel only, legend crops, print outside every crop the pilot reads, and the gold pass's crop count a heading only the image sees is labelled by a read made in the pass, not here; the reading itself is measured only by the r5 re-read (arm G against E)
Crop audit crop_audit{label}.csv, written by every pass; run_lexikon_1910_pilot.printed_line_count, crop_audit; tests/test_lexikon_1910_pilot_checks.py; 2026-10-15 (D-85) each section's printed lines from the image against the lines the reader returned (flag outside 60 %–167 %), and any heading in the reply against the crop's label; calibrated on 101 sections, median ratio 1.00 to the text layer, no false flag a line read but wrong; a reader that merges two lines into one and drops a third

A row-level reconciliation is necessary but not sufficient: in the Slovak lexicon 135 rows summed correctly while sitting under the wrong heading. Passing arithmetic is not passing semantics.


3. What none of them reach

Stated plainly, because a register is only useful if it admits its edges.

  • The linkage's recall. Precision is now tested against pairs that are wrong by construction; recall has no yardstick at all. The 1,340 confirmed overlaps are a lower bound on the men who appear in both files: a legionnaire whose VÚA record carries only a birth year and no Legion note cannot be confirmed, however well the name matches. Nor can anything say whether a dup_group pair inside one file is one man entered twice or two men the clerk could not tell apart — so it is flagged and never merged.

  • ~~The Czech→HISCO dictionary's hardest decision.~~ Closed 2026-08-02 (D-33), corroborated 2026-08-05 (D-34). No coder reaches rolník — 18 % of the sample — because the German bridge confirms only Landwirt → 61110 and CZ-ISCO does not contain the word. Two sources do. The 1910 Volkszählung Berufsstellung columns put these men on the own-account rather than the wage-earning side of agriculture (Spearman +0.402 within-agriculture over 99 okresy, flat placebos), ecologically. The Vienna Database household listings then supply the individual-level fact the census cannot — BAUER is 0.3 % Gesinde against KNECHT's 94.9 %, and among men of Legion age 97.5 % household head and 0.3 % son — in the wrong territory and through an assumed Landwirt ≈ Bauer synonymy. So the ecological limit is narrowed, not closed: still no individual Czech record is cleared. What remains unreached is the zámečník 5-digit code, which no longer moves any emitted variable.

  • The education ordinal, for half the sample's frame. Narrowed but not closed on 2026-09-07. The rungs themselves are now checked against the Austrian administration's own school census (Österreichische Statistik XCIII/1) and they hold — but that volume is "in den im Reichsrate vertretenen Königreichen und Ländern", i.e. Cisleithania by definition, so it cannot reach the other half. Moore (2020) is explicit that his account stops at the Austrian half — the Ausgleich handed Hungarian schools to Budapest — and Lassnigg (2015) is silent on Hungary too. Slovak and Rusyn births parse on the same Czech tokens and land on the same six rungs, but nothing establishes that a Transleithanian mě. is the Cisleithanian one; the ~10,500 Slovakia-born records are excluded from every test in Education. The variable needs a Cisleithanian/ Transleithanian indicator interacted with the level. This is why D-08 is still open, and it is a source gap, not a coder gap: the fix is the Eötvös 1868 and Apponyi 1907 school laws and the Magyar statistical series, not another pass over the strings.
  • The length of a vocational school year. Continuation-school hours and Fachschule course lengths varied by Land and by trade (regional laws 1907–1931), so pok. 3 and odb. 2 count years of a thing whose size is unknown. education_complete is <NA> across that whole branch rather than guessed, and no education_years_total is emitted. The 1908/09 volume classifies these schools but does not state their hours, so it does not close this either. See Education ladder.
  • How completely pok. is recorded at all. New, and the more useful half of what the 1908/09 comparison found: the Czech lands had 5.3 continuation pupils per full-time vocational pupil, while the records name them at 0.86 to one — about 6× fewer. The continuation school was compulsory, part-time and unremarkable, and men appear simply not to have written it down. education_apprentice (6.6 %) is therefore a lower bound, not a rate. Flow-vs-stock and selection into the Legion contribute too, and nothing here can apportion the three. See Education Check 4.
  • Whether a bare ob. N counts years or classes. education_complete assumes years against a standard of eight; Table 34 shows an eight-class Volksschule existed 10 times in Cisleithania. The column is wrong for ~7,000 records in one direction or the other and is not yet fixed — D-53. Nothing else in the education block is affected.
  • The Slovak half of the party scheme. affiliations.py 2.0.0 is grounded on Akademický atlas plate IV.37, whose title is Systém politických stran v českých zemích. It contains no Slovak party, so it can neither confirm nor deny Slovenská liga against Hlinka's Slovenská ľudová strana — the reading of SL (108 records, the largest unmatched token), ludovák and ludová. Recorded as refusals rather than coded (D-50). A source gap, not a coder gap.
  • Place matching below the district. Fidler covers units, not birthplaces; the settlement coordinates are checked only against their own okres polygons (89.3 %).
  • The exit codes, except in aggregate against Fidler's repatriation totals.
  • Two failures of the 1910 register read, by construction. The invariants are the table's own arithmetic, so they cannot see a row wrong in two compensating places — it still sums — and they cannot see a whole municipality block dropped off a page, because every surviving row remains individually consistent and every block still adds up. Neither has a machine check and neither will: the only instrument that reaches them is a person reading the print, which is what reference/places/transcription_gold/ is. Below the printed rules there is no arithmetic at all, so the apparatus has no self-check whatever — only a closed vocabulary a reader may not answer outside of, and a hand transcription. See Digitising the 1910 register. Since 2026-10-13 it has one, and a narrow one: the printed order of its marks (I22, D-73).
  • A 1910 symbol misread as another of the same slot. The printed-order check sees a mark only when it stands where the print never sets it. A railway station read as a stop, a revenue office as customs, one church for another, a savings bank written before the cooperative all stand where the right mark would, and are most of what the readers still get wrong. Its order is learned from ten sheets of the Czech edition, a pair seen fewer than twice is not judged, and the German edition has no reference at all.
  • A 1910 section heading the text layer never saw. Since D-76 its section's print is inside a crop, but the crop is still labelled with the section before it — a parts section read as offices of the previous court. The screen (print above a carried section's first line) found 13 candidates on the Czech table scans, 7 of them headings; it cannot see a missed heading that is not the first line of a column, and the image geometry of D-80, which finds 41 such headings, is not the default.
  • A 1910 apparatus line printed below the divider rule's foot. The crop check (D-75) measures the gap between the last text-layer line of a column and the foot of that column. Print below the foot lies in the footer band and is not measured, though a recognised line does end up to 17 px below it on the gold pages; a line the text layer dropped between two recognised ones is inside the crop and is not counted; and the 23 gaps that could hold a line were read once, by Claude, the smaller ones not at all.
  • The 1910 symbol reader's accuracy on the volume. E1 measures the reader against four purposive pages read once, and those four behave like four observations — entry_exact runs from 0.00 to 0.93 by page. It ranks readers; it certifies nothing about the other 476 pages, and the transcriber's own error rate (plausibly 2–5 %, the same order as the reader's) is unmeasured until two pages are double-coded. It also scores claims per municipality: at the settlement level the analysis will use, the figure is 0.915 after the decoder fix of 2026-09-23 (D-56), and the remaining ~0.05 of part attribution has not been diagnosed. The certification sample is drawn and sealed (D-45, D-48, D-55); none of it has been read.
  • Anything about the 1,809 records (1.5 %) left uncoded — deliberately, rather than guessed.

Detail pages: External validation — units & ranks · External validation — occupations · Decisions · Data QA · Data sources

External validation — units & ranksHistory & decisions

Checked against Fidler 2025 — what agreed, what it caught. · source: reference/VALIDATION_FIDLER.md

In short

One of four external checks. The register — what each one covers and what each is blind to — is VALIDATION.md; the occupation coding has its own detail page in VALIDATION_OCCUPATIONS.md.

Explain it simply

The best thing that can happen to a dataset is that somebody who has never seen it wrote down the answers first. In 2025 a historian published an encyclopedia of the Czechoslovak Legions: which units existed, where they fought, when they formed. That is an answer key produced with no knowledge of this project whatsoever.

Checked against it, the pipeline's theatre assignment agrees on 99.6 % of records, and every officer rank resolves correctly. More usefully, it found two real bugs. The rank ladder had no general grades, so generálmajor matched the word major and every Legion general was recorded three rungs too low. And notes about a rank — candidate for lieutenant, was not a soldier — were being scored as that rank.

It also found roughly 130 records where the source website contradicts the historical record: regiments placed in Russia that only ever existed in France. Those are documented and left alone. Silently correcting a source is how a dataset stops being evidence about the source.

Source. Fidler, Jiří (2025). Encyklopedie československých legií. Praha: Universum. ISBN 978‑80‑242‑9615‑4 (e‑book). Successor to the same author's Dějiny československých legií v datech. Held in library/books/ (gitignored — in copyright).

What it is. 1,289 entries: 280 officer biographies, 233 units, 230 sub‑units, 264 places, 84 institutions, 44 sea transports, plus 16 dated orders of battle. Every unit entry states its theatre, formation date, parent formation, commanders and renaming explicitly, which makes it usable as an authority file rather than only as reading.

Why it matters here. csol.cz records a unit as a label plus a formation in parentheses. Nothing in the source says which units existed, when, or where — so a mis‑keyed regiment number looks exactly like a real one. Fidler turns that into a checkable claim.

Derived artefact. reference/units/unit_authority.csv (361 military entries), built by utils/reference_tools/units/build_unit_authority.py. Facts only — unit name, theatre, kind, dates, lineage. No prose is extracted or stored.


1. Results — what agreed

Check Result Reading
Theatre vs the unit authority, Czechoslovak‑Legion records with a numbered rifle/infantry regiment 99.63 % agree (71,580 of 71,846); 266 conflict. Build of 2026-09-23: 71,588 of 71,854, the same 266 The four‑pass classifier reproduces the standard reference work
Exit codes — Russia records coded returned (exit_DM ∪ exit_V) 53,542 vs Fidler's ≈56,500 shipped home (28 southern + 8 eastern transports) 5 % apart, in the direction censoring predicts (17,226 Russia records carry no exit code)
Deaths — Russia exit_B/S/Z/P 3,288 vs the ≈4,100 commonly cited Same direction, same explanation
Rank ladder — every officer rank Fidler uses, through clean_rank podplukovník→13, plukovník→14, major→12, kapitán→11, nadporučík→10, poručík→9, podkapitán→10 — all correct §B ladder holds against an independent vocabulary

The exit‑code reconciliation is the load‑bearing one. It is an external check on exactly the variable the 2026‑07‑16 audit repaired: had the fabricated exit_N still been present (27,034 rows rather than 645), this comparison would have shown an implausible missing category. It does not.

Two theatre numbers, and why the difference matters. Comparing every record with a numbered regiment gives only 95.4 %. Restricting to Czechoslovak‑Legion records — the correct like‑for‑like — gives 99.63 %. The gap is not noise: it is almost entirely records on other armies whose regiment numbers coincide with Legion ones (the French Army's 33rd, the Foreign Legion's 1st), which the naive comparison scores as disagreements even though both sides are right. Discovering that is what produced unit_key (§2.4). The 95.4 % figure is reported here only because it is the number a first, under‑specified comparison produces; 99.63 % is the result.


2. Results — what it caught

2.1 Legion generals scored as field officers (fixed, ranks 1.4.0)

The Legion ladder stopped at plukovník (14) and carried no general grades. Rules match by substring, first hit wins, so:

raw scored as should be
generálmajor ×10 major, ord 12, OF‑3 major general, OF‑6
generálporučík ×1 poručík, ord 9, OF‑1 lieutenant general, OF‑7
brigádní generál ×3 unscored brigadier general, OF‑6

Small (≈17 records) but directional, and on rank_reset — the RHS variable of interest. One man went Habsburg captain (11) → Legion major‑general and recorded rank_reset = +3.

The Habsburg ladder was unaffected: it has generals, ordered before major. Its own generálmajor still resolves correctly (ord 16, OF‑6).

2.2 The general case — annotations read as ranks (fixed)

Sweeping every distinct rank string for the same shape found a second family, where a statement about a rank was read as the rank:

raw scored as means
kandidát na poručíka ×4 poručík (9) candidate for lieutenant
podporučík kandidát ×2 podporučík (8) candidate
pracovník / požitky poručíka ×1 poručík (9) labourer on a lieutenant's pay
nebyl vojínem ×1 vojín (1) was not a private
aspirant na podporučíka ×1 poručík (10) officer candidate

These are now refused (_DROP_FOR) and surfaced in the residual report, except aspirant na podporučíka, which the word‑start guard alone re‑routes to its correct kadet-aspirant (6). pracovník / požitky poručíka correctly keeps pracovník (1), because drops apply per split token.

2.3 The structural fix

Both families are one failure: an unguarded substring match. _match_token now requires a token to match at a word start (_at_word_start). A longer unknown word can no longer silently resolve to a shorter rank it embeds; it becomes unresolved and visible. Genuine compounds (nadstrážmistr, novobranec, nadělostřelec, nadzákopník, ostrostřelec, jdšikovatel) are listed as their own tokens.

This is the third instance of the recurring pattern in this codebase — an unguarded fallback that produces a plausible wrong answer rather than an error.

Total effect: 35 record‑slots changed across ~400k rank cells, each inspected.

2.4 regiment conflated armies (fixed, units 1.1.0)

The parenthetical names an army, not just a theatre — Cizinecké legie (French Foreign Legion), Francouská armáda, Srbská legie, Ruská armáda, Britská armáda. Regiment numbering restarts in each, and extract_regiment discarded it.

4,347 records (3.3 %) carry a foreign army's regiment number. Within the large Legion regiments contamination is ≈0.1 %, but 1.pl. is majority French Foreign Legion — it is the FFL's 1st regiment, and rota Nazdar with it, not the Czechoslovak 1st Rifle.

add_unit_columns now also emits legion_army and unit_key = "regiment @ army". 6,058 regiment labels resolve into 6,609 unit keys. Use unit_key for unit fixed effects, clustering and size denominators; regiment is a display label.

2.5 Contradictions inside the source (documented, not "fixed")

266 records (0.37 %) are internally inconsistent in csol.cz itself: 21.stř.pl. ( Čs. legie v Rusku ) and similar, when Fidler places the 21st–23rd only in France and the 31st–39th only in Italy, never Russia — confirmed by a full‑text search finding no Russian context for those regiment numbers anywhere in the encyclopedia. The pipeline faithfully reproduces the source's own contradiction. Surfaced by the soft QA check rather than silently corrected: they are a property of the archive, and a project that quietly "fixed" them would be inventing provenance it does not have.

2.6 Unit lineage — a caveat for unit fixed effects

Units were renamed repeatedly, and the same name denotes different units at different times:

  • 2. čs. náhradní pluk → 10. čs. střelecký pluk [I] (12 Jul 1918) → 10. čs. pěší pluk (20 Aug 1918) → 10. čs. střelecký pluk [II] (7 Oct 1918) — three months.
  • 1. čs. střelecká brigáda [I] is Russian; [II] is French.

So a recorded unit name is only interpretable together with the date the record refers to. Not yet exploited; relevant if unit fixed effects are ever taken to battalion level.

2.7 What this check could not see — the imputed theatres (audit 2026-09-22 A5)

The 99.63 % is measured on Czechoslovak-Legion records with a numbered regiment, which is the right like-for-like and also the part of the classifier least in need of checking: pass 1 reads the theatre off the unit itself. The imputation passes act on the records the check excludes. Two of their rules were wrong, and neither could have shown up here. US application cities imputed Britain/Dominions, although US applicants whose own unit names a theatre served in France 1,659 times and in a British or Dominion unit 26 times; and Polská sent all 41 Polish-Legion records to Serbia/Balkans, though their units are Haller's Armée polonaise and Russian formations. Both fixed in theater 1.1.0 (D-60), with a self-test the gate runs. The lesson generalises: an external check validates the population it is run on, and a classifier's weakest rules tend to live exactly where the yardstick has no entry.


3. Qualitative context — the non-combat statuses (legion_noncombat)

The rank-reset histogram surfaced a cluster of extreme values (colonel → −16). The positives are real (Habsburg enlisted men commissioned as Legion officers, confirmed by their last-held rank), as are the officer→private cases (vojín). The suspect group is Habsburg officers whose Legion entry rank is a non-combat status — pracovník (labourer) or občan (citizen). Two sources settle what those mean:

  • Labour was an entry track, not a demotion. Za svobodu / V boj (the front chronicles, library/diaries/) and Fidler agree that in Italy the National Council's Rome office formed pracovní prapory from February 1918 as one of three intake categories (of ~7,124 volunteers in early March: ~3,456 organisation members, ~2,650 army, ~1,036 "on labour"). Every Italian labour battalion was disbanded and merged into the combat zvláštní pluky by April–May 1918. So pracovník marks the labour-service entry stage, not a permanent rank.
  • Officer degradation was a rumour, not policy. V boj addresses head-on the claim that officers were to be "degraded to common soldiers", attributing it to former NCOs spreading it "to cover their cowardice" (to excuse not volunteering) and rebutting it with officers' own diaries.
  • občan appears across all three diaries overwhelmingly as citizenship (legal status), not a military grade.

Decision (2026‑07‑21). Treating these as grade‑1 ranks and differencing against a real officer rank manufactures deprivation the record does not support — but they are not simply erasable either. add_rank_reset (ranks 1.4.1) keeps rank_reset computed and adds a boolean legion_noncombat (True at Legion enrolment for pracovník/občan; 7,696 records, 5.8%). The headline reset is read on ~legion_noncombat (combat ranks, ≈85.7k of 91.6k resolved); the full column is the inclusive robustness variant. The Descriptives → Rank reset charts toggle between the two views. Full rationale: rank_ladders.md §H.

(The diaries are ~1918 first-hand chronicles, long out of copyright; only their factual substance is used here.)

4. Standing checks

Added to utils/validation/data_checks.py (v1.1.0), so the notebook's §6 gate enforces them on every run:

Check Hard? Asserts
Every rank label resolves to itself hard No rule re‑ordering silently re‑points a rung
No general scores below the OF‑6 band hard §2.1 cannot recur in the built data
unit_key present wherever a regiment is hard Unit identity does not silently revert to the ambiguous label
Theatre agrees with the Fidler authority soft External yardstick; §2.5 source contradictions warn, not fail

The theatre check compares Czechoslovak Legion records only — foreign armies numbered their own regiments, so their overlaps are coincidence, not error.


5. Reproducing

python utils/reference_tools/units/build_unit_authority.py            # -> unit_authority.csv
python -m utils.validation.data_checks                    # runs every invariant

build_unit_authority.py needs your own copy of the e‑book; pass its path as an argument, or place it at library/books/encyklopedie-ceskoslovenskych-legii.epub.

Extraction note. The first parse silently lost 73k characters: <p class="mezera"/> is self‑closing, and a <p …>(.*?)</p> pattern treats it as an opening tag, swallowing the next paragraph — every sub‑entry — into the spacer. The (?<!/)> in read_entries is what prevents it. All figures above come from the corrected parse.

External validation — occupationsHistory & decisions

The German bridge, OccCANINE and CZ-ISCO on the Czech→HISCO dictionary. · source: reference/VALIDATION_OCCUPATIONS.md

In short

External checks on the Czech→HISCO dictionary. Companion to VALIDATION_FIDLER.md, which does the same job for units, theatres and ranks; both are indexed, with what each one is blind to, in VALIDATION.md. Method being validated: OCCUPATION_CODING.md.

Explain it simply

The Czech-to-HISCO dictionary is the part of this project with the most human judgement in it, so it gets three independent second opinions, each blind to the others — and then two more aimed at the single word that carries a sixth of the sample.

The German bridge translates each Czech job into its 1910 German equivalent and looks that up in the international database — useful precisely because Habsburg records were kept in German, so the translation is contemporary rather than modern. OccCANINE is a neural network trained on millions of historical job titles in other languages; fed the same German glosses, it agrees on 88 % of records at the class level. CZ-ISCO is the modern Czech statistical classification — the only source that tests the Czech reading itself rather than a translation of it.

Each is blind in a different place, which is the point of having three. The German bridge cannot catch a mistranslation. OccCANINE cannot tell a clerk from an attendant, because that distinction is administrative and not in the title. CZ-ISCO is 2025 usage, so where a trade has drifted in a century it disagrees for the right reason and the wrong period — and it reaches none of the old rural words the sample is actually full of.

Which leaves the word none of them can reach. Rolník is a sixth of the sample and the difference between reading it as a farmer who owns his land and a hired farmhand is four social classes. So it gets its own two checks. The 1910 census carries columns for whether a man farmed on his own account or for wages, district by district: where the census counts more own-account farmers, the legionnaires from that district are more owner-coded — which is an argument about districts and never about one man. The Vienna Database supplies the missing half, because it records people one at a time: among men aged 18–30, a Bauer is 97.5 % a household head and 0.3 % a son. A farmer's son is not what the word names. Wrong territory, and the title and the position were written down by the same clerk — so it narrows the doubt rather than closing it.

Last updated: 2026-08-05.


Why this is needed

The Czech→HISCO dictionary is authored for this thesis — no external source supplies one. That makes it the least externally-anchored artefact in the pipeline, and the one most in need of an independent yardstick. csol.cz and VÚA share an archival lineage, so agreement between them proves nothing here either.

Status: passed, and it is doing real work.

For 39 of the 495 curated keys the HISCO code was not chosen by this project at all. A German gloss (obuvník → Schuhmacher) was matched exactly against the 1,199 German labels in the History of Work hisco_occupation_link_combined.csv, and the code came from the HISCO project's own coding of that German title. Those 39 keys are external evidence, not self-report.

Sanity spot-check on the highest-frequency trades — every one resolved to the code an economic historian would expect:

Czech German gloss HISCO from the link table Label
obuvník Schuhmacher 80110 Shoe-maker, General
zedník Maurer 95120 Bricklayer (Construction)
krejčí Schneider 79100 Tailor
pekař Bäcker 77610 Baker, General
řezník Fleischer 77310 Butcher, General
kovář Schmied 83110 Blacksmith, General
rolník Landwirt 61110 General Farmer
dělník Arbeiter 99900 Worker, No Further Information
kočí Kutscher 98620 Animal-Drawn Vehicle Driver
horník Bergmann 71105 Miner, General
student Student −1 (no occupation)

Two of these are load-bearing for the whole project and the bridge confirms both independently: rolník → 61110 (HISCLASS 8, self-employed farmer — 18 % of the sample rests on this) and dělník → 99900 (HISCLASS 13, unskilled-unspecified, rather than a guessed 11 or 12). Neither was chosen by this project; both came out of the HISCO project's German coding.

14 glosses were rejected as ambiguous rather than auto-assigned — Schlosser alone maps to three codes (84105 / 87105 / 87430). These went to hand assignment with the alternatives recorded in the curation's note column.

Limit of this check, and it is the larger half. The German label list is only 1,199 entries, so it reaches common trades and misses much else — Gärtner, Dreher, Heizer, Mälzer are all absent. 454 of the 495 keys are hand-assigned (read off hisco_authority.csv, never recalled) and 2 more come from an exact English-label match; the bridge says nothing about any of them. It is external evidence for 8 % of the dictionary, not for the dictionary. None of the 25 compounds added in D-29 is reachable by it — the German list holds no Maschinenschlosser or Bankbeamter; only OccCANINE could check those, and did.

Check 2 — OccCANINE second coder (run, 2026-08-02)

Status: passed at the level that matters. utils/reference_tools/occupations/run_occcanine_check.py coded the 255 glossed-and-coded keys with OccCANINE (Dahl, Johansen & Vedel 2024, OccCANINE_s2s_mix), independently of this project's dictionary, and wrote occcanine_hisco / occcanine_p / agree beside our codes. It never touches hisco.

The German gloss is fed to the model, not the Czech string — Czech is not among OccCANINE's 13 training languages (en, da, se, nl, ca, fr, no, is, pt, ge, es, it, gr). Feeding it Czech would measure the model's out-of-distribution behaviour rather than this project's coding. Both coders see identical input, so any disagreement is about the code, not the translation.

Re-run 2026-08-02 after the qualifier release (D-29) added 25 curated compounds, so the figures below cover 255 keys / 99,682 records rather than the original 221 / 96,043:

Agreement level Keys Records
Exact 5-digit HISCO code 142 / 255 (55.7 %) 77,930 (78.2 %)
Same unit group (3-digit) 184 (72.2 %) 87,372 (87.7 %)
Same minor group (2-digit) 208 (81.6 %) 91,013 (91.3 %)
Same major group (1-digit) 223 (87.5 %) 96,318 (96.6 %)
Same HISCLASS 186 (72.9 %) 87,857 (88.1 %)

The headline is unchanged by the release — 88.2 % → 88.1 % on HISCLASS, 96.7 % → 96.6 % on the major group, over 3,600 more records. The dictionary did not get worse by growing.

The 25 new compounds, checked (2026-08-02)

Worth separating out, because they are the only keys in the dictionary that no other check can reach: CZ-ISCO matches on a head noun and so misses every compound, and the German link table has no Maschinenschlosser or Bankbeamter. OccCANINE reached all 25.

13 of 25 returned the exact same 5-digit code independently — a far higher exact-agreement rate than the dictionary as a whole (55.7 %), which is what you would expect if a qualified compound names a less ambiguous job than a bare head noun:

key ours OccCANINE p
strojní zámečník 84100 Machinery Fitter 84105 Machinery Fitter, General 0.999
strojní inženýr 02410 02410 Mechanical Engineer 0.999
stavební kreslič / stavitelský kreslič 03140 03140 Civil Eng. Draughtsman 1.000
stavební technik 03310 03310 Civil Eng. Technician 0.999
lesní dělník 63230 63230 Forestry Worker 0.996
zubní technik 84250 84250 Dental Prosthesis Maker 0.989
poštovní zřízenec / poštovní sluha 37030 37030 Postman 0.965
strojní technik 03510 03510 Mech. Eng. Technician 0.920
železniční úředník 39960 39960 Railway Clerk 0.843
stavební inženýr 02210 02210 Civil Engineer 0.784
inženýr chemie 02510 02510 Chemical Engineer 0.739
hospodářský dělník / pomocník 62105 62105 Farm Worker 0.530

The strojní zámečník result is the one to read. The model returns Machinery Fitter at p = 0.999 — its joint-highest confidence in the whole set — for the compound, while for the bare zámečník it returns a different trade entirely at p = 0.12, its lowest. Two independent coders confident about the compound and both unsure about the bare word is the strongest evidence available that splitting them was right, and it is exactly the pattern D-29 predicted.

Record-weighted HISCLASS agreement on the new keys is 82.3 % (2,780 / 3,379), below the dictionary's 88.1 %. 599 disagreeing records, and 373 of them are one key.

What this check is blind to — grade inside an administrative hierarchy

The largest new disagreement is železniční zřízenec (373 records): ours 36090, HISCLASS 9; OccCANINE 39960 Railway Clerk, HISCLASS 5, p = 0.966. That looks damning until the gloss is probed directly, which was done rather than assumed:

German probe OccCANINE HISCLASS p
Eisenbahnbediensteter (the gloss used — "railway employee") 39960 Railway Clerk 5 0.966
Eisenbahnbeamter (unambiguously the official) 39960 Railway Clerk 5 0.843
Eisenbahndiener (unambiguously the servant) 39960 Railway Clerk 5 0.549
Bahnwärter (unambiguously a manual grade) 98990 Transport Equipment Operator 11 0.956
Eisenbahnarbeiter 97415 11 0.532

Bediensteter, Beamter and Diener — three German words spanning servant to official — all return the same code. The model does not discriminate grade within a "railway + person" compound; it defaults to the clerk unless the gloss is explicitly manual, at which point it jumps to HISCLASS 11. So it cannot adjudicate zřízenec (subordinate grade) against úředník (clerical grade), which is the entire question for those 373 records. Our 36090 (class 9) sits between the model's two readings — which is what a Czech railway zřízenec was: above the platelayer, below the clerk.

The same limitation covers the estate and forest officials, and it is worse there: Gutsbeamter → −1 Unknown (p = 0.486); Wirtschaftsbeamter → 31090 at p = 0.197; Forstbeamter → 39310 Office Clerk, General (p = 0.911) — the model dropping "Forst-" entirely and returning the bare head, which is precisely the failure mode ADJECTIVE_KEYS (D-25) and the qualifier work (D-29) exist to prevent.

Conclusion: OccCANINE is a strong check on technical and craft compounds and not a valid check on administrative-grade compounds. All 7 class disagreements among the new keys fall in the second group (železniční zřízenec 373, hospodářský úředník 124, hospodářský příručí 55, lesní úředník 12, hospodářský asistent/praktikant 20, lesní asistent/ praktikant 15). No code was changed on the strength of them; all stay flagged review. Two of the seven have the model placing the key higher than we do, not lower.

Read the HISCLASS row, not the first one. Exact 5-digit agreement understates the result badly, because most disagreements are neighbouring codes inside the same trade: kovář 83110 vs 83190 (both blacksmiths), holič 57030 vs 57025 (both hairdressers), zahradník 62740 vs 62700, knihař 92620 vs 92625 (bookbinder, hand vs hand-or-machine), lékař 06105 vs 06100. HISCO codes at that granularity are a distinction this thesis never uses. What the analysis consumes is HISCLASS and the major group, and there the two coders agree on 88.2 % and 96.7 % of records respectively.

Where the model is unsure, it is unsure about the things already flagged. Mean confidence is 0.83 on agreements and 0.75 on disagreements — but the largest single disagreement, zámečník / Schlosser (2,162 records), carries confidence 0.12, among the four lowest in the whole set (brašnář 0.022, uzenář 0.103, zámečník 0.12, řezník 0.172 — nothing else falls below 0.2). That is the key this project had already marked review for exactly the same reason: Schlosser is genuinely ambiguous between locksmith, machinery fitter and plumber. Two independent coders being uncertain about the same word is the check working.

Disagreements where this project is more likely right:

  • úředník → OccCANINE returns −1 "Unknown code" (p = 0.68) for Beamter. A Beamter is an official; 39310 Office Clerk, General stands.
  • obchodní příručí → the model reads Handlungsgehilfe as 41025 Working Proprietor. A příručí is an employee, not a proprietor; 45190 Shop Assistant stands.
  • čalouník → 95925 Paperhanger (p = 0.99) for Tapezierer. The German word carries both senses; in Czech čalouník is an upholsterer, so 79620 stands.

These are recorded in the curation with agree=0 and review=1, so they are auditable rather than quietly overridden.

Check 3 — internal invariants (run, automated)

In utils/validation/data_checks.py (v1.5.0), enforced by the notebook 02 QA gate. Every prefix-scoped invariant runs over both coded fields — occupation and occupation_interwar — which took the gate from 37 checks to 41; the 2026-08-02 audit took it to 46 and the qualifier / career-stage release to 50, all passing:

Invariant Result
code_occupation(pd.NA) and ("") return None pass
No HISCO output column non-null where the source field is <NA> — 14,044 rows at enrolment, 127,913 interwar pass ×2
Coverage ≥ 93 % at enrolment / ≥ 70 % interwar pass — 98.5 % / 84.5 %
Every emitted HISCO code exists in hisco_authority.csv pass ×2
HISCLASS ∈ 1..13, HISCLASS_5 ∈ 1..5 pass ×2
Every coded record carries a HISCO major group added v1.4.0
Every emitted status / relation is a real HISCO modifier code added v1.4.0
occupation_hisclass_high is the better of the two, never a new value pass
occupation_career_stage_ord is exactly the map of the label added v1.5.0
Every emitted career stage is a documented rung added v1.5.0
No qualifier on a dual match — a second occupation must never be read as a modifier added v1.5.0
The qualifier is never the whole normalised string — the head noun cannot be empty added v1.5.0

The last two are the shape this repo's bugs keep taking. A qualifier appearing on a dual would mean the second occupation had been silently demoted to an adjective and dropped from the analysis; a qualifier equal to the whole key would mean a record coded from nothing. Neither is visible in a coverage number, which is how both would have survived.

Plus unit checks on the matcher itself, which as of 2026-08-02 are executable rather than claimed — occupations.matcher_selftest(), 34 cases, called by the gate as a single check and needing no data. Every case in the first group is a bug that shipped once: obch.příručí and obchodní příručí code identically; cihlář and stavitel survive normalisation intact; generálmajor does not match major; akademický malíř and strojní zámečník match whole; lesní zřízenec still falls through to its head while hostinský, řezník stays a genuine dual. The second group asserts the qualifier survives a compound being promoted to a curated key — the failure mode the extraction was designed around — and the third that mistr is not found inside ministr and that nádeník scores no rung.

What the checks caught

  • Two real occupations were being destroyed by my own normaliser. Expanding abbreviations on the bare stem rewrote cihlář (brickmaker, 164 records) → cihlářský and stavitel (master builder, 111) → stavitelský. Fixed by requiring the trailing period as an anchor. This is the third appearance of the unanchored-matching bug pattern in this repo — see AUDIT_2026-07-16.md.
  • Keying the authority on the HISCO code alone would have been wrong. 806 of 1,346 codes (59.9 %) take more than one HISCLASS depending on the status/relation/product modifiers. The four-field key has zero conflicts across 5,762 keys. build_hisco_authority.py now asserts this instead of assuming it.
  • rolník,dělník (no space) was one token, not two — the splitter never saw the dual occupation. Fixed by splitting on , ; / (and later -) before tokenising.
  • The invariants were blind to half the data. Every occupation check read occupation_hisco only, so the entire occupation_interwar_* family was unvalidated. That is precisely how a HISCO code absent from the authority survived: četnictvo was coded 58210, which does not exist, but the string appears 0× at enrolment and 58× in the interwar field — an enrolment-only check was structurally incapable of seeing it. Both halves had to move together (widening the check alone fails the gate): the code is now 58220, and every prefix-scoped invariant iterates over both families.
  • Three status/modifier pairings HSN never observed — statuses 21, 23 and 33 with no relation or product — returned no scales at all, silently costing 9 keys / 204 records their HISCLASS. hospodářský adjunkt is 21230 with status 33, a combination absent from the Dutch data. These now fall back to the base code's scales with the modifier preserved in its own column. The fallback is suppressed when relation is set, because HISCO withholds HISCLASS there deliberately.

The 2026-08-02 output audit — what a passing gate could not see

The gate was passing 41/41, so this pass looked for exactly that: the delivered columns were compared row by row against hisco_authority.csv and the HISCO status/relation/product code lists. Four more defects, all now fixed, and all four are the repo's recurring bug pattern — a rule narrower than its source, or a fallback that produces a wrong value instead of no value (AUDIT_2026-07-16.md).

  • An adjective was hijacking its own compound — ~150 records. lesní is curated as the standalone noun (the forest master, 6 records) but is overwhelmingly the adjective "forest-". Because split_occupations tries dual before head, the adjective won and became the primary occupation: of 384 records coded 63220 Forest Supervisor, HISCLASS 3, about 150 were forest labourers (lesní dělník), attendants (lesní zřízenec, lesní příručí), clerks (lesní úředník) and gamekeepers (lesní hajný) promoted to lower managers. Same on mlynářský and sladovnický; hostinský looks like one of these but is a genuine noun and its duals are correct. Fixed by ADJECTIVE_KEYS in occupations.py 1.1.0: an adjective key is eligible only as an exact whole-string match. 63220 drops from 384 records to 247. This is the fifth appearance of unanchored or over-broad matching in this repo. The same pass found that OCCUPATION_CODING.md documented behaviour the code did not have: it gave mlynářský dělník → dělník as the head example, when the string was in fact matching dual and coding as Grain Miller. It matches the documentation now.
  • A qualifier that changes the trade was being decomposed — 258 records. All eleven malíř compounds took the bare head's 93120 Building Painter, HISCLASS 9, including akademický malíř (57 — an academically trained fine artist) and malíř písma (78 — a sign painter). Likewise brusič drahokamů (19, gem cutter as metal grinder) and zubní lékař (10, dentist as physician — though zubní lékařka was already curated correctly). Nine compounds are now curated whole. The German link bridge independently returned the same code for six of the nine, so these are not self-report.
  • 553 records held a valid HISCO code and a HISCLASS but no major group. build_hisco_authority.py labelled a code from its ancestor group without backfilling the ancestry codes, so the 18 codes labelled above micro level carried blank major/minor/unit — brusič skla → 89125 alone was 304 records. HISCO is positional (major == hisco[0], minor == hisco[1], unit == hisco[2], exactly, across all 1,675 micro rows), so the ancestry is derivable from the code itself. Fixed in 1.1.0; the major group is one of the two axes Check 2 above reports agreement on, so it had to be complete.
  • železničář (47) was coded and then silently declassed. The German bridge sends Eisenbahner to 99999 Workers Not Elsewhere Classified, a unit-group code HSN never gives a HISCLASS — while the parallel keys dělník and zřízenec get 99900, HISCLASS 13. Now 99900.
  • Status 43 does not exist. abiturient and žák emitted it in occupation_status; the HISCO list is 11–13, 21–24, 31–34, 41, 42, 51, 52. Now 42 and 41. The invalid key was also missing from the authority and falling back to the base code unnoticed.

Two invariants were added so none of these can recur silently: every coded record must carry a major group, and every emitted status/relation must be a code HISCO defines. The second is validated against the code list, not the combinations HSN happened to observe, so a legitimate modifier HSN never saw does not fail it. Run against the old data/final both fired immediately — 445 records with a code and no major group, and the value 43 — which is the demonstration that they work.

Known limit, and it is the same shape as the defect fixed in 52d19d0. Both new invariants are prefix-scoped and iterate over the two coded families, but they can only check a column the final file actually carries. schema.py prunes the interwar family to six columns, which do not include occupation_interwar_hisco_major, _status or _relation — so on the interwar side these two checks are skipped, not passed. That is tolerable because the interwar field is a secondary-project covariate rather than a headline regressor, but it is the same structural blindness that once let četnictvo through. Adding those three columns to schema.py's KEEP list is all it would take.

Check 4 — does the coding drive the result? (run)

The desertion gradient across social classes is stable when the compound-matching rules are switched off and only whole-string dictionary matches are kept (output/tables/descriptives/occupation/desertion_by_hisclass_robustness.tex):

Class All matches Exact only N exact
Elite 1.08 % 1.10 % 4,908
Lower middle class 2.30 % 2.41 % 10,087
Skilled workers 3.16 % 3.22 % 52,990
Farmers 3.23 % 3.31 % 21,774
Unskilled 2.39 % 2.59 % 11,257

Levels shift slightly upward; the shape does not move. The head, dual and prefix rules are therefore not manufacturing the pattern — which is the thing worth knowing, since those rules are the least externally-anchored part of the method.

Re-run 2026-08-27 on the post-adjudication build (D-41/D-42, notebook 03 re-executed). The table above is the new one; it is worth recording what the adjudication did not do. D-41 recoded 41 keys and moved 1,383 records between HISCLASS values, and no rate here moves by more than 0.18 pp — Farmers is identical to the digit, as expected, since rolník was answered keep. What did move is the denominators (lower middle +1,294, skilled +1,626, elite +209), which is the recoding showing up where it should. A large change in codes producing almost no change in the gradient is the useful reading: the class-level result was not resting on the contested keys. Previous values, for the record: Elite 1.09 / 1.17 / 4,699 · Lower middle 2.28 / 2.59 / 8,793 · Skilled 3.17 / 3.24 / 51,364 · Farmers 3.23 / 3.31 / 21,774 · Unskilled 2.36 / 2.61 / 11,147.

Check 5 — CZ-ISCO, a third coder on the Czech side (run, 2026-08-02)

Status: passed. Of the records it can reach and score, 99.1 % agree.

This is the check the other two structurally cannot be. The German link bridge starts from a German gloss this project wrote, and OccCANINE had to be fed that same gloss because Czech is outside its 13 training languages. Neither can test the step that carries the risk: whether the Czech word means what we said it means. CZ-ISCO tests that step and only that step, because ČSÚ publishes the Czech and the English label under one code.

build_czisco_bridge.py turns ČSÚ classification 80146 (2025) into reference/occupations/czisco_authority.csv; run_czisco_check.py matches each curated key against it and writes czisco_code / czisco_cs / czisco_en / czisco_isco08 / czisco_agree / czisco_n_hits beside our codes. It never touches hisco — the script asserts the column is identical before and after.

English is published only to the 4-digit level. All 1,374 five-digit national codes — the ones carrying real trade names like Zámečníci strojů — have an empty English field and inherit it from their ISCO-08 parent through nadvaz, which resolves for 1,374 of 1,374. So the bridge is Czech trade name → official English at ISCO-08 granularity.

Keys Records
Reached by CZ-ISCO 119 / 495 59,267 / 106,901 (55.4 %)
English label words overlap 62 39,718
Same broad band only 39 9,456
Disagree 15 457
Residual, not comparable 3 9,636

Re-run 2026-08-02 after the qualifier release. The reach fell — 57.3 % → 55.4 % — purely because the denominator grew by the 25 new compounds and CZ-ISCO reaches none of them: it matches on the head noun of a CZ-ISCO label, and strojní zámečník is not a CZ-ISCO label. That is a structural blind spot, not a regression, and it is why OccCANINE is the only check that could speak to D-29 at all.

Agreement among the comparable records is 49,174 / 49,631 = 99.1 %; on the strict test — the two English labels sharing a content word — it is 80.0 %.

Three deliberate refusals to score. A key coded to HSN's residual (99900/99910/99920) or to a sentinel is marked n/a rather than counted as a failure: ISCO has no "no further information" category, so dělník → 99900 meeting Kvalifikovaní stavební dělníci is neither agreement nor disagreement. That is 9,636 records, and letting them score would have depressed the headline for a reason that has nothing to do with the coding.

The broad-band column exists because the raw major digits are not comparable. HISCO 7, 8 and 9 are all production workers, while ISCO-08 splits the same people across 7 craft, 8 operators and 9 elementary; HISCO's separate 4 sales and 5 services both fall in ISCO-08's single major 5. Only coarse bands — professional / managerial / clerical / sales-service / agricultural / manual / protective — mean the same thing on both sides.

The 15 disagreements are the point of the check, and they are substantive. Most are ninety years of semantic drift, visible in a single line:

Key Ours CZ-ISCO 2025
skladník (140) 39140 Storeroom Clerk Skladníci → Lifting truck operators
správce (7) 21000 Manager Správci webu → Web technicians
vinař (19) 61235 wine grower Vinaři → Food and beverage tasters
mlékař (14) 41030 dairy retailer Mlékaři → Dairy products makers
kominík (164) 55240 chimney sweep Kominíci → Building structure cleaners

kominík is the one to read carefully: the match is correct, and it scores as a disagreement only because ISCO's 4-digit parent label is broader than the Czech one. That is a granularity artefact of the bridge, not a coding error — and a reminder that on 55 % of records this check is comparing a specific 1914 trade against a modern aggregate. skladník and vinař, by contrast, are real drift and worth knowing about; neither overturns the 1914 reading.

What the matcher had to get right first

The first run produced 22 disagreements, and reading them showed most were matcher artefacts, not findings — the sixth appearance of this repo's signature bug. sedlák (farmer) was matching Sedláři (saddlers); lesník matched lesnictví (forestry, the field); hospodář matched Hospodyně (housekeeper); obchodník matched obchodních, an adjective. All share a stem and none is the same word.

Two guards, both necessary:

  • Inflection, not derivation. The head may be at most two characters longer, and must be the key plus an ending or one of two regular Czech stem alternations — the k/h/ch → c/z/s palatalisation before the plural -i (sedlák → sedláci) and the soft -e → -i. sedlák → sedláři fails: ři is not a palatalised plural.
  • Head-noun position. Within each coordinate phrase, leading adjectives are stripped and the first remaining token is the head. So student does not match Řídící pracovníci ve studentských ubytovnách (head pracovníci → Hotel managers), and obchodník does not match Obchodní náměstci (head náměstci).

A third rule decides which hit wins when several match: a head carrying no qualifying adjective is preferred. That is what puts malíř on Malíři interiérů rather than Umělečtí malíři — both have head malíři, but the qualified one is a different trade. It is the same lesson as akademický malíř in the audit above, arriving from the other direction.

Both build scripts print these cases on every run, so a regression is visible immediately.

The uncoded tail

run_czisco_check.py --tail proposes a reading for 56 strings / 77 records of the 1,813 left uncoded, written to czisco_tail_candidates.csv with empty accept and hisco columns. Nothing is applied: an accepted row is hand-entered into czech_glosses.py like every other code, and the HISCO code is still read off hisco_authority.csv, never off CZ-ISCO. Good candidates include izolatér → Insulation workers, koncipient → Advokátní koncipienti, včelař → Apiarists, tlumočník → Translators, interpreters. Bad ones are in there too — administrátor → Systems administrators — which is why the list is a queue and not a patch.

Limits, stated plainly

  • It is the 2025 classification. rolník — 18 % of the sample and the single most consequential decision in the coding — is absent from CZ-ISCO at every level, as are hajný, myslivec, příručí, soustružník, tkadlec, mlynář, kolář, bednář, cihlář, stolař, topič, holič, havíř, svrškař and hostinský. The words carrying the biggest decisions are exactly the ones it cannot see.
  • It reaches no HISCO code. ILO's ISCO-08 Volume I carries correspondence tables to ISCO-88; HISCO is built on ISCO-68. Chaining two lossy crosswalks across forty years of occupational change, into a scheme designed for 1690–1970 data, would manufacture precision the sources do not carry. Not attempted, deliberately.
  • The tail list is current: regenerated after notebook 02 re-ran on the 2026-08-02 fixes, against the 50/50 build.

Reproducing Check 2

pip install unidecode                       # torch + transformers already present
git clone --depth 1 https://github.com/christianvedels/OccCANINE
python utils/reference_tools/occupations/run_occcanine_check.py --occcanine /path/to/OccCANINE

Model weights come from HuggingFace on first run. On this project's machine that download needs TLS verification relaxed — the intercepting proxy's root CA is rejected by OpenSSL 3 with "Basic Constraints of CA cert not marked critical", the same malformed-CA defect utils/scraping/scraper_vuapraha.py documents for vuapraha.cz. Exporting the Windows trust store does not help: the CA is found and then refused. The script scopes the relaxation to that one download via huggingface_hub's supported set_client_factory hook.

Check 6 — the 1910 census Berufsstellung, on rolník (run, 2026-08-02)

Status: passed. 61110 stands; the farm-worker alternative is refused. utils/reference_tools/occupations/run_census_berufsstellung_check.py.

This is the check the other four could not perform. rolník is 18 % of the sample and the choice between 61110 General Farmer (HISCLASS 8) and 62105 Farm Worker (HISCLASS 12) moves a fifth of the Legion between social classes. The German bridge confirms only that Landwirt → 61110, not that a Czech rolník is a Landwirt; OccCANINE is fed that same gloss; CZ-ISCO does not contain the word; and education does not discriminate — rolník sits at 1.13 on the 0–4 scale and domkář at 1.00, because schooling did not track landholding.

So the test uses a primary source instead. The 1910 Volkszählung records, for every politický okres, the agricultural workforce split by Berufsstellung — Selbständige, Pächter, Angestellte, Arbeiter, Taglöhner, mithelfende Familienangehörige. Those columns are already parsed into census_okres_1910.parquet and were, until now, unused.

Test 1 — share of births, with placebos. Across 144 okresy (2.13 m agricultural workers):

legionnaire measure vs census self-employed share vs census wage-labour share
rolník share of births +0.201 −0.259
nádeník share (placebo) −0.047 +0.041
dělník share (placebo) −0.083 +0.013

Signs as the owner reading predicts, both significant at n = 144; the two placebos are flat, so the design is not picking up okres size. Raw counts are deliberately not reported as a test — both census columns scale with how agricultural an okres is, so log n_rolník correlates with both (β +1.30 vs +1.22) and discriminates nothing.

Test 2 — within-agriculture composition, which removes the confound. Restrict the legionnaire denominator to men already in HISCO major group 6 and ask what fraction are coded own-account (61110/61115) rather than hired (62xxx, 99920); compare against the census's own Selbständige ÷ (Selbständige + Arbeiter + Taglöhner). Both sides are now compositions of the same population, so size and agricultural intensity cancel.

99 okresy, 15,584 farm-occupation legionnaires: Spearman r = +0.402, Pearson +0.394. Where the census says agriculture is more own-account, the legionnaires' farm occupations are more owner-coded — monotonically:

census own-account share legionnaire own-account share
8 most estate-dominated okresy 0.241 0.862
8 most smallholder okresy 0.643 0.931

The level gap is not evidence against the coding, and it is worth understanding. Legionnaire own-account share averages 0.907 against the census's 0.463. Two things:

  1. Assisting family members. Sons and brothers working the family holding are 1910's largest single agricultural category, are neither Selbständige nor wage labour, and are exactly who a 22-year-old volunteer saying rolník is likely to be. Counting them on the own-account side — they work their family's land, not an employer's, for no wage — moves the census mean to 0.633 and closes the gap from +0.444 to +0.273.
  2. Selection. The residual ~27 pp is plausibly real: own-account farming families volunteered for the Legion at higher rates than agricultural wage labour did. That is a substantive finding about who joined, not a coding defect — and it is the selection margin this documentation elsewhere lists as the largest missing piece.

Why this settles the coding question even under the most conservative reading. The alternative code, 62105, is Farm Worker, General — hired agricultural labour. Neither a farm holder nor a farmer's son on the family holding is hired labour. The census places these men on the own-account side of that line whichever way the family-member category is assigned, and the gradient shows the coding tracks real okres-level variation rather than applying one word indiscriminately. HISCLASS 8 is the class for own-account agriculture regardless of whether the man is the holder or the heir working the holding.

Limits, stated plainly. This is an ecological correlation at okres level. It cannot show that any individual rolník was not a labourer, and it does not separate 61110 (holder) from a family-labour reading — only own-account from wage-earning, which is the distinction the class scale turns on. Corroborating usage: Czech lexicography describes rolník as a plnoprávný hospodář na vlastní větší půdě living from his own holding's yield, against domkář — house and garden only, living by craft or day-labour — which is the contrast the dictionary already encodes (61110/class 8 against 61115/class 10). Cite Naše řeč or Ottův slovník naučný for that, not a search summary.

Check 7 — the Vienna Database household listings, on rolník (run, 2026-08-05)

Status: passed, as corroboration. 61110 stands, and the ecological limit Check 6 declared is narrowed — not removed. utils/reference_tools/occupations/run_mosaic_beruf_check.py.

Check 6 settled rolník against the 1910 Volkszählung and stated its own limit in the same breath: the test is ecological, so it cannot clear an individual record, and it separates own-account from wage-earning but not the holder from the farmer's son. This check goes at exactly that limit, because it is the only source here that is individual-level.

The Vienna Database on European Family History (source #14; the unharmonized Austrian numeric file, distributed by MOSAIC) records 163,400 individuals in 44 localities, 1632–1947, from census data and libri status animarum. It was built between 1974 and 1984 at the University of Vienna with the MPI for History in Göttingen and the University of Salzburg — which matters here, because it means the occupational coding was done by historical demographers working from the listings themselves, decades before and entirely independently of anything in this project.

Every person carries an occupation drawn from a closed list of 521 German nominal titles and, independently, a household position — Haushaltsvorstand, Hausfrau, Sohn, Gesindeperson. That pairing is what no other source in this project has: it shows, person by person, what the German occupational vocabulary this project's own glosses are written in actually denoted.

Test 1 — the vocabulary is split at source. The owner-side and hired-side terms are different entries on the same closed list, and fall in different berufs systematic classes:

nominal title N systematic class
BAUER 4,878 BAUERN
HAEUSLER,HALBBAUER 903 HAEUSLER
GRUNDBESITZER 217 GRUNDBESITZER
LANDARBEITER/KNECHT 5,086 LANDARBEITER
MAGD 9,213 KNECHTE UND MAEGDE
TAGLOEHNER IN DER STADT 761 TAGLOEHNER, HILFSARBEITER

A recording tradition that kept a separate word and a separate class for the hired hand was not reaching for BAUER to mean one. Note also that a bare LANDWIRT is not on the list at all — which matters for the limits below.

Test 2 — household position, person by person. This is the fact Check 6 could not produce. Row %, over persons with a position recorded:

nominal title N Gesindeperson Haushaltsvorstand Hausfrau Sohn
BAUER 4,876 0.3 87.2 11.4 0.4
HAEUSLER,HALBBAUER 903 0.0 95.5 3.3 0.7
GRUNDBESITZER 217 0.0 75.6 14.7 1.4
LANDARBEITER/KNECHT 5,086 94.9 0.3 0.0 0.7
MAGD 9,213 91.8 0.0 0.3 0.0

Test 3 — males 18–30, the Legion age band, which is where the farmer's-son objection lives.

nominal title N Gesindeperson Haushaltsvorstand Sohn
BAUER 589 1.0 97.5 0.3
HAEUSLER,HALBBAUER 83 0.0 97.6 2.4
LANDARBEITER/KNECHT 2,120 94.6 0.2 0.8

Of 589 men of Legion age recorded BAUER, 0.3 % are listed as a son. A farmer's son on these listings is written down as SOHN — the occupational title goes to the holder. That is the half of Check 6's blind spot the census could not reach: the word, applied to a man of enlistment age, names a holder rather than an heir working the family land.

Test 4 — the keys this reaches. The same table speaks to four curation keys at once, not one — domkář → 61115 and nádeník → 99920 are corroborated by the existence and distinct position of HÄUSLER/HALBBAUER and the two TAGLÖHNER terms, and čeledín → 62120 by KNECHT. Together the keys carry 21,707 records, 18.3 % of non-empty occupations — essentially all of it rolník (21,461).

Stability. BAUER appears in 32 localities across 49 survey years, KNECHT in 30 across 47, and the split holds in every period thick enough to read (BAUER 0.2–0.3 % Gesinde pre-1800, 1800–1856 and post-1857 alike). The 1857 Vienna census — 34 % of the whole database — contributes only 45 of these records, because it is the urban bulk and farm terms are rural. The result is therefore not an artefact of the single largest listing.

Limits, stated plainly. More severe here than in Check 6, and the order matters.

  1. Household position and occupational title are not independent in a Seelenbeschreibung. One clerk assigned both, following a listing convention in which the holder gets the farm title and everyone else in the household gets a relational one. Part of the near-perfect BAUER/Haushaltsvorstand alignment is that convention. This is why the status line above says corroboration: the check shows what the word meant to the people writing it down, not an independent measurement of who farmed what.
  2. It validates the German term, not the Czech reading — the same gap Checks 1 and 2 carry, and it is wider than it looks. rolník is glossed Landwirt; the term present here is Bauer, which is this project's gloss for sedlák — a key with 6 records. A bare LANDWIRT is absent from the 521-term list. So the bridge from this evidence to rolník runs through a Landwirt ≈ Bauer synonymy that the check assumes rather than shows.
  3. Wrong territory. 44 localities in present-day Austria, none in the Czech lands. Alpine impartible inheritance is not Bohemian estate agriculture, and this check says nothing about Czech landholding — only about a word.
  4. Wrong period. Bulk 1857, with the farm records concentrated in the earlier rural listings; this is the legionnaires' parents' and grandparents' generation.

Taken with Check 6 the two are complementary in the useful way: the census is the right territory, the right decade and the wrong level of aggregation; this is the right level and the wrong territory. Neither alone would settle 18 % of the sample. Both pointing the same way is why D-34 records the limit as narrowed rather than closed.

How certain is each piece? (2026-08-02)

Written in answer to a direct question — do we have the variables we need, and how certain are we of the profession match and the employment status? The two halves have very different answers, and the second is a source limit, not a coding shortfall.

Profession matching — high confidence on 87.7 %, and the doubt is concentrated

records share of non-empty what it rests on
exact — whole normalised string is a curated key 104,293 87.7 % the dictionary entry itself
dual — two curated keys, or a list coded segment by segment 7,039 5.9 % both entries; which is primary is a convention (for a list, the first coded segment)
head — curated head noun, modifier now kept 3,790 3.2 % the head entry + the assumption the modifier does not change the trade
prefix — curated key + uncurated tail 2,018 1.7 % the prefix entry + the same assumption
uncoded 1,809 1.5 % nothing — left <NA>

Build of 2026-09-23 (occupations 1.4.0). A comma or semicolon list — rolník, tovární dělník — is now coded segment by segment instead of on the last segment's head noun (audit 2026-09-22 B4): against the previous build dual +521, head −462, prefix −73. The uncoded share was previously printed against all 132,993 records (1.36 %); against the non-empty base the table uses it is 1.5 %.

External anchoring. OccCANINE agrees on 88.2 % of records at HISCLASS and 96.7 % at the HISCO major group (Check 2); CZ-ISCO agrees on 99.1 % of the 57 % of records it can reach (Check 5); the German link bridge corroborates 39 keys independently.

Where the doubt sits. 172 keys covering 25,312 records are flagged review — 21 % of coded records. That is not evenly spread: rolník alone is 18 % of the sample and is checked by nothing. CZ-ISCO does not contain the word; the German bridge confirms only that Landwirt → 61110, not that a Czech rolník is a Landwirt. The review flag and the occupation_match column are the two handles: any result can be re-run on exact matches only, and any result leaning on rolník should say so.

How much of it actually matters. build_conflict_workbook.py enumerates all 172 with every rival code its label and its class (see below). Only 75 keys have a rival that would move a record's HISCLASS at all — 12,019 records, 10 % of the coded sample. On the other 97, including zámečník, every candidate sits on the same rung: the argument is about the five-digit code and nothing downstream can see it.

What 1.2.0 changed here. The head and prefix tiers — the two that rest on an assumption rather than on an entry — fell from 8.3 % to 5.3 % of records, because the high-volume compounds that motivated the worry are now curated whole. And the assumption those tiers make is no longer silent: the modifier is in occupation_qualifier for every record, so "does the qualifier change the trade?" is now a question the data can be asked rather than one the parser answers by discarding the evidence.

Employment status — the variable barely exists, and cannot be made to

occupation_status (HISCO's own slot) is populated on 3,236 records once students are excluded — 2.8 % of coded records:

status records
41 Student 3,098
33 Subordinate 2,563
21 Master 302
23 Apprentice 179
42 Graduate 160
11 Owner 32
−9 none 110,785

This is not fixable by more curation. csol.cz records a job title, not a labour-force return: for ~97 % of men the source simply never states whether he owned the workshop, worked in one, or was learning the trade. No dictionary can recover what was not written down.

The ceiling on extending it was measured rather than guessed. A naive token scan suggests 17,301 records (14.5 %) carry a stage word — but 16,224 of those are dělník, zřízenec or příručí standing alone, which are occupations in their own right, not stages of a trade; counting them as "status 33" would double-code the largest categories in the sample. Where a stage token genuinely modifies a curated trade it is 982 records over 397 strings.

occupation_career_stage (D-31) takes the honest version of this: read the ladder off the title wherever the title states it, including where the stage word is the job, and stop. 3,502 records, 2.9 % — assistant 2,824 · master 359 · apprentice 153 · senior 123 · owner 37. Better than 2.8 % and uniform, but not a stratification axis, and it should not be used as one.

So: do we have the variables we need?

For the class gradient, yes. For anything finer, the honest register is:

  • Have, and well anchored — HISCO code, HISCLASS, HISCLASS-5, major group, the match tier, the dual second occupation.
  • Have, new, and worth using — occupation_qualifier (11.4 % of non-empty, and it separates within HISCO code where HISCLASS does not: 3.38 % vs 6.06 % desertion for strojní zámečník against bare zámečník in Russia).
  • Have, but thin — do not stratify on it — occupation_career_stage (2.9 %), occupation_status (2.8 %).
  • Do not have — an occupational denominator. Desertion rates by class are measurable; whether a class was over- or under-represented among volunteers relative to its share of the district workforce is not, without joining the 1910 Volkszählung Beruf columns already sitting parsed in census_okres_1910.parquet. That is a selection margin rather than a desertion margin, and it is the largest genuinely missing piece.

Open

Everything in this section — and the 160-odd smaller flags this section does not name — was enumerated in one place and then answered. python utils/reference_tools/occupations/build_conflict_workbook.py writes output/occupation_conflicts.xlsx: one row per (flagged key × candidate code), each candidate with its English job title and its HISCLASS, a moves column separating the class-changing disputes from the code-only ones, and a dropdown per key. The list below is the argument; the workbook was the ballot.

The 2026-08-26 adjudication (D-41)

All 181 questions went in front of the author; 171 came back decided.

Verdict Keys Records What it means
keep 130 21,031 the code stands and the argument is answered
recoded 41 4,359 the code changed — 23 keys / 1,434 records move HISCLASS
open 10 163 looked at, not settled; the flag stays up with its reason

The answers live in czech_glosses.ADJUDICATED, not in the spreadsheet, and are applied by run_link_bridge.py as the only writer of review. The flag therefore now means unsettled, not unread — it falls from 172 keys / 25.3k records to 10 keys / 163 records, and the next workbook build opens on those ten rather than repeating itself.

The single biggest change is slévač (890) → 72420 Metal Pourer, HISCLASS 7 → 9, on OccCANINE at p=0.99. The most expensive is invisible in the class variable: štukatér (95) → 95530, cementář (55) → 95240 and rukavičkář (44) → 79475 are all sharper five-digit codes that HSN gives no HISCLASS, so 199 curation records — 219 in the built legion frame, where the matcher's diacritic fold reaches spelling variants the key list does not name — trade the class for the code. This is exactly the hazard the third bullet below warned of, now realised and priced rather than avoided.

The ten that stayed open, and why each survived — the flag is now this list and nothing else:

Key n Code today Why it is still open
čsd 59 -2 names ČSD, not a job. 36000 Transport Conductor was floated but asserts a grade the source never gives
zaměstnanec čsd 30 -2 an employee of ČSD, task unnamed — the same question
čsa 19 -2 ČSA is the army or the airline, and neither is an occupation
vlakvedoucí 13 36020 marked wrong against strojvedoucí — Railway Engine-Drivers and Firemen, but a vlakvedoucí is the guard and strojvedoucí is already coded 98320 in its own right. 36020 stands until the two words are separated
dílny čsd v nymburce 10 -2 a workplace, not a trade
politik 9 20110 CZ-ISCO reads the modern policy professional; Legislative Official may still be the right 1918–38 reading
pošta 8 -2 the post office, not a postman
správce 7 21000 steward, estate manager or caretaker — the bare word does not say which
dílny čsd nymburk 4 -2 a workplace, not a trade
pracoval v dílnách čsd v nymburce 4 -2 a workplace, not a trade

Seven of the ten (134 records) are one question: an employer or a workplace written where a job was asked for. The adjudicator's own note is the argument for leaving them — "we could guess a general worker" — and a guess is what -2 exists to refuse. The project already has a documented residual for a railwayman whose task is unnamed (železničář → 36000, D-32); what it does not have is a reason to believe ČSD names a railwayman at all rather than a clerk, a fitter or a stoker in the Nymburk workshops. If that changes, the whole family moves together or none of it does.

This is now a decision, not a queue (D-43, 2026-08-26). Leaving the ten is the choice that keeps the sample unbiased, and the arithmetic says why: 156 of the 163 records sit in the interwar occupation column, which codes 4,295 records in all. The ten are 3.6 % of that variable and the employer family alone 3.1 % — so a guess here would not be a rounding error spread thinly over 133k soldiers, it would be a fabricated occupational profile concentrated in the one measure the interwar-electoral project depends on, and it would look like a finding rather than like missing data. Since D-42 the -2 sentinel carries a code, a label and a major group and no stratification scale, so these men stay visible in the data and absent from every class cut, which is what "not recorded" should look like. The flag stays up. Reopening it takes a source — a ČSD staff schematism, the Nymburk workshop rolls — not another pass over the evidence already here.

Three answers were reconciled before they were applied, and all three are the same kind of error — an answer that is defensible about one word and wrong about the dictionary it sits in.

  • účetní (615) and knihvedoucí (19) were the same row: both at 33110 Bookkeeper, General, both with 11090 Other Accountants as their only rival, both from OccCANINE at p=0.86 — and they came back with opposite answers. Both are glossed Buchhalter. Both go to 33110, the answer the larger word got; 11090 is HISCLASS 2, a chartered profession, and a Bohemian works bookkeeper was not one. Same principle as D-32: the same men must not sit apart under two names.
  • profesor (119) → 13100 University or Higher Education Teacher was accepted and reversed to 13290. In 1910 Czech usage a gymnázium master was styled profesor, so the university reading asserts a post most of these men did not hold. HISCLASS 2 either way, so only the five-digit code was at stake — which is exactly why the cheap reading should be the true one.
  • hospodářský úředník (124) → 31090 Government Executive Officials nec was accepted and redirected to 39310 Office Clerk, General. The class move the verdict wanted (out of 21230 Farm Manager) is kept and goes further, 3 → 5; what is dropped is the sector claim, since an estate official did not work for a government body. 39310 is also what the same adjudication gave the four trainee grades, so the family now travels together.

What the adjudication caught that no check had. D-32 was applied by editing occupation_hisco_curation.csv and not czech_glosses.py, so HISCO_OVERRIDE still held železničář at 99900 while the CSV held 36000. Overrides are written unconditionally, so the next run of run_link_bridge.py would have pulled 47 records back down four HISCLASS rungs with nothing in the output saying so. Fixed in the same pass; the same audit found no other disagreement between the two.

Three things the workbook made visible that this file could not:

  • 44 candidate rows carry two proposers. Where the German bridge and OccCANINE independently reach the same rival code — obchodní příručí → 41025, kameník → 95145, holič → 57025 — that agreement was invisible, because the two coders wrote into separate columns and nothing ever compared them.
  • Nine CZ-ISCO disagreements had never reached a human. (Seven kept, two — politik and správce — still open, 2026-08-26.) run_czisco_check.py writes the czisco_* columns but never the review column, so a disagreement it found was seen only if some other check happened to flag the same key. kominík, vinař, průvodčí, masér, politik, správce, disponent, plavčík and sanitář (241 records) are on their own sheet.
  • Two codes exist in the authority only under a modifier. 88060 Precious Metal Roller appears solely as status 21 (Master) and 95240 Cement Finisher solely as 32/33, so a default-modifier lookup returns no class for either. Both are candidates here (valcíř, cementář); if either is ever accepted, the status has to be curated with it or the record will silently carry no HISCLASS. cementář was accepted (2026-08-26) and the status was not curated with it — the 55 records now carry no class, deliberately. valcíř was kept at its current code, so 88060 never arises.

  • zámečník (2,162) — now three independent sources point away from 83930 Locksmith: German Schlosser links to 84105 Machinery Fitter and 87105 Plumber, OccCANINE returns 87430 (at confidence 0.12, the lowest in its set), and CZ-ISCO splits the modern word three ways with none of them a locksmith — Stavební zámečníci → 7126 Plumbers and pipe fitters, Zámečníci strojů and Provozní zámečníci, údržbáři → 7222 Toolmakers and related workers. 83930 stands (author's call, 2026-08-02): CZ-ISCO is 2025 usage, and 1910 Bohemian zámečník genuinely included lock and key work that has since left the trade — modern drift is not evidence of a historical error. Narrowed on 2026-08-02 (D-29, D-30). The 1,204 strojní zámečník records — the ones every dissenting source was really pointing at — are now curated to 84100 Machinery Fitter in their own right, so the machine-shop reading is in the data rather than contested against the whole 2,162. And HISCAM, the only scale on which the remaining choice moved anything (60 vs 66), is no longer emitted. HISCLASS is 7 under every candidate. What is still open is the 5-digit code on the ~1,900 bare zámečník, which is a smaller and better-posed question than the one this entry was written about.

  • ~~The three disagreements listed under Check 2 where the model looks wrong~~ adjudicated 2026-08-26: all three kept, obchodní příručí (1,740) included. The second opinion that entry asked for is what the workbook was; it is now an answered question rather than an open one, though it remains a judgement, not evidence.
  • The nine compound keys curated in the 2026-08-02 audit have not been through OccCANINE — the run predates them. Six carry independent corroboration from the German link bridge; brusič kamene, malíř porcelánu and hajný do not, and are flagged accordingly.
  • ~~železniční zřízenec~~ settled 2026-08-02, D-32 — see the closing note below.
  • The estate and forest officials — partly overturned 2026-08-26. hospodářský úředník (124) is recoded 21230 → 39310 Office Clerk, General, HISCLASS 3 → 5: an estate official keeps the books, and the family resemblance to adjunkt and šafář was carrying him into management. The four trainee grades go further — hospodářský praktikant (10), hospodářský asistent (10), lesní praktikant (9) and lesní asistent (6) are all now 39310 Office Clerk, General, HISCLASS 3 → 5. lesní úředník (12) is kept at 63220. So OccCANINE's dissent was not simply the D-25 bare-head defect: it was pointing at something real, even though the code it proposed was not the one accepted. Consequence for D-29: 159 of the 178 records that compound curation moved 5 → 3 as "estate and forest officials" go back to 5, and 39310 is what the bare head úředník would have given them anyway — the qualifier still buys the recorded modifier, not a class.
  • ~~skladník (140) and mlékař (14)~~ adjudicated 2026-08-26, and they split. skladník is kept at 39140 Storeroom Clerk — modern drift, as this entry supposed. mlékař is recoded 41030 → 77510 Dairy Product Processor, General, HISCLASS 4 → 9: the word names the trade, not the shop, and CZ-ISCO was right about it. One of two is a poor rate for a guess, which is the argument for asking rather than supposing.

Closed 2026-08-02: hajný → 64960 Trapper and Hunter, HISCLASS 10 — the rung between Forest Supervisor and Forestry Worker, and the code OccCANINE returned independently for Waldheger. Reasoning in OCCUPATION_CODING.md §6.

Closed 2026-08-02 (D-32): železniční zřízenec → 36000 Transport Conductor, Specialisation Unknown, HISCLASS 9, and železničář moved onto the same code from 99900 (HISCLASS 13 → 9). Czech lexicography gives železniční zřízenec as a souhrnný pojem — a collective term covering výpravčí, průvodčí, posunovač and traťový dělník, with železničář and drážník as its modern successors. That resolves the category: the named grades are already coded individually wherever csol.cz states them (výpravčí 22235 class 4, strojvedoucí 98320 class 7, průvodčí 36000 / vlakvedoucí 36020 / výhybkář 98430 all class 9), so the plain-zřízenec records are the unspecified residual and class 9 is its modal value. 36000 replaces 36090 because "Other" asserts a known-but-unlisted specialisation we do not have. Internal evidence agrees: mean education 1.44 against 2.91 for železniční úředník and 1.04 for železniční dělník, 5.7 % reaching NCO against 77.4 % of the clerks, and an education sd of 0.67 with a 9.8 % upper-secondary tail consistent with a mixture. OccCANINE's Railway Clerk reading is refused: the gloss probe shows it returns the same code for Bediensteter, Beamter and Diener alike.

Still open: whether hospodář (108 records) belongs on 61110 with rolník at all — it carries the same code and deserts at 10.19 % against 3.28 %, on 11 deserters. Small enough to be noise, and the same within-code shape the qualifier work (D-29) found elsewhere. Not investigated.

External validation — educationHistory & decisions

The Austrian school statistics of 1908/09 on the education ladder. · source: reference/VALIDATION_EDUCATION.md

In short

Status: run 2026-09-07. One external source, five tests, utils/reference_tools/education/run_unterricht_check.py. Two tests confirm the scheme, one fails it, one is inconclusive-but-informative, one is descriptive. The failure is recorded as D-53 and is not yet fixed — it changes an emitted column and wants a decision, not a patch.

Explain it simply

What a soldier wrote in the schooling box — ob. 5 mě. 3 and the like — only means something if you know what those schools were. Until now that knowledge came from two modern history books. Neither counts schools; one of them starts its figures in 1924, in a different country.

This page checks the scheme against the thing itself: the Austrian government's own annual census of every school it ran, printed in Vienna in 1912 for the year 1908/09, with a table for each kind of school in each province and the language it taught in. It is the register the ladder should have been built on, and it says three things.

It confirms the awkward call at the heart of the variable. The pokračovací school — compulsory evening classes for apprentices — had been treated as a rung above the rest, which would have made an apprentice look better-educated than a grammar-school boy. The government filed it in a category of its own, and it was enormous: 504 of them in Bohemia teaching 50,836 pupils, against nine state trade schools teaching 3,518. Leaving it off the ladder was right.

It breaks something else. The code marks an elementary schooling “complete” at eight years, because the law required eight years of schooling. But the census counts schools by how many year-groups they actually had, and a eight-class elementary school existed ten times in the whole empire and twice in the Czech lands. Nearly four thousand men are recorded as having finished a school that was almost nowhere, and three thousand more who wrote down the ordinary full five years are marked as having dropped out. The column is being left alone until that is decided properly rather than patched.

And it shows that the evening classes are badly under-recorded: in life there were five of those pupils for every full-time trade-school pupil, but in these records there are fewer than one. Men simply did not write down the classes they were obliged to attend. So that flag counts some apprentices, not all of them.

What none of it touches is Hungary. The volume covers only the Austrian half of the monarchy, and about ten thousand men here were schooled in the Hungarian half under different laws. That gap is old, and it stays.

Until this, education was the one major constructed variable with no external check at all: it appeared in VALIDATION.md only under §3, "What none of them reach".


The source

Österreichische Statistik, XCIII. Band, 1. Heft — *Statistik der Unterrichtsanstalten in den im Reichsrate vertretenen Königreichen und Ländern für das Jahr 1908/1909* (k.k. Statistische Zentralkommission, Wien 1912). See DATA_SOURCES.md #21.

Why it is the right source, and why it was not available before:

  • The ladder in education.py was built from two secondary works — Moore (2020) for the general ladder, Lassnigg (2015) for the vocational branch. Neither counts schools. Moore describes the law; Lassnigg's own quantitative series begins in 1924, in the Republic of Austria, on territory this sample does not cover. EDUCATION_LADDER.md §5 nominated this class of source by name as the missing filler.
  • It is the administration's own classification of its own schools, contemporaneous with the years this cohort was at school, tabulated for Böhmen / Mähren / Schlesien and split by Unterrichtssprache — so the benchmark can be restricted to Czech-language institutions rather than to all of Cisleithania.

Held here: a 279-page image-only scan, merged from 15 ÖNB download parts, at library/education/OeStat-1912_Bd-XCIII-1_Statistik-der-Unterrichtsanstalten-1908-09.pdf (gitignored). Folio → PDF-page map in reference/education/unterricht_page_index.csv (build_page_index.py). ⚠ Einleitung X–XI and table page 15 were not downloaded.

Transcribed to three wide CSVs shaped like the printed pages — unterricht_t34_volksschulen.csv, unterricht_t20_gewerbeschulen.csv, unterricht_t19_handelslehranstalten.csv — and gated by check_unterricht_transcription.py (27/27), which is the same guard as check_atlas_parties.py and exists for the same reason: a row-offset in a table read off a scan leaves no grammatical trace. It works because the volume over-determines itself — every table prints the parts and the roll-ups and the column totals, and in Table 34 the eight class columns must also sum across each row.


Check 1 — the ladder against the state's own list of school kinds (run_taxonomy)

Status: passed, with two documented gaps, neither a defect.

21 rungs in INSTITUTIONS, 17 matched to a school kind the volume enumerates.

  • Four rungs the volume does not list: illiterate, no_school, secondary, cadet. Correct — the Kadettenschule is a military establishment outside the k.k. Ministerium für Kultus und Unterricht, illiteracy is a census category rather than an institution, and secondary is the parser's own "unspecified" bucket.
  • Two school kinds with no token: Handwerkerschule and Notschule.
  • Handwerkerschule is folded into trade. The volume counts 13 in the whole empire, 8 of them in Böhmen — the merge costs almost nothing.
  • Notschule (the emergency one-teacher school) has none, and needs none here: 1 in the Czech lands against 379 in Cisleithania, of which Tirol alone has 277. It is an Alpine and Galician phenomenon, not a Bohemian one. ⚠ This is contingent on the sample, not on the parser: a study of Tirolean or Krain births would need the token.

Check 2 — is pok. a rung, or a separate kind of thing? (run_continuation)

Status: passed. The pok. decision is confirmed by the administration's own filing.

p. XX splits the vocational branch into a) Fachschulen für gewerbliche Hauptgruppen, b) Fachschulen für einzelne gewerbliche Zweige, c) Handwerkerschulen, and d) gewerbliche Fortbildungs- und Zeichenschulen — a separate category. p. XVI does the same commercially: 24 höhere Handelsschulen + 115 kommerzielle Tagesschulen + 168 kaufmännische Fortbildungsschulen.

Land full-time Anstalten Fortbild. Anstalten full-time Schüler Fortbild. Schüler Fortbild. % of pupils
Böhmen 102 504 9,672 50,836 84.0
Mähren 27 173 2,645 14,278 84.4
Schlesien 8 47 639 3,956 86.1
Im ganzen 327 1,200 24,089 128,462 84.2

Keeping continuation off the ordinal (INSTITUTIONS ordinal None) agrees with the source. The v1.0.0 reading — pok. as Upper secondary, level with gym. — would have put the largest and least selective school kind in the empire at the top of the ladder.

Check 3 — Table 34 against education_complete (run_klassen) ⚠ FAILS

Status: FAILED. Recorded as D-53; not yet fixed.

Table 34 counts allgemeine Volksschulen by number of ascending classes:

classes 1 2 3 4 5 6 7 8
Cisleithania 6,940 6,433 2,372 1,914 2,624 620 62 10
Böhmen 1,112 1,703 951 562 1,114 97 4 2
Czech lands 2,108 2,639 1,412 783 1,562 191 14 2

education.py scores completion for a bare Volksschule against 8 (education.py, "the standard here is always the full eight years (Moore p. 30)"). An eight-class Volksschule existed ten times in all Cisleithania and twice in the Czech lands, against 1,114 five-class schools in Böhmen alone.

Among the 11,986 Czech-lands-born records with education_top == "elementary" and a stated year:

stated years 1 2 3 4 5 6 7 8 9
complete = False 154 925 1,772 1,377 3,137 590 137 – –
complete = True – – – – – – – 3,890 4
  • 3,894 records are scored complete against a school form that barely existed;
  • 3,137 records reading ob. 5 — the modal full Bohemian Volksschule — are scored incomplete.

The defect is a conflation of units. Moore's eight years are a legal obligation spanning institutions (Volksschule 5 + Bürgerschule 3); Table 34's classes are an institutional form. A bare ob. N in Vzdělání does not say which of the two it records, and education_complete currently resolves that ambiguity by assumption. This is the "rule narrower than its source" shape the project has hit before (ranks 1.4.0, occupations 1.1.0, education 1.0.0).

Note what is not affected: education_ord, education_top, education_track and education_years_elementary are all untouched by this. Only the completion flag moves.

Check 4 — continuation : full-time vocational (run_composition)

Status: inconclusive as a placebo test; informative as a level comparison.

ratio
Records (Czech-lands-born, non-empty Vzdělání, n=86,858) — A:B 0.86
Benchmark, Czech lands (pupils) 5.33
Benchmark, Galizien (placebo) 3.41
Benchmark, Tirol (placebo) 1.32

where A = names a continuation school (8,236), B = names a full-time vocational school (9,549), overlap 796. Built from education_institutions rather than the ordinal, so a record naming both a Gymnasium and a Handelsschule is not silently dropped.

The placebo does not separate, and on inspection it cannot. The benchmark ratio varies across crownlands for reasons unrelated to how well a crownland describes this sample — Tirol's low ratio comes from its many Fachschulen, not from any affinity with Czech births. The placebo is not exchangeable with the treatment, so its ordering carries no information. Recorded so nobody re-runs it expecting one.

What the levels say is worth keeping. The Czech lands had 5.3 continuation pupils per full-time vocational pupil; the records name them at 0.86 to one — roughly 6× fewer — and the gap survives every reasonable reconstruction of the two sets. The likeliest reading is that pok. is under-recorded: the continuation school was compulsory, part-time and unremarkable, and a man setting down his schooling names the trade school he chose rather than the evening classes he was obliged to sit. Part of the gap is also flow-vs-stock and part is selection into the Legion, and this design cannot apportion it.

Consequence for the paper: education_apprentice (6.6% of records) is a lower bound on apprenticeship, not a rate. A specification treating it as an exhaustive category will understate the group.

Check 5 — the Czech-language benchmark, and how selective rung 2 was (run_language)

Status: descriptive; no pass/fail.

Land Bürgerschulen deutsch čecho-slavisch Czech %
Böhmen 608 234 374 61.5
Mähren 223 90 133 59.6
Schlesien 36 31 4 11.1

Cisleithania: 1,266 Bürgerschulen, 511 čecho-slavisch (40.4%). The Bürgerschule was 1,266 of 22,241 public elementary institutions — 5.7%. Rung 2 of the ladder is a selective step, not the ordinary continuation of rung 1, and should be read that way.


Errata in the printed volume

Both were found by the checks, not assumed; each is decided by a second arithmetic path, because "the source is wrong" is otherwise unfalsifiable and would let any bad read pass.

Where What Which side wins, and why
Table 20 (p. XX), Tirol und Vorarlberg Tirol 30 + Vorarlberg 13 gewerbliche Fortbildungsschulen, rolled up to 42; and 52 + 14 Anstalten rolled up to 65 The crownland rows. Every other cell of the same roll-up adds exactly (Lehrkräfte 83+53=136, Schüler 1.839+447=2.286, Zusammen 250+60=310 and 3.237+516=3.753), and the empire total of 1.200 Anstalten agrees with the rows, not the roll-up
Einleitung p. XXXII (prose) "22.341 öffentliche Volksschulen" 22,241. Table 34 sums to 1,266 + 20,975 = 22,241, and the Einleitung's own summary table on p. I also prints 22.241. The prose figure is one hundred too high

The transcription follows the printed page in both cases; the errata are declared in check_unterricht_transcription.py (KNOWN_SOURCE_ERRATA) and reported separately from failures.



Check 6 — hand-coding a stratified sample (score_education_gold.py) — drawn, not yet coded

Status: instrument built 2026-09-07, sample drawn, worksheets empty.

The five checks above test the ladder — whether the rungs are the school kinds the state operated. None of them tests the tokeniser: whether education.py reads out of ob. 5 mě. 3 pok. 3 what a Czech reader reads. No external source can, because the strings are this archive's own shorthand. Only a person can, and this is the instrument for it.

Design. 300 records, disproportionately stratified by risk rather than frequency, split into a calibration half (iterate freely) and a sealed certification half (coded once, reported):

stratum in frame drawn why it is over-sampled
plain 71,622 90 one school, no complication — the bulk
multi_rung 17,341 60 the itinerary and the D-51 tie-break
elem_with_years 14,516 60 the population D-53 is about
imputed_floor 1,112 30 educ_top inferred, not named
unparsed 952 40 non-empty, parser returned nothing
rung0 449 20 small and high-consequence

Three properties that decide whether the number means anything:

  • The worksheet is blind. It carries the raw string and nothing else — not the parser's answer, not even the stratum, since unparsed would tell the coder the machine had already failed. Showing the machine's answer measures assent, not accuracy.
  • The coder names institutions, never a rung. The rung is derived afterwards from their institutions using the parser's own INSTITUTIONS table. So a disagreement is unambiguously about reading; a rung that differs while the institutions agree would be a bug in the ranking code, and the scorer reports that cell separately and says it should be zero.
  • The headline is reweighted to the true stratum sizes. An unweighted mean over a sample that is 13 % unparsed records describes a population that does not exist.

A blank cell counts as not coded, not as agreement; blanks are excluded and reported, because a half-filled worksheet otherwise yields a confident wrong number.

Already visible from the draw, before anyone codes: 952 non-empty strings parse to nothing (0.9 %), and the tail contains real dictionary gaps rather than noise — negr. (negramotný, i.e. illiterate), bez (no_school), lesní ak., řemesl.š., st.tech. 2, tech. 4sem.. The patterns require negram and bez vzděl respectively, so both abbreviations fall through. These are exactly the kind of miss the instrument exists to find systematically rather than anecdotally.

Run:

python utils/reference_tools/education/draw_education_sample.py          # reproducible, seed 1910
python utils/reference_tools/education/score_education_gold.py calibration

Instructions for the coder, in Czech, with the closed token list: education_gold/NAVOD.md.

What these checks are blind to

  • Hungary — the reason D-08 stays open. The volume is "in den im Reichsrate vertretenen Königreichen und Ländern": Cisleithania by definition. Every test above restricts to Czech-lands births and the ~10,500 Slovakia-born records are excluded. Nothing here establishes that a Transleithanian mě. is the Cisleithanian one. The fix is the Eötvös 1868 and Apponyi 1907 school laws and the Magyar statistical series, not another pass over these tables.
  • Flow versus stock. The volume counts pupils enrolled in 1908/09; the field records the schooling of men born mostly 1875–1900, observed years later. No test here treats a benchmark share as an expected sample share, and Check 4 documents what that costs.
  • Institutions versus pupils. Tables 20 and 34 count schools in some columns and pupils in others. A ratio of school counts is not a ratio of people.
  • One year only. 1908/09 is a single cross-section near the end of the cohort's schooling. The Volksschule class distribution in particular was changing (Table 34 is a stock at end-1909), so it understates how few eight-class schools existed when the older birth cohorts were at school — which if anything strengthens Check 3.
  • Attendance is still not reached at the individual level. Table 39 defines a Schulbesuchserleichterung — schools where "die Kinder ganzer Ortschaften oder Weiler die Schule wöchentlich nur ein bis dreimal besuchen" — and counts them, but by Land, not by Bezirk. It narrows EDUCATION_LADDER.md §5's attendance-vs-enrolment caveat; it does not close it, and gives no okres covariate.
  • The two missing pages. Einleitung X–XI (Gymnasium detail) and table page 15 were not downloaded. Nothing above depends on them.
  • The tokeniser, until Check 6 is actually coded. The sample is drawn and the scorer works, but the worksheets are empty. Everything above tests the ladder, not the reading of the strings.
  • The transcription is three tables, not forty-one. Tables 21–33 and 35–41 are in the scan and unread. Table 20's per-institution lists (pp. 114–175) carry a Standort for every school and are geocodable to 1910 okres through settlements_1910 — the route to an okres-level schooling covariate, and a second-wave project, not this one.